• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

Js2Py.egg-info/H03-May-2022-2717

js2py/H01-Apr-2021-114,556110,371

PKG-INFOH A D01-Apr-2021907 2717

README.mdH A D16-Jun-20198.5 KiB301246

setup.cfgH A D01-Apr-2021109 117

setup.pyH A D01-Apr-20211.3 KiB4327

README.md

1[![Build Status](https://travis-ci.org/PiotrDabkowski/Js2Py.svg?branch=master)](https://travis-ci.org/PiotrDabkowski/Js2Py) [![Downloads](https://pepy.tech/badge/js2py/month)](https://pepy.tech/project/js2py)
2
3#### Pure Python JavaScript Translator/Interpreter
4
5Everything 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.
6<hr>
7
8Simple Example:
9
10```python
11    >>> import js2py
12    >>> js2py.eval_js('console.log( "Hello World!" )')
13    'Hello World!'
14    >>> add = js2py.eval_js('function add(a, b) {return a + b}')
15    >>> add(1, 2) + 3
16    6
17    >>> add.constructor
18    function Function() { [python code] }
19    >>> js2py.require('underscore')
20    'function _(obj) { [python code] }'
21```
22You can also import a big number of node modules as if they were written in Python!
23For example, here we import a pure JS library [crypto-js](https://www.npmjs.com/package/crypto-js):
24
25```python
26    >>> CryptoJS = js2py.require('crypto-js')
27    >>> data = [{'id': 1}, {'id': 2}]
28    >>> JSON = js2py.eval_js('JSON')
29    >>> ciphertext = CryptoJS.AES.encrypt(JSON.stringify(data), 'secret key 123')
30    >>> bytes  = CryptoJS.AES.decrypt(ciphertext.toString(), 'secret key 123')
31    >>> decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)).to_list()
32    >>> decryptedData
33    [{u'id': 1}, {u'id': 2}]
34```
35
36
37
38Now also supports JavaScript 6 (still experimental):
39
40```python
41    >>> js2py.eval_js6('let a = () => 11; a()')
42    11
43```
44JavaScript 6 support was achieved by using Js2Py to translate javascript library called <a href="https://github.com/babel/babel">Babel</a>. 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!
45
46<hr>
47
48Translating a JavaScript file:
49
50```python
51    # this will translate example.js to example.py
52    >>> js2py.translate_file('example.js', 'example.py')
53    # example.py can be now imported and used!
54    >>> from example import example
55    >>> example.someFunction()
56    ...
57```
58
59Every feature of ECMA 5.1 is implemented (except of 'with' statement):
60
61```python
62>>> js2py.eval_js("Object.prototype.toString.call(Function('s', 'return s+arguments[1]')(new Date(), 7).__proto__)")
63[object String]
64```
65Unfortunately 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.
66
67#### Installation
68
69    pip install js2py
70
71<hr>
72
73#### More advanced usage example
74
75It 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.
76In this example we will be using Python built-in sum function to sum elements in JS array. It will stand under python_sum.
77
78```python
79# Adding Python built-in sum to the JS context:
80>>> context = js2py.EvalJs({'python_sum': sum})
81>>> js_code = '''
82var a = 10
83function f(x) {return x*x}
84'''
85>>> context.execute(js_code)
86# Get value of variable a:
87>>> context.a
8810
89# context.f behaves just like js function so you can supply more than 1 argument. '9'*'9' in javascript is 81.
90>>> context.f('9', 0)
9181
92# context.f has all attributes of normal JavaScript object
93>>> context.f.toString()
94u'function f(x) { [python code] }'
95>>> context.f.bind
96function bind(thisArg) { [python code] }
97# You can also add variables to the context:
98>>> context.foo = [1,2,3]  # context.foo is now Js Array object and behaves just like javascript array!
99>>> context.foo.push(4)
1004
101>>> context.foo.to_list() # convert to python list
102[1, 2, 3, 4]
103# You can use Python objects that you put inside the context!
104>>> context.eval('python_sum(new Array(1, 2, 3))')
1056
106 ```
107
108You can also enable require support in JavaScript like this:
109
110```python
111>>> context = js2py.EvalJs(enable_require=True)
112>>> context.eval("require('esprima').parse('var a = 1')")
113```
114<hr>
115
116### JavaScript 'VirtualMachine' in Python
117
118As a fun experimental project I have also implemented a VM-based JavaScript
119(yes - there are 2 separate JS implementations in this repo). It is feature complete and faster than the translation based version.
120Below you can see a demo with a nice debug view (bytecode + execution sequence):
121
122```python
123>>> from js2py.internals import seval
124>>> seval.eval_js_vm("try {throw 3+3} catch (e) {console.log(e)}", debug=True)
125[LOAD_UNDEFINED(),
126 JUMP(4,),
127 LABEL(1,),
128 LOAD_UNDEFINED(),
129 POP(),
130 LOAD_NUMBER(3.0,),
131 LOAD_NUMBER(3.0,),
132 BINARY_OP('+',),
133 THROW(),
134 NOP(),
135 LABEL(2,),
136 LOAD_UNDEFINED(),
137 POP(),
138 LOAD('console',),
139 LOAD('e',),
140 LOAD_N_TUPLE(1,),
141 CALL_METHOD_DOT('log',),
142 NOP(),
143 LABEL(3,),
144 LOAD_UNDEFINED(),
145 NOP(),
146 LABEL(4,),
147 TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)]
148
1490 LOAD_UNDEFINED()
1501 JUMP(4,)
15118 TRY_CATCH_FINALLY(1, 2, 'e', 3, False, 4)
152  ctx entry (from:2, to:9)
153  2 LOAD_UNDEFINED()
154  3 POP()
155  4 LOAD_NUMBER(3.0,)
156  5 LOAD_NUMBER(3.0,)
157  6 BINARY_OP('+',)
158  7 THROW()
159  ctx exit (js errors)
160  ctx entry (from:9, to:16)
161  9 LOAD_UNDEFINED()
162  10 POP()
163  11 LOAD('console',)
164  12 LOAD('e',)
165  13 LOAD_N_TUPLE(1,)
166  14 CALL_METHOD_DOT('log',)
1676
168  15 NOP()
169  ctx exit (normal)
170
171```
172
173This is just a curiosity and I do not recommend using VM in practice (requires more polishing).
174
175<hr>
176
177#### Limitations
178
179There are 3 main limitations:
180<ul>
181<li>"strict mode" is ignored</li>
182<li>with statement is not supported</li>
183<li>Indirect call to eval is treated as direct call to eval (hence always evals in local scope)</li>
184</ul>
185
186They are generally not a big issue in practice.
187In practice more problematic are minor edge cases that unfortunately
188sometimes do happen. Please report a bug if you find one.
189
190Js2Py was able to successfully
191translate and run huge JS libraries like Babel (100k+ loc), esprima, crypto-js and more.
192You can try it yourself by importing any supported npm package via `js2py.require('your_package')`.
193
194<hr>
195
196
197
198#### Other Examples
199
200
201In Js2Py all JavaScript objects are a subclass of PyJs object. For example JS Number is represented by PyJsNumber class.
202js2py.eval_js and js2py.EvalJs automatically tries to convert PyJs type to builtin python type. So for example if you
203execute:
204
205```python
206>>> js2py.eval_js('var a = "hello"; a')
207```
208
209eval_js will return unicode type (u"hello"). However for complex types such conversion is impossible and JsObjectWrapper is returned.
210See the conversion table JsType -> PyType:
211
212    Boolean -> bool
213    String -> unicode (str in Python 3)
214    Number -> float (or int/long if whole number)
215    undefined -> None
216    null -> None
217    OTHER -> JsObjectWrapper
218
219JsObjectWrapper supports: getitem, getattr, setitem, setattr, repr and call.
220Moreover it has to_list and to_dict methods if you want to convert it to builtin python type.
221
222```python
223>>> js = js2py.eval_js('d = {a:1, b:2}')
224>>> js
225{a: 1, b: 2}
226>>> type(js)
227<class 'js2py.base.JsObjectWrapper'>
228>>> js.a
2291
230>>> js['a']
2311
232>>> js.b = 20
233>>> js
234{a: 1, b: 20}
235>>> js['c'] = 30
236>>> js.to_dict()
237{u'a': 1, 'c': 30, u'b': 20}
238```
239
240<hr>
241
242Also, of course you can use Js2Py to parse (tree is the same as in esprima.js) and translate JavaScript
243
244#### Parsing:
245```python
246>>> js2py.parse_js('var $ = 5')
247{
248    "body": [
249        {
250            "declarations": [
251                {
252                    "id": {
253                        "name": "$",
254                        "type": "Identifier"
255                    },
256                    "init": {
257                        "raw": "5",
258                        "type": "Literal",
259                        "value": 5
260                    },
261                    "type": "VariableDeclarator"
262                }
263            ],
264            "kind": "var",
265            "type": "VariableDeclaration"
266        }
267    ],
268    "type": "Program"
269}
270```
271#### Translating:
272
273```python
274>>> print(js2py.translate_js('var $ = 5'))
275from js2py.pyjs import *
276# setting scope
277var = Scope( JS_BUILTINS )
278set_global_object(var)
279
280# Code follows:
281var.registers(['$'])
282var.put('$', Js(5.0))
283 ```
284<hr>
285
286#### pyimport statement
287Finally, Js2Py also supports importing any Python code from JavaScript using 'pyimport' statement:
288
289```python
290>>> x = """pyimport urllib;
291           var result = urllib.urlopen('https://www.google.com/').read();
292           console.log(result.length)
293        """
294>>> js2py.eval_js(x)
29518211
296undefined
297```
298
299
300
301