To implement a custom python-level type, one can use the
py.type()
builtin. At the JS-level, it is a function with the
same signature as the type
builtin 1. It returns a
child type of its one base (or py.object
if no base is
provided).
The dict
parameter to py.type()
can contain any
attribute, javascript-level or python-level: the default
__getattribute__
implementation will ensure they are converted to
Python-level attributes if needed. Most methods are also wrapped and
converted to Python-level callable, although there are a number
of special cases:
- Most “magic methods” of the data model (“dunder” methods) remain javascript-level. See the listing of magic methods and their signatures. As a result, they do not respect the Python calling conventions
- The
toJSON
andfromJSON
methods are special-cased to remain javascript-level and don’t follow the Python calling conventions - Functions which have been wrapped explicitly (via
py.PY_def
,py.classmethod
orpy.staticmethod
) are associated to the class untouched. But due to their wrapper, they will use the Python calling conventions anyway
Python-level callable
Wrapped javascript function or the __call__()
method itself
follow the Python calling conventions. As a result, they can’t
(easily) be called directly from javascript code. Because
__new__()
and __init__()
follow from __call__()
,
they also follow the Python calling conventions.
py.PY_call()
should be used when interacting with them from
javascript is necessary.
Because __call__
follows the Python calling conventions,
instantiating a py.js
type from javascript requires using
py.PY_call()
.
Python calling conventions
The python-level arguments should be considered completely opaque,
they should be interacted with through py.PY_parseArgs()
(to
extract python-level arguments to javascript implementation code) and
py.PY_call()
(to call Python-level callable from
javascript code).
A callable following the Python calling conventions must
return a py.js
object, an error will be generated when failing to
do so.
Magic methods
py.js
doesn’t support calling magic (“dunder”) methods of the
datamodel from Python code, and these methods remain javascript-level
(they don’t follow the Python calling conventions).
Here is a list of the understood datamodel methods, refer to the relevant Python documentation for their roles.
Basic customization
__hash__()
__eq__(other)
The default implementation tests for identity
__ne__(other)
The default implementation calls __eq__()
and reverses
its result.
__lt__(other)
The default implementation simply returns
py.NotImplemented
.
__le__(other)
The default implementation simply returns
py.NotImplemented
.
__ge__(other)
The default implementation simply returns
py.NotImplemented
.
__gt__(other)
The default implementation simply returns
py.NotImplemented
.
__str__()
Simply calls __unicode__()
. This method should not be
overridden, __unicode__()
should be overridden instead.
__unicode__()
__nonzero__()
The default implementation always returns py.True
Customizing attribute access
__getattribute__(name)
String
) – name of the attribute, as a javascript string__getattr__(name)
String
) – name of the attribute, as a javascript string__setattr__(name, value)
- name (
String
) – name of the attribute, as a javascript string - value –
py.object
Implementing descriptors
__get__(instance)
Note
readable descriptors don’t currently handle “owner classes”
__set__(instance, value)
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 returnspy.NotImplemented
). They take a singlepy.object
parameter and return a singlepy.object
parameter. Unary operator numeric methods are all supported:
__pos__()
Returns__neg__()
Returns__invert__()
Returns- For non-operator numeric methods, support is contingent on the corresponding builtins being implemented
Emulating container types
__len__()
py.int