--- a +++ b/pyproject.toml @@ -0,0 +1,363 @@ +[build-system] +requires = ["setuptools>=61", "setuptools-scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "moscot" +dynamic = ["version"] +description = "Multi-omic single-cell optimal transport tools" +readme = "README.rst" +requires-python = ">=3.10" +license = {file = "LICENSE"} +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Typing :: Typed", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Topic :: Scientific/Engineering :: Bio-Informatics", + "Topic :: Scientific/Engineering :: Mathematics" +] +keywords = [ + "single-cell", + "bio-informatics", + "optimal transport" +] +authors = [ + {name = "Dominik Klein"}, + {name = "Giovanni Palla"}, + {name = "Michal Klein"}, + {name = "Zoe Piran"}, + {name = "Marius Lange"}, +] +maintainers = [ + {name = "Dominik Klein", email = "dominik.klein@helmholtz-muenchen.de"}, + {name = "Giovanni Palla", email = "giovanni.palla@helmholtz-muenchen.de"}, + {name = "Michal Klein", email = "michal.klein@helmholtz-muenchen.de"} +] + + +dependencies = [ + "numpy>=1.20.0", + "scipy>=1.7.0", + "pandas>=2.0.1", + "networkx>=3.2", + # https://github.com/scverse/scanpy/issues/2411 + "matplotlib>=3.5.0", + "anndata>=0.9.1", + "scanpy>=1.9.3", + "wrapt>=1.13.2", + "docrep>=0.3.2", + "ott-jax>=0.5.0", + "cloudpickle>=2.2.0", + "rich>=13.5", + "docstring_inheritance>=2.0.0", + "mudata>=0.2.2" +] + +[project.optional-dependencies] +spatial = [ + "squidpy>=1.2.3" +] + +neural = [ + "optax", + "flax", + "diffrax", + "ott-jax[neural]>=0.5.0", + +] +dev = [ + "pre-commit>=3.0.0", + "tox>=4", +] +test = [ + "pytest>=7", + "pytest-xdist>=3", + "pytest-mock>=3.5.0", + "pytest-cov>=4", + "coverage[toml]>=7", + "moscot[neural]" +] +docs = [ + "sphinx>=5.1.1", + "sphinx_copybutton>=0.5.0", + "sphinxcontrib-bibtex>=2.3.0", + "sphinxcontrib-spelling>=7.6.2", + "sphinx-autodoc-typehints", + "furo>=2022.09.29", + "sphinx-tippy>=0.4.1", + "myst-nb>=0.17.1", + "ipython>=7.20.0", + "sphinx_design>=0.3.0", +] + +[project.urls] +Homepage = "https://github.com/theislab/moscot" +Download = "https://moscot.readthedocs.io/en/latest/installation.html" +"Bug Tracker" = "https://github.com/theislab/moscot/issues" +Documentation = "https://moscot.readthedocs.io" +"Source Code" = "https://github.com/theislab/moscot" + +[tool.setuptools] +package-dir = {"" = "src"} +include-package-data = true + +[tool.setuptools_scm] + +[tool.ruff] +exclude = [ + ".git", + "__pycache__", + "build", + "docs/_build", + "dist" +] +ignore = [ + # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient + "E731", + # allow I, O, l as variable names -> I is the identity matrix, i, j, k, l is reasonable indexing notation + "E741", + # Missing docstring in public package + "D104", + # Missing docstring in public module + "D100", + # Missing docstring in __init__ + "D107", + # Missing docstring in magic method + "D105", + # Use `X | Y` for type annotations + "UP007", +] +line-length = 120 +select = [ + "D", # flake8-docstrings + # TODO(michalk8): enable this in https://github.com/theislab/moscot/issues/483 + # "I", # isort + "E", # pycodestyle + "F", # pyflakes + "W", # pycodestyle + "Q", # flake8-quotes + "SIM", # flake8-simplify + "NPY", # NumPy-specific rules + "PT", # flake8-pytest-style + "TID", # flake8-tidy-imports + "B", # flake8-bugbear + "UP", # pyupgrade + "C4", # flake8-comprehensions + "BLE", # flake8-blind-except + "T20", # flake8-print + "RET", # flake8-raise +] +unfixable = ["B", "UP", "C4", "BLE", "T20", "RET"] +target-version = "py38" +[tool.ruff.per-file-ignores] +"tests/*" = ["D"] +"*/__init__.py" = ["F401"] +"docs/*" = ["D"] +"src/moscot/constants.py" = ["D101"] +"src/moscot/utils/subset_policy.py" = ["D101", "D102"] +[tool.ruff.pydocstyle] +convention = "numpy" +[tool.ruff.flake8-tidy-imports] +ban-relative-imports = "all" +[tool.ruff.flake8-quotes] +inline-quotes = "double" + +[tool.black] +line-length = 120 +target-version = ['py38'] +include = '\.pyi?$' + +[tool.isort] +profile = "black" +include_trailing_comma = true +multi_line_output = 3 +sections = ["FUTURE", "STDLIB", "THIRDPARTY", "GENERIC", "NUMERIC", "PLOTTING", "BIO", "FIRSTPARTY", "LOCALFOLDER"] +# also contains what we import in notebooks +known_generic = ["wrapt", "joblib"] +known_numeric = ["numpy", "scipy", "jax", "ott", "pandas", "sklearn", "networkx", "statsmodels"] +known_bio = ["anndata", "scanpy", "squidpy"] +known_plotting = ["IPython", "matplotlib", "mpl_toolkits", "seaborn"] + +[tool.pytest.ini_options] +markers = ["fast: marks tests as fask"] +xfail_strict = true +filterwarnings = [ + "ignore:No data for colormapping:UserWarning", + "ignore:The dtype argument will be deprecated in anndata:PendingDeprecationWarning" +] + +[tool.coverage.run] +branch = true +parallel = true +source = ["src/"] +omit = [ + "*/__init__.py", + "*/_version.py", +] + +[tool.coverage.report] +exclude_lines = [ + '\#.*pragma:\s*no.?cover', + "^if __name__ == .__main__.:$", + '^\s*raise AssertionError\b', + '^\s*raise NotImplementedError\b', + '^\s*return NotImplemented\b', +] +precision = 2 +show_missing = true +skip_empty = true +sort = "Miss" + +[tool.rstcheck] +ignore_directives = [ + "toctree", + "currentmodule", + "autosummary", + "module", + "automodule", + "autoclass", + "bibliography", + "glossary", + "card", + "grid", +] +ignore_roles = [ + "mod", +] + +[tool.mypy] +mypy_path = "$MYPY_CONFIG_FILE_DIR/src" +python_version = "3.10" +plugins = "numpy.typing.mypy_plugin" + +ignore_errors = false + +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true + +disallow_untyped_calls = false +disallow_untyped_defs = true +disallow_incomplete_defs = true +disallow_any_generics = true + +strict_optional = true +strict_equality = true +warn_return_any = false +warn_unreachable = false +check_untyped_defs = true +no_implicit_optional = true +no_implicit_reexport = true +no_warn_no_return = true + +show_error_codes = true +show_column_numbers = true +error_summary = true +ignore_missing_imports = true + +disable_error_code = ["assignment", "comparison-overlap", "no-untyped-def", "override"] + +[tool.doc8] +max_line_length = 120 + +[tool.tox] +legacy_tox_ini = """ +[tox] +min_version = 4.0 +env_list = lint-code,py{3.10,3.11,3.12} +skip_missing_interpreters = true + +[testenv] +extras = test,neural +commands = + python -m pytest {tty:--color=yes} {posargs: \ + --cov={env_site_packages_dir}{/}moscot --cov-config={tox_root}{/}pyproject.toml \ + --no-cov-on-fail --cov-report=xml --cov-report=term-missing:skip-covered} +passenv = PYTEST_*,CI + +[testenv:lint-code] +description = Lint the code. +deps = pre-commit>=3.0.0 +skip_install = true +commands = + pre-commit run --all-files --show-diff-on-failure + +[testenv:lint-docs] +description = Lint the documentation. +extras = docs,neural +ignore_errors = true +allowlist_externals = make +pass_env = PYENCHANT_LIBRARY_PATH +set_env = SPHINXOPTS = -W -q --keep-going +changedir = {tox_root}{/}docs +commands = + make linkcheck {posargs} + # TODO(michalk8): uncomment after https://github.com/theislab/moscot/issues/490 + # make spelling {posargs} + +[testenv:examples-docs] +allowlist_externals = bash +description = Run the notebooks. +use_develop = true +deps = + ipykernel + jupytext + nbconvert + leidenalg +extras = docs,neural +changedir = {tox_root}{/}docs +commands = + python -m ipykernel install --user --name=moscot + bash {tox_root}/.run_notebooks.sh {tox_root}{/}docs/notebooks + +[testenv:clean-docs] +description = Remove the documentation. +deps = +skip_install = true +changedir = {tox_root}{/}docs +allowlist_externals = make +commands = + make clean + +[testenv:build-docs] +description = Build the documentation. +deps = +extras = docs,neural +allowlist_externals = make +changedir = {tox_root}{/}docs +commands = + make html {posargs} +commands_post = + python -c 'import pathlib; print("Documentation is under:", pathlib.Path("{tox_root}") / "docs" / "_build" / "html" / "index.html")' + +[testenv:build-package] +description = Build the package. +deps = + build + twine +allowlist_externals = rm +commands = + rm -rf {tox_root}{/}dist + python -m build --sdist --wheel --outdir {tox_root}{/}dist{/} {posargs:} + python -m twine check {tox_root}{/}dist{/}* +commands_post = + python -c 'import pathlib; print(f"Package is under:", pathlib.Path("{tox_root}") / "dist")' + +[testenv:format-references] +description = Format references.bib. +deps = +skip_install = true +allowlist_externals = biber +commands = biber --tool --output_file={tox_root}{/}docs{/}references.bib --nolog \ + --output_align --output_indent=2 --output_fieldcase=lower \ + --output_legacy_dates --output-field-replace=journaltitle:journal,thesis:phdthesis,institution:school \ + {tox_root}{/}docs{/}references.bib +"""