Open amv opened 7 years ago
Hey, I am afraid the named parameters and tuples are not supported directly.
Both tuples and lists are converted to Arrays when entering Js2Py and Array. When leaving Js2Py the Array is converted back to list. So tuple will be converted to list during this process. In most cases in Python there is not a big difference between lists and tuples and in most cases they can be used interchangeably.
Keyword arguments and *args
are not supported directly yet.
However, you can solve these problems by creating a wrapper function in Python.
def wsgi_application_wrapper(handlers, debug):
import webapp2
return webapp2.WSGIApplication(map(tuple, handlers), debug=debug)
Later you add this function to the execution context as usual.
ex = js2py.EvalJs({'wsgi_application_wrapper': wsgi_application_wrapper})
ex.eval("wsgi_application_wrapper([['/', 1],], true)")
Otherwise, you can create a new python file with wrappers and use a pyimport to import it. I understand this is not ideal and maybe I will solve it in the future.
Ok. I figured so much that by making Python wrappers, I could make things happen as I wanted, but as you said it is not ideal, and I have for the time being put my project on hold as infeasible.
Maybe a bit of context will help on what I was trying to do, and whether you want to even try to support this use case with the project in the future:
My aim was to create some Google App Engine Standard environment deployable projects with Javascript, but as GAE standard does not support JS, I was inspecting the possibility to use Js2Py .translate_file
as a part of my build process to generate Python files which GAE could then run.
The initial aim was to write code using ES6, transpile it to ES5 with Babel in my build scripts, and then Js2Py the files to Python 2.7 files for GAE Standard.
Even from the rudimentary "Hello world" -tests I found the following things to pose difficulty:
1) Some kind of support for "require" would be necessary to allow using NPM modules as part of a natural JS development process.
2) It is difficult to get non-javascript syntax, specifically pyimport
to pass through Babel
3) GAE requires an app entry point to be configured in the form of mymodule.variable
, which Python code then looks for, and I was not easily able to introduce this kind of top level module variable with Js2Py generated Python module.
4) Some level of interaction with Python native functions is required in the GAE environment, and Js2Py does not allow JS code to specify the exact types of Python variables one wants to pass in to those Python function calls (without writing a Python wrapper for each of them).
5) Some level of interaction with native Python variables is required, which is not possible to generate from Js2Py generated code (without writing a Python wrapper for each of them).
A specific example of the last one is that I get a "headers" dict from Python to my request handler and I am supposed to set a key to that dict to add headers, but setting self.request.headers["Content-Type"]
in JS code does not actually add the key to the underlying dict, but adds it to the JsObjectWrapper instead.
In the end I did get a Hello World example handler working, but in order to do that I had to manually write Python wrappers for:
1) the module variable
2) creating Python subclasses (which were required as parameters)
3) calling Python native functions with correct parameters
4) setting the dict key value properly
5) and to have the dict value be of type str
instead of unicode
.. and this was without ES6 transpiling and without the use of any NPM modules, so all in all I concluded that for now there are too many obstacles to continue.
Interesting project, but I agree it may be slightly hard to do with Js2Py.
require
is not really a problem because if you can use Babel to compile everything to the single file. Just like you compile ES6 to ES5.var
. You can retrieve any object from it using var.to_python().name
. unicode
vs str
and tuple
vs list
. Also, keyword arguments are not supported in JS. No easy fix for this.Yeah, compiling NPM modules may be sometimes a bit complicated because some of them depend on node
host environment. For example on object like process
or Buffer
which is not implemented by Js2Py. It is possible to make everything work, but that would involve much more work on the Js2Py project. And I dont think its worth it just for a single interesting project.
On 1: After thinking about this a bit in the morning, I too concluded that using a packager like Webpack, which creates a single file meant for web browser usage, would probably help with the require bit. I am not sure if they actually bundle some polyfills of the node environment, but at least those modules which are supposed to work in the browser with Webpack would also be supposed to work with Js2Py, right?
I am starting to think that this would actually be less of a problem in my specific use case where the App Engine environment itself is very limited in regards to the access to the native environment anyway.
On 2: So there is a pyimport function available for JS? I did not find mention of it in the documentation, which only points to a "statement" which does not look like Javascript. Changing the statements to pyimport('webapp2');
results in translation error: TypeError: 'bool' object is not callable
, so I guess I am doing it wrong?
On 3: I actually did already use the the var
variable to get my code working, by pointing the App Engine entry point to mainwrapper.app
and then providing the following file:
# mainwrapper.py
import main;
app = main.var.get(u'app').to_python();
.. but it would be nice to have a javascript function for setting a python module variable so I would not need to have a wrapper for this.
On 4: I think you are right that there are no ways to write javascript that would execute "the same", but since we are talking about a situation which is specific to interfacing with pure Python functions, maybe it would be possible to just provide some simple functions to the JS scope that would allow specifying the type that is passed in? I am talking about something like the following:
my_python_function.call( [ py_str( "hello" ), py_tuple( [1,2] ) ], { "name": var } )
.. or:
py_call( mymodule.myfunction, [ py_str( "hello" ), py_tuple( [1,2] ) ], { "name": var } ) ]
As Python does not allow providing unnamed variables after named variables, and Python seems to allow providing a mix of unnamed and named arguments to function calls dynamically, maybe the implementation could be as simple as:
def py_call( actual_python_function, args=[], named_args={} ):
if type( args ) == 'dict':
actual_python_function( **args )
else:
actual_python_function( *args, **named_args )
Currently it is not possible to do this in a Js2Py translated code, because even if py_str
would be an imported Python function that would return a Python string, the return value of the function would always be converted to unicode
before being passed again into the Python function as an argument. The same goes for tuples and arrays as showcased in #86
But just adding some special casting functions for the JS code to use, which would bypass the encapsulation and simply pass the variable on as is, might be enough to make this possible.
I dabbled a bit more with this, and the result is here:
https://github.com/amv/gae-js2py-es2015-example
Some notes based on that work:
Babel and Browserify seemed to work well together to translate ES6 code and package NPM modules for Js2Py.
However using Browserify made it harder to set top level module variables, as Browserify also wraps the entry file in it's own namespace.
From reading the Browserify generated code, it turned out that setting a window = {}
object before running the bundled code and using global.myvar = ..
in the handlers helped to get the variables out to Python (using vars.get(u'global').get(u'myvar')
. From the perspective of Browserify, global
variable should work the same as window
variable, but I got some weird errors when running it through Js2Py, which were fixed by switching to window
.
Also I not added the pyimport
directives after the Babel compilation step, because I did not find a way to do it from the JS file so that Babel would not have choked on them.. Trying eval("pyimort time")
got me some weird warning messages with the GAE environment, so I veered away from it.
For example the webapp2 framework expects to get it's parameters in this format:
If I
pyimport webapp2
, how do I create variables in javascript to cause the parameters to be in the right format when entering the function call (one or more tuples inside a list + a named attribute) ?