Curious behavior with multiple contexts.

25 views
Skip to first unread message

mcot

unread,
Dec 14, 2010, 3:02:25 PM12/14/10
to PyV8
I used a __defineGetter__ from JavaScript land and found the following
behavior:

import PyV8

class Global1(PyV8.JSClass):

def __init__(self, testval):

self.testval = testval

contextA = None
contextB = None

with PyV8.JSContext(Global1("Test Value A")) as ctx:

contextA = ctx
print ctx.locals.testval
ctx.eval("""
this.__defineGetter__("test", function() {
return "Test A";
});
""")
print ctx.locals.test

with PyV8.JSContext(Global1("Test Value B")) as ctx:

contextB = ctx
print ctx.locals.testval
ctx.eval("""
this.__defineGetter__("test", function() {
return "Test B";
});
""")
print ctx.locals.test

with contextA:
print contextA.locals.testval
print contextA.locals.test

with contextB:
print contextB.locals.testval
print contextB.locals.test



My output is:

Test Value A
Test A
Test Value B
Test B
Test Value A
Test B
Test Value B
Test B

I would expect the third to last value to be: TestA

mcot

unread,
Dec 14, 2010, 5:01:51 PM12/14/10
to PyV8
So I have another example which shows this better I think.

import PyV8

class Global1(PyV8.JSClass):
def __init__(self, testval):
self.testval = testval
self.global1 = self
self.__defineGetter__("test", self.getTest)

def getTest(self, val):
print "getTest called: %s %s" % (self, val)
print "self.testval: %s" % self.testval
print "val.testval: %s" % val.testval
return self.testval

g1 = Global1("Test Value A")
g2 = Global1("Test Value B")

print "g1: %s" % g1
print "g2: %s" % g2

print "g1.testval: %s" % g1.testval
print "g2.testval: %s" % g2.testval

print "g1.test: %s" % g1.test
print "g2.test: %s" % g2.test


My output is:

g1: <__main__.Global1 object at 0x7fb5475f5090>
g2: <__main__.Global1 object at 0x7fb5475f9d90>
g1.testval: Test Value A
g2.testval: Test Value B
getTest called: <__main__.Global1 object at 0x7fb5475f9d90>
<__main__.Global1 object at 0x7fb5475f5090>
self.testval: Test Value B
val.testval: Test Value A
g1.test: Test Value B
getTest called: <__main__.Global1 object at 0x7fb5475f9d90>
<__main__.Global1 object at 0x7fb5475f9d90>
self.testval: Test Value B
val.testval: Test Value B
g2.test: Test Value B

mcot

unread,
Dec 14, 2010, 5:20:37 PM12/14/10
to PyV8
JSClass.__defineGetter__

"Binds an object's property to a function to be called when
that property is looked up."
if hasattr(type(self), name):
setter = getattr(type(self), name).fset
else:
setter = None

setattr(type(self), name, property(fget=getter, fset=setter))

That looks like it is binding the property to type(self) and not self.

mcot

unread,
Dec 14, 2010, 5:34:12 PM12/14/10
to PyV8
Confirmed that on spidermonkey this is not the behavior:

js> function x() {}
js> y = new x()
[object Object]
js> y.__defineGetter__("test", function() { return "test called" })
js> y.test
test called
js> z = new x()
[object Object]
js> print(z.test)
undefined
js>

Flier Lu

unread,
Dec 15, 2010, 11:01:09 AM12/15/10
to PyV8
I agree, it should be a bug, because __defineGetter__/__defineSetter__
should work on the object instead of type.

https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_with_Objects#Defining_Getters_and_Setters

But, in the python world, property can only binding to type instead of
object, and I must find some way to simulate its behaviors base on
__getattr__/__setattr__

On solution is to use a internal field __properties__ to store getter/
setter, but it must change the fields list, and may introduce new
issue.

What's your suggestion?

class JSClass(object):
__properties__ = {}

def __getattr__(self, name):
if name == 'constructor':
return JSClassConstructor(self.__class__)

