Skip to content

implement toString on javascript functions #331

Open

Description

@hansthen

Describe your feature request here.

In javascript I can do the following:

exports.hello = () => { console.log('hello, world') };
console.log(exports.hello.toString())

However, I cannot call toString() from pythonmonkey. I would like to do this, so I could access the function definition from inside python. If there is another way to access the function definition as a string that would be acceptable as well.

Code example

import pythonmonkey as pm
module = pm.require("./my.js")
print(module.hello.toString())

==>

"() => { console.log('hello, world') }"

Activity

  1. philippedistributive commented on Apr 30, 2024

    @philippedistributive
    Contributor

    @hansthen Please elaborate on the actual case to help us prioritise this feature request

  2. hansthen commented on Apr 30, 2024

    @hansthen
    Author

    It is for the Folium library. Folium is a python library wrapping the javascript Leaflet library to generate maps. This is done by generating the html and javascript code for a Leaflet map. Users can configure many Leaflet objects by defining javascript functions (event handlers and stuff). Currently these functions are defined as strings in python code. Like this:

    event_handler = JsCode("""
    function (event) {
        //
    }
    """)
    

    This is not really comfortable. Editor and linting support is lacking and it is difficult to write unit tests.

    Several users have asked for a way to "import" the javascript functions from a *.js file into python. The javascript functions are really defined as strings, which are then passed to the templating engine. If we could parse the javascript files and retrieve the function definitions this would be a trivial task.

  3. hansthen commented on Apr 30, 2024

    @hansthen
    Author

    I know this is probably not your core use case, but the PythonMonkey library is really fast and does almost everything else we need to implement this. It also has a really natural API for our purposes. I would be very grateful if you could add this. It does not seem like it would be terribly difficult to implement, but I do not know much about your code internals.

  4. zollqir commented on May 3, 2024

    @zollqir
    Collaborator

    @hansthen
    We plan on implementing being able to access JS function properties from python (such as toString), but in the meantime, to get a JSFunction as a string, you can do:

    import pythonmonkey as pm
    module = pm.require("./my.js")
    print(pm.eval("(func) => { return func.toString(); })")(module.hello))

    or, if you don't like dense one-liners:

    import pythonmonkey as pm
    module = pm.require("./my.js")
    getFunctionString = pm.eval("(func) => { return func.toString(); }")
    helloString = getFunctionString(module.hello)
    print(helloString)

    This just calls toString in JS land, and returns the result back to python.

  5. hansthen commented on May 3, 2024

    @hansthen
    Author

    Thanks for the suggestion. Unfortunately, it does not seem to work in all cases. For me it returns this:

    function() {
        [native code]
    }
    
  6. philippedistributive commented on May 7, 2024

    @philippedistributive
    Contributor

    Hi, we'll look into this further, eventually, no timeline for now besides by mid-summer

  7. hansthen commented on May 8, 2024

    @hansthen
    Author

    Thanks for the heads up.

  8. zollqir commented on May 8, 2024

    @zollqir
    Collaborator

    @hansthen

    Thanks for the suggestion. Unfortunately, it does not seem to work in all cases. For me it returns this:

    function() {
        [native code]
    }
    

    Ah, I forgot that we bind b to a when we do a.b in python if a is a JSObjectProxy and b is a function in order to get methods to work properly, since the this value of a JS method is determined by how it is accessed, while in python the self value is determined at the moment the method is created, i.e. all methods in python are bound functions (pyodide also does this, see here: https://github.com/pyodide/pyodide/blob/ee863a7f7907dfb6ee4948bde6908453c9d7ac43/src/core/jsproxy.c#L388 )

    To get around this, you can do:

    import pythonmonkey as pm
    module = pm.require("./my.js")
    unboundFunction = pm.eval("(module) => module.hello")(module)
    getFunctionString = pm.eval("(func) => func.toString()")
    print(getFunctionString(unboundFunction))

    I made sure to actually run this code myself this time to check that it works 😉

  9. wesgarland commented on May 8, 2024

    @wesgarland
    Collaborator

    @caleb-distributive I think our wrappers should have a better toString method if possible. '[native code]' could mention that it is an proxy and, ideally, also mentions the function name when it's available

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions