PiotrDabkowski/Js2Py
{ "createdAt": "2014-10-02T21:08:48Z", "defaultBranch": "master", "description": "JavaScript to Python Translator & JavaScript interpreter written in 100% pure Pythonđ Try it online:", "fullName": "PiotrDabkowski/Js2Py", "homepage": "http://piter.io/projects/js2py", "language": "JavaScript", "name": "Js2Py", "pushedAt": "2024-05-06T12:15:15Z", "stargazersCount": 2561, "topics": [], "updatedAt": "2025-11-22T08:08:02Z", "url": "https://github.com/PiotrDabkowski/Js2Py"}Pure Python JavaScript Translator/Interpreter
Section titled âPure Python JavaScript Translator/InterpreterâEverything is done in 100% pure Python so itâs extremely easy to install and use. Supports Python 2 & 3. Full support for ECMAScript 5.1, ECMA 6 support is still experimental.
Simple Example:
>>> import js2py >>> js2py.eval_js('console.log( "Hello World!" )') 'Hello World!' >>> add = js2py.eval_js('function add(a, b) {return a + b}') >>> add(1, 2) + 3 6 >>> add.constructor function Function() { [python code] } >>> js2py.require('underscore') 'function _(obj) { [python code] }'You can also import a big number of node modules as if they were written in Python! For example, here we import a pure JS library crypto-js:
>>> CryptoJS = js2py.require('crypto-js') >>> data = [{'id': 1}, {'id': 2}] >>> JSON = js2py.eval_js('JSON') >>> ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123') >>> bytes = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123') >>> decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list() >>> decryptedData [{u'id': 1}, {u'id': 2}]Now also supports JavaScript 6 (still experimental):
>>> js2py.eval_js6('let a = () => 11; a()') 11JavaScript 6 support was achieved by using Js2Py to translate javascript library called Babel. Babel translates JS 6 to JS 5 and afterwards Js2Py translates JS 5 to Python. The only downside is that translated babel.js has about 4 MB and importing such a long Python file takes about 15 seconds!
Translating a JavaScript file:
# this will translate example.js to example.py >>> js2py.translate_file('example.js', 'example.py') # example.py can be now imported and used! >>> from example import example >>> example.someFunction() ...Every feature of ECMA 5.1 is implemented (except of âwithâ statement):
>>> js2py.eval_js("Object.prototype.toString.call(Function('s', 'return s+arguments[1]')(new Date(), 7).__proto__)")[object String]Unfortunately even though Js2Py can be generally used to translate huge Js files (over 50k lines long), in rare cases you may encounter some unexpected problems (like javascript calling a function with 300 arguments - python allows only 255). These problems are very hard to fix with current translation approach. I will try to implement an interpreter in near future which will hopefully fix all the edge cases.
Installation
Section titled âInstallationâpip install js2pyMore advanced usage example
Section titled âMore advanced usage exampleâIt is possible to access all the variables from JS scope using EvalJs. Moreover, you can use Python objects from your JavaScript code if you add them to the scope. In this example we will be using Python built-in sum function to sum elements in JS array. It will stand under python_sum.
# Adding Python built-in sum to the JS context:>>> context = js2py.EvalJs({'python_sum': sum})>>> js_code = '''var a = 10function f(x) {return x*x}'''>>> context.execute(js_code)# Get value of variable a:>>> context.a10# context.f behaves just like js function so you can supply more than 1 argument. '9'*'9' in javascript is 81.>>> context.f('9', 0)81# context.f has all attributes of normal JavaScript object>>> context.f.toString()u'function f(x) { [python code] }'>>> context.f.bindfunction bind(thisArg) { [python code] }# You can also add variables to the context:>>> context.foo = [1,2,3] # context.foo is now Js Array object and behaves just like javascript array!>>> context.foo.push(4)4>>> context.foo.to_list() # convert to python list[1, 2, 3, 4]# You can use Python objects that you put inside the context!>>> context.eval('python_sum(new Array(1, 2, 3))')6You can also enable require support in JavaScript like this:
>>> context = js2py.EvalJs(enable_require=True)>>> context.eval("require('esprima').parse('var a = 1')")JavaScript âVirtualMachineâ in Python
Section titled âJavaScript âVirtualMachineâ in PythonâAs a fun experimental project I have also implemented a VM-based JavaScript (yes - there are 2 separate JS implementations in this repo). It is feature complete and faster than the translation based version. Below you can see a demo with a nice debug view (bytecode + execution sequence):
>>> from js2py.internals import seval>>> seval.eval_js_vm("try {throw 3+3} catch (e) {console.log(e)}", debug=True)[LOAD_UNDEFINED(), JUMP(4,), LABEL(1,), LOAD_UNDEFINED(), POP(), LOAD_NUMBER(3.0,), LOAD_NUMBER(3.0,), BINARY_OP('+',), THROW(), NOP(), LABEL(2,), LOAD_UNDEFINED(), POP(), LOAD('console',), LOAD('e',), LOAD_N_TUPLE(1,), CALL_METHOD_DOT('log',), NOP(), LABEL(3,), LOAD_UNDEFINED(), NOP(), LABEL(4,), TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)]
0 LOAD_UNDEFINED()1 JUMP(4,)18 TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4) ctx entry (from:2, to:9) 2 LOAD_UNDEFINED() 3 POP() 4 LOAD_NUMBER(3.0,) 5 LOAD_NUMBER(3.0,) 6 BINARY_OP('+',) 7 THROW() ctx exit (js errors) ctx entry (from:9, to:16) 9 LOAD_UNDEFINED() 10 POP() 11 LOAD('console',) 12 LOAD('e',) 13 LOAD_N_TUPLE(1,) 14 CALL_METHOD_DOT('log',)6 15 NOP() ctx exit (normal)This is just a curiosity and I do not recommend using VM in practice (requires more polishing).
Limitations
Section titled âLimitationsâThere are 3 main limitations:
- "strict mode" is ignored
- with statement is not supported
- Indirect call to eval is treated as direct call to eval (hence always evals in local scope)
They are generally not a big issue in practice. In practice more problematic are minor edge cases that unfortunately sometimes do happen. Please report a bug if you find one.
Js2Py was able to successfully
translate and run huge JS libraries like Babel (100k+ loc), esprima, crypto-js and more.
You can try it yourself by importing any supported npm package via js2py.require('your_package').
Other Examples
Section titled âOther ExamplesâIn Js2Py all JavaScript objects are a subclass of PyJs object. For example JS Number is represented by PyJsNumber class. js2py.eval_js and js2py.EvalJs automatically tries to convert PyJs type to builtin python type. So for example if you execute:
>>> js2py.eval_js('var a = "hello"; a')eval_js will return unicode type (uâhelloâ). However for complex types such conversion is impossible and JsObjectWrapper is returned. See the conversion table JsType -> PyType:
Boolean -> boolString -> unicode (str in Python 3)Number -> float (or int/long if whole number)undefined -> Nonenull -> NoneOTHER -> JsObjectWrapperJsObjectWrapper supports: getitem, getattr, setitem, setattr, repr and call. Moreover it has to_list and to_dict methods if you want to convert it to builtin python type.
>>> js = js2py.eval_js('d = {a:1, b:2}')>>> js{a: 1, b: 2}>>> type(js)<class 'js2py.base.JsObjectWrapper'>>>> js.a1>>> js['a']1>>> js.b = 20>>> js{a: 1, b: 20}>>> js['c'] = 30>>> js.to_dict(){u'a': 1, 'c': 30, u'b': 20}Also, of course you can use Js2Py to parse (tree is the same as in esprima.js) and translate JavaScript
Parsing:
Section titled âParsing:â>>> js2py.parse_js('var $ = 5'){ "body": [ { "declarations": [ { "id": { "name": "$", "type": "Identifier" }, "init": { "raw": "5", "type": "Literal", "value": 5 }, "type": "VariableDeclarator" } ], "kind": "var", "type": "VariableDeclaration" } ], "type": "Program"}Translating:
Section titled âTranslating:â>>> print(js2py.translate_js('var $ = 5'))from js2py.pyjs import *# setting scopevar = Scope( JS_BUILTINS )set_global_object(var)
# Code follows:var.registers(['$'])var.put('$', Js(5.0))pyimport statement
Section titled âpyimport statementâFinally, Js2Py also supports importing any Python code from JavaScript using âpyimportâ statement:
>>> x = """pyimport urllib; var result = urllib.urlopen('https://www.google.com/').read(); console.log(result.length) """>>> js2py.eval_js(x)18211undefinedA Javascript-to-Python translation assistant. It does a simple translation from Javascript to Python (as detailed below). Youâll most likely need to make further changes by hand, but this automates part of the work.
I thought it would be nice to be able to import some libraries directly into
Python, rather than shelling out to NodeJS. This script is the result of trying
to make that a reality.
Iâve successfully used it to port [Esprima][1] to Python â the result is [PyEsprima][2].
Setup & Usage
Section titled âSetup & Usageânpm install./js2py.coffee file.js > out.pyTransformations and Shims
Section titled âTransformations and Shimsâ- Create
globaldeclarations where necessary - Transform simple prototype-based classes into corresponding Python classes (but no inheritance)
- Remove assignment statements from conditional tests
- Convert switch statements into if-else chains
- Convert pre-/post-increments into assignment statements
Array.prototype.sliceandString.prototype.substrare converted to Pythonâs slice notation- Function expressions are hoisted out as fully declared functions since Pythonâs lambdas are limited
- Shims for RegExp and JS-style dictionaries
- Some support for
typeof
Limitations
Section titled âLimitationsâ- No support for modifying non-global nonlocals (but easy enough to add with
the
nonlocalkeyword in Python 3) a[i]in JS will returnundefinedifi >= a.length, but in Python it raises anIndexError.- No support for
call/apply. - There many more; pull requests are welcome!
[1] !: https://github.com/ariya/esprima [2] !: https://github.com/int3/pyesprima