[build-system] requires = ["hatchling>=1.25"] build-backend = "hatchling.build" [project] name = "neuropose" version = "0.1.0.dev0" description = "3D human pose estimation pipeline for clinical movement research, built on MeTRAbs." readme = "PYPI_README.md" license = { file = "LICENSE" } requires-python = ">=3.11,<3.12" authors = [ { name = "Liqi Shu", email = "liqi_shu@brown.edu" }, { name = "Levi Neuwirth", email = "ln@levineuwirth.org" }, { name = "David Man", email = "david_man@brown.edu" }, { name = "Praneeth Tummala", email = "praneeth_tummala@brown.edu" }, ] maintainers = [ { name = "Levi Neuwirth", email = "ln@levineuwirth.org" }, ] keywords = [ "pose-estimation", "biomechanics", "motion-capture", "clinical-research", "metrabs", ] classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Operating System :: MacOS", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Topic :: Scientific/Engineering :: Image Recognition", "Topic :: Scientific/Engineering :: Medical Science Apps.", "Typing :: Typed", ] # Runtime dependencies. The TensorFlow floor is set to 2.16 because that is # the first release with native ``darwin/arm64`` wheels published under the # ``tensorflow`` package name on PyPI. Older versions would force a marker # split to ``tensorflow-macos`` on Apple Silicon. The MeTRAbs SavedModel was # serialized with TF 2.10 and uses only stock ops (no custom kernels), so # any TF >= 2.10 loads it; 2.16 is chosen for cross-platform install # uniformity, not for any MeTRAbs-side requirement. # # The ``<2.19`` ceiling is a tensorflow-metal compatibility constraint, not # a MeTRAbs one. Apple's ``tensorflow-metal`` 1.2.0 (the latest release, # Jan 2025) advertises "TF 2.18+" support, but community reports on the # Apple Developer forums and tensorflow/tensorflow#84167 show it actually # breaks on 2.19+ with symbol-not-found errors and ``InvalidArgumentError`` # at graph execution time. 2.18.x is the last version confirmed to work # cleanly on Apple Silicon GPU. Cap applied globally rather than with a # darwin/arm64 marker split to keep dependency resolution identical across # platforms; revisit once Apple ships a Metal plugin that tracks mainline # TensorFlow again. dependencies = [ "typer>=0.12", "pydantic>=2.6", "pydantic-settings>=2.2", "pyyaml>=6.0", "numpy>=1.26", "opencv-python-headless>=4.9", "matplotlib>=3.8", # psutil powers the peak-RSS and process-memory readings captured by # PerformanceMetrics in neuropose.io. It is a cross-platform pure-Python # wrapper around /proc (Linux), mach (macOS), and Win32 APIs with a # small C extension; brought in at runtime because metrics are always # collected, not an optional feature. "psutil>=5.9", "tensorflow>=2.16,<2.19", "tensorflow-hub>=0.16", ] [project.optional-dependencies] analysis = [ "fastdtw>=0.3.4", "scipy>=1.12", "scikit-learn>=1.4", "sktime>=0.28", ] # Optional Apple Silicon GPU acceleration via Metal Performance Shaders. # This is a PluggableDevice maintained by Apple, NOT by the TensorFlow team, # with its own version-compatibility table (see # https://developer.apple.com/metal/tensorflow-plugin/) and a documented # history of producing numerically-divergent results on some ops under # Keras 3. It is deliberately opt-in. Install with # `pip install 'neuropose[metal]'` or `uv sync --extra metal`. The platform # markers ensure it is silently ignored on every non-Apple-Silicon install. metal = [ "tensorflow-metal>=1.2,<2; sys_platform == 'darwin' and platform_machine == 'arm64'", ] [project.urls] Homepage = "https://git.levineuwirth.org/neuwirth/neuropose" Repository = "https://git.levineuwirth.org/neuwirth/neuropose" Issues = "https://git.levineuwirth.org/neuwirth/neuropose/issues" Changelog = "https://git.levineuwirth.org/neuwirth/neuropose/src/branch/main/CHANGELOG.md" [project.scripts] neuropose = "neuropose.cli:run" # --------------------------------------------------------------------------- # Dependency groups (PEP 735). Install a group with `uv sync --group dev` or # `uv pip install --group dev`. These are not shipped with the wheel. # --------------------------------------------------------------------------- [dependency-groups] dev = [ "pytest>=8.0", "pytest-cov>=5.0", "ruff>=0.8", "pyright>=1.1.390", "pre-commit>=4.0", "mkdocs-material>=9.5", "mkdocstrings[python]>=0.26", # Analyzer subpackage runtime deps. Duplicated from the `analysis` # optional-dependencies extra so dev contributors can run the analyzer # tests without also having to install the extra. sktime is NOT # included here because we do not (yet) wrap it — users who want # classification install `pip install neuropose[analysis]` themselves. "fastdtw>=0.3.4", "scipy>=1.12", ] # --------------------------------------------------------------------------- # Ruff (linter + formatter). See https://docs.astral.sh/ruff/rules/ for the # meaning of each rule code. The selection is deliberately broader than the # Python community default — we want lint noise early rather than cruft late. # --------------------------------------------------------------------------- [tool.ruff] line-length = 100 target-version = "py311" src = ["src", "tests"] extend-exclude = ["notebooks"] [tool.ruff.lint] select = [ "E", "W", # pycodestyle "F", # pyflakes "I", # isort "B", # flake8-bugbear "UP", # pyupgrade "SIM", # flake8-simplify "RUF", # ruff-specific "N", # pep8-naming "C4", # flake8-comprehensions "PTH", # flake8-use-pathlib "PT", # flake8-pytest-style "TID", # flake8-tidy-imports "NPY", # numpy-specific "D", # pydocstyle ] ignore = [ "D100", # missing module docstring — too strict for internal modules "D104", # missing package docstring — too strict for __init__.py "D203", # conflicts with D211 "D213", # conflicts with D212 ] [tool.ruff.lint.per-file-ignores] "tests/**" = ["D", "PLR2004"] "scripts/**" = ["D"] "src/neuropose/_*.py" = ["D"] [tool.ruff.lint.pydocstyle] convention = "numpy" [tool.ruff.lint.isort] known-first-party = ["neuropose"] [tool.ruff.format] docstring-code-format = true # --------------------------------------------------------------------------- # Pytest. # --------------------------------------------------------------------------- [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] addopts = ["--strict-markers", "--strict-config", "-ra"] markers = [ "slow: tests that require model download or are otherwise slow (deselect with '-m \"not slow\"')", ] # --------------------------------------------------------------------------- # Pyright. "standard" rather than "strict" because the TensorFlow / OpenCV / # scikit-learn stubs would generate thousands of unknown-type warnings under # strict mode. We tighten toward strict after the MeTRAbs stack is pinned. # --------------------------------------------------------------------------- [tool.pyright] include = ["src", "tests"] exclude = [ "**/__pycache__", "**/.venv", "notebooks", ] pythonVersion = "3.11" typeCheckingMode = "standard" reportMissingTypeStubs = "none" reportUnknownMemberType = "none" reportUnknownArgumentType = "none" reportUnknownVariableType = "none" reportUnknownParameterType = "none" # --------------------------------------------------------------------------- # Hatch build configuration. # --------------------------------------------------------------------------- [tool.hatch.build.targets.wheel] packages = ["src/neuropose"] [tool.hatch.build.targets.sdist] include = [ "src/neuropose", "README.md", "LICENSE", "CHANGELOG.md", "AUTHORS.md", "CITATION.cff", ]