From a93936467b43c13616ac63ba3cf3bbdf3e43c494 Mon Sep 17 00:00:00 2001 From: Rushin Gindra Date: Fri, 11 Sep 2026 15:01:47 +0200 Subject: [PATCH 1/2] package dependency update + enabling worktrees --- .gitignore | 5 +++++ .vscode/settings.json | 6 +++++- .worktreeinclude | 2 ++ README.md | 2 +- pyproject.toml | 19 ++++++++++++++----- 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 .worktreeinclude diff --git a/.gitignore b/.gitignore index 0a0c395..66dc177 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,8 @@ __pycache__/ # docs /docs/generated/ /docs/_build/ + +# local scratch +/graphify-out/ +/scripts/ +CLAUDE.md diff --git a/.vscode/settings.json b/.vscode/settings.json index e034b91..297e39a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,5 +14,9 @@ }, "python.analysis.typeCheckingMode": "basic", "python.testing.pytestEnabled": true, - "python.testing.pytestArgs": ["-vv", "--color=yes"], + "python.testing.pytestArgs": [ + "-vv", + "--color=yes" + ], + "python.defaultInterpreterPath": "", } diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 0000000..e978110 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,2 @@ +/scripts/ +CLAUDE.md \ No newline at end of file diff --git a/README.md b/README.md index 00cfe3d..7163d63 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Phoenix is a (latent) flow matching generative model that predicts spatially res ## Installation -You need to have Python 3.11 or newer installed on your system. +You need to have Python 3.12 or newer installed on your system. If you don't have Python installed, we recommend installing [uv][]. We recommend managing dependencies in project-specific virtual environments to avoid dependency conflicts. diff --git a/pyproject.toml b/pyproject.toml index 80e6f27..9e521cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,11 +14,10 @@ maintainers = [ authors = [ { name = "Rushin Gindra" }, ] -requires-python = ">=3.11" +requires-python = ">=3.12" classifiers = [ "License :: Other/Proprietary License", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", @@ -27,7 +26,9 @@ classifiers = [ dependencies = [ "anndata>=0.12.2", "matplotlib", - "numpy", + # numpy 2.x only: the spatialdata/zarr-3 stack resolves to numpy>=2 anyway, and NEP 50 + # promotion semantics differ from 1.x, so a 1.x install would be a different numeric path. + "numpy>=2", "pillow", "pyyaml", "scipy", @@ -42,11 +43,19 @@ dependencies = [ optional-dependencies.full = [ "h5py", "pytorch-lightning", - "spatialdata", + # SpatialDataset reads zarr v3 stores (v2 still reads). spatialdata gained zarr>=3 support and + # dropped the unmaintained xarray-schema (whose pkg_resources import broke against + # setuptools>=81); 0.8 is the first release we've verified end-to-end. It is Python >=3.12 + # only, which is part of why requires-python is 3.12. + "spatialdata>=0.8", "timm", "torch", "torchvision", "vector-quantize-pytorch", + # Explicit floor: we target the zarr v3 on-disk format (v2 stores still read). Redundant with + # spatialdata>=0.8's own zarr>=3 pin, but stated unmarked so pinning an older spatialdata + # conflicts loudly here instead of silently resolving back to zarr-python 2. + "zarr>=3", ] # Nuclei segmentation pipeline (helpers/segmentor.py). optional-dependencies.segmentation = [ @@ -112,7 +121,7 @@ envs.docs.dependency-groups = [ "doc" ] envs.hatch-test.features = [ "full" ] envs.hatch-test.matrix = [ # Test the lowest and highest supported Python versions with normal deps - { deps = [ "stable" ], python = [ "3.11", "3.12", "3.13" ] }, + { deps = [ "stable" ], python = [ "3.12", "3.13" ] }, # Test the newest supported Python version also with pre-release deps { deps = [ "pre" ], python = [ "3.13" ] }, ] From d7c8be9b8a1c2d62a60d500dbcf0693639c23c8e Mon Sep 17 00:00:00 2001 From: Rushin Gindra Date: Fri, 11 Sep 2026 16:16:25 +0200 Subject: [PATCH 2/2] Replace the segmentation extra with spatialrefinery[segmentation] in full `uv sync` failed to resolve because the `segmentation` extra listed `instanseg`, and the PyPI project of that name is a dead 0.0.2 release pinned to numpy<2, which conflicts with our numpy>=2 floor. uv resolves every extra together even when it is not selected, so the stale pin broke the base install too. The maintained InstanSeg ships as `instanseg-torch`. The sibling repo peng-lab/spatialrefinery already carries the numpy-2-compatible segmentation stack (instanseg-torch, scikit-image, openslide-python + openslide-bin, verified against zarr 3 / spatialdata 0.8 in its lockfile), so the `full` extra now depends on `spatialrefinery[segmentation]` from its git main and the `segmentation` extra is removed. spatialrefinery is not on PyPI, hence the direct reference, which also needs `tool.hatch.metadata.allow-direct-references`; the wheel is therefore not PyPI-uploadable, which is fine as phoenix installs from git. Consequences: `full` gains spatialrefinery's base dependencies (dask, geopandas, opencv-python-headless, spatialdata-io/-plot, ...) and an anndata>=0.13 floor; helpers/segmentor.py is now importable in CI and the docs env, so its import test moved to the `full` case and NucleiPatchExtractor joins the API reference. `.vscode/settings.json` and `.worktreeinclude` only carry prek formatter fixes. Verified on CPU against spatialrefinery 799472d: `uv lock`, `uv sync`, `uv sync --all-extras`, importing spatialrefinery/instanseg/openslide/skimage and phoenix.helpers.segmentor, `pytest tests` (28 passed, 4 apex skips), `uv build` + `twine check --strict`, a dry-run `phoenix[full]` install into an empty venv resolving spatialrefinery from GitHub, `prek run -a`, and `hatch check types`. The `-W` docs build fails before and after with the same 381 torch/lightning cross-reference warnings; no new warnings, tracked separately. Claude-Session: https://claude.ai/code/session_01GG873AhByfhuLZcPrMabwL --- .vscode/settings.json | 5 +---- .worktreeinclude | 2 +- docs/api.md | 5 +---- pyproject.toml | 25 ++++++++++++++----------- tests/test_imports.py | 6 +++--- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 297e39a..e8a258d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,9 +14,6 @@ }, "python.analysis.typeCheckingMode": "basic", "python.testing.pytestEnabled": true, - "python.testing.pytestArgs": [ - "-vv", - "--color=yes" - ], + "python.testing.pytestArgs": ["-vv", "--color=yes"], "python.defaultInterpreterPath": "", } diff --git a/.worktreeinclude b/.worktreeinclude index e978110..3322619 100644 --- a/.worktreeinclude +++ b/.worktreeinclude @@ -1,2 +1,2 @@ /scripts/ -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/docs/api.md b/docs/api.md index 0f6f155..6bf9f80 100644 --- a/docs/api.md +++ b/docs/api.md @@ -64,8 +64,5 @@ docstrings in the source for usage. helpers.demo_plot.to_dense helpers.demo_plot.norm01 helpers.demo_plot.set_active_layer + helpers.segmentor.NucleiPatchExtractor ``` - -`helpers.segmentor.NucleiPatchExtractor` depends on `openslide-python` and `instanseg` (the -`segmentation` extra), which are not yet installed in the docs build environment; it is omitted -from the generated API reference for the same reason as above. diff --git a/pyproject.toml b/pyproject.toml index 9e521cc..1ba370e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,12 @@ optional-dependencies.full = [ # setuptools>=81); 0.8 is the first release we've verified end-to-end. It is Python >=3.12 # only, which is part of why requires-python is 3.12. "spatialdata>=0.8", + # Nuclei segmentation (helpers/segmentor.py). The sibling repo's `segmentation` extra brings + # instanseg-torch (the maintained InstanSeg; the PyPI project literally named `instanseg` is a dead + # 0.0.2 release that pins numpy<2 and broke `uv sync`), scikit-image, openslide-python + openslide-bin. + # Not on PyPI, hence the git reference; tracks main by decision (a spatialrefinery push can change + # the resolved environment -- pin a rev here if a release needs freezing). + "spatialrefinery[segmentation] @ git+https://github.com/peng-lab/spatialrefinery.git@main", "timm", "torch", "torchvision", @@ -57,12 +63,6 @@ optional-dependencies.full = [ # conflicts loudly here instead of silently resolving back to zarr-python 2. "zarr>=3", ] -# Nuclei segmentation pipeline (helpers/segmentor.py). -optional-dependencies.segmentation = [ - "instanseg", - "openslide-python", - "scikit-image", -] # https://docs.pypi.org/project_metadata/#project-urls urls.Documentation = "https://phoenix.readthedocs.io/" urls.Homepage = "https://github.com/peng-lab/phoenix" @@ -103,21 +103,24 @@ typecheck = [ ] [tool.hatch] +# Required for the spatialrefinery git reference in the `full` extra; hatchling refuses to build +# metadata containing direct references otherwise. This also means the built wheel is not +# uploadable to PyPI (which forbids direct references) -- phoenix is installed from git, not PyPI. +metadata.allow-direct-references = true envs.default.installer = "uv" envs.default.dependency-groups = [ "dev" ] # The `full` extra is needed so autosummary can actually import datasets/, models.flow_simple, # trainers/, and helpers/ for API doc generation (see docs/api.md for what's deliberately -# excluded: apex/flash-attn/xformers-dependent modules, and segmentor.py pending `segmentation`). +# excluded: the apex/flash-attn/xformers-dependent modules). envs.docs.features = [ "full" ] envs.docs.scripts.build = "sphinx-build -M html docs docs/_build -W {args}" envs.docs.scripts.clean = "git clean -fdX -- {args:docs}" envs.docs.scripts.open = "python -m webbrowser -t docs/_build/html/index.html" envs.docs.dependency-groups = [ "doc" ] # Install the `full` extra so CI actually exercises datasets/, models.flow_simple, -# trainers/, and helpers/ instead of skipping every torch-dependent test. The -# apex/flash-attn/xformers-only modules (flow_llama3, mlp_mixer_ae) and the -# `segmentation` extra (segmentor.py) still skip -- see tests/test_imports.py -# and tests/test_models.py for why. +# trainers/, and helpers/ (segmentor.py included) instead of skipping every +# torch-dependent test. The apex/flash-attn/xformers-only modules (flow_llama3, +# mlp_mixer_ae) still skip -- see tests/test_imports.py and tests/test_models.py for why. envs.hatch-test.features = [ "full" ] envs.hatch-test.matrix = [ # Test the lowest and highest supported Python versions with normal deps diff --git a/tests/test_imports.py b/tests/test_imports.py index c45e81f..54acbe0 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -12,6 +12,7 @@ ("phoenix.datasets.zarr_dataset", ["spatialdata"]), ("phoenix.helpers.demo_plot", []), ("phoenix.helpers.inference", []), + ("phoenix.helpers.segmentor", ["openslide", "instanseg", "skimage"]), ("phoenix.models.flow_simple", []), ("phoenix.trainers.mixer_trainer", ["pytorch_lightning"]), ], @@ -29,14 +30,13 @@ def test_importable_with_full_extra(module_name, extra_deps): [ ("phoenix.models.flow_llama3", ["apex", "flash_attn", "xformers"]), ("phoenix.models.mlp_mixer_ae", ["apex", "xformers", "vector_quantize_pytorch"]), - ("phoenix.helpers.segmentor", ["openslide", "instanseg", "skimage"]), ], ) def test_importable_with_optional_extras(module_name, required): """ These modules need packages that are deliberately not part of any pip extra - (apex/flash-attn/xformers) or belong to the `segmentation` extra; skipped - whenever they aren't present, which is the expected default CI environment. + (apex/flash-attn/xformers); skipped whenever they aren't present, which is the + expected default CI environment. """ for dep in required: pytest.importorskip(dep)