Interoperability

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.

Type Conversion

Python and Javascript has different type system and build-in primitives. PyV8 have to guess the conversion by the type of source value.

Python to Javascript

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.

Javascript to Python

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'>

Sequence and Array

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

Mapping and Property

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

Function and Constructor

Exception Translation

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

JSClass

class PyV8.JSClass
hasOwnProperty(name)

Returns a Boolean value indicating whether an object has a property with the specified name.

isPrototypeOf(obj)

Returns a Boolean value indicating whether an object exists in the prototype chain of another object.

toLocaleString()

Returns a value as a string value appropriate to the host environment’s current locale.

toString()

Returns a string representation of an object.

unwatch(prop)

Removes a watchpoint set with the watch method.

valueOf()

Returns the primitive value of the specified object.

watch(prop, handler)

Watches for a property to be assigned a value and runs a function when that occurs.

JSObject

class PyV8.JSObject
__getattr__(name) → object

__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.

__setattr__(name, value) → None

__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.

__delattr__(name) → None

__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.

__hash__() → int

__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.

__members__

Deprecated, use the JSObject.keys() to get a list of an object’s attributes.

__getitem__(key) → object

__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.

__setitem__(key, value) → None

__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.

__delitem__(key) → None

__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.

__contains__(key) → bool

__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.

__nonzero__() → bool

__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.

__eq__(other) → bool

__eq__( (JSObject)arg1, (JSObject)arg2) -> bool :

C++ signature :
bool __eq__(class CJavascriptObject {lvalue},class boost::shared_ptr<class CJavascriptObject>)

See also

object.__eq__()

__ne__(other) → bool

__ne__( (JSObject)arg1, (JSObject)arg2) -> bool :

C++ signature :
bool __ne__(class CJavascriptObject {lvalue},class boost::shared_ptr<class CJavascriptObject>)

See also

object.__ne__()

__int__() → int

__int__( (JSObject)arg1) -> object :

C++ signature :
struct _object * __int__(class CJavascriptObject {lvalue})

See also

object.__int__()

__float__() → float

__float__( (JSObject)arg1) -> object :

C++ signature :
struct _object * __float__(class CJavascriptObject {lvalue})
__str__() → str

__str__( (JSObject)arg1) -> object :

C++ signature :
struct _object * __str__(class CJavascriptObject {lvalue})

See also

object.__str__()

clone()
clone( (JSObject)arg1) -> JSObject :

Clone the object.

C++ signature :
class boost::shared_ptr<class CJavascriptObject> clone(class CJavascriptObject {lvalue})
keys()
keys( (JSObject)arg1) -> list :

Get a list of an object¡¯s attributes.

C++ signature :
class boost::python::list keys(class CJavascriptObject {lvalue})

JSArray

class PyV8.JSArray
__init__(size or items) → JSArray object

__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:
  • size (int) – The array size
  • items (list) – The item list
__len__() → int

__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

object.__len__()

__getitem__(key) → object

__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.

__setitem__(key, value) → None

__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.

__delitem__(key) → None

__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.

__iter__() → iterator

__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().

__contains__(item) → bool

__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.

clone()
clone( (JSObject)arg1) -> JSObject :

Clone the object.

C++ signature :
class boost::shared_ptr<class CJavascriptObject> clone(class CJavascriptObject {lvalue})
keys()
keys( (JSObject)arg1) -> list :

Get a list of an object¡¯s attributes.

C++ signature :
class boost::python::list keys(class CJavascriptObject {lvalue})

JSFunction

class PyV8.JSFunction
__call__(*args, **kwds) → object

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:
  • args (list) – the argument list
  • kwds (dict) –

    the argument dictionary whose keys are strings

apply(self, args=[], kwds={}) → object
apply( (JSFunction)arg1, (JSObject)self [, (list)args=[] [, (dict)kwds={}]]) -> object :

Performs a function call using the parameters.

C++ signature :
class boost::python::api::object apply(class CJavascriptFunction {lvalue},class boost::shared_ptr<class CJavascriptObject> [,class boost::python::list=[] [,class boost::python::dict={}]])

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:
  • self (JSObject) – The JSObject instance as self
  • args (list) – the argument list
  • kwds (dict) – the argument dictionary whose keys are strings
apply()
apply( (JSFunction)arg1, (JSObject)self [, (list)args=[] [, (dict)kwds={}]]) -> object :

Performs a function call using the parameters.

C++ signature :
class boost::python::api::object apply(class CJavascriptFunction {lvalue},class boost::shared_ptr<class CJavascriptObject> [,class boost::python::list=[] [,class boost::python::dict={}]])
clone()
clone( (JSObject)arg1) -> JSObject :

Clone the object.

C++ signature :
class boost::shared_ptr<class CJavascriptObject> clone(class CJavascriptObject {lvalue})
keys()
keys( (JSObject)arg1) -> list :

Get a list of an object¡¯s attributes.

C++ signature :
class boost::python::list keys(class CJavascriptObject {lvalue})
coloff

The column offset of function in the script

linenum

The line number of function in the script

lineoff

The line offset of function in the script

name

The name of function

resname

The resource name of script

JSError

class PyV8.JSError(impl)
__str__() → str

See also

object.__str__()

__unicode__() → unicode
__getattribute__(attr) → object
name -> str

The exception name.

message -> str

The exception message.

scriptName -> str

The script name which throw the exception.

lineNum -> int

The line number of error statement.

startPos -> int

The start position of error statement in the script.

endPos -> int

The end position of error statement in the script.

startCol -> int

The start column of error statement in the script.

endCol -> int

The end column of error statement in the script.

sourceLine -> str

The source line of error statement.

stackTrace -> str

The stack trace of error statement.

print_tb(out=sys.stdout -> file object) → None

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]

Sparse Array

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]

Associative Array

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.