diff --git a/.cruft.json b/.cruft.json index b430fc9..e3e1dce 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "/home/runner/work/cookiecutter-scverse/cookiecutter-scverse", - "commit": "a370e73f7e342f377fee8e902111bbe2bca30b3b", + "commit": "ca6fc800653f9b1dc1f2025b5b2637c4db7e6167", "checkout": null, "context": { "cookiecutter": { @@ -36,7 +36,7 @@ "trim_blocks": true }, "_template": "/home/runner/work/cookiecutter-scverse/cookiecutter-scverse", - "_commit": "a370e73f7e342f377fee8e902111bbe2bca30b3b" + "_commit": "ca6fc800653f9b1dc1f2025b5b2637c4db7e6167" } }, "directory": null diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3ca1ccb..8131d3d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,6 @@ name: Bug report description: Report something that is broken or incorrect -labels: bug +type: Bug body: - type: markdown attributes: @@ -9,8 +9,7 @@ body: detailing how to provide the necessary information for us to reproduce your bug. In brief: * Please provide exact steps how to reproduce the bug in a clean Python environment. * In case it's not clear what's causing this bug, please provide the data or the data generation procedure. - * Sometimes it is not possible to share the data, but usually it is possible to replicate problems on publicly - available datasets or to share a subset of your data. + * Replicate problems on public datasets or share data subsets when full sharing isn't possible. - type: textarea id: report @@ -20,6 +19,31 @@ body: validations: required: true + - type: textarea + id: code + attributes: + label: Minimal code sample + description: | + Reproducible code sample. Must list dependencies in [inline script metadata][]. When put in a file named `issue.py` using [uv run][] i.e., `uv run issue.py`, should show the issue. + + [uv run]: https://docs.astral.sh/uv/guides/scripts/#running-a-script-with-dependencies + [inline script metadata]: https://packaging.python.org/en/latest/specifications/inline-script-metadata/#example + render: python + value: | + ```python + # /// script + # requires-python = ">=3.12" + # dependencies = [ + # "cookiecutter_scverse_instance@git+https://github.com/scverse/cookiecutter-scverse-instance.git@main", + # ] + # /// + # + # This script automatically imports the development branch of cookiecutter_scverse_instance to check for issues + + import cookiecutter_scverse_instance + # your reproducer code + ``` + - type: textarea id: versions attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 3466756..e9a58c2 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,6 +1,6 @@ name: Feature request description: Propose a new feature for cookiecutter-scverse-instance -labels: enhancement +type: Enhancement body: - type: textarea id: description diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a1ccf58..b690b7c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -55,6 +55,7 @@ jobs: name: ${{ matrix.env.label }} runs-on: ${{ matrix.os }} + continue-on-error: ${{ contains(matrix.env.name, 'pre') }} # make "all-green" pass even if pre-release job fails steps: - uses: actions/checkout@v5 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 50db394..6c28477 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -4,6 +4,7 @@ build: os: ubuntu-24.04 tools: python: "3.13" + nodejs: latest jobs: create_environment: - asdf plugin add uv diff --git a/docs/conf.py b/docs/conf.py index 21fecbf..adacf36 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,11 +5,14 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- +import shutil import sys from datetime import datetime from importlib.metadata import metadata from pathlib import Path +from sphinxcontrib import katex + HERE = Path(__file__).parent sys.path.insert(0, str(HERE / "extensions")) @@ -54,9 +57,9 @@ "sphinx.ext.autosummary", "sphinx.ext.napoleon", "sphinxcontrib.bibtex", + "sphinxcontrib.katex", "sphinx_autodoc_typehints", "sphinx_tabs.tabs", - "sphinx.ext.mathjax", "IPython.sphinxext.ipython_console_highlighting", "sphinxext.opengraph", *[p.stem for p in (HERE / "extensions").glob("*.py")], @@ -124,6 +127,7 @@ } pygments_style = "default" +katex_prerender = shutil.which(katex.NODEJS_BINARY) is not None nitpick_ignore = [ # If building the documentation fails because of a missing link that is outside your control, diff --git a/pyproject.toml b/pyproject.toml index 65be610..03d6d64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,42 +27,42 @@ dependencies = [ # for debug logging (referenced from the issue template) "session-info2", ] -optional-dependencies.dev = [ +# https://docs.pypi.org/project_metadata/#project-urls +urls.Documentation = "https://cookiecutter-scverse-instance.readthedocs.io/" +urls.Homepage = "https://github.com/scverse/cookiecutter-scverse-instance" +urls.Source = "https://github.com/scverse/cookiecutter-scverse-instance" + +[dependency-groups] +dev = [ "pre-commit", "twine>=4.0.2", ] -optional-dependencies.doc = [ - "docutils>=0.8,!=0.18.*,!=0.19.*", +test = [ + "coverage>=7.10", + "pytest", + "pytest-cov", # For VS Code’s coverage functionality +] +doc = [ "ipykernel", "ipython", "myst-nb>=1.1", "pandas", - # Until pybtex >0.24.0 releases: https://bitbucket.org/pybtex-devs/pybtex/issues/169/ - "setuptools", "sphinx>=8.1", "sphinx-autodoc-typehints", "sphinx-book-theme>=1", "sphinx-copybutton", "sphinx-tabs", "sphinxcontrib-bibtex>=1", + "sphinxcontrib-katex", "sphinxext-opengraph", ] -optional-dependencies.test = [ - "coverage>=7.10", - "pytest", - "pytest-cov", # For VS Code’s coverage functionality -] -# https://docs.pypi.org/project_metadata/#project-urls -urls.Documentation = "https://cookiecutter-scverse-instance.readthedocs.io/" -urls.Homepage = "https://github.com/scverse/cookiecutter-scverse-instance" -urls.Source = "https://github.com/scverse/cookiecutter-scverse-instance" [tool.hatch.envs.default] installer = "uv" -features = [ "dev" ] +dependency-groups = [ "dev" ] [tool.hatch.envs.docs] -features = [ "doc" ] +dependency-groups = [ "doc" ] scripts.build = "sphinx-build -M html docs docs/_build -W {args}" scripts.open = "python -m webbrowser -t docs/_build/html/index.html" scripts.clean = "git clean -fdX -- {args:docs}" @@ -78,7 +78,7 @@ deps = [ "pre" ] python = [ "3.14" ] [tool.hatch.envs.hatch-test] -features = [ "dev", "test" ] +dependency-groups = [ "dev", "test" ] [tool.hatch.envs.hatch-test.overrides] # If the matrix variable `deps` is set to "pre", @@ -127,9 +127,9 @@ lint.per-file-ignores."docs/*" = [ "I" ] lint.per-file-ignores."tests/*" = [ "D" ] lint.pydocstyle.convention = "numpy" -[tool.pytest.ini_options] +[tool.pytest] +strict = true testpaths = [ "tests" ] -xfail_strict = true addopts = [ "--import-mode=importlib", # allow using test files with same name ] diff --git a/src/cookiecutter_scverse_instance/pp/basic.py b/src/cookiecutter_scverse_instance/pp/basic.py index 7d6c4a7..87c58ab 100644 --- a/src/cookiecutter_scverse_instance/pp/basic.py +++ b/src/cookiecutter_scverse_instance/pp/basic.py @@ -29,18 +29,25 @@ def basic_preproc(adata: AnnData) -> int: def elaborate_example( items: Iterable[ScverseDataStructures], transform: Callable[[Any], str], - *, # functions after the asterix are key word only arguments + *, # functions after the asterix are keyword-only arguments layer_key: str | None = None, - mudata_mod: str | None = "rna", # Only specify defaults in the signature, not the docstring! + # Only specify defaults and types in the signature, not the docstring! + mudata_mod: str | None = "rna", sdata_table_key: str | None = "table1", max_items: int = 100, ) -> list[str]: - """A method with a more complex docstring. + r"""A method with a more complex docstring. This is where you add more details. Try to support general container classes such as Sequence, Mapping, or Collection where possible to ensure that your functions can be widely used. + Data science means there’s lots of math too: + + .. math:: + + x = \frac{-b \pm \sqrt{b^2-4ac}}{2a} + Parameters ---------- items @@ -77,10 +84,11 @@ def elaborate_example( elif isinstance(item, SpatialData): matrix = item.tables[sdata_table_key].X if not layer_key else item.tables[sdata_table_key].layers[layer_key] else: - raise ValueError(f"Item {item} must be of type AnnData, MuData, or SpatialData but is {item.__class__}.") - + msg = f"Item {item} must be of type AnnData, MuData, or SpatialData but is {item.__class__}." + raise ValueError(msg) if not isinstance(matrix, np.ndarray): - raise ValueError(f"Item {item} matrix is not a Numpy matrix but of type {matrix.__class__}") + msg = f"Item {item} matrix is not a Numpy matrix but of type {matrix.__class__}" + raise ValueError(msg) result.append(transform(matrix.flatten()))