diff options
| author | Xavier Morel <xmo@openerp.com> | 2012-02-29 09:52:50 +0100 |
|---|---|---|
| committer | Xavier Morel <xmo@openerp.com> | 2012-02-29 09:52:50 +0100 |
| commit | 27ca901a92d1cdec292951b50499915027f34399 (patch) | |
| tree | ed5d69770e72560c1f2d84b1b98e3eb3475864c4 /addons/web/static/lib/py.js | |
| parent | 3bfff5dd18e17a0f2b610b9cd123b2942a80b6bc (diff) | |
[FIX] update py.js to correctly handle comparisons between strings and booleans
bzr revid: xmo@openerp.com-20120229085250-ihg2vgjrpl2eyd0i
Diffstat (limited to 'addons/web/static/lib/py.js')
| -rw-r--r-- | addons/web/static/lib/py.js/.hg_archival.txt | 5 | ||||
| -rw-r--r-- | addons/web/static/lib/py.js/README.rst | 100 | ||||
| -rw-r--r-- | addons/web/static/lib/py.js/lib/py.js | 38 | ||||
| -rw-r--r-- | addons/web/static/lib/py.js/test/test.js | 30 |
4 files changed, 166 insertions, 7 deletions
diff --git a/addons/web/static/lib/py.js/.hg_archival.txt b/addons/web/static/lib/py.js/.hg_archival.txt index e94f7336462..d78693267b7 100644 --- a/addons/web/static/lib/py.js/.hg_archival.txt +++ b/addons/web/static/lib/py.js/.hg_archival.txt @@ -1,4 +1,5 @@ repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea -node: a2f78eecb94af8e38839e47b3a5b2ca21f1ed6c3 +node: 0274b3792917c9eba7b096dc2e8f74a508ddaee6 branch: default -tag: 0.4 +latesttag: 0.4 +latesttagdistance: 4 diff --git a/addons/web/static/lib/py.js/README.rst b/addons/web/static/lib/py.js/README.rst index ee5406e6b61..a5aaa289a46 100644 --- a/addons/web/static/lib/py.js/README.rst +++ b/addons/web/static/lib/py.js/README.rst @@ -10,6 +10,106 @@ specification document is the `Python 2.7 Expressions spec <http://docs.python.org/reference/expressions.html>`_ (along with the lexical analysis part). +Builtins +-------- + +``py.js`` currently implements the following builtins: + +``type`` + Restricted to creating new types, can't be used to get an object's + type (yet) + +``None`` + +``True`` + +``False`` + +``NotImplemented`` + Returned from rich comparison methods when the comparison is not + implemented for this combination of operands. In ``py.js``, this + is also the default implementation for all rich comparison methods. + +``issubclass`` + +``object`` + +``bool`` + Does not inherit from ``int``, since ``int`` is not currently + implemented. + +``float`` + +``str`` + +``tuple`` + Constructor/coercer is not implemented, only handles literals + +``list`` + Same as tuple (``list`` is currently an alias for ``tuple``) + +``dict`` + Implements just about nothing + +Note that most methods are probably missing from all of these. + +Data model protocols +-------------------- + +``py.js`` currently implements the following protocols (or +sub-protocols) of the `Python 2.7 data model +<http://docs.python.org/reference/datamodel.html>`_: + +Rich comparisons + Roughly complete implementation but for two limits: ``__eq__`` and + ``__ne__`` can't return ``NotImplemented`` (well they can but it's + not going to work right), and the behavior is undefined if a + rich-comparison operation does not return a ``py.bool``. + + Also, a ``NotImplemented`` result does not try the reverse + operation, not sure if it's supposed to. It directly falls back to + comparing type names. + +Boolean conversion + Implementing ``__nonzero__`` should work. + +Customizing attribute access + Protocols for getting and setting attributes (including new-style + extension) fully implemented but for ``__delattr__`` (since + ``del`` is a statement) + +Descriptor protocol + As with attributes, ``__delete__`` is not implemented. + +Callable objects + +Collections Abstract Base Classes + Container is the only implemented ABC protocol (ABCs themselves + are not currently implemented) (well technically Callable and + Hashable are kind-of implemented as well) + +Numeric type emulation + Basically not implemented, the only part of it which is + implemented is the unary ``-`` (because it's used to create + negative floats, they're parsed as a negated positive number) + +Utilities +--------- + +``py.js`` also provides (and exposes) a few utilities for "userland" +implementation: + +``def`` + Wraps a native javascript function into a ``py.js`` function, so + that it can be called from native expressions. + + Does not ensure the return types are type-compatible with + ``py.js`` types. + + When accessing instance methods, ``py.js`` automatically wraps + these in a variant of ``py.def`` automatically, to behave as + Python's (bound) methods. + Why === diff --git a/addons/web/static/lib/py.js/lib/py.js b/addons/web/static/lib/py.js/lib/py.js index 036af1f3d93..35607b5bdaf 100644 --- a/addons/web/static/lib/py.js/lib/py.js +++ b/addons/web/static/lib/py.js/lib/py.js @@ -384,6 +384,7 @@ var py = {}; base = py.object; } proto = constructor.prototype = create(base.prototype); + proto.constructor = constructor; if (dict) { for(var k in dict) { if (!dict.hasOwnProperty(k)) { continue; } @@ -420,6 +421,10 @@ var py = {}; return py.True; } }, + __lt__: function () { return py.NotImplemented; }, + __le__: function () { return py.NotImplemented; }, + __ge__: function () { return py.NotImplemented; }, + __gt__: function () { return py.NotImplemented; }, __str__: function () { return this.__unicode__(); }, @@ -462,11 +467,13 @@ var py = {}; throw new Error(this.constructor.name + ' can not be converted to JSON'); } }); - NoneType = py.type(function () {}, py.object, { + NoneType = py.type(function NoneType() {}, py.object, { __nonzero__: function () { return py.False; }, toJSON: function () { return null; } }); py.None = new NoneType(); + NotImplementedType = py.type(function NotImplementedType(){}); + py.NotImplemented = new NotImplementedType(); var booleans_initialized = false; py.bool = py.type(function bool(value) { // The only actual instance of py.bool should be py.True @@ -492,15 +499,19 @@ var py = {}; return this._value === other._value ? py.True : py.False; }, __lt__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } return this._value < other._value ? py.True : py.False; }, __le__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } return this._value <= other._value ? py.True : py.False; }, __gt__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } return this._value > other._value ? py.True : py.False; }, __ge__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } return this._value >= other._value ? py.True : py.False; }, __neg__: function () { @@ -523,15 +534,19 @@ var py = {}; return py.False; }, __lt__: function (other) { + if (!(other instanceof py.str)) { return py.NotImplemented; } return this._value < other._value ? py.True : py.False; }, __le__: function (other) { + if (!(other instanceof py.str)) { return py.NotImplemented; } return this._value <= other._value ? py.True : py.False; }, __gt__: function (other) { + if (!(other instanceof py.str)) { return py.NotImplemented; } return this._value > other._value ? py.True : py.False; }, __ge__: function (other) { + if (!(other instanceof py.str)) { return py.NotImplemented; } return this._value >= other._value ? py.True : py.False; }, __nonzero__: function () { @@ -608,6 +623,7 @@ var py = {}; None: py.None, True: py.True, False: py.False, + NotImplemented: py.NotImplemented, object: py.object, bool: py.bool, @@ -630,10 +646,22 @@ var py = {}; case 'is': return a === b ? py.True : py.False; case '!=': return a.__ne__(b) case 'is not': return a !== b ? py.True : py.False; - case '<': return a.__lt__(b); - case '<=': return a.__le__(b); - case '>': return a.__gt__(b); - case '>=': return a.__ge__(b); + case '<': + var v = a.__lt__(b); + if (v !== py.NotImplemented) { return v; } + return PY_ensurepy(a.constructor.name < b.constructor.name); + case '<=': + var v = a.__le__(b); + if (v !== py.NotImplemented) { return v; } + return PY_ensurepy(a.constructor.name <= b.constructor.name); + case '>': + var v = a.__gt__(b); + if (v !== py.NotImplemented) { return v; } + return PY_ensurepy(a.constructor.name > b.constructor.name); + case '>=': + var v = a.__ge__(b); + if (v !== py.NotImplemented) { return v; } + return PY_ensurepy(a.constructor.name >= b.constructor.name); case 'in': return b.__contains__(a); case 'not in': diff --git a/addons/web/static/lib/py.js/test/test.js b/addons/web/static/lib/py.js/test/test.js index 2f6d3b34395..cd448e45d79 100644 --- a/addons/web/static/lib/py.js/test/test.js +++ b/addons/web/static/lib/py.js/test/test.js @@ -137,6 +137,36 @@ describe('Comparisons', function () { }); }); + describe('missing eq/neq', function () { + it('should fall back on identity', function () { + var typ = new py.type(function MyType() {}); + expect(py.eval('MyType() == MyType()', {MyType: typ})).to.be(false); + }); + }); + describe('un-comparable types', function () { + it('should default to type-name ordering', function () { + var t1 = new py.type(function Type1() {}); + var t2 = new py.type(function Type2() {}); + expect(py.eval('T1() < T2()', {T1: t1, T2: t2})).to.be(true); + expect(py.eval('T1() > T2()', {T1: t1, T2: t2})).to.be(false); + }); + it('should handle native stuff', function () { + expect(py.eval('None < 42')).to.be(true); + expect(py.eval('42 > None')).to.be(true); + expect(py.eval('None > 42')).to.be(false); + + expect(py.eval('None < False')).to.be(true); + expect(py.eval('None < True')).to.be(true); + expect(py.eval('False > None')).to.be(true); + expect(py.eval('True > None')).to.be(true); + expect(py.eval('None > False')).to.be(false); + expect(py.eval('None > True')).to.be(false); + + expect(py.eval('False < ""')).to.be(true); + expect(py.eval('"" > False')).to.be(true); + expect(py.eval('False > ""')).to.be(false); + }); + }); }); describe('Boolean operators', function () { it('should work', function () { |
