[FIX] update py.js to correctly handle comparisons between strings and booleans
bzr revid: xmo@openerp.com-20120229085250-ihg2vgjrpl2eyd0i
This commit is contained in:
parent
3bfff5dd18
commit
27ca901a92
|
@ -1,4 +1,5 @@
|
|||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: a2f78eecb94af8e38839e47b3a5b2ca21f1ed6c3
|
||||
node: 0274b3792917c9eba7b096dc2e8f74a508ddaee6
|
||||
branch: default
|
||||
tag: 0.4
|
||||
latesttag: 0.4
|
||||
latesttagdistance: 4
|
||||
|
|
|
@ -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
|
||||
===
|
||||
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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 () {
|
||||
|
|
Loading…
Reference in New Issue