masklinn avatar masklinn committed 06ee9b4

Add classmethods, fixes #4; list supported builtins

Comments (0)

Files changed (5)

+.. default-domain: python
+
+.. _builtins:
+
+Supported Python builtins
+=========================
+
+.. function:: py.type(object)
+
+    Gets the class of a provided object, if possible.
+
+    .. note:: currently doesn't work correctly when called on a class
+              object, will return the class itself (also, classes
+              don't currently have a type).
+
+.. js:function:: py.type(name, bases, dict)
+
+    Not exactly a builtin as this form is solely javascript-level
+    (currently). Used to create new ``py.js`` types. See :doc:`types`
+    for its usage.
+
+.. data:: py.None
+
+.. data:: py.True
+
+.. data:: py.False
+
+.. data:: py.NotImplemented
+
+.. class:: py.object
+
+    Base class for all types, even implicitly (if no bases are
+    provided to :js:func:`py.type`)
+
+.. class:: py.bool([object])
+
+.. class:: py.float([object])
+
+.. class:: py.str([object])
+
+.. class:: py.unicode([object])
+
+.. class:: py.tuple()
+
+.. class:: py.list()
+
+.. class:: py.dict()
+
+.. function:: py.isinstance(object, type)
+
+.. function:: py.issubclass(type, other_type)
+
+.. class:: py.classmethod
 .. toctree::
     :maxdepth: 2
 
+    builtins
     types
     utility
     differences
               evaluation
     :rtype: :class:`py.object`
 
-Implementation details
-----------------------
-
 .. _convert-py:
 
 Conversions from Javascript to Python
 To implement a custom python-level type, one can use the
 :func:`py.type` builtin. At the JS-level, it is a function with the
 same signature as the :py:class:`type` builtin [#bases]_. It returns a
-child type of its one base (or :class:`py.object` if no base is
+child type of its one base (or :py:class:`py.object` if no base is
 provided).
 
 The ``dict`` parameter to :func:`py.type` can contain any
   :ref:`types-methods-python-call`
 
 * Functions which have been wrapped explicitly (via
-  :class:`py.PY_def`, :class:`py.classmethod` or
-  :class:`py.staticmethod`) are associated to the class
+  :class:`py.PY_def`, :py:class:`py.classmethod` or
+  :py:class:`py.staticmethod`) are associated to the class
   untouched. But due to their wrapper, they will use the
   :ref:`types-methods-python-call` anyway
 
 Python-level callable
 ---------------------
 
-Wrapped javascript function *or* the :js:func:`__call__` method itself
+Wrapped javascript function *or* the :func:`__call__` method itself
 follow the :ref:`types-methods-python-call`. As a result, they can't
 (easily) be called directly from javascript code. Because
-:js:func:`__new__` and :js:func:`__init__` follow from
-:js:func:`__call__`, they also follow the
-:ref:`types-methods-python-call`.
+:func:`__new__` and :func:`__init__` follow from :func:`__call__`,
+they also follow the :ref:`types-methods-python-call`.
 
 :func:`py.PY_call` should be used when interacting with them from
 javascript is necessary.
 
     The default implementation tests for identity
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 .. function:: __ne__(other)
 
     The default implementation calls :func:`__eq__` and reverses
     its result.
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 .. function:: __lt__(other)
 
     The default implementation simply returns
     :data:`py.NotImplemented`.
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 
 .. function:: __le__(other)
     The default implementation simply returns
     :data:`py.NotImplemented`.
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 
 .. function:: __ge__(other)
     The default implementation simply returns
     :data:`py.NotImplemented`.
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 
 .. function:: __gt__(other)
     The default implementation simply returns
     :data:`py.NotImplemented`.
 
-    :param other: :class:`py.object` to compare this object with
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object` to compare this object with
+    :returns: :py:class:`py.bool`
 
 .. function:: __str__()
 
     Simply calls :func:`__unicode__`. This method should not be
     overridden, :func:`__unicode__` should be overridden instead.
 
-    :returns: :class:`py.str`
+    :returns: :py:class:`py.str`
 
 .. function:: __unicode__()
 
-    :returns: :class:`py.unicode`
+    :returns: :py:class:`py.unicode`
 
 .. function:: __nonzero__()
 
     The default implementation always returns :data:`py.True`
 
-    :returns: :class:`py.bool`
+    :returns: :py:class:`py.bool`
 
 Customizing attribute access
 ++++++++++++++++++++++++++++
 .. function:: __getattribute__(name)
 
     :param String name: name of the attribute, as a javascript string
-    :returns: :class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __getattr__(name)
 
     :param String name: name of the attribute, as a javascript string
-    :returns: :class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __setattr__(name, value)
 
     :param String name: name of the attribute, as a javascript string
-    :param value: :class:`py.object`
+    :param value: :py:class:`py.object`
 
 Implementing descriptors
 ++++++++++++++++++++++++
     .. note:: readable descriptors don't currently handle "owner
               classes"
 
-    :param instance: :class:`py.object`
-    :returns: :class:`py.object`
+    :param instance: :py:class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __set__(instance, value)
 
-    :param instance: :class:`py.object`
-    :param value: :class:`py.object`
+    :param instance: :py:class:`py.object`
+    :param value: :py:class:`py.object`
 
 Emulating Numeric Types
 +++++++++++++++++++++++
 * Non-in-place binary numeric methods (e.g. ``__add__``, ``__mul__``,
   ...) should all be supported including reversed calls (in case the
   primary call is not available or returns
-  :js:data:`py.NotImplemented`). They take a single :class:`py.object`
-  parameter and return a single :class:`py.object` parameter.
+  :py:data:`py.NotImplemented`). They take a single
+  :py:class:`py.object` parameter and return a single
+  :py:class:`py.object` parameter.
 
 * Unary operator numeric methods are all supported:
 
   .. function:: __pos__()
 
-      :returns: :class:`py.object`
+      :returns: :py:class:`py.object`
 
   .. function:: __neg__()
 
-      :returns: :class:`py.object`
+      :returns: :py:class:`py.object`
 
   .. function:: __invert__()
 
-      :returns: :class:`py.object`
+      :returns: :py:class:`py.object`
 
 * For non-operator numeric methods, support is contingent on the
-  corresponding :ref:`builtin` being implemented
+  corresponding :ref:`builtins <builtins>` being implemented
 
 Emulating container types
 +++++++++++++++++++++++++
 
 .. function:: __len__()
 
-    :returns: :class:`py.int`
+    :returns: :py:class:`py.int`
 
 .. function:: __getitem__(name)
 
-    :param name: :class:`py.object`
-    :returns: :class:`py.object`
+    :param name: :py:class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __setitem__(name, value)
 
-    :param name: :class:`py.object`
-    :param value: :class:`py.object`
+    :param name: :py:class:`py.object`
+    :param value: :py:class:`py.object`
 
 .. function:: __iter__()
 
-    :returns: :class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __reversed__()
 
-    :returns: :class:`py.object`
+    :returns: :py:class:`py.object`
 
 .. function:: __contains__(other)
 
-    :param other: :class:`py.object`
-    :returns: :class:`py.bool`
+    :param other: :py:class:`py.object`
+    :returns: :py:class:`py.bool`
 
 .. [#bases] with the limitation that, because :ref:`py.js builds its
             object model on top of javascript's
             return o;
         case Array:
             var a = py.PY_call(py.list);
-            a.values = val;
+            a._values = val;
             return a;
         }
 
         }
     });
     py.tuple = py.type('tuple', null, {
+        __init__: function () {
+            this._values = [];
+        },
         __contains__: function (value) {
-            for(var i=0, len=this.values.length; i<len; ++i) {
-                if (this.values[i].__eq__(value) === py.True) {
+            for(var i=0, len=this._values.length; i<len; ++i) {
+                if (this._values[i].__eq__(value) === py.True) {
                     return py.True;
                 }
             }
             return py.False;
         },
         __getitem__: function (index) {
-            return PY_ensurepy(this.values[index.toJSON()]);
+            return PY_ensurepy(this._values[index.toJSON()]);
         },
         toJSON: function () {
             var out = [];
-            for (var i=0; i<this.values.length; ++i) {
-                out.push(this.values[i].toJSON());
+            for (var i=0; i<this._values.length; ++i) {
+                out.push(this._values[i].toJSON());
             }
             return out;
         }
             return this._func;
         }
     });
+    py.classmethod = py.type('classmethod', null, {
+        __init__: function () {
+            var args = py.PY_parseArgs(arguments, 'function');
+            this._func = args['function'];
+        },
+        __get__: function (obj, type) {
+            return PY_instancemethod.fromJSON(this._func, type);
+        },
+        fromJSON: function (func) {
+            return py.PY_call(py.classmethod, [func]);
+        }
+    });
     var PY_instancemethod = py.type('instancemethod', [py.PY_def], {
         fromJSON: function (nativefunc, instance) {
             var inst = py.PY_call(PY_instancemethod);
         object: py.object,
         bool: py.bool,
         float: py.float,
+        str: py.str,
+        unicode: py.unicode,
         tuple: py.tuple,
         list: py.list,
         dict: py.dict,
+
         isinstance: py.isinstance,
-        issubclass: py.issubclass
+        issubclass: py.issubclass,
+        classmethod: py.classmethod,
     };
 
     py.parse = function (toks) {
                     tuple_exprs[j], context));
             }
             var t = py.PY_call(py.tuple);
-            t.values = tuple_values;
+            t._values = tuple_values;
             return t;
         case '[':
             if (expr.second) {
                     list_exprs[k], context));
             }
             var l = py.PY_call(py.list);
-            l.values = list_values;
+            l._values = list_values;
             return l;
         case '{':
             var dict_exprs = expr.first, dict = py.PY_call(py.dict);

test/builtins/type.js

             }).to.throwException(/^ValueError/);
         });
     });
+    describe('class methods', function () {
+        var typ = py.type('MyType', null, {
+            source: 'class',
+            __init__: function () {
+                this.source = 'instance';
+            },
+            meth: py.classmethod.fromJSON(function () {
+                return py.str.fromJSON(this.source);
+            })
+        });
+        it('should use the class as its context when called on the class', function () {
+            expect(py.eval('MyType.meth()', {MyType: typ}))
+                .to.be('class');
+        });
+        it('should also use the class as its context when called on the instance', function () {
+            expect(py.eval('MyType().meth()', {MyType: typ}))
+                .to.be('class');
+        });
+    });
 });
 describe('Get object type', function () {
     expect(ev('type(True)')).to.be(py.bool);
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.