support for file I/O (JS_EvaluateScript)
Reported by AnC | June 30th, 2009 @ 12:26 PM
It appears python-spidermonkey does not yet support
Spidermonkey's filename argument:
http://stackoverflow.com/questions/1055850/file-i-o-in-spidermonkey...
Apparently, this variant of python-spidermonkey - in contrast to this one - is written almost entirely in C. (Not sure where the difference is between the two variants.)
Since I'm not C-literate, I don't even know where to start to confirm this or even submit a patch.
Any pointers would be welcome.
Comments and changes to this ticket
-
AnC June 30th, 2009 @ 12:45 PM
A grep on the repository reveals occurrences of
JS_EvaluateScript
inspidermonkey/libjs/jsapi.c
- however, there's no hint in the documentation ($ pydoc spidermonkey
) on how that might be exposed via the API. -
Paul J. Davis June 30th, 2009 @ 01:28 PM
- Tag set to execfile
AnC,
I sure don't provide access to functions like that, but do you really need id? Implementing someting like this should be relatively simple:
>>> import spidermonkey >>> def loadfile(fname): ... return open(fname).read() >>> rt = spidermonkey.Runtime() >>> cx = rt.new_context() >>> cx.add_global("loadfile", loadfile) >>> ret = cx.execute('eval(loadfile("foo.js")); f;') >>> ret.foo True
This assumes that the file foo.js looks like this:
var f = {"foo": true};
Also of note, the version of python-spidermonkey on Google Code is quite old. Its the version I originally used when I took over development a few months ago. At first I was drawn in by the Pyrex but it turned out to cause more problems than it solved.
-
AnC July 1st, 2009 @ 09:05 AM
Thanks for the quick response!
You're absolutely correct - I don't actually need
JS_EvaluateScript
.
I had no idea you could provide access to Python functions from within Spidermonkey!That, I assume, shows a fundamental lack of understanding of python-spidermonkey.
Sorry about that, I will review the README in more detail.Also thanks for the clarification on the different version of python-spidermonkey.
If the previous maintainer is still around, it would be useful if he added a note to the Google Code page. -
Paul J. Davis July 1st, 2009 @ 11:38 AM
- State changed from new to resolved
AnC
No prob. I tried to make the library interaction as transparent as possible so you should be able to do most anything that comes to mind in terms of passing objects back and forth.
Paul
-
AnC July 6th, 2009 @ 01:23 PM
I tried to make the library interaction as transparent as possible so you should be able to do most anything that comes to mind in terms of passing objects back and forth.
I hate to be a pain and abuse this ticket for support - but I can't seem to pass objects between Python and JavaScript:
import spidermonkey
myVar1 = { "p1": 123, "p2": "abc" } myVar2 = {}
rt = spidermonkey.Runtime() cx = rt.new_context()
cx.add_global("myVar1", myVar1) cx.add_global("myVar2", myVar2)
cx.execute("myVar1.p2 = 'xxx'"); cx.execute("delete myVar1.p1"); assert myVar1["p2"] == "xxx", "test myVar1 (a)" assert not myVar1.has_key("p1"), "test myVar1 (b)"
cx.execute("myVar2 = { aaa: 'yyy' }"); assert myVar2.has_key("aaa"), "test myVar2 (a)" assert myVar2["aaa"] == "yyy", "test myVar2 (b)"
Presumably that's because the assignment on line 17 creates a new object and thus doesn't reference the same memory address as before?
What's the right way to work around that when working with complex objects? -
Paul J. Davis July 6th, 2009 @ 01:44 PM
AnC,
If you're wanting to create a variable in JS and return it to Python, the proper method is to make that variable the result of the last expression, like such:
myvar = cx.execute('var foo = {"foo": "bar"}; foo;') assert myvar["foo"] == "bar"
Passing Python variables into JavaScript can be done using the Context.add_global method, or you can create a function and call it with python parameters, like such:
jsfunc = cx.execute("function(obj) {if(obj.key === null) throw("is null!"); return 2;}") assert jsfunc({"key": "bar"}) == 2
There are more examples on passing data in and out of JavaScript in the README and in the unit tests. Though I should probably write up a clearer document that describes the different methods for passing data in and out. And as a matter of fact I had another idea for passing data in and out...
For this though:
myVar2 = {"bb": "cc"} cx.add_global("myVar2", myVar2) cx.execute("myVar2 = { aaa: 'yyy' }"); assert myVar2.has_key("aaa"), "test myVar2 (a)" assert myVar2["aaa"] == "yyy", "test myVar2 (b)"
Assigning a value to the name 'myVar2' in the JavaScript interperter shouldn't affect the value of the Python 'myVar2'. The fact that you're naming them the same in both places does not confer any sort of relation between them. As you show above, you can affect the value that 'myVar1' refers to, but you're not affecting the name->value assignment.
Paul
-
AnC July 6th, 2009 @ 02:52 PM
Thanks again for your considerate response, Paul!
I had actually tried using return values early on, but objects are returned as "[object Object]":
import spidermonkey
rt = spidermonkey.Runtime() cx = rt.new_context()
myVar = cx.execute("var obj = { aaa: 'yyy' }; obj;"); print myVar # DEBUG; outputs "[object Object]" assert myVar.has_key("aaa"), "test #1" # fails assert myVar["aaa"] == "yyy", "test #2" # fails
import spidermonkey
rt = spidermonkey.Runtime() cx = rt.new_context()
myVar = { "aaa": "yyy" } cx.add_global("obj", myVar) myVar = cx.execute("obj;"); print myVar # DEBUG; outputs "{'aaa': 'yyy'}" myVar = cx.execute("obj.bbb = 'zzz'; obj;"); print myVar # DEBUG; outputs "{'aaa': 'yyy', u'bbb': u'zzz'}" assert myVar.has_key("aaa"), "test #1" # passes assert myVar["aaa"] == "yyy", "test #2" # passesPerhaps this is because I'm using the somewhat outdated spidermonkey-bin package from the Ubuntu repositories (version info: JavaScript-C 1.7.0 2007-10-03)?
-
Paul J. Davis July 7th, 2009 @ 03:20 PM
AnC,
Hey, these two cases do seem quite similar, but they're quite different.
import spidermonkey rt = spidermonkey.Runtime() cx = rt.new_context() # myVar is a wrapper to a JSObject* in the JavaScript virtual # machine. As such, it only has access to the JS functions and # a few special cases that are faked for Python. myVar = cx.execute("var obj = { aaa: 'yyy' }; obj;"); # Here I could try and do something like pretty printing the object # or similar, but I'm not sure its such a good idea as it starts # to blur the line a bit too far between what is JS and what is Py print myVar # DEBUG; outputs "[object Object]" # Alternatively for debugging, you can call: print myVar.toSource() # Part of being a JavaScript object means it doesn't have all of # the Python dict methods. To test whether the key "aaa" is in the # object, you can use the 'in' keyword that has been proxied. # assert myVar.has_key("aaa"), "test [#1](/projects/26898/tickets/1 "Ticket #1")" # fails assert "aaa" in myVar # This test passes just fine for me. assert myVar["aaa"] == "yyy", "test [#2](/projects/26898/tickets/2 "Ticket #2")" # fails
For the other case you're creating a Python object and modifying that object from JavaScript
import spidermonkey rt = spidermonkey.Runtime() cx = rt.new_context() # myVar is a Python dict here myVar = { "aaa": "yyy" } cx.add_global("obj", myVar) myVar2 = cx.execute("obj;"); # You should even be able to go further and say: assert id(myVar) == id(myVar2), "myVar2 refers to the same object as myVar1" # myVar is still a python dict. Using myVar2 above should hopefully make this # more clear. print myVar # DEBUG; outputs "{'aaa': 'yyy'}" # To point out that you're editing the original myVar live # I'm not returning it so you can see we can assert against # the original. # myVar = cx.execute("obj.bbb = 'zzz'; obj;"); cx.execute("obj.bbb = 'zzz';") assert myVar["aaa"] == "yyy" assert myVar["bbb"] == "zzz" # and to make it more clear: assert myVar2["aaa"] == "yyy" assert myVar2["bbb"] == "zzz"
Let me know if that helps.
-
AnC July 9th, 2009 @ 04:23 AM
Right, I shouldn't just assume that a JavaScript object is identical to a Python dictionary - I can see that now.
In my particular case, I have a complex JS object like this:
[
{ id: 123, name: "my item", description: "hello world", attributes: { shape: "square", color: "red" } }, { id: 789, [...] }
] -
Paul J. Davis July 9th, 2009 @ 11:21 AM
AnC,
A thought just occurred to me, you might look into something like
copy.deepcopy to see if you can use that to convert the object
recrusively. -
marc July 10th, 2009 @ 04:58 PM
- Assigned user cleared.
hi anc,
i had a quite similar problem. i wanted to return a json string that i can return to the user without running a json.dumps(). all i got from spidermonkey was an object.
you can use the code below and modify it slightly to convert your jsobject to a python dict.
def convert(obj): obj_type = type(obj) # spidermonkey objects if obj_type == spidermonkey.Object: data = [] for k in obj: data.append( ''.join(['"', k, '"', ':', self._convert(obj[k])]) ) return ''.join(['{', ','.join(data), '}']) # spidermonkey array elif obj_type == spidermonkey.Array: data = [] for i in obj: data.append(self._convert(i)) return ''.join(['[', ','.join(data), ']']) # string / unicode elif obj_type == unicode or obj_type == str: obj = obj.replace('"', '\\"').replace('\n', '\\n') \ .replace('\r', '\\r').replace('\t', '\\t') return ''.join(['"', obj, '"']) # everything else else: return unicode(obj)
in this piece of crap code i don't care about int/long/float. it's just do demonstrate how to recursively convert an jsobj into a python dict.
hope this helps,
cheers marc
-
AnC September 11th, 2009 @ 02:37 PM
- Assigned user set to Paul J. Davis
My apologies for the late response - I didn't have much time to attend to this lately.
I'm now using JSON to exchange objects, as that seemed to be the least complex way.
(It's a pity python-spidermonkey doesn't support Spidermonkey 1.8's built-in JSON methods yet, so I had to include a separate JavaScript library.)you might look into something like copy.deepcopy to see if you can use that to convert the object recrusively
Not sure what that might look like, to be honest.
i wanted to return a json string that i can return to the user without running a json.dumps()
Thanks for sharing the code!
Any particular reason you don't like json.dumps()? -
Paul J. Davis September 11th, 2009 @ 03:08 PM
AnC,
Yeah, I was putting off the upgrade to 1.8 because I wanted to go directly to 1.8.1. In fact I don't even know if the native JSON methods are in 1.8. Either way, the Mozilla folks haven't put out a 1.8.1 tarball to use in building which is probably why almost everyone is still using 1.7 when embedding JavaScript.
JSON is awesome, the reason I'd avoid it in this case though is that python-spidermonkey is trying to be transparent to the caller. Obviously, getting a spidermonkey.Array is a bit different than getting a native array but that's an implementation detail so that your objects are live in the JS VM. Ie, if you edit a spidermonkey.Array object, it's value in the JS vm should be updated as well. (I should probably write a test for that though).
The deepcopy was to do similar to the function the code that marc posted above. Using a tad bit of python to convert spidermonkey.Object and spidermonkey.Array into Python dicts and arrays should be pretty simple.
I looked back but I never said anything about not liking json.dumps near as I can tell. I don't like the obj -> JSON -> obj approach because its unnecessary, but then again, a working implementation is always better than a theoretical one :)
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป
Python/JavaScript bridge module, making use of Mozilla's spidermonkey JavaScript implementation. Allows implementation of JavaScript classes, objects and functions in Python, and evaluation and calling of JavaScript scripts and functions respectively. Borrows heavily from Claes Jacobssen's Javascript Perl module, in turn based on Mozilla's 'PerlConnect' Perl binding.