Python in Browser: How to choose between Brython,

2019-01-16 02:50发布

问题:

I'm very excited to see that it is now possible to code Python in the browser. These are the main candidates (please add any I may have overlooked):

  • Brython
  • Skulpt
  • PyPy.js
  • Transcrypt

But how to choose between them? The only obvious difference I can see is that Skulpt is based on Python 2, whereas Brython is based on Python 3.

Please note: This is not a request for recommendations or opinions. I'm seeking objective facts that would inform an educated choice.

回答1:

This might be helpful too: http://stromberg.dnsalias.org/~strombrg/pybrowser/python-browser.html

It compares several Python-in-the-browser technologies.



回答2:

Here's some info on Brython vs Transcrypt (July 2016, since Transcrypt was added as an option on this question by the OP), gleaned by starting off a project with Brython a few months ago and moving to Transcrypt (completed moving last week). I like Brython and Transcrypt and can see uses for both of them.

For people that are new to this, Brython and Transcrypt both 'transpile' python input to javascript (Edit: maybe it's better to view Brython as a 'a Python implementation for the browser' because it doesn't produce standalone javascript). Both require Python 3 syntax. Brython includes a substantial number of Python standard libraries and some of it's own for dealing with web related things, whereas Transcrypt avoids that for the most part and suggests using Javascript libraries instead.

