diff --git a/.github/workflows/pyodide.yml b/.github/workflows/pyodide.yml new file mode 100644 index 000000000..c5d5f655e --- /dev/null +++ b/.github/workflows/pyodide.yml @@ -0,0 +1,64 @@ +# Copied from SymPy https://github.com/sympy/sympy/pull/27183 + +name: Mathics3 (Pyodide) + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + pyodide-test: + runs-on: ubuntu-latest + env: + PYODIDE_VERSION: 0.27.0a2 + # PYTHON_VERSION and EMSCRIPTEN_VERSION are determined by PYODIDE_VERSION. + # The appropriate versions can be found in the Pyodide repodata.json + # "info" field, or in Makefile.envs: + # https://github.com/pyodide/pyodide/blob/main/Makefile.envs#L2 + PYTHON_VERSION: 3.12.1 + EMSCRIPTEN_VERSION: 3.1.58 + NODE_VERSION: 20 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python ${{ env.PYTHON_VERSION }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Set up Emscripten toolchain + uses: mymindstorm/setup-emsdk@v14 + with: + version: ${{ env.EMSCRIPTEN_VERSION }} + actions-cache-folder: emsdk-cache + + - name: Install pyodide-build + run: pip install pyodide-build + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Set up Pyodide virtual environment and run tests + run: | + # Set up Pyodide virtual environment + pyodide xbuildenv install ${{ env.PYODIDE_VERSION }} + pyodide venv .venv-pyodide + + # Activate the virtual environment + source .venv-pyodide/bin/activate + + pip install "setuptools>=70.0.0" PyYAML click packaging pytest + + git clone https://github.com/Mathics3/mathics-scanner.git + cd mathics-scanner/ + pip install --no-build-isolation -e . + cd .. + + make mathics/data/op-tables.json mathics/data/operator-tables.json + pip install --no-build-isolation -e . + make -j3 check diff --git a/.gitignore b/.gitignore index cdf405b6c..68aa74947 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ /.cache /.gdbinit /.python-version +/.pyodide-xbuildenv-* /Mathics.egg-info /Mathics3.egg-info ChangeLog diff --git a/mathics/builtin/file_operations/file_properties.py b/mathics/builtin/file_operations/file_properties.py index d16fa5d2e..0a6dd26ff 100644 --- a/mathics/builtin/file_operations/file_properties.py +++ b/mathics/builtin/file_operations/file_properties.py @@ -241,9 +241,6 @@ class SetFileDate(Builtin): >> SetFileDate[tmpfilename, {2002, 1, 1, 0, 0, 0.}, "Access"]; - >> FileDate[tmpfilename, "Access"] - = {2002, 1, 1, 0, 0, 0.} - #> DeleteFile[tmpfilename] """ diff --git a/mathics/builtin/files_io/files.py b/mathics/builtin/files_io/files.py index dd7df7146..3746d3915 100644 --- a/mathics/builtin/files_io/files.py +++ b/mathics/builtin/files_io/files.py @@ -461,8 +461,6 @@ class OpenRead(_OpenAction): The stream must be closed after using it to release the resource: >> Close[%]; - - S> Close[OpenRead["https://raw.githubusercontent.com/Mathics3/mathics-core/master/README.rst"]]; """ summary_text = "open a file for reading" diff --git a/test/builtin/files_io/test_files.py b/test/builtin/files_io/test_files.py index 7042a728b..a66d1d821 100644 --- a/test/builtin/files_io/test_files.py +++ b/test/builtin/files_io/test_files.py @@ -99,6 +99,10 @@ def test_close(): ), f"temporary filename {temp_filename} should not appear" +@pytest.mark.skipif( + sys.platform in ("emscripten",), + reason="Pyodide has restricted filesystem access", +) @pytest.mark.parametrize( ("str_expr", "msgs", "str_expected", "fail_msg"), [ @@ -147,6 +151,12 @@ def test_close(): "OpenRead[]", "", ), + ( + 'Close[OpenRead["https://raw.githubusercontent.com/Mathics3/mathics-core/master/README.rst"]];', + None, + "Null", + "", + ), ( 'fd=OpenRead["ExampleData/EinsteinSzilLetter.txt", BinaryFormat -> True, CharacterEncoding->"UTF8"]//Head', None, @@ -314,6 +324,16 @@ def test_close(): ), ("FilePrint[pathname]", None, "Null", ""), ("DeleteFile[pathname];Clear[pathname];", None, "Null", ""), + ('tmpfilename = $TemporaryDirectory <> "/tmp0";', None, "Null", ""), + ("Close[OpenWrite[tmpfilename]];", None, "Null", ""), + ( + 'SetFileDate[tmpfilename, {2002, 1, 1, 0, 0, 0.}, "Access"];', + None, + "Null", + "", + ), + ('FileDate[tmpfilename, "Access"]', None, "{2002, 1, 1, 0, 0, 0.}", ""), + ("DeleteFile[tmpfilename]", None, "Null", ""), ], ) def test_private_doctests_files(str_expr, msgs, str_expected, fail_msg): diff --git a/test/builtin/test_datentime.py b/test/builtin/test_datentime.py index 0fc894483..01583ecfd 100644 --- a/test/builtin/test_datentime.py +++ b/test/builtin/test_datentime.py @@ -11,7 +11,7 @@ @pytest.mark.skipif( - sys.platform in ("win32",) or hasattr(sys, "pyston_version_info"), + sys.platform in ("win32", "emscripten") or hasattr(sys, "pyston_version_info"), reason="TimeConstrained needs to be rewritten", ) def test_timeremaining(): diff --git a/test/test_main.py b/test/test_main.py index 982e593e1..dd5671e2b 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -2,6 +2,9 @@ import os.path as osp import re import subprocess +import sys + +import pytest def get_testdir(): @@ -9,6 +12,10 @@ def get_testdir(): return osp.realpath(filename) +@pytest.mark.skipif( + sys.platform in ("emscripten",), + reason="Pyodide does not support processes", +) def test_cli(): script_file = osp.join(get_testdir(), "data", "script.m") diff --git a/test/test_returncode.py b/test/test_returncode.py index a1eb67dc8..1e1d11abb 100644 --- a/test/test_returncode.py +++ b/test/test_returncode.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- import os.path as osp import subprocess +import sys + +import pytest def get_testdir(): @@ -8,6 +11,10 @@ def get_testdir(): return osp.realpath(filename) +@pytest.mark.skipif( + sys.platform in ("emscripten",), + reason="Pyodide does not support processes", +) def test_returncode(): assert subprocess.run(["mathics", "-e", "Quit[5]"]).returncode == 5 assert subprocess.run(["mathics", "-e", "1 + 2'"]).returncode == 0