Transcryptを使って、PythonをJavaScriptに変換する



PythonのライブラリをJavaScriptに変換したい個人的な要望があり、調べてみると Transcrypt · PyPI というPythonのライブラリで出来そうなことがわかったので、インストールして使ってみた。
目的は冒頭でも書いたが、Pythonをブラウザで動作させたいというよりも、Pythonで作成されたライブラリを、JavaScriptで流用したいになる。
実際に使用した限り、GitHubのissueにもあるが、Pythonのライブラリを変換してJavaScriptで使用するという用途で使用するのは難しい。
オプションが大量にあるので、もしかするとCPythonのimport解決もよしなにやってくれるのかもしれないが、見つけられなかったので知っている方がいたら教えて欲しい。
前提
- PythonのVersion
Python 3.7.2
。
python -V
Python 3.7.2
インストール
pip install Transcrypt
Collecting Transcrypt
Downloading https://files.pythonhosted.org/packages/48/e4/1e52db523078afef6790d46cc6bea484cd4a99b33ffa4ec62b504ff96ddf/Transcrypt-3.7.16.tar.gz (32.1MB)
100% |████████████████████████████████| 32.2MB 945kB/s
Collecting mypy (from Transcrypt)
Downloading https://files.pythonhosted.org/packages/42/0c/590328b20aa20e03a45ccfa16998d1bfc0af6d0413d52dfa0aca99e0aaba/mypy-0.730-cp37-cp37m-macosx_10_6_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl (16.0MB)
100% |████████████████████████████████| 16.0MB 1.2MB/s
Collecting mypy-extensions<0.5.0,>=0.4.0 (from mypy->Transcrypt)
Downloading https://files.pythonhosted.org/packages/4d/72/8d54e2b296631b9b14961d583e56e90d9d7fba8a240d5ce7f1113cc5e887/mypy_extensions-0.4.1-py2.py3-none-any.whl
Collecting typing-extensions>=3.7.4 (from mypy->Transcrypt)
Downloading https://files.pythonhosted.org/packages/27/aa/bd1442cfb0224da1b671ab334d3b0a4302e4161ea916e28904ff9618d471/typing_extensions-3.7.4-py3-none-any.whl
Collecting typed-ast<1.5.0,>=1.4.0 (from mypy->Transcrypt)
Downloading https://files.pythonhosted.org/packages/a0/03/266268b053ad81b8aea17bc3f5e6d3cf074bb4372d229336867a67e17076/typed_ast-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl (215kB)
100% |████████████████████████████████| 225kB 1.1MB/s
Installing collected packages: mypy-extensions, typing-extensions, typed-ast, mypy, Transcrypt
Running setup.py install for Transcrypt ... done
Successfully installed Transcrypt-3.7.16 mypy-0.730 mypy-extensions-0.4.1 typed-ast-1.4.0 typing-extensions-3.7.4
使い方
個人的にドキュメントがわかりにくくて、少し使い方を調べたので記載する。
- ヘルプ出力
transcrypt -h
でヘルプが出力できる。これでオプションとして何が指定できるか確認できる。
Transcrypt (TM) Python to JavaScript Small Sane Subset Transpiler Version 3.7.16
Copyright (C) Geatec Engineering. License: Apache 2.0
usage: transcrypt [-h] [-a] [-am] [-b] [-c] [-d] [-da] [-dc] [-de] [-dl] [-dm]
[-dn] [-ds] [-dt] [-e [ESV]] [-ec] [-f] [-g] [-i] [-jc]
[-jk] [-jm] [-k] [-kc] [-l] [-m] [-n] [-o] [-p [PARENT]]
[-r] [-s [SYMBOLS]] [-sf] [-t] [-u [UNIT]] [-v] [-x X] [-xr]
[-xg] [-xp [XPATH]] [-xt] [-*]
[source]
positional arguments:
source .py file containing source code of main module
optional arguments:
-h, --help show this help message and exit
-a, --anno annotate target files that were compiled from Python
with source file names and source line numbers
-am, --alimod use aliasing for module paths
-b, --build rebuild all target files from scratch
-c, --complex enable complex number support, locally requires
operator overloading
-d, --docat enable __doc__ attributes. Apply sparsely, since it
will make docstrings part of the generated code
-da, --dassert debug: activate assertions
-dc, --dcheck debug: perform lightweight consistency check
-de, --dextex debug: show extended exception reports
-dl, --dlog debug: log compiler messages to disk
-dm, --dmap debug: dump human readable source map
-dn, --dnostrip debug: no comment stripping of __core__ and
__builtin__ in-line modules
-ds, --dstat debug: validate static typing using annotations
-dt, --dtree debug: dump syntax tree
-e [ESV], --esv [ESV]
ecma script version of generated code, default = 6.
The symbol __esv<versionnr>__ is added to the global
symbol list, e.g. __esv7__.
-ec, --ecom enable executable comments, seen as comments by
CPython but as executable statements by Transcrypt
-f, --fcall enable fastcall mechanism by default. You can also use
__pragma__ ('fcal') and __pragma__ ('nofcall')
-g, --gen enable generators and iterators. Disadvised, since it
will result in a function call for each loop
iteration. Preferably use __pragma__ ('gen') and
__pragma__ ('nogen')
-i, --iconv enable automatic conversion to iterable by default.
Disadvised, since it will result in a type check for
each for-loop. Preferably use __pragma__ ('iconv') and
__pragma__ ('noiconv') to enable automatic conversion
locally
-jc, --jscall enable native JavaScript calls for Python methods.
This is fast, but doesn't support bound method
assignment, decorators and non-instance methods.
Preferably use __pragma__ ('jscall') and __pragma__
('nojscall') to enable native JavaScript calls locally
-jk, --jskeys interpret {key: 'value'} as {'key': 'value'} and
forbid {key (): 'value'}, as JavaScript does.
Disadvised, since it's less flexible than the Python
interpretation. Either follow Python semantics by
using {'key': 'value'} explicitly if you want literal
keys or use __pragma__ ('jskeys') and __pragma__
('nojskeys') locally instead to make clear local
deviation from Python semantics
-jm, --jsmod give % and %= JavaScript rather than Python behaviour.
Disadvised, since it deviates from the mathematical
'modulo' operator. Either follow Python semantics or
use __pragma__ ('jsmod') and __pragma__ ('nojsmod')
locally instead to make clear local deviation.
-k, --kwargs enable keyword arguments by default. In general this
is disadvised, use __pragma__ ('kwargs') and
__pragma__('nokwargs') locally instead to prevent
bloated code
-kc, --keycheck enable checking for existence of dictionary keys. In
general this is disadvised, use __pragma__
('keycheck') and __pragma__('nokeycheck') locally
instead to prevent bloated code
-l, --license show license
-m, --map generate source map
-n, --nomin no minification
-o, --opov enable operator overloading by default. In general
this is disadvised, use __pragma__ ('opov') and
__pragma__('noopov') locally instead to prevent slow
code
-p [PARENT], --parent [PARENT]
object that will hold application, default is window.
Use -p .none to generate orphan application, e.g. for
use in node.js
-r, --run run source file rather than compiling it
-s [SYMBOLS], --symbols [SYMBOLS]
names, joined by $, separately passed to main module
in __symbols__ variable
-sf, --sform enable support for string formatting mini language
-t, --tconv enable automatic conversion to truth value by default.
Disadvised, since it will result in a conversion for
each boolean. Preferably use __pragma__ ('tconv') and
__pragma__ ('notconv') to enable automatic conversion
locally
-u [UNIT], --unit [UNIT]
compile to units rather than to monolithic
application. Use -u .auto to autogenerate dynamically
loadable native JavaScript modules, one per Python
module. Use -u .run to generate the loader and the
staticcally loadable runtime unit. Use -u .com to
generate a statically loadable component unit.
-v, --verbose show all messages
-x X, --x X reserved for extended options
-xr, --xreex re-export all imported names
-xg, --xglobs allow use of the 'globals' function
-xp [XPATH], --xpath [XPATH]
additional module search paths, joined by $, #'s will
be replaced by spaces
-xt, --xtiny generate tiny version of runtime, a.o. lacking support
for implicit and explicit operator overloading. Use
only if generated code can be validated, since it will
introduce semantic alterations in edge cases
-*, --star Like it? Grow it! Go to GitHub and then click [* Star]
Ready
1. Transcrypt: what and why — Transcrypt 3.7.16 documentation
に記載されているプログラムを変換する。
transcrypt classes.py
を実行すると、カレントディレクトリに、__target__
というディレクトリが作成されその中に、JavaScriptが生成される。
six の import に失敗する
sixを使用しているPythonスクリプトを変換しようとしたところ、以下のエラーが発生した。
Error while compiling (offending file last):
File 'allpairs', line 10, namely:
Import error, can't find any of:
/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/six/moves.py
/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/six/moves.js
/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/six/moves.py
/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/six/moves.js
sixのmovesというモジュールのimportに失敗している。
Six: Python 2 と 3 の互換性ライブラリ — six 1.9.0 ドキュメント を引用する。
Python 3 は標準ライブラリの再編成を行い、いくつかの機能は異なるモジュールに移動しました。 Six は six.moves なるフェイクを介すことで、それらに一貫したインターフェイスを提供しています。例えば、HTML を解析するモジュールをロードするために、このようにします:
Pythonではフェイクモジュールを作成できる。おそらくこの形式のモジュールがあると、importに失敗するような動きをしている。
Use a Python class as a fake module
CPython モジュールの import に失敗する。
sixがimport出来ないので、対象ライブラリをPython3文法で書き直し、再度変換をかけたところ以下のエラーが発生した。
Error while compiling (offending file last):
File '/allpairspy/__init__.py', line 11, at import of:
File 'allpairspy/allpairs.py', line 5, at import of:
File 'functools', line 246, namely:
Can't import module 'functools'
Aborted
functools
はcpython/Lib at master · python/cpython のモジュールで以下のIssue にCPythonのモジュールはあえて含めていない旨が記載されている。
- Attempt to load module sys · Issue #115 · QQuick/Transcrypt
- Cannot find import with underscore "_functools" · Issue #541 · QQuick/Transcrypt
関数などで、ソース移植で対応できるものもあるが、cpython/init.py at master · python/cpython のOrderedDictの移植が必要になり、これは無理だと思って諦めた。