if name == 'prototype':
return JSClassPrototype(self.__class__)

prop = self.__dict__.setdefault('__properties__',
{}).get(name, None)

if prop and callable(prop[0]):
return prop[0]()

raise AttributeError(name)

def __setattr__(self, name, value):
prop = self.__dict__.setdefault('__properties__',
{}).get(name, None)

if prop and callable(prop[1]):
return prop[1](value)
else:
return object.__setattr__(self, name, value)

raise AttributeError(name)

def __defineGetter__(self, name, getter):
"Binds an object's property to a function to be called when
that property is looked up."
self.__properties__[name] = (getter,
self.__lookupSetter__(name))

def __lookupGetter__(self, name):
"Return the function bound as a getter to the specified
property."
return self.__properties__.get(name, (None, None))[0]

def __defineSetter__(self, name, setter):
"Binds an object's property to a function to be called when an
attempt is made to set that property."
self.__properties__[name] = (self.__lookupGetter__(name),
setter)

def __lookupSetter__(self, name):
"Return the function bound as a setter to the specified
property."
return self.__properties__.get(name, (None, None))[1]

Flier Lu

unread,
Dec 15, 2010, 11:03:17 AM12/15/10
to PyV8
"Binds an object's property to a function to be called when
that property is looked up."
self.__properties__[name] = (getter,
self.__lookupSetter__(name))

def __lookupGetter__(self, name):
"Return the function bound as a getter to the specified
property."
return self.__properties__.get(name, (None, None))[0]

def __defineSetter__(self, name, setter):
"Binds an object's property to a function to be called when an
attempt is made to set that property."
self.__properties__[name] = (self.__lookupGetter__(name),
setter)

def __lookupSetter__(self, name):
"Return the function bound as a setter to the specified
property."
return self.__properties__.get(name, (None, None))[1]

On Dec 15, 6:34 am, mcot <atm1...@gmail.com> wrote:

mcot

unread,
Dec 15, 2010, 11:57:19 AM12/15/10
to PyV8
I will test that out and see if I can find any problems. I don't
expect adding __properties__ will cause any bugs.

Matt



On Dec 15, 11:03 am, Flier Lu <flier...@gmail.com> wrote:
> I agree, it should be a bug, because __defineGetter__/__defineSetter__
> should work on the object instead of type.
>
> https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Working_wi...

Flier Lu

unread,
Dec 15, 2010, 12:01:17 PM12/15/10
to PyV8
Thanks, I have submitted a issue #64 to trace it

http://code.google.com/p/pyv8/issues/detail?id=64

Flier Lu

unread,
Dec 15, 2010, 12:22:55 PM12/15/10
to PyV8
Please verify the issue with SVN trunk after r310

The current implementation base on a internal __properties__ field,
which was used to store property at the object level, since Python
only support binding property at type level.

There are some hard code in CPythonObject::NamedEnumerator method,
which try to hide the internal field (starts/ends with '__') from 'for
key in object' syntax, that may break some exist code.

On Dec 16, 12:57 am, mcot <atm1...@gmail.com> wrote:

mcot

unread,
Dec 15, 2010, 2:03:52 PM12/15/10
to PyV8
Hey I am getting a build error on r310.

running install
running build
running build_py
running build_ext
building '_PyV8' extension
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -
Wstrict-prototypes -fPIC -DBOOST_PYTHON_STATIC_LIB -DV8_NATIVE_REGEXP -
DENABLE_DISASSEMBLER -DENABLE_LOGGING_AND_PROFILING -
DENABLE_DEBUGGER_SUPPORT -DV8_TARGET_ARCH_X64 -I../v8/include -I../v8 -
I../v8/src -I/usr/lib/ -I/usr/include/python2.6 -c src/Exception.cpp -
o build/temp.linux-x86_64-2.6/src/Exception.o -Wno-write-strings
cc1plus: warning: command line option "-Wstrict-prototypes" is valid
for Ada/C/ObjC but not for C++
src/Exception.cpp: In member function ‘void
CJavascriptStackTrace::Dump(std::ostream&) const’:
src/Exception.cpp:149: warning: comparison between signed and unsigned
integer expressions
src/Exception.cpp: In static member function ‘static void
CJavascriptException::ThrowIf(v8::TryCatch&)’:
src/Exception.cpp:365: error: ‘_countof’ was not declared in this
scope
src/Exception.cpp:367: error: ‘strnicmp’ was not declared in this
scope
src/Exception.cpp: In static member function ‘static void
ExceptionTranslator::Construct(PyObject*,
boost::python::converter::rvalue_from_python_stage1_data*)’:
src/Exception.cpp:454: warning: unused variable ‘cpp_err’
error: command 'gcc' failed with exit status 1