Brython (Github) can do the conversion in the browser. So you write in python and the brython.js engine converts it to javascript on the fly when the page is loaded. This is really convenient, and is much faster than you might think. However, the brython.js engine that you need to include in your pages is about 500Kb. Also, there's the matter of importing standard libraries, which Brython handles by fetching separate .js files with XHR requests. Some libs are already compiled into brython.js, so not every import will pull in new files, but if you use many imports, things can get slow. However, there are ways around this. What i did was to check the network tab in browser dev tools to see what files were being pulled in when the page was loaded, then delete all the files my project wasn't using in a copy of the Brython src folder, and run the script included with Brython (i think it's at Brython/www/scripts/make_VFS.py) that compiles all of the available libs into one file called py_VFS.js that you need to also link to from your html. Normally, it will make one huge 2MB+ file, but if you delete the things you aren't using, it can be quite tiny. Doing it this way, means you only need to pull in brython.js, py_VFS.js and your python code, and no additional XHR requests will be needed.

Transcrypt (Github)on the other hand, is distributed as a python 3 package that you can use manually, or hook into your toolchain, to compile python to javascript in advance. So with Transcrypt, you write in python, run transcrypt against the python and it spits out javascript that you can link to in your project. It is more like a traditional compiler also in that it offers some control over the output. For example, you can choose to compile to ES6 or ES5, or ask it to output sourcemaps (that during debugging let's the browser take you directly to the corresponding python code, insead of the generated javascript code.) Transcrypt's javascript output is pretty terse (or put another way, it's pretty and terse). In my case 150kB of python is converted to 165kB of unminified ES5 javascript. By way of comparison, the Brython version of my project used about 800Kb after conversion.

However, getting the benefits of Transcrypts terseness, requires reading the docs a bit (really just a bit). For example, with Transcrypt, Python's 'truthiness' for data structures like dict, set and list isn't enabled by default and globally enabling it is discouraged because of potential performance issues related to typechecking. For clarity: Under CPython, an empty dict, set or list has truth value False, whereas in Javascript it's considered 'true'.. Example:

myList = []
if myList:    # False in CPython bcs it's empty, true in javascript bcs it exists
    # do some things.

There are at least three ways to address this:

  • Use the -t flag when converting python to javascript e.g.: $ transcrypt -t python.py (not recommended, but probably isn't a problem unless you check for truthiness many times in inner loops of performance sensitive code..)
  • Use __pragma__(tconv) or __pragma__(notconv) within your code to tell the transcrypt compiler to switch on automatic conversion to python-like truth values locally.
  • Instead of checking for the truth value, avoid the problem altogether by just checking len(myList) > 0... Maybe that will be fine for most situations, does the job for my light use.

Right, so my project was getting bigger and i wanted to pre-compile for a performance gain but found it hard to do so with Brython (though it's technically possible, an easy way being to use the online editor and click the javascript button to see the output). I did that and linked to the generated javascript from project.html but it didn't work for some reason. Also, I find it hard to understand error messages from Brython so i didn't know where to start after this step failed. Also, the big size of the outputted code and the size of the brython engine was beginning to bug me. So i decided to have a closer look at Transcrypt, which had at first seemed to be higher grade because i prefer dumbed down instructions that tell me how to get started immediately (these have since been added).

The main thing getting it set up after installing Python3.5 was 1) use venv (it's like a new built-in version of virtualenv that uses less space for each project) to set up a python3.5 project folder (just type: python3.5 -m venv foldername - workaround for ubuntu with package issues for 3.5). This makes 'foldername' with a bin subfolder among other things. 2) install Transcrypt python package with pip ('foldername/bin/pip install transcrypt') which installs it to foldername/lib/python3.5/site-packages/transcrypt. 3) 'activate' the current terminal if you don't want to have to type the full path to foldername/bin/python3.5 every time. Activate by typing: 'source foldername/bin/activate' 4) begin writing code and compiling it to javascript for testing. Compile from within the folder you write your code in. For example, i used foldername/www/project. So CD into that folder and run: 'transcrypt -b your_python_script.py'. That puts the output in a subfolder called __javascript__. You can then link to the outputted javascript from your html.

Main issues moving across

I have rather simple needs, so your mileage may vary.

  • You need to replace brython or python standard libs with javascript libs. So for example 'import json' is provided by Brython, but under Transcrypt you could use a javascript lib or just use JSON.parse / JSON.stringify directly in your Python code. To include a minified version of a javascript library directly in your python code use this format (note the triple quotes):

    __pragma__ ('js', '{}', ''' // javascript code ''')

  • Brython's html specific functions don't work with Transcrypt obviously. Just use the normal javascript ways. Examples: 1) under Brython, you might have referred to a specific HTML tag using 'document['id']', but with Transcrypt you'd use 'document.getElementById('id') (which is the same way you do it from javascript). 2) You can't delete a node with 'del nodeName' (bcs that's a brython function). Use something like 'node.parentNode.removeChild(node)'. 3) replace all of brython's DOM functions with the javascript alternatives. e.g. class_name = className; text = textContent; html = innerHTML; parent = parentNode; children = childNodes etc. I guess if you need something that contains alternatives required by some older browsers then there are javascript libraries for that. 4) Brython's set_timeout is replaced with javascripts setTimeout 5) Brython's html tags such as BR() need to be replaced using the normal javascript ways as well as redoing any places you used it's <= dom manipulation syntax. Either inject plain text markup as innerHTML or make the elements using javascript syntax and then attach them using normal javascript DOM syntax. I also noticed that for checkboxes brython uses "if checkbox = 'checked':" but Transcrypt is happy with "if checkbox:"..

  • I finished moving a 2700 line project over last week at which time Transcrypt didn't have support for a few minor things (though they were easy enough to replace with fillers), these were 1) str.lower, str.split (str.split is present, but seems to be the javascript split, which works differently to the python version, the behavior of which i was relying on), 2) round (this seems to be supported in the dev version now) and 3) isinstance didn't work on str, int and float, only on dict, list and set. 4) Another difference from Brython i noticed is that if i pull in a JSON representation of a dict, i need to do so using 'myDict = dict(data)', whereas brython was happy with 'myDict = data'. But that might be related to something in Brython's json.loads, which i replaced directly with JSON.parse. 5) Also without specifically enabled Transcrypts operator overloading (using -o switch for global, or __pragma__('opov') for local), you can't do things like set operations using the overloaded format, but need to use the corresponding functions. E.g.

    a = set([1, 2, 3])
    b = set([3, 4, 5])
    a.difference(b)             # is used instead of a - b
    a.union(b)                  # used instead of a | b
    a.intersection(b)           # used instead of a & b
    a.symmetric_difference(b)   # used instead of a ^ b
    

6) Also, you can't iterate dicts by default using 'for i in dict:', without enabling that (cmd line -i or __pragma__('iconv'), but you can avoid having to enable it by just using the keys() member e.g.:

for key, value in dict.items():
    # do things for each key and value..

