diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index d104660e..4ca336dc 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -159,8 +159,6 @@ jobs: poetry run python -m pip install --force-reinstall --verbose ./dist/* poetry run python -m pytest tests/python - name: Run JS tests (peter-jr) - if: ${{ runner.os != 'Windows' }} # Python on Windows doesn't have the readline library - # FIXME: on macOS we must make sure to use the GNU version of wc and realpath run: | poetry run bash ./peter-jr ./tests/js/ sdist: diff --git a/poetry.lock b/poetry.lock index 2be5c526..62d1a77f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -148,6 +148,17 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pyreadline3" +version = "3.4.1" +description = "A python implementation of GNU readline." +optional = false +python-versions = "*" +files = [ + {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, + {file = "pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae"}, +] + [[package]] name = "pytest" version = "7.4.0" @@ -184,4 +195,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "b6c04db7deac0a2e9850f6ea1e00e2a00c7e612dfc765171864ca906386e2878" +content-hash = "f4de4ae4242c925c62044ba107e732a45941c0568a3c434e3daa446da24956de" diff --git a/pyproject.toml b/pyproject.toml index 599eaf10..3109227a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ include = [ [tool.poetry.dependencies] python = "^3.8" +pyreadline3 = "^3.4.1" pminit = { version = "*", allow-prereleases = true } diff --git a/python/pythonmonkey/cli/pmjs.py b/python/pythonmonkey/cli/pmjs.py index 016b7fe0..6fa15c54 100755 --- a/python/pythonmonkey/cli/pmjs.py +++ b/python/pythonmonkey/cli/pmjs.py @@ -3,13 +3,17 @@ # @author Wes Garland, wes@distributive.network # @date June 2023 -import sys, os, readline, signal, getopt +import sys, os, signal, getopt +try: + import readline # Unix +except ImportError: + import pyreadline3 as readline # Windows import pythonmonkey as pm globalThis = pm.eval("globalThis") evalOpts = { 'filename': __file__, 'fromPythonFrame': True, 'strict': False } if (os.getenv('PMJS_PATH')): - requirePath = list(map(os.path.abspath, os.getenv('PMJS_PATH').split(':'))) + requirePath = list(map(os.path.abspath, os.getenv('PMJS_PATH').split(','))) else: requirePath = False; @@ -108,7 +112,7 @@ * like that which is also a valid compilation unit with parens, then if that is a syntax error, * we re-evaluate without the parens. */ - if (/^\\s*\{.*[^;\\s]\\s*$/.test(statement)) + if (/^\\s*\\{.*[^;\\s]\\s*$/.test(statement)) { const testStatement = `(${statement})`; if (globalThis.python.pythonMonkey.isCompilableUnit(testStatement)) @@ -195,8 +199,7 @@ def sigint_handler(signum, frame): got_sigint = got_sigint + 1 if (got_sigint > 1): - sys.stdout.write("\n") - quit() + raise EOFError if (inner_loop != True): if (got_sigint == 1 and len(readline.get_line_buffer()) == readline_skip_chars): @@ -304,7 +307,7 @@ def initGlobalThis(): require = pm.createRequire(os.path.abspath(os.getcwd() + '/__pmjs_virtual__'), requirePath) globalThis.require = require - globalInitModule = require(os.path.dirname(__file__) + "/../lib/pmjs/global-init") # module load has side-effects + globalInitModule = require(os.path.realpath(os.path.dirname(__file__) + "/../lib/pmjs/global-init")) # module load has side-effects argvBuilder = globalInitModule.makeArgvBuilder() for arg in sys.argv: argvBuilder(arg); # list=>Array not working yet diff --git a/python/pythonmonkey/require.py b/python/pythonmonkey/require.py index aa79c3fb..83c238aa 100644 --- a/python/pythonmonkey/require.py +++ b/python/pythonmonkey/require.py @@ -23,7 +23,7 @@ # @date May 2023 # -import sys, os +import sys, os, io from typing import Union, Dict, Literal, List import importlib import importlib.util @@ -42,6 +42,12 @@ ) evalOpts = { 'filename': __file__, 'fromPythonFrame': True } +# Force to use UTF-8 encoding +# Windows may use other encodings / code pages that have many characters missing/unrepresentable +# Error: Python UnicodeEncodeError: 'charmap' codec can't encode characters in position xx-xx: character maps to +sys.stdout.reconfigure(encoding='utf-8') +sys.stderr.reconfigure(encoding='utf-8') + # Add some python functions to the global python object for code in this file to use. globalThis = pm.eval("globalThis;", evalOpts) pm.eval("globalThis.python = { pythonMonkey: {}, stdout: {}, stderr: {} }", evalOpts); @@ -59,8 +65,7 @@ globalThis.python.eval = eval globalThis.python.exec = exec globalThis.python.getenv = os.getenv -globalThis.python.paths = ':'.join(sys.path) -pm.eval("python.paths = python.paths.split(':');", evalOpts); # fix when pm supports arrays +globalThis.python.paths = sys.path globalThis.python.exit = pm.eval("""'use strict'; (exit) => function pythonExitWrapper(exitCode) { @@ -268,6 +273,7 @@ def _createRequireInner(*args): */ function createRequire(filename, bootstrap_broken, extraPaths, isMain) { + filename = filename.split('\\\\').join('/'); const bootstrap = globalThis.bootstrap; /** @bug PM-65 */ const CtxModule = bootstrap.modules['ctx-module'].CtxModule; const moduleCache = globalThis.require?.cache || {}; @@ -282,7 +288,7 @@ def _createRequireInner(*args): const module = new CtxModule(globalThis, filename, moduleCache); moduleCache[filename] = module; - for (let path of python.paths) + for (let path of Array.from(python.paths)) module.paths.push(path + '/node_modules'); module.require.path.push(python.pythonMonkey.dir + '/builtin_modules'); module.require.path.push(python.pythonMonkey.nodeModules); @@ -299,7 +305,7 @@ def _createRequireInner(*args): } if (extraPaths) - module.require.path.splice(module.require.path.length, 0, ...(extraPaths.split(':'))); + module.require.path.splice(module.require.path.length, 0, ...(extraPaths.split(','))); return module.require; })""", evalOpts)(*args) diff --git a/tests/js/commonjs-modules.bash b/tests/js/commonjs-modules.bash index 636533ba..c805a89b 100755 --- a/tests/js/commonjs-modules.bash +++ b/tests/js/commonjs-modules.bash @@ -20,6 +20,7 @@ runTest() echo -n "${testName}: " PMJS_PATH="`pwd`" pmjs -e 'print=python.print' program.js\ + | tr -d '\r'\ | while read word rest do case "$word" in diff --git a/tests/js/console-stdio.bash b/tests/js/console-stdio.bash index e0584ad8..e85d02e9 100755 --- a/tests/js/console-stdio.bash +++ b/tests/js/console-stdio.bash @@ -22,6 +22,7 @@ cd `dirname "$0"` || panic "could not change to test directory" -e 'console.debug("stdout")' \ -e 'console.info("stdout")' \ < /dev/null \ +| tr -d '\r' \ | grep -c '^stdout$' \ | while read qty do @@ -34,6 +35,7 @@ cd `dirname "$0"` || panic "could not change to test directory" -e 'console.error("stderr")' \ -e 'console.warn("stderr")' \ < /dev/null 2>&1 \ +| tr -d '\r' \ | grep -c '^stderr$' \ | while read qty do diff --git a/tests/js/pmjs-eopt.bash b/tests/js/pmjs-eopt.bash index 677b69c8..52c2e196 100755 --- a/tests/js/pmjs-eopt.bash +++ b/tests/js/pmjs-eopt.bash @@ -18,6 +18,7 @@ panic() cd `dirname "$0"` || panic "could not change to test directory" "${PMJS:-pmjs}" -e 'console.log("OKAY")' < /dev/null |\ +tr -d '\r' |\ while read keyword rest do case "$keyword" in diff --git a/tests/js/pmjs-global-arguments.bash b/tests/js/pmjs-global-arguments.bash index e91bc423..ac42539d 100755 --- a/tests/js/pmjs-global-arguments.bash +++ b/tests/js/pmjs-global-arguments.bash @@ -20,6 +20,7 @@ cd `dirname "$0"` || panic "could not change to test directory" argc=0 "${PMJS:-pmjs}" program.js abc easy as one two three |\ +tr -d '\r' |\ while read keyword rest do case "$keyword" in diff --git a/tests/js/pmjs-interactive-smoke.bash b/tests/js/pmjs-interactive-smoke.bash index 26dfd7d3..5814ddcc 100755 --- a/tests/js/pmjs-interactive-smoke.bash +++ b/tests/js/pmjs-interactive-smoke.bash @@ -30,7 +30,7 @@ a; b(${rnd}); EOF )\ -| cat -u | while read prompt keyword rest +| cat -u | tr -d '\r' | while read prompt keyword rest do case "$keyword" in "...") diff --git a/tests/js/pmjs-popt.bash b/tests/js/pmjs-popt.bash index b1c4c419..f108d8e8 100755 --- a/tests/js/pmjs-popt.bash +++ b/tests/js/pmjs-popt.bash @@ -18,6 +18,7 @@ panic() cd `dirname "$0"` || panic "could not change to test directory" "${PMJS:-pmjs}" -p '"OKAY"' < /dev/null |\ +tr -d '\r' |\ while read keyword rest do case "$keyword" in diff --git a/tests/js/pmjs-require-cache.bash b/tests/js/pmjs-require-cache.bash index c4b16de4..6b39ac08 100755 --- a/tests/js/pmjs-require-cache.bash +++ b/tests/js/pmjs-require-cache.bash @@ -20,6 +20,7 @@ cd `dirname "$0"` || panic "could not change to test directory" loaded=0 "${PMJS:-pmjs}" -r ./modules/print-load -r ./modules/print-load program.js |\ +tr -d '\r' |\ while read keyword rest do case "$keyword" in diff --git a/tests/js/pmjs-ropt.bash b/tests/js/pmjs-ropt.bash index 4894b57c..f5a4b897 100755 --- a/tests/js/pmjs-ropt.bash +++ b/tests/js/pmjs-ropt.bash @@ -18,6 +18,7 @@ panic() cd `dirname "$0"` || panic "could not change to test directory" "${PMJS:-pmjs}" -r ./modules/print-load < /dev/null |\ +tr -d '\r' |\ while read keyword rest do case "$keyword" in diff --git a/tests/js/typeofs.simple b/tests/js/typeofs.simple index 7f168776..e9e56859 100755 --- a/tests/js/typeofs.simple +++ b/tests/js/typeofs.simple @@ -9,7 +9,7 @@ 'use strict'; const throughJS = x => x; -const throughBoth = python.eval('(lambda x: throughJS(x))', { throughJS }); +const throughBoth = python.eval('(lambda func: lambda x: func(x))')(throughJS); function check(jsval, expected) {