mcot

unread,
Dec 15, 2010, 2:54:47 PM12/15/10
to PyV8
I am also getting an error:

gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -
Wstrict-prototypes -fPIC -DBOOST_PYTHON_STATIC_LIB -DV8_NATIVE_REGEXP -
DENABLE_DISASSEMBLER -DENABLE_LOGGING_AND_PROFILING -
DENABLE_DEBUGGER_SUPPORT -DV8_TARGET_ARCH_X64 -I../v8/include -I../v8 -
I../v8/src -I/usr/lib/ -I/usr/include/python2.6 -c src/Engine.cpp -o
build/temp.linux-x86_64-2.6/src/Engine.o -Wno-write-strings
cc1plus: warning: command line option "-Wstrict-prototypes" is valid
for Ada/C/ObjC but not for C++
In file included from src/Engine.cpp:42:
src/AST.h: In member function ‘boost::python::api::object
CAstScope::GetReceiver() const’:
src/AST.h:736: error: no matching function for call to
‘to_python(CAstVariableProxy)’
src/AST.h:23: note: candidates are: boost::python::api::object
to_python(T&) [with T = CAstVariableProxy]
src/AST.h:37: note: boost::python::api::object
to_python(v8::internal::Handle<v8::internal::String>)
src/AST.h: In member function ‘boost::python::api::object
CAstScope::GetFunction() const’:
src/AST.h:737: error: no matching function for call to
‘to_python(CAstVariable)’
src/AST.h:23: note: candidates are: boost::python::api::object
to_python(T&) [with T = CAstVariable]
src/AST.h:37: note: boost::python::api::object
to_python(v8::internal::Handle<v8::internal::String>)
src/AST.h: In member function ‘boost::python::api::object
CAstScope::GetArguments() const’:
src/AST.h:739: error: no matching function for call to
‘to_python(CAstVariable)’
src/AST.h:23: note: candidates are: boost::python::api::object
to_python(T&) [with T = CAstVariable]
src/AST.h:37: note: boost::python::api::object
to_python(v8::internal::Handle<v8::internal::String>)
src/AST.h: In member function ‘boost::python::api::object
CAstDeclaration::GetFunction() const’:
src/AST.h:745: error: no matching function for call to
‘to_python(CAstFunctionLiteral)’
src/AST.h:23: note: candidates are: boost::python::api::object
to_python(T&) [with T = CAstFunctionLiteral]
src/AST.h:37: note: boost::python::api::object
to_python(v8::internal::Handle<v8::internal::String>)
error: command 'gcc' failed with exit status 1


Happening on x64 linux with libboost 1.40

Flier Lu

unread,
Dec 15, 2010, 11:26:25 PM12/15/10
to PyV8
Sorry, please update to the latest SVN trunk, it fixed some GCC issues

Btw, please ensure to build v8 with RTTI and exception supports

http://code.google.com/p/pyv8/wiki/HowToBuild?ts=1292471733&updated=HowToBuild#Runtime_Type_Information_and_C++_Exception_in_v8_3.0
> ...
>
> read more »

mcot

unread,
Dec 16, 2010, 12:40:29 PM12/16/10
to PyV8
Ok now I am seeing the getter called multiple times. I am still
looking into what might be causing this issue but I am going to post
it here to document it at first.

import PyV8

