summaryrefslogtreecommitdiff
path: root/addons/web/static/lib/py.js
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-02-29 09:52:50 +0100
committerXavier Morel <xmo@openerp.com>2012-02-29 09:52:50 +0100
commit27ca901a92d1cdec292951b50499915027f34399 (patch)
treeed5d69770e72560c1f2d84b1b98e3eb3475864c4 /addons/web/static/lib/py.js
parent3bfff5dd18e17a0f2b610b9cd123b2942a80b6bc (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.txt5
-rw-r--r--addons/web/static/lib/py.js/README.rst100
-rw-r--r--addons/web/static/lib/py.js/lib/py.js38
-rw-r--r--addons/web/static/lib/py.js/test/test.js30
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 () {