They are prefixed with ``PY_``.
-.. function:: py.PY_call(callable[, args][, kwargs])
-
- Call an arbitrary python-level callable from javascript.
-
- :param callable: A ``py.js`` callable object (broadly speaking,
- either a class or an object with a ``__call__``
- method)
-
- :param args: javascript Array of :class:`py.object`, used as
- positional arguments to ``callable``
-
- :param kwargs: javascript Object mapping names to
- :class:`py.object`, used as named arguments to
- ``callable``
-
- :returns: nothing or :class:`py.object`
-
.. function:: py.PY_parseArgs(arguments, format)
Arguments parser converting from the :ref:`user-defined calling
:param Function fn: the javascript function to wrap
:returns: a callable py.js object
+Object Protocol
+---------------
+
+.. function:: py.PY_hasAttr(o, attr_name)
+
+ Returns ``true`` if ``o`` has the attribute ``attr_name``,
+ otherwise returns ``false``. Equivalent to Python's ``hasattr(o,
+ attr_name)``
+
+ :param o: A :class:`py.object`
+ :param attr_name: a javascript ``String``
+ :rtype: ``Boolean``
+
+.. function:: py.PY_getAttr(o, attr_name)
+
+ Retrieve an attribute ``attr_name`` from the object ``o``. Returns
+ the attribute value on success, raises ``AttributeError`` on
+ failure. Equivalent to the python expression ``o.attr_name``.
+
+ :param o: A :class:`py.object`
+ :param attr_name: a javascript ``String``
+ :returns: A :class:`py.object`
+ :raises: ``AttributeError``
+
+.. function:: py.PY_str(o)
+
+ Computes a string representation of ``o``, returns the string
+ representation. Equivalent to ``str(o)``
+
+ :param o: A :class:`py.object`
+ :returns: :class:`py.str`
+
+.. function:: py.PY_isInstance(inst, cls)
+
+ Returns ``true`` if ``inst`` is an instance of ``cls``, ``false``
+ otherwise.
+
+.. function:: py.PY_isSubclass(derived, cls)
+
+ Returns ``true`` if ``derived`` is ``cls`` or a subclass thereof.
+
+.. function:: py.PY_call(callable[, args][, kwargs])
+
+ Call an arbitrary python-level callable from javascript.
+
+ :param callable: A ``py.js`` callable object (broadly speaking,
+ either a class or an object with a ``__call__``
+ method)
+
+ :param args: javascript Array of :class:`py.object`, used as
+ positional arguments to ``callable``
+
+ :param kwargs: javascript Object mapping names to
+ :class:`py.object`, used as named arguments to
+ ``callable``
+
+ :returns: nothing or :class:`py.object`
+
+.. function:: py.PY_isTrue(o)
+
+ Returns ``true`` if the object is considered truthy, ``false``
+ otherwise. Equivalent to ``bool(o)``.
+
+ :param o: A :class:`py.object`
+ :rtype: Boolean
+
+.. function:: py.PY_not(o)
+
+ Inverse of :func:`py.PY_isTrue`.
+
+.. function:: py.PY_size(o)
+
+ If ``o`` is a sequence or mapping, returns its length. Otherwise,
+ raises ``TypeError``.
+
+ :param o: A :class:`py.object`
+ :returns: ``Number``
+ :raises: ``TypeError`` if the object doesn't have a length
+
+Number Protocol
+---------------
+
+.. function:: py.PY_add(o1, o2)
+
+ Returns the result of adding ``o1`` and ``o2``, equivalent to
+ ``o1 + o2``.
+
+ :param o1: :class:`py.object`
+ :param o2: :class:`py.object`
+ :returns: :class:`py.object`
+
+.. function:: py.PY_subtract(o1, o2)
+
+ Returns the result of subtracting ``o2`` from ``o1``, equivalent
+ to ``o1 - o2``.
+
+ :param o1: :class:`py.object`
+ :param o2: :class:`py.object`
+ :returns: :class:`py.object`
+
+.. function:: py.PY_multiply(o1, o2)
+
+ Returns the result of multiplying ``o1`` by ``o2``, equivalent to
+ ``o1 * o2``.
+
+ :param o1: :class:`py.object`
+ :param o2: :class:`py.object`
+ :returns: :class:`py.object`
+
+.. function:: py.PY_divide(o1, o2)
+
+ Returns the result of dividing ``o1`` by ``o2``, equivalent to
+ ``o1 / o2``.
+
+ :param o1: :class:`py.object`
+ :param o2: :class:`py.object`
+ :returns: :class:`py.object`
+
+.. function:: py.PY_negative(o)
+
+ Returns the negation of ``o``, equivalent to ``-o``.
+
+ :param o: :class:`py.object`
+ :returns: :class:`py.object`
+
+.. function:: py.PY_positive(o)
+
+ Returns the "positive" of ``o``, equivalent to ``+o``.
+
+ :param o: :class:`py.object`
+ :returns: :class:`py.object`
+
.. [#kwonly] Python 2, which py.js currently implements, does not
support Python-level keyword-only parameters (it can be
done through the C-API), but it seemed neat and easy
var fn = function () {}
fn.prototype = py.object;
- if (py.PY_call(py.isinstance, [val, py.object]) === py.True
- || py.PY_call(py.issubclass, [val, py.object]) === py.True) {
+ if (py.PY_isInstance(val, py.object)
+ || py.PY_isSubclass(val, py.object)) {
return val;
}
return out;
};
- // Builtins
+ py.PY_hasAttr = function (o, attr_name) {
+ try {
+ py.PY_getAttr(o, attr_name);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ };
+ py.PY_getAttr = function (o, attr_name) {
+ return PY_ensurepy(o.__getattribute__(attr_name));
+ };
+ py.PY_str = function (o) {
+ var v = o.__str__();
+ if (py.PY_isInstance(v, py.str)) {
+ return v;
+ }
+ var typename;
+ if (v.__class__) { // py type
+ typename = v.__class__.__name__;
+ } else if(typeof v !== 'object') { // JS primitive
+ typename = typeof v;
+ } else { // JS object
+ typename = v.constructor.name;
+ }
+ throw new Error(
+ 'TypeError: __str__ returned non-string (type '+typename+')');
+ };
+ py.PY_isInstance = function (inst, cls) {
+ var fn = function () {};
+ fn.prototype = cls;
+ return inst instanceof fn;
+ };
+ py.PY_isSubclass = function (derived, cls) {
+ var fn = function () {};
+ fn.prototype = cls;
+ return derived === cls || derived instanceof fn;
+ };
py.PY_call = function (callable, args, kwargs) {
if (!args) {
args = []; kwargs = {};
}
return callable.__call__(args, kwargs);
};
+ py.PY_isTrue = function (o) {
+ var res = o.__nonzero__();
+ if (res === py.True) {
+ return true;
+ }
+ if (res === py.False) {
+ return false;
+ }
+ throw new Error(
+ "TypeError: __nonzero__ should return bool, returned "
+ + res.__class__.__name__);
+ };
+ py.PY_not = function (o) {
+ return !py.PY_isTrue(o);
+ };
+ py.PY_size = function (o) {
+ if (!o.__len__) {
+ throw new Error(
+ "TypeError: object of type '" +
+ o.__class__.__name__ +
+ "' has no len()");
+ }
+ var v = o.__len__();
+ if (typeof v !== 'number') {
+ throw new Error("TypeError: a number is required");
+ }
+ return v;
+ };
+ py.PY_add = function (o1, o2) {
+ return PY_op(o1, o2, '+');
+ };
+ py.PY_subtract = function (o1, o2) {
+ return PY_op(o1, o2, '-');
+ };
+ py.PY_multiply = function (o1, o2) {
+ return PY_op(o1, o2, '*');
+ };
+ py.PY_divide = function (o1, o2) {
+ return PY_op(o1, o2, '/');
+ };
+ py.PY_negative = function (o) {
+ if (!o.__neg__) {
+ throw new Error(
+ "TypeError: bad operand for unary -: '"
+ + o.__class__.__name
+ + "'");
+ }
+ return o.__neg__();
+ };
+ py.PY_positive = function (o) {
+ if (!o.__pos__) {
+ throw new Error(
+ "TypeError: bad operand for unary +: '"
+ + o.__class__.__name
+ + "'");
+ }
+ return o.__pos__();
+ };
+
+ // Builtins
py.type = function type(name, bases, dict) {
var proto;
if (typeof name !== 'string') {
return this.__unicode__();
},
__unicode__: function () {
- // TODO: return python string
- return '<object ' + this.constructor.name + '>';
+ return py.str.fromJSON('<' + this.__class__.__name__ + ' object>');
},
__nonzero__: function () {
return py.True;
// val is a method from the class
return PY_instancemethod.fromJSON(val, this);
}
- return PY_ensurepy(val);
+ return val;
}
if ('__getattr__' in this) {
return this.__getattr__(name);
if (args.value === ph) {
return py.False;
}
- return args.value.__nonzero__() === py.True ? py.True : py.False;
+ return py.PY_isTrue(args.value) ? py.True : py.False;
+ },
+ __str__: function () {
+ return py.str.fromJSON((this === py.True) ? "True" : "False");
},
__nonzero__: function () { return this; },
+ fromJSON: function (val) { return val ? py.True : py.False },
toJSON: function () { return this === py.True; }
});
py.True = py.PY_call(py.bool);
if (value === placeholder) {
this._value = 0; return;
}
- if (py.PY_call(py.isinstance, [value, py.float]) === py.True) {
+ if (py.PYisInstance(value, py.float)) {
this._value = value._value;
}
- if (py.PY_call(py.isinstance, [value, py.object]) === py.True
- && '__float__' in value) {
+ if (py.PY_isInstance(value, py.object) && '__float__' in value) {
var res = value.__float__();
- if (py.PY_call(py.isinstance, [res, py.float]) === py.True) {
+ if (py.PY_isInstance(res, py.float)) {
this._value = res._value;
return;
}
}
throw new Error('TypeError: float() argument must be a string or a number');
},
+ __str__: function () {
+ return py.str.fromJSON(String(this._value));
+ },
__eq__: function (other) {
return this._value === other._value ? py.True : py.False;
},
__lt__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return this._value < other._value ? py.True : py.False;
},
__le__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return this._value <= other._value ? py.True : py.False;
},
__gt__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return this._value > other._value ? py.True : py.False;
},
__ge__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return this._value >= other._value ? py.True : py.False;
},
__add__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return py.float.fromJSON(this._value + other._value);
return py.float.fromJSON(-this._value);
},
__sub__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return py.float.fromJSON(this._value - other._value);
},
__mul__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return py.float.fromJSON(this._value * other._value);
},
__div__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.float]) !== py.True) {
+ if (!py.PY_isInstance(other, py.float)) {
return py.NotImplemented;
}
return py.float.fromJSON(this._value / other._value);
},
fromJSON: function (v) {
if (!(typeof v === 'number')) {
- throw new Error('py.float.fromJSON can only take numbers ');
+ throw new Error('py.float.fromJSON can only take numbers');
}
var instance = py.PY_call(py.float);
instance._value = v;
var args = py.PY_parseArgs(arguments, [['value', placeholder]]);
var s = args.value;
if (s === placeholder) { this._value = ''; return; }
- if (py.PY_call(py.isinstance, [s, py.str]) === py.True) {
- this._value = s._value;
- return;
- }
- var v = s.__str__();
- if (py.PY_call(py.isinstance, [v, py.str]) === py.True) {
- this._value = v._value;
- return;
- }
- throw new Error('TypeError: __str__ returned non-string (type ' +
- v.__class__.__name__ + ')');
+ this._value = py.PY_str(s)._value;
},
__hash__: function () {
return '\1\0\1' + this._value;
},
+ __str__: function () {
+ return this;
+ },
__eq__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) === py.True
+ if (py.PY_isInstance(other, py.str)
&& this._value === other._value) {
return py.True;
}
return py.False;
},
__lt__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) !== py.True) {
+ if (py.PY_not(py.PY_call(py.isinstance, [other, py.str]))) {
return py.NotImplemented;
}
return this._value < other._value ? py.True : py.False;
},
__le__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) !== py.True) {
+ if (!py.PY_isInstance(other, py.str)) {
return py.NotImplemented;
}
return this._value <= other._value ? py.True : py.False;
},
__gt__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) !== py.True) {
+ if (!py.PY_isInstance(other, py.str)) {
return py.NotImplemented;
}
return this._value > other._value ? py.True : py.False;
},
__ge__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) !== py.True) {
+ if (!py.PY_isInstance(other, py.str)) {
return py.NotImplemented;
}
return this._value >= other._value ? py.True : py.False;
},
__add__: function (other) {
- if (py.PY_call(py.isinstance, [other, py.str]) !== py.True) {
+ if (!py.PY_isInstance(other, py.str)) {
return py.NotImplemented;
}
return py.str.fromJSON(this._value + other._value);
var instance = py.PY_call(py.str);
instance._value = s;
return instance;
- };
+ }
throw new Error("str.fromJSON can only take strings");
},
toJSON: function () {
}
});
+ py.len = new py.PY_def.fromJSON(function len() {
+ var args = py.PY_parseArgs(arguments, ['object']);
+ return py.float.fromJSON(py.PY_size(args.object));
+ });
py.isinstance = new py.PY_def.fromJSON(function isinstance() {
var args = py.PY_parseArgs(arguments, ['object', 'class']);
- var fn = function () {};
- fn.prototype = args['class'];
- return (args.object instanceof fn) ? py.True : py.False;
+ return py.PY_isInstance(args.object, args['class'])
+ ? py.True : py.False;
});
py.issubclass = new py.PY_def.fromJSON(function issubclass() {
var args = py.PY_parseArgs(arguments, ['C', 'B']);
- var fn = function () {};
- fn.prototype = args.B;
- if (args.C === args.B || args.C instanceof fn) {
- return py.True;
- }
- return py.False;
+ return py.PY_isSubclass(args.C, args.B)
+ ? py.True : py.False;
});
list: py.list,
dict: py.dict,
+ len: py.len,
isinstance: py.isinstance,
issubclass: py.issubclass,
classmethod: py.classmethod,
}
return py.True;
case 'not':
- return py.evaluate(expr.first, context).__nonzero__() === py.True
- ? py.False
- : py.True;
+ return py.PY_isTrue(py.evaluate(expr.first, context)) ? py.False : py.True;
case 'and':
var and_first = py.evaluate(expr.first, context);
if (and_first.__nonzero__() === py.True) {
if (expr.second.id !== '(name)') {
throw new Error('SyntaxError: ' + expr);
}
- return py.evaluate(expr.first, context)
- .__getattribute__(expr.second.value);
+ return py.PY_getAttr(py.evaluate(expr.first, context),
+ expr.second.value);
// numerical operators
case '~':
return (py.evaluate(expr.first, context)).__invert__();
case '+':
if (!expr.second) {
- return (py.evaluate(expr.first, context)).__pos__();
+ return py.PY_positive(py.evaluate(expr.first, context));
}
case '-':
if (!expr.second) {
- return (py.evaluate(expr.first, context)).__neg__();
+ return py.PY_negative(py.evaluate(expr.first, context));
}
case '*': case '/': case '//':
case '%':