class Global(PyV8.JSClass):
def __init__(self):
self._private = 5
self.globalObj = self

def getPrivate(self):
print "getPrivate Called"
return self._private

def setPrivate(self, private):
print "setPrivate Called"
self._private = private

private = property(getPrivate, setPrivate)

def write(self, val):
print val

with PyV8.JSContext(Global()) as ctx:
ctx.eval("""write(private); private = 10; write(private)""")
ctx.eval("""write(globalObj.private); globalObj.private = 10;
write(globalObj.private)""")

Output:

getPrivate Called
5
10
getPrivate Called
5
getPrivate Called
getPrivate Called
getPrivate Called
getPrivate Called
5


On Dec 15, 11:26 pm, Flier Lu <flier...@gmail.com> wrote:
> Sorry, please update to the latest SVN trunk, it fixed some GCC issues
>
> Btw, please ensure to build v8 with RTTI and exception supports
>
> http://code.google.com/p/pyv8/wiki/HowToBuild?ts=1292471733&updated=H...
> ...
>
> read more »

mcot

unread,
Dec 16, 2010, 1:17:05 PM12/16/10
to PyV8
On further review It appears that the setter is not being called at
all.
> ...
>
> read more »

Flier Lu

unread,
Dec 16, 2010, 9:20:23 PM12/16/10
to PyV8
I think the root cause is pyv8 use PyObject_GetAttrString to check the
attrubites of object, which may invoke __getattr__ and getter of
property. I will try to find other way to do the same task later. :)
> ...
>
> read more »

mcot

unread,
Dec 16, 2010, 11:05:10 PM12/16/10
to PyV8
Thanks. I was using property(getter, setter) just fine on revision
295. I'm not sure at which revision it broke on because I also
updated my v8 to the current trunk. As far as I can tell no changes
to PyV8 since 295 would affect this but maybe. I am still learning
PyV8's code, I don't have much experience with boost-python or v8 api.

Thanks,
Matt
> ...
>
> read more »

mcot

unread,
Dec 17, 2010, 11:06:04 AM12/17/10
to PyV8
Hi. I made some unittests based on what I *think* should be correct
behavior. Please let me know if any of these assertions are wrong or
simply cannot be done because of python/js differences. I am using
the getter/setter pattern so it would be helpful to have one method
supported or at least know the quirks beforehand so I can code around
them.

http://pastebin.com/n3BXfUQV

Thanks,
Matt
> ...
>
> read more »

Flier Lu

unread,
Dec 17, 2010, 11:19:19 AM12/17/10
to py...@googlegroups.com
I have made a fast evaluation, most of failure seems caused by the
multi-level global objects.

In the current implementation, pyv8 use __proto__ attribute to hook
the global object of context, pyv8 act as the 2nd level global object,
it means when the global object doesn't have a attribute or method,
the request will forward to pyv8's global object. This solution is
very easy to implement but may introduce some strange situation, like
the failure cases in your code.

I think I must rewrite it to replace the global object of v8 context,
which may solve the issues in those test cases

Thanks very much for your contribution :)

2010/12/18 mcot <atm...@gmail.com>:

> --
> You received this message because you are subscribed to the Google Groups "PyV8" group.
> To post to this group, send email to py...@googlegroups.com.
> To unsubscribe from this group, send email to pyv8+uns...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/pyv8?hl=en.
>
>

Flier Lu

unread,
Dec 17, 2010, 11:23:28 AM12/17/10
to PyV8
I have made a fast evaluation, most of failure seems caused by the
multi-level global objects.

In the current implementation, pyv8 use __proto__ attribute to hook
the global object of context, pyv8 act as the 2nd level global object,
it means when the global object doesn't have a attribute or method,
the request will forward to pyv8's global object. This solution is
very easy to implement but may introduce some strange situation, like
the failure cases in your code.

I think I must rewrite it to replace the global object of v8 context,
which may solve the issues in those test cases

Thanks very much for your contribution :)

> ...
>
> 阅读更多 »
Reply all
Reply to author
Forward
0 new messages