From 42ccb3ac9dc3fd073c03d724609a28153202b6ae Mon Sep 17 00:00:00 2001 From: Alessandro Molina Date: Mon, 11 Apr 2016 00:01:16 +0200 Subject: [PATCH] Experimental minimal installer for js modules from npmjs.org --- dukpy/__init__.py | 3 +- dukpy/evaljs.py | 3 +- dukpy/install.py | 80 +++++++++++++++++++++++++++++++++++++++++ dukpy/module_loader.py | 2 +- setup.py | 7 +++- tests/test_installer.py | 44 +++++++++++++++++++++++ 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 dukpy/install.py create mode 100644 tests/test_installer.py diff --git a/dukpy/__init__.py b/dukpy/__init__.py index 7dfe272..8eb4998 100644 --- a/dukpy/__init__.py +++ b/dukpy/__init__.py @@ -2,4 +2,5 @@ from .evaljs import evaljs, JSInterpreter from ._dukpy import JSRuntimeError from .coffee import coffee_compile from .babel import babel_compile, jsx_compile -from .tsc import typescript_compile \ No newline at end of file +from .tsc import typescript_compile +from .install import install_jspackage \ No newline at end of file diff --git a/dukpy/evaljs.py b/dukpy/evaljs.py index 69e2b9a..d3430fc 100644 --- a/dukpy/evaljs.py +++ b/dukpy/evaljs.py @@ -95,7 +95,8 @@ class JSInterpreter(object): var m = call_python('dukpy.lookup_module', id); if (!m) throw new Error('cannot find module: ' + id); - return m; + module.filename = m[0]; + return m[1]; }; """) diff --git a/dukpy/install.py b/dukpy/install.py new file mode 100644 index 0000000..9fc3aa4 --- /dev/null +++ b/dukpy/install.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +from __future__ import print_function +import json +import os +import sys +import tarfile +import tempfile +import shutil +from contextlib import closing +from io import BytesIO + +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen + + +def main(): + args = sys.argv[1:] + try: + package_name = args[0] + version = args[1] + except: + print('Usage: dukpy-install package_name version') + print('') + print('Downloads a specific package version from npmjs.org. Note this is' + 'a very basic script that does not support dependencies.') + return 1 + + install_jspackage(package_name, version, './node_modules') + + +def install_jspackage(package_name, version, modulesdir): + """Installs a JavaScript package downloaded from npmjs.org. + + For example to install React:: + + install_jspackage('react', '0.14.8', './node_modules') + """ + url = 'http://registry.npmjs.org/{}' + with closing(urlopen(url.format(package_name))) as data: + package_info = json.loads(data.read()) + package_versions = package_info['versions'] + version_info = package_versions.get(version) + if version_info is None: + print('Version {} not found, available versions are {}'.format( + version, ', '.join(sorted(package_versions.keys())) + )) + return 2 + + try: + download_url = version_info['dist']['tarball'] + except KeyError: + print('Unable to detect a supported download url for package') + return 3 + + tarball = BytesIO() + print('Downloading {}'.format(download_url)) + with closing(urlopen(download_url)) as data: + chunk = data.read(1024) + while chunk: + print('.', end='') + tarball.write(chunk) + chunk = data.read(1024) + print('') + + tarball.seek(0) + print('Extracting... ') + with tarfile.open(fileobj=tarball) as tb: + dest = os.path.join(modulesdir, version_info['name']) + tmpdir = tempfile.mkdtemp() + try: + tb.extractall(tmpdir) + shutil.move(os.path.join(tmpdir, 'package'), + os.path.abspath(dest)) + finally: + shutil.rmtree(tmpdir) + + print('Installed in {}'.format(dest)) + diff --git a/dukpy/module_loader.py b/dukpy/module_loader.py index 948f2db..0349cf3 100644 --- a/dukpy/module_loader.py +++ b/dukpy/module_loader.py @@ -40,7 +40,7 @@ class JSModuleLoader(object): path = self.lookup(module_name) if path: with open(path, 'rb') as f: - return f.read().decode('utf-8') + return path, f.read().decode('utf-8') def _lookup(self, module_path): # Module is a plain .js file diff --git a/setup.py b/setup.py index b17a75d..5c4eb80 100644 --- a/setup.py +++ b/setup.py @@ -40,5 +40,10 @@ setup( 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.6', 'Programming Language :: JavaScript', - ] + ], + entry_points={ + 'console_scripts': [ + 'dukpy-install = dukpy.install:main' + ] + } ) diff --git a/tests/test_installer.py b/tests/test_installer.py new file mode 100644 index 0000000..f8fa010 --- /dev/null +++ b/tests/test_installer.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +import shutil +import tempfile + +import dukpy + + +class TestPackageInstaller(object): + def setup(self): + self.tmpdir = tempfile.mkdtemp() + + def tearDown(self): + #shutil.rmtree(self.tmpdir) + pass + + def test_install_react(self): + dukpy.install_jspackage('react', '0.14.8', self.tmpdir) + dukpy.install_jspackage('react-dom', '0.14.8', self.tmpdir) + dukpy.install_jspackage('fbjs', '0.8.0', self.tmpdir) + + jsx = dukpy.jsx_compile(TEST_CODE) + + jsi = dukpy.JSInterpreter() + jsi.loader.register_path(self.tmpdir) + res = jsi.evaljs(jsx, data={'id': 1, 'name': "Alessandro"}) + assert res == '
Hello Alessandro
', res + + +TEST_CODE = ''' +var React = require('react/react'), + ReactDOM = require('react-dom/server'); + +var HelloWorld = React.createClass({ + render: function() { + return ( +
+ Hello {this.props.data.name} +
+ ); + } +}); + +ReactDOM.renderToStaticMarkup(, null); +'''