As the most basic and important design principle, the PyV8 user doesn’t need pay additional efforts for interoperability. It means that your Python code could seamless access the property or call the function of Javascript code, or vice versa. PyV8 will do the dirty work under the table, such as Type Conversion, Function and Constructor and Exception Translation.
Python and Javascript has different type system and build-in primitives. PyV8 have to guess the conversion by the type of source value.
When convert Python value to Javascript value, PyV8 will try the following rules first. The remaining unknown Python type will be convert to a plain Javascript object.
Python Type | Python Value | Javascript Type | Javascript Value |
---|---|---|---|
NoneType | None | Object/Null [1] | null |
bool() | True/False | Boolean [2] | true/false |
int() | 123 | Number [3] | 123 |
long() | 123 | Number | 123 |
float() | 3.14 | Number | 3.14 |
str() | ‘test’ | String [4] | ‘test’ |
unicode() | u’test’ | String | ‘test’ |
datetime.datetime | Date [5] | ||
datetime.time | Date | ||
built-in function | Object/Function | ||
function/method | Object/Function | ||
type() | Object/Function |
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> typeof = ctxt.eval("(function type(value) { return typeof value; })")
>>> protoof = ctxt.eval("(function protoof(value) { return Object.prototype.toString.apply(value); })")
>>> protoof(None)
'[object Null]'
>>> typeof(True)
'boolean'
>>> typeof(123)
'number'
>>> typeof(123l)
'number'
>>> typeof(3.14)
'number'
>>> typeof('test')
'string'
>>> typeof(u'test')
'string'
>>> from datetime import *
>>> protoof(datetime.now())
'[object Date]'
>>> protoof(date.today())
'[object Date]'
>>> protoof(time())
'[object Date]'
>>> protoof(abs)
'[object Function]'
>>> def test():
... pass
>>> protoof(test)
'[object Function]'
>>> protoof(int)
'[object Function]'
Note
All the Python function, method and type will be convert to a Javascript function object, because the Python type could be used as a constructor and create a new instance.
When reverse direction of conversion, PyV8 will try the following rules to map Javascript type to Python type.
Javascript Type | Javascript Value | Python Type | Python Value |
---|---|---|---|
Null | null | NoneType | None |
Undefined | undefined | NoneType | None |
Boolean | true/false | bool() | True/False |
String | ‘test’ | str() | ‘test’ |
Number/Int32 | 123 | int() | 123 |
Number | 3.14 | float() | 3.14 |
Date | datetime.datetime | ||
Array [6] | JSArray | ||
Function | JSFunction | ||
Object | JSObject |
Note
Even ECMAScript standard has only one Number type for both integer and float number, Google V8 defined the standalone Integer/Int32/Uint32 class to improve the performance. PyV8 use the internal V8 type system to do the same job.
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> type(ctxt.eval("null"))
<type 'NoneType'>
>>> type(ctxt.eval("undefined"))
<type 'NoneType'>
>>> type(ctxt.eval("true"))
<type 'bool'>
>>> type(ctxt.eval("'test'"))
<type 'str'>
>>> type(ctxt.eval("123"))
<type 'int'>
>>> type(ctxt.eval("3.14"))
<type 'float'>
>>> type(ctxt.eval("new Date()"))
<type 'datetime.datetime'>
>>> type(ctxt.eval("[1, 2, 3]"))
<class '_PyV8.JSArray'>
>>> type(ctxt.eval("(function() {})"))
<class '_PyV8.JSFunction'>
>>> type(ctxt.eval("new Object()"))
<class '_PyV8.JSObject'>
Since Python haven’t build-in Array, it defined a sequence concept, you could access a object as a sequence if it implement the sequence interface.
PyV8 provide the JSArray class which wrap the Javascript Array object, and act as a Python sequence. You could access and modify it just like a normal Python list.
Note
The Javascript Array object support the Sparse Array [7], because it was implemented as a Associative Array [8], just like the dict class in the Python. So, if we add a new item in JSArray with a index value larger than the length, the padding item will always be None.
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> array = ctxt.eval('[1, 2, 3]')
>>> array[1] # via :py:meth:`JSArray.__getitem__`
2
>>> len(array) # via :py:meth:`JSArray.__len__`
3
>>> 2 in array # via :py:meth:`JSArray.__contains__`
True
>>> del array[1] # via :py:meth:`JSArray.__delitem__`
>>> 2 in array
False
>>> array[5] = 3 # via :py:meth:`JSArray.__setitem__`
>>> [i for i in array] # via :py:meth:`JSArray.__iter__`
[1, None, 3, None, None, 3]
On the other hand, your Javascript code could access all the Python sequence types as a array like object.
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> ctxt.locals.array = [1, 2, 3]
>>> ctxt.eval('array[1]')
2
>>> ctxt.eval('delete array[1]')
True
>>> ctxt.locals.array
[1, 3]
>>> ctxt.eval('array[0] = 4')
4
>>> ctxt.locals.array
[4, 3]
>>> ctxt.eval('var sum=0; for (i in array) sum+= array[i]; sum')
7
>>> ctxt.eval('0 in array') # check whether the index is exists
True
>>> ctxt.eval('3 in array') # array contains the value 3 but not the index 3
False
>>> ctxt.locals.len = len
>>> ctxt.eval('len(array)')
2
Note
The Python sequence doesn’t support the Javascript Array properties or methods, such as length etc. You could directly use the Python len() function in the Javascript code.
If you want to pass a real Javascript Array, you could directly create a JSArray instance, pass a Python list() as the parameter to the JSArray.__init__() constructor.
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> ctxt.locals.array = JSArray([1, 2, 3])
>>> ctxt.eval("Object.prototype.toString.apply(array)")
'[object Array]'
>>> ctxt.eval("array.length")
3
Like the sequence types, Python also defined the mapping types, such as dict etc.
You could access the Python mapping with a named index or as a property, PyV8 will access the value base on its type.
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> ctxt.locals.dict = { 'a':1, 'b':2 }
>>> ctxt.eval("dict['a']") # named index
1
>>> ctxt.eval('dict.a') # property
1
>>> ctxt.eval("'a' in dict")
True
>>> ctxt.eval("dict['c'] = 3")
3
>>> ctxt.locals.dict
{'a': 1, 'c': 3, 'b': 2}
From the Python side, all the Javascript object could be access as a mapping. You could get the keys with JSObject.keys(), or check whether a key is exists with JSObject.__contains__().
>>> ctxt = JSContext()
>>> ctxt.enter()
>>> ctxt.eval("var obj = {a:1, b:2};")
>>> ctxt.locals.obj['a'] # via :py:meth:`JSObject.__getitem__`
1
>>> ctxt.locals.obj.a # via :py:meth:`JSObject.__getattr__`
1
>>> 'a' in ctxt.locals.obj # via :py:meth:`JSObject.__contains__`
True
>>> ctxt.locals.obj['c'] = 3 # via :py:meth:`JSObject.__setitem__`
>>> dict([(k, ctxt.locals.obj[k]) for k in ctxt.locals.obj.keys()])
{'a': 1, 'c': 3, 'b': 2}
The Python new-style object [9] support to define a property with getter, setter and deleter. PyV8 will handle it if you build with SUPPORT_PROPERTY enabled (by default) in the Config.h file. The getter, setter or deleter will be call when the Javascript code access the property
class Global(JSClass):
def __init__(self, name):
self._name = name
def getname(self):
return self._name
def setname(self, name):
self._name = name
def delname(self):
self._name = 'deleted'
name = property(getname, setname, delname)
with JSContext(Global('test')) as ctxt:
print ctxt.eval("name") # test
print ctxt.eval("this.name = 'flier';") # flier
print ctxt.eval("name") # flier
print ctxt.eval("delete name") # True
Base on the mentioned design principle, PyV8 will translate the exception between Python and Javascript, you could directly use try...except statement to handle the Javascript exception in the Python code, and vice versa.
with JSContext() as ctxt:
try:
ctxt.eval("throw Error('test');")
except JSError, e:
print e # JSError: Error: test ( @ 1 : 6 ) -> throw Error('test');
print e.name
print e.message
If the Javascript code throw a well known exception, it will be translate to an equivalent Python exception. Other Javascript exceptions will be wrapped as a JSError instance. You could access the JSError properties for more detail.
Javascript Exception | Python Exception |
---|---|
RangeError | IndexError |
ReferenceError | ReferenceError |
SyntaxError | SyntaxError |
TypeError | TypeError |
Error | JSError |
From the Javascript side, you could also use try...catch statement to catch the Python exception.
class Global(JSClass):
def raiseIndexError(self):
return [1, 2, 3][5]
with JSContext(Global()) as ctxt:
ctxt.eval("try { this.raiseIndexError(); } catch (e) { msg = e; }")
print ctxt.locals.msg # RangeError: list index out of range
Returns a Boolean value indicating whether an object has a property with the specified name.
Returns a Boolean value indicating whether an object exists in the prototype chain of another object.
Returns a value as a string value appropriate to the host environment’s current locale.
Returns a string representation of an object.
Removes a watchpoint set with the watch method.
Returns the primitive value of the specified object.
Watches for a property to be assigned a value and runs a function when that occurs.
__getattr__( (JSObject)arg1, (str)arg2) -> object :
- C++ signature :
- class boost::python::api::object __getattr__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.
See also
__setattr__( (JSObject)arg1, (str)arg2, (object)arg3) -> None :
- C++ signature :
- void __setattr__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class boost::python::api::object)
Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.
See also
__delattr__( (JSObject)arg1, (str)arg2) -> None :
- C++ signature :
- void __delattr__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
Like __setattr__() but for attribute deletion instead of assignment. This should only be implemented if del obj.name is meaningful for the object.
See also
__hash__( (JSObject)arg1) -> int :
- C++ signature :
- int __hash__(class CJavascriptObject {lvalue})
Called by built-in function hash() and for operations on members of hashed collections including set, frozenset, and dict. __hash__() should return an integer. The only required property is that objects which compare equal have the same hash value; it is advised to somehow mix together (e.g. using exclusive or) the hash values for the components of the object that also play a part in comparison of objects.
See also
Deprecated, use the JSObject.keys() to get a list of an object’s attributes.
See also
__getitem__( (JSObject)arg1, (str)arg2) -> object :
- C++ signature :
- class boost::python::api::object __getitem__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__() method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.
See also
__setitem__( (JSObject)arg1, (str)arg2, (object)arg3) -> None :
- C++ signature :
- void __setitem__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class boost::python::api::object)
Called to implement assignment to self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support changes to the values for keys, or if new keys can be added, or for sequences if elements can be replaced. The same exceptions should be raised for improper key values as for the __getitem__() method.
See also
__delitem__( (JSObject)arg1, (str)arg2) -> None :
- C++ signature :
- void __delitem__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
Called to implement deletion of self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support removal of keys, or for sequences if elements can be removed from the sequence. The same exceptions should be raised for improper key values as for the __getitem__() method.
See also
__contains__( (JSObject)arg1, (str)arg2) -> bool :
- C++ signature :
- bool __contains__(class CJavascriptObject {lvalue},class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)
Called to implement membership test operators. Should return true if item is in self, false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs.
See also
__nonzero__( (JSObject)arg1) -> bool :
- C++ signature :
- bool __nonzero__(class CJavascriptObject {lvalue})
Called to implement truth value testing and the built-in operation bool(); should return False or True, or their integer equivalents 0 or 1. When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __nonzero__(), all its instances are considered true.
See also
__eq__( (JSObject)arg1, (JSObject)arg2) -> bool :
- C++ signature :
- bool __eq__(class CJavascriptObject {lvalue},class boost::shared_ptr<class CJavascriptObject>)
See also
__ne__( (JSObject)arg1, (JSObject)arg2) -> bool :
- C++ signature :
- bool __ne__(class CJavascriptObject {lvalue},class boost::shared_ptr<class CJavascriptObject>)
See also
__int__( (JSObject)arg1) -> object :
- C++ signature :
- struct _object * __int__(class CJavascriptObject {lvalue})
See also
__float__( (JSObject)arg1) -> object :
- C++ signature :
- struct _object * __float__(class CJavascriptObject {lvalue})
See also
__str__( (JSObject)arg1) -> object :
- C++ signature :
- struct _object * __str__(class CJavascriptObject {lvalue})
See also
Clone the object.
Get a list of an object¡¯s attributes.
__init__( (object)arg1, (int)arg2) -> None :
- C++ signature :
- void __init__(struct _object *,unsigned int)
__init__( (object)arg1, (list)arg2) -> None :
- C++ signature :
- void __init__(struct _object *,class boost::python::list)
Parameters: |
---|
__len__( (JSArray)arg1) -> int :
- C++ signature :
- unsigned int __len__(class CJavascriptArray {lvalue})
Called to implement the built-in function len(). Should return the length of the object, an integer >= 0. Also, an object that doesn’t define a __nonzero__() method and whose __len__() method returns zero is considered to be false in a Boolean context.
See also
__getitem__( (JSArray)arg1, (int)arg2) -> object :
- C++ signature :
- class boost::python::api::object __getitem__(class CJavascriptArray {lvalue},unsigned int)
Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__() method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.
See also
__setitem__( (JSArray)arg1, (int)arg2, (object)arg3) -> object :
- C++ signature :
- class boost::python::api::object __setitem__(class CJavascriptArray {lvalue},unsigned int,class boost::python::api::object)
Called to implement assignment to self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support changes to the values for keys, or if new keys can be added, or for sequences if elements can be replaced. The same exceptions should be raised for improper key values as for the __getitem__() method.
See also
__delitem__( (JSArray)arg1, (int)arg2) -> object :
- C++ signature :
- class boost::python::api::object __delitem__(class CJavascriptArray {lvalue},unsigned int)
Called to implement deletion of self[key]. Same note as for __getitem__(). This should only be implemented for mappings if the objects support removal of keys, or for sequences if elements can be removed from the sequence. The same exceptions should be raised for improper key values as for the __getitem__() method.
See also
__iter__( (object)arg1) -> object :
- C++ signature :
- struct boost::python::objects::iterator_range<struct boost::python::return_value_policy<struct boost::python::return_by_value,struct boost::python::default_call_policies>,class CJavascriptArray::ArrayIterator> __iter__(struct boost::python::back_reference<class CJavascriptArray &>)
This method is called when an iterator is required for a container. This method should return a new iterator object that can iterate over all the objects in the container. For mappings, it should iterate over the keys of the container, and should also be made available as the method iterkeys().
See also
__contains__( (JSArray)arg1, (object)arg2) -> bool :
- C++ signature :
- bool __contains__(class CJavascriptArray {lvalue},class boost::python::api::object)
Called to implement membership test operators. Should return true if item is in self, false otherwise. For mapping objects, this should consider the keys of the mapping rather than the values or the key-item pairs.
See also
Clone the object.
Get a list of an object¡¯s attributes.
object __call__(tuple args, dict kwds) :
- C++ signature :
- object __call__(tuple args, dict kwds)
Called when the instance is “called” as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
Parameters: |
---|
Performs a function call using the parameters.
The function is called with args as the argument list; the number of arguments is the length of the tuple. If the optional kwds argument is present, it must be a dictionary whose keys are strings. It specifies keyword arguments to be added to the end of the argument list.
Parameters: |
---|
Performs a function call using the parameters.
Clone the object.
Get a list of an object¡¯s attributes.
The column offset of function in the script
The line number of function in the script
The line offset of function in the script
The name of function
The resource name of script
See also
See also
See also
The exception name.
The exception message.
The script name which throw the exception.
The line number of error statement.
The start position of error statement in the script.
The end position of error statement in the script.
The start column of error statement in the script.
The end column of error statement in the script.
The source line of error statement.
The stack trace of error statement.
Print the stack trace of error statement.
Footnotes
[1] | Null Type The type Null has exactly one value, called null. |
[2] | Boolean Type The type Boolean represents a logical entity and consists of exactly two unique values. One is called true and the other is called false. |
[3] | Number Type The type Number is a set of values representing numbers. In ECMAScript, the set of values represents the double-precision 64-bit format IEEE 754 values including the special “Not-a-Number” (NaN) values, positive infinity, and negative infinity. |
[4] | String Type The type String is the set of all string values. A string value is a finite ordered sequence of zero or more 16-bit unsigned integer values. |
[5] | Date Object A Date object contains a number indicating a particular instant in time to within a millisecond. The number may also be NaN, indicating that the Date object does not represent a specific instant of time. |
[6] | Array Object Array objects give special treatment to a certain class of property names. A property name P (in the form of a string value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 232−1. |
[7] |
A sparse array is an array in which most of the elements have the same value (known as the default value—usually 0 or null). The occurence of zero elements in a large array is both computational and storage inconvenient. An array in which there is large number of zero elements is referred to as being sparse. |
[8] |
An associative array (also associative container, map, mapping, dictionary, finite map, table, and in query processing, an index or index file) is an abstract data type composed of a collection of unique keys and a collection of values, where each key is associated with one value (or set of values). The operation of finding the value associated with a key is called a lookup or indexing, and this is the most important operation supported by an associative array. The relationship between a key and its value is sometimes called a mapping or binding. For example, if the value associated with the key “joel” is 1, we say that our array maps “joel” to 1. Associative arrays are very closely related to the mathematical concept of a function with a finite domain. As a consequence, a common and important use of associative arrays is in memoization. |
[9] | new-style class Any class which inherits from object. This includes all built-in types like list() and dict. Only new-style classes can use Python’s newer, versatile features like object.__slots__(), descriptors, properties, and object.__getattribute__(). New-style classes were introduced in Python 2.2 to unify classes and types. A new-style class is neither more nor less than a user-defined type. If x is an instance of a new-style class, then type(x) is typically the same as x.__class__ (although this is not guaranteed - a new-style class instance is permitted to override the value returned for x.__class__). The major motivation for introducing new-style classes is to provide a unified object model with a full meta-model. It also has a number of practical benefits, like the ability to subclass most built-in types, or the introduction of “descriptors”, which enable computed properties. |