[FIX] update py.js to correctly handle comparisons between strings and booleans

bzr revid: xmo@openerp.com-20120229085250-ihg2vgjrpl2eyd0i
This commit is contained in:
Xavier Morel 2012-02-29 09:52:50 +01:00
parent 3bfff5dd18
commit 27ca901a92
4 changed files with 166 additions and 7 deletions

View File

@ -1,4 +1,5 @@
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
node: a2f78eecb94af8e38839e47b3a5b2ca21f1ed6c3
node: 0274b3792917c9eba7b096dc2e8f74a508ddaee6
branch: default
tag: 0.4
latesttag: 0.4
latesttagdistance: 4

View File

@ -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
===

View File

@ -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':

View File

@ -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 () {