Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

support for file I/O (JS_EvaluateScript)

#23

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.

Reported by AnC · June 30th, 2009 @ 12:26 PM

State: resolved
Milestone: none
Assigned to: Paul J. Davis Paul J. Davis

Activity

  1. AnC
    AnC

    A grep on the repository reveals occurrences of JS_EvaluateScript in spidermonkey/libjs/jsapi.c - however, there's no hint in the documentation ($ pydoc spidermonkey) on how that might be exposed via the API.

    June 30th, 2009 @ 12:45 PM

  2. Paul J. Davis
    Paul J. Davis
    • 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.

    June 30th, 2009 @ 01:28 PM

  3. AnC
    AnC

    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.

    July 1st, 2009 @ 09:05 AM

  4. Paul J. Davis
    Paul J. Davis
    • 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

    July 1st, 2009 @ 11:38 AM

  5. AnC
    AnC

    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)"
    The second set of assertions fails.
    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?

    July 6th, 2009 @ 01:23 PM

  6. Paul J. Davis
    Paul J. Davis

    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

    July 6th, 2009 @ 01:44 PM

  7. AnC
    AnC

    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
    However, that's not always the case:
    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" # passes
    Note that I'm just modifying an existing global variable here, not returning a newly-created object.

    Perhaps 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)?

    July 6th, 2009 @ 02:52 PM

  8. Paul J. Davis
    Paul J. Davis

    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.

    July 7th, 2009 @ 03:20 PM

  9. AnC
    AnC

    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,
        [...]
    }
    
    
    
    
    ]
    I guess JSON might be the answer here, returning the object as a string to Python and deserializing it there.

    July 9th, 2009 @ 04:23 AM

  10. Paul J. Davis
    Paul J. Davis

    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.

    July 9th, 2009 @ 11:21 AM

  11. marc
    marc
    • 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

    July 10th, 2009 @ 04:58 PM

  12. AnC
    AnC
    • 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()?

    September 11th, 2009 @ 02:37 PM

  13. Paul J. Davis
    Paul J. Davis

    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 :)

    September 11th, 2009 @ 03:08 PM

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.