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..e8a258d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,4 +15,5 @@ "python.analysis.typeCheckingMode": "basic", "python.testing.pytestEnabled": true, "python.testing.pytestArgs": ["-vv", "--color=yes"], + "python.defaultInterpreterPath": "", } diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 0000000..3322619 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,2 @@ +/scripts/ +CLAUDE.md 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/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 80e6f27..1ba370e 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,17 +43,25 @@ 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", + # 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", "vector-quantize-pytorch", -] -# Nuclei segmentation pipeline (helpers/segmentor.py). -optional-dependencies.segmentation = [ - "instanseg", - "openslide-python", - "scikit-image", + # 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", ] # https://docs.pypi.org/project_metadata/#project-urls urls.Documentation = "https://phoenix.readthedocs.io/" @@ -94,25 +103,28 @@ 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 - { 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" ] }, ] 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)