[UP] py.js, old != operator and basic dict handling

bzr revid: xmo@openerp.com-20120806110638-1m4rg205sb3vjvm5
This commit is contained in:
Xavier Morel 2012-08-06 13:06:38 +02:00
parent 6f4abdfbd5
commit 6a23c24da1
4 changed files with 75 additions and 15 deletions

View File

@ -1,5 +1,5 @@
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
node: 1758bfec1ec1dcff95dcc4c72269cc0e3d000afd
node: 87e977311edbbb5f281b87390a9a304eb194ce89
branch: default
latesttag: 0.5
latesttagdistance: 11
latesttagdistance: 15

View File

@ -60,7 +60,7 @@ Builtins
Same as tuple (``list`` is currently an alias for ``tuple``)
``dict``
Implements just about nothing
Implements trivial getting and setting, nothing beyond that.
Note that most methods are probably missing from all of these.
@ -72,14 +72,14 @@ 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``.
Pretty much complete (including operator fallbacks), although the
behavior is currently undefined if an operation does not return
either a ``py.bool`` or ``NotImplemented``.
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.
``__hash__`` is supported (and used), but it should return **a
javascript string**. ``py.js``'s dict build on javascript objects,
reimplementing numeral hashing is worthless complexity at this
point.
Boolean conversion
Implementing ``__nonzero__`` should work.
@ -93,6 +93,12 @@ Descriptor protocol
As with attributes, ``__delete__`` is not implemented.
Callable objects
Work, although the handling of arguments isn't exactly nailed
down. For now, callables get two (javascript) arguments ``args``
and ``kwargs``, holding (respectively) positional and keyword
arguments.
Conflicts are *not* handled at this point.
Collections Abstract Base Classes
Container is the only implemented ABC protocol (ABCs themselves
@ -119,8 +125,8 @@ implementation:
``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.
these in a variant of ``py.def``, to behave as Python's (bound)
methods.
Why
===

View File

@ -433,7 +433,8 @@ var py = {};
if (this._hash) {
return this._hash;
}
return this._hash = hash_counter++;
// tagged counter, to avoid collisions with e.g. number hashes
return this._hash = '\0\0\0' + String(hash_counter++);
},
__eq__: function (other) {
return (this === other) ? py.True : py.False;
@ -597,6 +598,9 @@ var py = {};
throw new Error('TypeError: __str__ returned non-string (type ' +
v.constructor.name + ')');
}, py.object, {
__hash__: function () {
return '\1\0\1' + this._value;
},
__eq__: function (other) {
if (other instanceof py.str && this._value === other._value) {
return py.True;
@ -654,12 +658,33 @@ var py = {};
}
});
py.list = py.tuple;
py.dict = py.type(function dict() {
py.dict = py.type(function dict(d) {
this._store = {};
for (var k in (d || {})) {
if (!d.hasOwnProperty(k)) { continue; }
var py_k = new py.str(k);
var val = PY_ensurepy(d[k]);
this._store[py_k.__hash__()] = [py_k, val];
}
}, py.object, {
__getitem__: function (key) {
var h = key.__hash__();
if (!(h in this._store)) {
throw new Error("KeyError: '" + key.toJSON() + "'");
}
return this._store[h][1];
},
__setitem__: function (key, value) {
this._store[key.__hash__()] = [key, value];
},
get: function (args) {
var h = args[0].__hash__();
var def = args.length > 1 ? args[1] : py.None;
if (!(h in this._store)) {
return def;
}
return this._store[h][1];
},
toJSON: function () {
var out = {};
for(var k in this._store) {
@ -700,6 +725,7 @@ var py = {};
var PY_operators = {
'==': ['eq', 'eq', function (a, b) { return a === b; }],
'!=': ['ne', 'ne', function (a, b) { return a !== b; }],
'<>': ['ne', 'ne', function (a, b) { return a !== b; }],
'<': ['lt', 'gt', function (a, b) {return a.constructor.name < b.constructor.name;}],
'<=': ['le', 'ge', function (a, b) {return a.constructor.name <= b.constructor.name;}],
'>': ['gt', 'lt', function (a, b) {return a.constructor.name > b.constructor.name;}],
@ -782,7 +808,7 @@ var py = {};
return b.__contains__(a);
case 'not in':
return b.__contains__(a) === py.True ? py.False : py.True;
case '==': case '!=':
case '==': case '!=': case '<>':
case '<': case '<=':
case '>': case '>=':
return PY_op(a, b, operator);

View File

@ -117,6 +117,11 @@ describe('Comparisons', function () {
expect(py.eval('foo != bar', {foo: 'qux', bar: 'quux'}))
.to.be(true);
});
it('should accept deprecated form', function () {
expect(py.eval('1 <> 2')).to.be(true);
expect(py.eval('"foo" <> "foo"')).to.be(false);
expect(py.eval('"foo" <> "bar"')).to.be(true);
});
});
describe('rich comparisons', function () {
it('should work with numbers', function () {
@ -377,6 +382,29 @@ describe('numerical protocols', function () {
});
});
});
describe('dicts', function () {
it('should be possible to retrieve their value', function () {
var d = new py.dict({foo: 3, bar: 4, baz: 5});
expect(py.eval('d["foo"]', {d: d})).to.be(3);
expect(py.eval('d["baz"]', {d: d})).to.be(5);
});
it('should raise KeyError if a key is missing', function () {
var d = new py.dict();
expect(function () {
py.eval('d["foo"]', {d: d});
}).to.throwException(/^KeyError/);
});
it('should have a method to provide a default value', function () {
var d = new py.dict({foo: 3});
expect(py.eval('d.get("foo")', {d: d})).to.be(3);
expect(py.eval('d.get("bar")', {d: d})).to.be(null);
expect(py.eval('d.get("bar", 42)', {d: d})).to.be(42);
var e = new py.dict({foo: null});
expect(py.eval('d.get("foo")', {d: e})).to.be(null);
expect(py.eval('d.get("bar")', {d: e})).to.be(null);
});
});
describe('Type converter', function () {
it('should convert bare objects to objects', function () {
expect(py.eval('foo.bar', {foo: {bar: 3}})).to.be(3);