diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ceb627..cb1dfe4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,30 +7,35 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] - python-version: [3.6, 3.7, 3.8] - exclude: - - os: macos-latest - python-version: 3.8 + os: [ubuntu-latest] + # Python 3.8 is the newest interpreter for which the pinned + # dependencies (matplotlib==3.5.1, pyjulia==0.5.7) resolve a + # compatible NumPy 1.x. Newer interpreters pull NumPy 2.x and break + # matplotlib 3.5.1's ABI. + python-version: ['3.8'] steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 with: - python-version: 3.7 - - uses: actions/checkout@v2.4.0 - - uses: julia-actions/setup-julia@v1 + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - version: '1.4.1' - - name: Install Julia dependencies - run: | - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/APLA-Toolbox/PDDL.jl"))' - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/JuliaPy/PyCall.jl"))' + python-version: ${{ matrix.python-version }} + - uses: julia-actions/setup-julia@v2 + with: + version: '1.5.2' - name: Install Python dependencies run: | python -m pip install --upgrade pip python -m pip install -r requirements.txt + - name: Install Julia dependencies + # Build PyCall against *this* Python interpreter (via pyjulia's + # installer) so the in-process bridge used by jupyddl works, then add + # the PDDL.jl parser fork. + run: | + python -c "import julia; julia.install()" + julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))' - name: Lint with flake8 run: | python -m pip install flake8 @@ -38,8 +43,3 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Checkout reposistory - uses: actions/checkout@master - - name: Checkout submodules - uses: snickerbockers/submodules-init@v4 - diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1557cb5..760ac12 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,31 +7,28 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] - python-version: [3.6, 3.7, 3.8] - exclude: - - os: macos-latest - python-version: 3.8 + os: [ubuntu-latest] + python-version: ['3.8'] steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 with: - python-version: 3.7 - - uses: actions/checkout@v2.4.0 - - uses: julia-actions/setup-julia@v1 + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - version: '1.4.1' - - name: Install Julia dependencies - run: | - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/APLA-Toolbox/PDDL.jl"))' - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/JuliaPy/PyCall.jl"))' + python-version: ${{ matrix.python-version }} + - uses: julia-actions/setup-julia@v2 + with: + version: '1.5.2' - name: Install Python dependencies run: | python -m pip install --upgrade pip - python -m pip install julia - python -m pip install pycall + python -m pip install -r requirements.txt + - name: Install Julia dependencies + run: | + python -c "import julia; julia.install()" + julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))' - name: Lint with flake8 run: | python -m pip install flake8 @@ -39,55 +36,45 @@ jobs: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Checkout reposistory - uses: actions/checkout@master - - name: Checkout submodules - uses: snickerbockers/submodules-init@v4 test: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] - python-version: [3.6, 3.7, 3.8] - exclude: - - os: macos-latest - python-version: 3.8 + os: [ubuntu-latest] + python-version: ['3.8'] steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 with: - python-version: 3.7 - - uses: actions/checkout@v2.4.0 - - uses: julia-actions/setup-julia@v1 + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - version: '1.4.1' - - name: Install Julia dependencies - run: | - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/JuliaPy/PyCall.jl"))' - julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(path="https://github.com/APLA-Toolbox/PDDL.jl"))' + python-version: ${{ matrix.python-version }} + - uses: julia-actions/setup-julia@v2 + with: + version: '1.5.2' - name: Install Python dependencies run: | python -m pip install --upgrade pip python -m pip install -r requirements.txt + python -m pip install flake8 pytest pytest-cov + - name: Install Julia dependencies + # Build PyCall against this Python (needed so pytest can import the + # jupyddl -> PDDL.jl bridge), then add the PDDL.jl parser fork. + run: | + python -c "import julia; julia.install()" + julia --color=yes -e 'using Pkg; Pkg.add(Pkg.PackageSpec(url="https://github.com/APLA-Toolbox/PDDL.jl"))' - name: Lint with flake8 run: | - python -m pip install flake8 # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Checkout reposistory - uses: actions/checkout@master - - name: Checkout submodules - uses: snickerbockers/submodules-init@v4 - name: Test with pytest run: | - pip install pytest - pip install pytest-cov pytest --cov=./ - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v4 with: name: codecov-umbrella diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..327bf00 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,49 @@ +# AGENTS.md + +## Cursor Cloud specific instructions + +`jupyddl` (PythonPDDL) is a Python library + CLI for PDDL automated planning. It is a +thin wrapper around the Julia `PDDL.jl` parser, bridged through `pyjulia`/`PyCall.jl`. +There are no servers/databases — "running the app" means parsing PDDL domain/problem +files and running the planners, either via the library or the `scripts/ipc.py` CLI. + +### Environment layout (baked into the VM snapshot) +- Julia 1.5.2 in `/opt/julia-1.5.2` (symlinked at `/usr/local/bin/julia`). +- Python 3.8 venv at `.venv`, built from the **deadsnakes** `/usr/bin/python3.8`. +- Julia packages `PyCall.jl` + the `APLA-Toolbox/PDDL.jl` fork live in `~/.julia`. + `PyCall` is built against `.venv/bin/python`. +- `.venv`, `logs/*`, and the `pddl-examples` submodule contents are git-ignored. + +### Running / testing / linting +Always use the venv interpreter and run from the repo root (relative `pddl-examples/...` +paths and the auto-created `logs/` dir depend on CWD): +- Library / hello-world: `.venv/bin/python -c "from jupyddl import AutomatedPlanner; ..."` +- CLI: `cd scripts && ../.venv/bin/python ipc.py ` +- Tests: `.venv/bin/python -m pytest --cov=./` (from repo root). +- Lint (as CI): `.venv/bin/python -m flake8 . --select=E9,F63,F7,F82` is the build-gating + check; the second CI `flake8` pass uses `--exit-zero` (style warnings only, non-blocking). + +### Non-obvious gotchas +- **pyjulia needs a dynamically-linked Python.** The `.venv` intentionally uses the + deadsnakes `python3.8` (dynamically linked). Do **not** rebuild the venv from a + `uv`-managed / python-build-standalone interpreter — those are statically linked to + libpython and break the in-process Julia bridge. +- **If the venv is recreated at a different path, rebuild PyCall** so it points at the new + interpreter: `PYTHON=/workspace/.venv/bin/python .venv/bin/python -c "import julia; julia.install()"`. +- **matplotlib backend.** `jupyddl/data_analyst.py` picks `TkAgg` when `DISPLAY` is set and + `Agg` otherwise. The VM has a virtual display (`DISPLAY=:1`) and `python3.8-tk` is + installed, so the default import works. For a purely headless run, invoke with + `env -u DISPLAY ...` (or `MPLBACKEND=Agg` when the import path allows it) to force `Agg`. +- **`DISPLAY` changes the test outcome.** `DataAnalyst.__get_all_pddl_from_data` only walks + the whole `pddl-examples/` folder when `DISPLAY` is set; otherwise it returns a hardcoded + `dinner`-only list. Headless (no `DISPLAY`, as in CI) the full suite is green — run tests + with `env -u DISPLAY .venv/bin/python -m pytest` to reproduce CI (all 77 pass). With this + VM's `DISPLAY=:1`, 19 `DataAnalyst` tests fail with `PyCall.jlwrap ... 'domain' keyword is + missing`: the folder walk pairs files from an unsorted `os.walk` assuming `domain.pddl` + precedes `problem.pddl`, but the `pallet` example is ordered the other way, so a problem + file is parsed as a domain. That is a pre-existing code bug, not an environment issue. +- **CI (`.github/workflows/*.yml`) is pinned to Python 3.8 on `ubuntu-latest`.** 3.8 is the + newest interpreter whose resolved deps keep NumPy at 1.x (matplotlib 3.5.1 breaks on NumPy + 2.x). macOS runners were dropped: they are arm64, which has no Julia 1.5.2 build and no EOL + Python build. CI builds PyCall against the runner Python with `python -c "import julia; + julia.install()"` before adding `PDDL.jl`.