To summarise

  • I like Brython because it's easy to get going with it and to test your code (just F5). It's closer to true python because most of the standard lib is there. I dislike having to include the transpilation engine (Edit: Or one might view it as a python VM) in the browser and the large outputted javascript size. If i had to do things over (but still using Brython), i would have used javascript methods to manipulate the DOM from brython (which you can do..), instead of leaning so much on the brython methods because that wasted time moving across to another transpiler when my needs changed.

  • I like Transcrypt because the outputted javascript is really 'lean and mean' and because the only thing you load browser side is your generated javascript code which is similar in size to your python code. Also because it supports sourcemaps and because it gives me a measure of control over the outputted javascript. And using it taught me quite a bit about optimization.

Hope that helps someone see which of these might be good for their particular project.



回答3:

I've used and committed to skulpt as well as pypyjs. And they are all three very different that any comparison is moot if you ask me.

It depends on what you are looking for which will make the most sense.

PyPyJS

pypyjs is huge it's a 12MB javascript file that contains the entire pypy virtual machine. So if you want python implementation completeness this is your baby. It has a javascript bridge that works really good but it's not a viable option for writing your javascript website code in python. It will however let you import compiler.

It's built with emscripten and is faster then CPython, in running the pystone benchmark.

I gave a short talk about pypyjs here are the slides.

Skulpt

Is a teaching tool (or it has evolved into that over time), it compiles your python into a state machine very closely emulating the cpython compiler. At it's core it's a handwritten implementation of the python compiler in javascript. It allows for asynchronous execution which lets you do:

while (True):
    print "hi"

Without locking up the browser.

Skulpt is the only one that supports asynchronous continuations, it lets you pause the execution of python while it's resolving some asynchronous thing to happen. Making this work:

from time import sleep
sleep(1)

Skulpt runs at about a tenth of the speed of CPython, when comparing pystone.

Brython

I know least about this one maybe @olemis-lang can expand this one. But next to the obvious difference that Brython is py3 and the others py2. Brython is also a transpiler.

Brython doesn't run the pystone benchmark because time.clock isn't implemented, because officially it's a hardware function.



回答4:

https://brythonista.wordpress.com/2015/03/28/comparing-the-speed-of-cpython-brython-skulpt-and-pypy-js/

This page benchmarks the three candidates. Brython emerges as a clear winner.

Despite the 'help' explaining that S.O. is not good for this kind of question, it appears that a concise answer is in this case possible.

Maybe people are being too hasty?



回答5:

First of all I'm a Brython committer . Nevertheless I'll try to be as impartial as possible for the sake of doing an objective assessment .

The last time I used it Skulpt did not support features like generator expressions . Brython and PyPy.js do so , so at the feature level IMHO the later are superior .

Brython (at this time) is still work in progress . Some modules cannot be imported (e.g. xml.ElementTree ) . Nevertheless this situation is starting to change since we are working towards running the whole CPython test suite in spite of achieving full compatibility with standards (at least when it makes sense) .

Brython also supports .vfs.js to speed up module imports .

PyPy.js has a number of characteristics that follow straightforward from the fact of it being powered by PyPy (JIT compilation , well tested , ...) but I'm not sure of whether it is suitable for running in the browser . This might change as the project evolves .

TODO: I'll try to complement my answer with reliable benchmarks .



回答6:

Not mentioned here is RapydScript or RapydScript-NG. They produce very efficient JavaScript code, which is used in GlowScript VPython (glowscript.org). I used to use the original RapydScript of Alex Tsepkov (https://github.com/atsepkov/RapydScript) but recently switched to RapydScript-NG of Kovid Goyal (https://github.com/kovidgoyal/rapydscript-ng). I recently ran the pystone benchmark on CPython, RapydScript, and Brython, and you can see the results here:

https://groups.google.com/forum/?fromgroups&hl=en#!topic/brython/20hAC9L3ayE



回答7:

Since nobody has mentioned it I thought it was worth it to mention Batavia which implements the Python virtual machine for running precompiled Python bytecode.

I just tried it and, while it's an interesting concept, it is still in early stages as there is little documentation.

In the end it will depend on what you are trying to do. I chose Transcrypt after having a look because it was more pragmatic and better performant, also more recently released/maintained.



回答8:

This is an updated conference which compares all available options in the market right now:

https://www.youtube.com/watch?v=2XSeNQyPlTY

The speaker is Russell Keith-Magee, who is a well known developer in the area.