diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index fdba9e4..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[bumpversion] -current_version = 0.0.1 -tag = True -commit = True - -[bumpversion:file:./pyproject.toml] -search = version = "{current_version}" -replace = version = "{new_version}" diff --git a/.codecov.yaml b/.codecov.yaml index 829e56c..d0c0e29 100644 --- a/.codecov.yaml +++ b/.codecov.yaml @@ -1,17 +1,17 @@ # Based on pydata/xarray codecov: - require_ci_to_pass: no + require_ci_to_pass: no coverage: - status: - project: - default: - # Require 1% coverage, i.e., always succeed - target: 1 - patch: false - changes: false + status: + project: + default: + # Require 1% coverage, i.e., always succeed + target: 1 + patch: false + changes: false comment: - layout: diff, flags, files - behavior: once - require_base: no + layout: diff, flags, files + behavior: once + require_base: no diff --git a/.cruft.json b/.cruft.json index cd4163a..0c9c182 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,23 +1,43 @@ { - "template": "https://github.com/scverse/cookiecutter-scverse", - "commit": "5f091ed0952e4adac0c95bda4006bec0b4532995", - "checkout": null, - "context": { - "cookiecutter": { - "project_name": "genomic-annotations", - "package_name": "genomic_annotations", - "project_description": "Genomic annotations using BioConductor resources in Python.", - "author_full_name": "Isaac Virshup", - "author_email": "ivirshup@gmail.com", - "github_user": "ivirshup", - "project_repo": "https://github.com/ivirshup/genomic-annotations", - "license": "BSD 3-Clause License", - "_copy_without_render": [ - ".github/workflows/**.yaml", - "docs/_templates/autosummary/**.rst" - ], - "_template": "https://github.com/scverse/cookiecutter-scverse" - } - }, - "directory": null + "template": "https://github.com/scverse/cookiecutter-scverse", + "commit": "d383d94fadff9e4e6fdb59d77c68cb900d7cedec", + "checkout": "v0.6.0", + "context": { + "cookiecutter": { + "project_name": "genomic-features", + "package_name": "genomic_features", + "project_description": "Genomic annotations using BioConductor resources in Python.", + "author_full_name": "Isaac Virshup", + "author_email": "ivirshup@gmail.com", + "github_user": "ivirshup", + "github_repo": "genomic-features", + "license": "BSD 3-Clause License", + "ide_integration": true, + "_copy_without_render": [ + ".github/workflows/build.yaml", + ".github/workflows/test.yaml", + "docs/_templates/autosummary/**.rst" + ], + "_exclude_on_template_update": [ + "CHANGELOG.md", + "LICENSE", + "README.md", + "docs/api.md", + "docs/index.md", + "docs/notebooks/example.ipynb", + "docs/references.bib", + "docs/references.md", + "src/**", + "tests/**" + ], + "_render_devdocs": false, + "_jinja2_env_vars": { + "lstrip_blocks": true, + "trim_blocks": true + }, + "_template": "https://github.com/scverse/cookiecutter-scverse", + "_commit": "d383d94fadff9e4e6fdb59d77c68cb900d7cedec" + } + }, + "directory": null } diff --git a/.editorconfig b/.editorconfig index 2fe0ce0..66678e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,8 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +[{*.{yml,yaml,toml},.cruft.json}] +indent_size = 2 + [Makefile] indent_style = tab diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8d3ddaa..3ca1ccb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -2,88 +2,93 @@ name: Bug report description: Report something that is broken or incorrect labels: bug body: - - type: markdown - attributes: - value: | - **Note**: Please read [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) - 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. + - type: markdown + attributes: + value: | + **Note**: Please read [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) + 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. - - type: textarea - id: report - attributes: - label: Report - description: A clear and concise description of what the bug is. - validations: - required: true + - type: textarea + id: report + attributes: + label: Report + description: A clear and concise description of what the bug is. + validations: + required: true - - type: textarea - id: versions - attributes: - label: Version information - description: | - Please paste below the output of + - type: textarea + id: versions + attributes: + label: Versions + description: | + Which version of packages. - ```python - import session_info - session_info.show(html=False, dependencies=True) - ``` - placeholder: | - ----- - anndata 0.8.0rc2.dev27+ge524389 - session_info 1.0.0 - ----- - asttokens NA - awkward 1.8.0 - backcall 0.2.0 - cython_runtime NA - dateutil 2.8.2 - debugpy 1.6.0 - decorator 5.1.1 - entrypoints 0.4 - executing 0.8.3 - h5py 3.7.0 - ipykernel 6.15.0 - jedi 0.18.1 - mpl_toolkits NA - natsort 8.1.0 - numpy 1.22.4 - packaging 21.3 - pandas 1.4.2 - parso 0.8.3 - pexpect 4.8.0 - pickleshare 0.7.5 - pkg_resources NA - prompt_toolkit 3.0.29 - psutil 5.9.1 - ptyprocess 0.7.0 - pure_eval 0.2.2 - pydev_ipython NA - pydevconsole NA - pydevd 2.8.0 - pydevd_file_utils NA - pydevd_plugins NA - pydevd_tracing NA - pygments 2.12.0 - pytz 2022.1 - scipy 1.8.1 - setuptools 62.5.0 - setuptools_scm NA - six 1.16.0 - stack_data 0.3.0 - tornado 6.1 - traitlets 5.3.0 - wcwidth 0.2.5 - zmq 23.1.0 - ----- - IPython 8.4.0 - jupyter_client 7.3.4 - jupyter_core 4.10.0 - ----- - Python 3.9.13 | packaged by conda-forge | (main, May 27 2022, 16:58:50) [GCC 10.3.0] - Linux-5.18.6-arch1-1-x86_64-with-glibc2.35 - ----- - Session information updated at 2022-07-07 17:55 + Please install `session-info2`, run the following command in a notebook, + click the “Copy as Markdown” button, then paste the results into the text box below. + + ```python + In[1]: import session_info2; session_info2.session_info(dependencies=True) + ``` + + Alternatively, run this in a console: + + ```python + >>> import session_info2; print(session_info2.session_info(dependencies=True)._repr_mimebundle_()["text/markdown"]) + ``` + render: python + placeholder: | + anndata 0.11.3 + ---- ---- + charset-normalizer 3.4.1 + coverage 7.7.0 + psutil 7.0.0 + dask 2024.7.1 + jaraco.context 5.3.0 + numcodecs 0.15.1 + jaraco.functools 4.0.1 + Jinja2 3.1.6 + sphinxcontrib-jsmath 1.0.1 + sphinxcontrib-htmlhelp 2.1.0 + toolz 1.0.0 + session-info2 0.1.2 + PyYAML 6.0.2 + llvmlite 0.44.0 + scipy 1.15.2 + pandas 2.2.3 + sphinxcontrib-devhelp 2.0.0 + h5py 3.13.0 + tblib 3.0.0 + setuptools-scm 8.2.0 + more-itertools 10.3.0 + msgpack 1.1.0 + sparse 0.15.5 + wrapt 1.17.2 + jaraco.collections 5.1.0 + numba 0.61.0 + pyarrow 19.0.1 + pytz 2025.1 + MarkupSafe 3.0.2 + crc32c 2.7.1 + sphinxcontrib-qthelp 2.0.0 + sphinxcontrib-serializinghtml 2.0.0 + zarr 2.18.4 + asciitree 0.3.3 + six 1.17.0 + sphinxcontrib-applehelp 2.0.0 + numpy 2.1.3 + cloudpickle 3.1.1 + sphinxcontrib-bibtex 2.6.3 + natsort 8.4.0 + jaraco.text 3.12.1 + setuptools 76.1.0 + Deprecated 1.2.18 + packaging 24.2 + python-dateutil 2.9.0.post0 + ---- ---- + Python 3.13.2 | packaged by conda-forge | (main, Feb 17 2025, 14:10:22) [GCC 13.3.0] + OS Linux-6.11.0-109019-tuxedo-x86_64-with-glibc2.39 + Updated 2025-03-18 15:47 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 5cad625..5b62547 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: Scverse Community Forum - url: https://discourse.scverse.org/ - about: If you have questions about “How to do X”, please ask them here. + - name: Scverse Community Forum + url: https://discourse.scverse.org/ + about: If you have questions about “How to do X”, please ask them here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 4921254..35f0493 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,11 +1,11 @@ name: Feature request -description: Propose a new feature for genomic-annotations +description: Propose a new feature for genomic-features labels: enhancement body: - - type: textarea - id: description - attributes: - label: Description of feature - description: Please describe your suggestion for a new feature. It might help to describe a problem or use case, plus any alternatives that you have considered. - validations: - required: true + - type: textarea + id: description + attributes: + label: Description of feature + description: Please describe your suggestion for a new feature. It might help to describe a problem or use case, plus any alternatives that you have considered. + validations: + required: true diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d6bc33f..83e01a1 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,29 +1,33 @@ name: Check Build on: - push: - branches: [main] - pull_request: - branches: [main] + push: + branches: [main] + pull_request: + branches: [main] concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + # to fail on error in multiline statements (-e), in pipes (-o pipefail), and on unset variables (-u). + shell: bash -euo pipefail {0} jobs: - package: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v4 - with: - python-version: "3.10" - cache: "pip" - cache-dependency-path: "**/pyproject.toml" - - name: Install build dependencies - run: python -m pip install --upgrade pip wheel twine build - - name: Build package - run: python -m build - - name: Check package - run: twine check --strict dist/*.whl + package: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + filter: blob:none + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + cache-dependency-glob: pyproject.toml + - name: Build package + run: uv build + - name: Check package + run: uvx twine check --strict dist/*.whl diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..f3d4541 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,34 @@ +name: Release + +on: + release: + types: [published] + +defaults: + run: + # to fail on error in multiline statements (-e), in pipes (-o pipefail), and on unset variables (-u). + shell: bash -euo pipefail {0} + +# Use "trusted publishing", see https://docs.pypi.org/trusted-publishers/ +jobs: + release: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/genomic_features + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - uses: actions/checkout@v4 + with: + filter: blob:none + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + cache-dependency-glob: pyproject.toml + - name: Build package + run: uv build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 13267d1..0bd76e8 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,53 +1,103 @@ name: Test on: - push: - branches: [main] - pull_request: - branches: [main] + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: "0 5 1,15 * *" concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + # to fail on error in multiline statements (-e), in pipes (-o pipefail), and on unset variables (-u). + shell: bash -euo pipefail {0} jobs: - test: - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash -e {0} # -e to fail on error - - strategy: - fail-fast: false - matrix: - python: ["3.8", "3.10"] - os: [ubuntu-latest] + # Get the test environment from hatch as defined in pyproject.toml. + # This ensures that the pyproject.toml is the single point of truth for test definitions and the same tests are + # run locally and on continuous integration. + # Check [[tool.hatch.envs.hatch-test.matrix]] in pyproject.toml and https://hatch.pypa.io/latest/environment/ for + # more details. + get-environments: + runs-on: ubuntu-latest + outputs: + envs: ${{ steps.get-envs.outputs.envs }} + steps: + - uses: actions/checkout@v4 + with: + filter: blob:none + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@v5 + - name: Get test environments + id: get-envs + run: | + ENVS_JSON=$(uvx hatch env show --json | jq -c 'to_entries + | map( + select(.key | startswith("hatch-test")) + | { + name: .key, + label: (if (.key | contains("pre")) then .key + " (PRE-RELEASE DEPENDENCIES)" else .key end), + python: .value.python + } + )') + echo "envs=${ENVS_JSON}" | tee $GITHUB_OUTPUT + + # Run tests through hatch. Spawns a separate runner for each environment defined in the hatch matrix obtained above. + test: + needs: get-environments + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + env: ${{ fromJSON(needs.get-environments.outputs.envs) }} + name: ${{ matrix.env.label }} + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + with: + filter: blob:none + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + python-version: ${{ matrix.env.python }} + cache-dependency-glob: pyproject.toml + - name: create hatch environment + run: uvx hatch env create ${{ matrix.env.name }} + - name: run tests using hatch env: - OS: ${{ matrix.os }} - PYTHON: ${{ matrix.python }} - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python }} - cache: "pip" - cache-dependency-path: "**/pyproject.toml" - - - name: Install test dependencies - run: | - python -m pip install --upgrade pip wheel - - name: Install dependencies - run: | - pip install ".[dev,test]" - - name: Test - env: - MPLBACKEND: agg - PLATFORM: ${{ matrix.os }} - DISPLAY: :42 - run: | - pytest -v --cov --color=yes - - name: Upload coverage - uses: codecov/codecov-action@v3 + MPLBACKEND: agg + PLATFORM: ${{ matrix.os }} + DISPLAY: :42 + run: uvx hatch run ${{ matrix.env.name }}:run-cov -v --color=yes -n auto + - name: generate coverage report + run: | + # See https://coverage.readthedocs.io/en/latest/config.html#run-patch + test -f .coverage || uvx hatch run ${{ matrix.env.name }}:cov-combine + uvx hatch run ${{ matrix.env.name }}:cov-report # report visibly + uvx hatch run ${{ matrix.env.name }}:coverage xml # create report for upload + - name: Upload coverage + uses: codecov/codecov-action@v5 + + # Check that all tests defined above pass. This makes it easy to set a single "required" test in branch + # protection instead of having to update it frequently. See https://github.com/re-actors/alls-green#why. + check: + name: Tests pass in all hatch environments + if: always() + needs: + - get-environments + - test + runs-on: ubuntu-latest + steps: + - uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/.gitignore b/.gitignore index dcdc618..bd24e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,23 +6,16 @@ buck-out/ # Compiled files .venv/ __pycache__/ -.mypy_cache/ -.ruff_cache/ +.*cache/ # Distribution / packaging -/build/ /dist/ -/*.egg-info/ # Tests and coverage -/.pytest_cache/ -/.cache/ /data/ +/node_modules/ +/.coverage* # docs /docs/generated/ /docs/_build/ - -# IDEs -/.idea/ -/.vscode/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3aafe67..b9de3fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,50 +1,38 @@ fail_fast: false default_language_version: - python: python3 + python: python3 default_stages: - - commit - - push + - pre-commit + - pre-push minimum_pre_commit_version: 2.16.0 repos: - - repo: https://github.com/psf/black - rev: "23.3.0" - hooks: - - id: black - - repo: https://github.com/asottile/blacken-docs - rev: 1.13.0 - hooks: - - id: blacken-docs - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.9-for-vscode - hooks: - - id: prettier - # Newer versions of node don't work on systems that have an older version of GLIBC - # (in particular Ubuntu 18.04 and Centos 7) - # EOL of Centos 7 is in 2024-06, we can probably get rid of this then. - # See https://github.com/scverse/cookiecutter-scverse/issues/143 and - # https://github.com/jupyterlab/jupyterlab/issues/12675 - language_version: "17.9.1" - - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.262 - hooks: - - id: ruff - args: [--fix, --exit-non-zero-on-fix] - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 - hooks: - - id: detect-private-key - - id: check-ast - - id: end-of-file-fixer - - id: mixed-line-ending - args: [--fix=lf] - - id: trailing-whitespace - - id: check-case-conflict - - repo: local - hooks: - - id: forbid-to-commit - name: Don't commit rej files - entry: | - Cannot commit .rej files. These indicate merge conflicts that arise during automated template updates. - Fix the merge conflicts manually and remove the .rej files. - language: fail - files: '.*\.rej$' + - repo: https://github.com/biomejs/pre-commit + rev: v2.2.4 + hooks: + - id: biome-format + exclude: ^\.cruft\.json$ # inconsistent indentation with cruft - file never to be modified manually. + - repo: https://github.com/tox-dev/pyproject-fmt + rev: v2.6.0 + hooks: + - id: pyproject-fmt + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.13.2 + hooks: + - id: ruff-check + types_or: [python, pyi, jupyter] + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + types_or: [python, pyi, jupyter] + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: detect-private-key + - id: check-ast + - id: end-of-file-fixer + - id: mixed-line-ending + args: [--fix=lf] + - id: trailing-whitespace + - id: check-case-conflict + # Check that there are no merge conflicts (could be generated by template sync) + - id: check-merge-conflict + args: [--assume-in-merge] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 9e5d5fa..c3f3f96 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,16 +1,15 @@ # https://docs.readthedocs.io/en/stable/config-file/v2.html version: 2 build: - os: ubuntu-20.04 - tools: - python: "3.10" -sphinx: - configuration: docs/conf.py - # disable this for more lenient docs builds - fail_on_warning: true -python: - install: - - method: pip - path: . - extra_requirements: - - doc + os: ubuntu-24.04 + tools: + python: "3.12" + jobs: + create_environment: + - asdf plugin add uv + - asdf install uv latest + - asdf global uv latest + build: + html: + - uvx hatch run docs:build + - mv docs/_build $READTHEDOCS_OUTPUT diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..caaeb4f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,18 @@ +{ + "recommendations": [ + // GitHub integration + "github.vscode-github-actions", + "github.vscode-pull-request-github", + // Language support + "ms-python.python", + "ms-python.vscode-pylance", + "ms-toolsai.jupyter", + "tamasfe.even-better-toml", + // Dependency management + "ninoseki.vscode-mogami", + // Linting and formatting + "editorconfig.editorconfig", + "charliermarsh.ruff", + "biomejs.biome", + ], +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..36d1874 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Build Documentation", + "type": "debugpy", + "request": "launch", + "module": "sphinx", + "args": ["-M", "html", ".", "_build"], + "cwd": "${workspaceFolder}/docs", + "console": "internalConsole", + "justMyCode": false, + }, + { + "name": "Python: Debug Test", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "purpose": ["debug-test"], + "console": "internalConsole", + "justMyCode": false, + "env": { + "PYTEST_ADDOPTS": "--color=yes", + }, + "presentation": { + "hidden": true, + }, + }, + ], +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e034b91 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "[python][json][jsonc]": { + "editor.formatOnSave": true, + }, + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.codeActionsOnSave": { + "source.fixAll": "always", + "source.organizeImports": "always", + }, + }, + "[json][jsonc]": { + "editor.defaultFormatter": "biomejs.biome", + }, + "python.analysis.typeCheckingMode": "basic", + "python.testing.pytestEnabled": true, + "python.testing.pytestArgs": ["-vv", "--color=yes"], +} diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..9f8f220 --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,17 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", + "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, + "formatter": { "useEditorconfig": true }, + "overrides": [ + { + "includes": ["./.vscode/*.json", "**/*.jsonc"], + "json": { + "formatter": { "trailingCommas": "all" }, + "parser": { + "allowComments": true, + "allowTrailingCommas": true, + }, + }, + }, + ], +} diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d4bb2cb..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 0000000..b8c8d47 --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,4 @@ +/* Reduce the font size in data frames - See https://github.com/scverse/cookiecutter-scverse/issues/193 */ +div.cell_output table.dataframe { + font-size: 0.8em; +} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst index e4665df..7b4a0cf 100644 --- a/docs/_templates/autosummary/class.rst +++ b/docs/_templates/autosummary/class.rst @@ -9,11 +9,11 @@ {% block attributes %} {% if attributes %} Attributes table -~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~ .. autosummary:: {% for item in attributes %} - ~{{ fullname }}.{{ item }} + ~{{ name }}.{{ item }} {%- endfor %} {% endif %} {% endblock %} @@ -26,7 +26,7 @@ Methods table .. autosummary:: {% for item in methods %} {%- if item != '__init__' %} - ~{{ fullname }}.{{ item }} + ~{{ name }}.{{ item }} {%- endif -%} {%- endfor %} {% endif %} @@ -35,7 +35,7 @@ Methods table {% block attributes_documentation %} {% if attributes %} Attributes -~~~~~~~~~~~ +~~~~~~~~~~ {% for item in attributes %} diff --git a/docs/conf.py b/docs/conf.py index db7ff96..03e0605 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # Configuration file for the Sphinx documentation builder. -# + # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html @@ -18,7 +18,7 @@ # NOTE: If you installed your project in editable mode, this might be stale. # If this is the case, reinstall it to refresh the metadata -info = metadata("genomic-annotations") +info = metadata("genomic-features") project_name = info["Name"] author = info["Author"] copyright = f"{datetime.now():%Y}, {author}." @@ -36,10 +36,10 @@ html_context = { "display_github": True, # Integrate GitHub - "github_user": "ivirshup", # Username - "github_repo": project_name, # Repo name - "github_version": "main", # Version - "conf_py_path": "/docs/", # Path in the checkout to the docs root + "github_user": "ivirshup", + "github_repo": project_name, + "github_version": "main", + "conf_py_path": "/docs/", } # -- General configuration --------------------------------------------------- @@ -55,8 +55,10 @@ "sphinx.ext.napoleon", "sphinxcontrib.bibtex", "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")], ] @@ -92,6 +94,7 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "anndata": ("https://anndata.readthedocs.io/en/stable/", None), + "scanpy": ("https://scanpy.readthedocs.io/en/stable/", None), "numpy": ("https://numpy.org/doc/stable/", None), } @@ -108,12 +111,15 @@ # html_theme = "sphinx_book_theme" html_static_path = ["_static"] +html_css_files = ["css/custom.css"] + html_title = project_name html_theme_options = { "repository_url": repository_url, "use_repository_button": True, "path_to_docs": "docs/", + "navigation_with_keys": False, } pygments_style = "default" @@ -123,18 +129,3 @@ # you can add an exception to this list. # ("py:class", "igraph.Graph"), ] - - -def setup(app): - """App setup hook.""" - app.add_config_value( - "recommonmark_config", - { - "auto_toc_tree_section": "Contents", - "enable_auto_toc_tree": True, - "enable_math": True, - "enable_inline_math": False, - "enable_eval_rst": True, - }, - True, - ) diff --git a/docs/contributing.md b/docs/contributing.md index 8f10bd8..699d942 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,198 +1,330 @@ # Contributing guide -Scanpy provides extensive [developer documentation][scanpy developer guide], most of which applies to this repo, too. -This document will not reproduce the entire content from there. Instead, it aims at summarizing the most important -information to get you started on contributing. +This document aims at summarizing the most important information for getting you started on contributing to this project. +We assume that you are already familiar with git and with making pull requests on GitHub. -We assume that you are already familiar with git and with making pull requests on GitHub. If not, please refer -to the [scanpy developer guide][]. +For more extensive tutorials, that also cover the absolute basics, +please refer to other resources such as the [pyopensci tutorials][], +the [scientific Python tutorials][], or the [scanpy developer guide][]. + +[pyopensci tutorials]: https://www.pyopensci.org/learn.html +[scientific Python tutorials]: https://learn.scientific-python.org/development/tutorials/ +[scanpy developer guide]: https://scanpy.readthedocs.io/en/latest/dev/index.html + +:::{tip} The *hatch* project manager + +We highly recommend to familiarize yourself with [`hatch`][hatch]. +Hatch is a Python project manager that + +- manages virtual environments, separately for development, testing and building the documentation. + Separating the environments is useful to avoid dependency conflicts. +- allows to run tests locally in different environments (e.g. different python versions) +- allows to run tasks defined in `pyproject.toml`, e.g. to build documentation. + +While the project is setup with `hatch` in mind, +it is still possible to use different tools to manage dependencies, such as `uv` or `pip`. + +::: + +[hatch]: https://hatch.pypa.io/latest/ ## Installing dev dependencies -In addition to the packages needed to _use_ this package, you need additional python packages to _run tests_ and _build -the documentation_. It's easy to install them using `pip`: +In addition to the packages needed to _use_ this package, +you need additional python packages to [run tests](#writing-tests) and [build the documentation](#docs-building). + +:::::{tabs} +::::{group-tab} Hatch + +On the command line, you typically interact with hatch through its command line interface (CLI). +Running one of the following commands will automatically resolve the environments for testing and +building the documentation in the background: ```bash -cd genomic-annotations -pip install -e ".[dev,test,doc]" +hatch test # defined in the table [tool.hatch.envs.hatch-test] in pyproject.toml +hatch run docs:build # defined in the table [tool.hatch.envs.docs] ``` -## Code-style +When using an IDE such as VS Code, +you’ll have to point the editor at the paths to the virtual environments manually. +The environment you typically want to use as your main development environment is the `hatch-test` +environment with the latest Python version. -This template uses [pre-commit][] to enforce consistent code-styles. On every commit, pre-commit checks will either -automatically fix issues with the code, or raise an error message. See [pre-commit checks](template_usage.md#pre-commit-checks) for -a full list of checks enabled for this repository. - -To enable pre-commit locally, simply run +To get a list of all environments for your projects, run ```bash -pre-commit install +hatch env show -i ``` -in the root of the repository. Pre-commit will automatically download all dependencies when it is run for the first time. +This will list “Standalone” environments and a table of “Matrix” environments like the following: -Alternatively, you can rely on the [pre-commit.ci][] service enabled on GitHub. If you didn't run `pre-commit` before -pushing changes to GitHub it will automatically commit fixes to your pull request, or show an error message. +``` ++------------+---------+--------------------------+----------+---------------------------------+-------------+ +| Name | Type | Envs | Features | Dependencies | Scripts | ++------------+---------+--------------------------+----------+---------------------------------+-------------+ +| hatch-test | virtual | hatch-test.py3.10-stable | dev | coverage-enable-subprocess==1.0 | cov-combine | +| | | hatch-test.py3.13-stable | test | coverage[toml]~=7.4 | cov-report | +| | | hatch-test.py3.13-pre | | pytest-mock~=3.12 | run | +| | | | | pytest-randomly~=3.15 | run-cov | +| | | | | pytest-rerunfailures~=14.0 | | +| | | | | pytest-xdist[psutil]~=3.5 | | +| | | | | pytest~=8.1 | | ++------------+---------+--------------------------+----------+---------------------------------+-------------+ +``` -If pre-commit.ci added a commit on a branch you still have been working on locally, simply use +From the `Envs` column, select the environment name you want to use for development. +In this example, it would be `hatch-test.py3.13-stable`. + +Next, create the environment with ```bash -git pull --rebase +hatch env create hatch-test.py3.13-stable ``` -to integrate the changes into yours. -While the [pre-commit.ci][] is useful, we strongly encourage installing and running pre-commit locally first to understand its usage. +Then, obtain the path to the environment using -Finally, most editors have an _autoformat on save_ feature. Consider enabling this option for [black][black-editors] -and [prettier][prettier-editors]. +```bash +hatch env find hatch-test.py3.13-stable +``` -[black-editors]: https://black.readthedocs.io/en/stable/integrations/editors.html -[prettier-editors]: https://prettier.io/docs/en/editors.html +In case you are using VScode, now open the command palette (Ctrl+Shift+P) and search for `Python: Select Interpreter`. +Choose `Enter Interpreter Path` and paste the path to the virtual environment from above. -## Writing tests +In this future, this may become easier through a hatch vscode extension. -```{note} -Remember to first install the package with `pip install '-e[dev,test]'` -``` +:::: -This package uses the [pytest][] for automated testing. Please [write tests][scanpy-test-docs] for every function added -to the package. +::::{group-tab} uv -Most IDEs integrate with pytest and provide a GUI to run tests. Alternatively, you can run all tests from the -command line by executing +A popular choice for managing virtual environments is [uv][]. +The main disadvantage compared to hatch is that it supports only a single environment per project at a time, +which requires you to mix the dependencies for running tests and building docs. +This can have undesired side-effects, +such as requiring to install a lower version of a library your project depends on, +only because an outdated sphinx plugin pins an older version. + +To initalize a virtual environment in the `.venv` directory of your project, simply run ```bash -pytest +uv sync --all-extras ``` -in the root of the repository. Continuous integration will automatically run the tests on all pull requests. +The `.venv` directory is typically automatically discovered by IDEs such as VS Code. -[scanpy-test-docs]: https://scanpy.readthedocs.io/en/latest/dev/testing.html#writing-tests +:::: -## Publishing a release +::::{group-tab} Pip -### Updating the version number +Pip is nowadays mostly superseded by environment manager such as [hatch][]. +However, for the sake of completeness, and since it’s ubiquitously available, +we describe how you can manage environments manually using `pip`: -Before making a release, you need to update the version number. Please adhere to [Semantic Versioning][semver], in brief +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -e ".[dev,test,doc]" +``` -> Given a version number MAJOR.MINOR.PATCH, increment the: -> -> 1. MAJOR version when you make incompatible API changes, -> 2. MINOR version when you add functionality in a backwards compatible manner, and -> 3. PATCH version when you make backwards compatible bug fixes. -> -> Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. +The `.venv` directory is typically automatically discovered by IDEs such as VS Code. + +:::: +::::: -We use [bump2version][] to automatically update the version number in all places and automatically create a git tag. -Run one of the following commands in the root of the repository +[hatch environments]: https://hatch.pypa.io/latest/tutorials/environment/basic-usage/ +[uv]: https://docs.astral.sh/uv/ + +## Code-style + +This package uses [pre-commit][] to enforce consistent code-styles. +On every commit, pre-commit checks will either automatically fix issues with the code, or raise an error message. + +To enable pre-commit locally, simply run ```bash -bump2version patch -bump2version minor -bump2version major +pre-commit install ``` -Once you are done, run +in the root of the repository. +Pre-commit will automatically download all dependencies when it is run for the first time. -``` -git push --tags +Alternatively, you can rely on the [pre-commit.ci][] service enabled on GitHub. +If you didn’t run `pre-commit` before pushing changes to GitHub it will automatically commit fixes to your pull request, or show an error message. + +If pre-commit.ci added a commit on a branch you still have been working on locally, simply use + +```bash +git pull --rebase ``` -to publish the created tag on GitHub. +to integrate the changes into yours. +While the [pre-commit.ci][] is useful, we strongly encourage installing and running pre-commit locally first to understand its usage. -[bump2version]: https://github.com/c4urself/bump2version +Finally, most editors have an _autoformat on save_ feature. +Consider enabling this option for [ruff][ruff-editors] and [biome][biome-editors]. -### Building and publishing the package on PyPI +[pre-commit]: https://pre-commit.com/ +[pre-commit.ci]: https://pre-commit.ci/ +[ruff-editors]: https://docs.astral.sh/ruff/integrations/ +[biome-editors]: https://biomejs.dev/guides/integrate-in-editor/ + +(writing-tests)= + +## Writing tests -Python packages are not distributed as source code, but as _distributions_. The most common distribution format is the so-called _wheel_. To build a _wheel_, run +This package uses [pytest][] for automated testing. +Please write {doc}`scanpy:dev/testing` for every function added to the package. + +Most IDEs integrate with pytest and provide a GUI to run tests. +Just point yours to one of the environments returned by ```bash -python -m build +hatch env create hatch-test # create test environments for all supported versions +hatch env find hatch-test # list all possible test environment paths ``` -This command creates a _source archive_ and a _wheel_, which are required for publishing your package to [PyPI][]. These files are created directly in the root of the repository. +Alternatively, you can run all tests from the command line by executing -Before uploading them to [PyPI][] you can check that your _distribution_ is valid by running: +:::::{tabs} +::::{group-tab} Hatch ```bash -twine check dist/* +hatch test # test with the highest supported Python version +# or +hatch test --all # test with all supported Python versions ``` -and finally publishing it with: +:::: + +::::{group-tab} uv + +```bash +uv run pytest +``` + +:::: + +::::{group-tab} Pip ```bash -twine upload dist/* +source .venv/bin/activate +pytest ``` -Provide your username and password when requested and then go check out your package on [PyPI][]! +:::: +::::: + +in the root of the repository. + +[pytest]: https://docs.pytest.org/ + +### Continuous integration + +Continuous integration via GitHub actions will automatically run the tests on all pull requests and test +against the minimum and maximum supported Python version. + +Additionally, there’s a CI job that tests against pre-releases of all dependencies (if there are any). +The purpose of this check is to detect incompatibilities of new package versions early on and +gives you time to fix the issue or reach out to the developers of the dependency before the package +is released to a wider audience. -For more information, follow the [Python packaging tutorial][]. +The CI job is defined in `.github/workflows/test.yaml`, +however the single point of truth for CI jobs is the Hatch test matrix defined in `pyproject.toml`. +This means that local testing via hatch and remote testing on CI tests against the same python versions and uses the same environments. -It is possible to automate this with GitHub actions, see also [this feature request][pypi-feature-request] -in the cookiecutter-scverse template. +## Publishing a release + +### Updating the version number + +Before making a release, you need to update the version number in the `pyproject.toml` file. +Please adhere to [Semantic Versioning][semver], in brief + +> Given a version number MAJOR.MINOR.PATCH, increment the: +> +> 1. MAJOR version when you make incompatible API changes, +> 2. MINOR version when you add functionality in a backwards compatible manner, and +> 3. PATCH version when you make backwards compatible bug fixes. +> +> Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. -[python packaging tutorial]: https://packaging.python.org/en/latest/tutorials/packaging-projects/#generating-distribution-archives -[pypi-feature-request]: https://github.com/scverse/cookiecutter-scverse/issues/88 +Once you are done, commit and push your changes and navigate to the "Releases" page of this project on GitHub. +Specify `vX.X.X` as a tag name and create a release. +For more information, see [managing GitHub releases][]. +This will automatically create a git tag and trigger a Github workflow that creates a release on [PyPI][]. + +[semver]: https://semver.org/ +[managing GitHub releases]: https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository +[pypi]: https://pypi.org/ ## Writing documentation -Please write documentation for new or changed features and use-cases. This project uses [sphinx][] with the following features: +Please write documentation for new or changed features and use-cases. +This project uses [sphinx][] with the following features: + +- The [myst][] extension allows to write documentation in markdown/Markedly Structured Text +- [Numpy-style docstrings][numpydoc] (through the [napoloen][numpydoc-napoleon] extension). +- Jupyter notebooks as tutorials through [myst-nb][] (See [Tutorials with myst-nb](#tutorials-with-myst-nb-and-jupyter-notebooks)) +- [sphinx-autodoc-typehints][], to automatically reference annotated input and output types +- Citations (like {cite:p}`Virshup_2023`) can be included with [sphinxcontrib-bibtex](https://sphinxcontrib-bibtex.readthedocs.io/) -- the [myst][] extension allows to write documentation in markdown/Markedly Structured Text -- [Numpy-style docstrings][numpydoc] (through the [napoloen][numpydoc-napoleon] extension). -- Jupyter notebooks as tutorials through [myst-nb][] (See [Tutorials with myst-nb](#tutorials-with-myst-nb-and-jupyter-notebooks)) -- [Sphinx autodoc typehints][], to automatically reference annotated input and output types -- Citations (like {cite:p}`Virshup_2023`) can be included with [sphinxcontrib-bibtex](https://sphinxcontrib-bibtex.readthedocs.io/) +See scanpy’s {doc}`scanpy:dev/documentation` for more information on how to write your own. -See the [scanpy developer docs](https://scanpy.readthedocs.io/en/latest/dev/documentation.html) for more information -on how to write documentation. +[sphinx]: https://www.sphinx-doc.org/en/master/ +[myst]: https://myst-parser.readthedocs.io/en/latest/intro.html +[myst-nb]: https://myst-nb.readthedocs.io/en/latest/ +[numpydoc-napoleon]: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html +[numpydoc]: https://numpydoc.readthedocs.io/en/latest/format.html +[sphinx-autodoc-typehints]: https://github.com/tox-dev/sphinx-autodoc-typehints ### Tutorials with myst-nb and jupyter notebooks The documentation is set-up to render jupyter notebooks stored in the `docs/notebooks` directory using [myst-nb][]. Currently, only notebooks in `.ipynb` format are supported that will be included with both their input and output cells. -It is your reponsibility to update and re-run the notebook whenever necessary. +It is your responsibility to update and re-run the notebook whenever necessary. -If you are interested in automatically running notebooks as part of the continuous integration, please check -out [this feature request](https://github.com/scverse/cookiecutter-scverse/issues/40) in the `cookiecutter-scverse` -repository. +If you are interested in automatically running notebooks as part of the continuous integration, +please check out [this feature request][issue-render-notebooks] in the `cookiecutter-scverse` repository. + +[issue-render-notebooks]: https://github.com/scverse/cookiecutter-scverse/issues/40 #### Hints -- If you refer to objects from other packages, please add an entry to `intersphinx_mapping` in `docs/conf.py`. Only - if you do so can sphinx automatically create a link to the external documentation. -- If building the documentation fails because of a missing link that is outside your control, you can add an entry to - the `nitpick_ignore` list in `docs/conf.py` +- If you refer to objects from other packages, please add an entry to `intersphinx_mapping` in `docs/conf.py`. + Only if you do so can sphinx automatically create a link to the external documentation. +- If building the documentation fails because of a missing link that is outside your control, + you can add an entry to the `nitpick_ignore` list in `docs/conf.py` + +(docs-building)= + +### Building the docs locally -#### Building the docs locally +:::::{tabs} +::::{group-tab} Hatch + +```bash +hatch run docs:build +hatch run docs:open +``` + +:::: + +::::{group-tab} uv ```bash cd docs -make html -open _build/html/index.html +uv run sphinx-build -M html . _build -W +(xdg-)open _build/html/index.html ``` - +:::: -[scanpy developer guide]: https://scanpy.readthedocs.io/en/latest/dev/index.html -[cookiecutter-scverse-instance]: https://cookiecutter-scverse-instance.readthedocs.io/en/latest/template_usage.html -[github quickstart guide]: https://docs.github.com/en/get-started/quickstart/create-a-repo?tool=webui -[codecov]: https://about.codecov.io/sign-up/ -[codecov docs]: https://docs.codecov.com/docs -[codecov bot]: https://docs.codecov.com/docs/team-bot -[codecov app]: https://github.com/apps/codecov -[pre-commit.ci]: https://pre-commit.ci/ -[readthedocs.org]: https://readthedocs.org/ -[myst-nb]: https://myst-nb.readthedocs.io/en/latest/ -[jupytext]: https://jupytext.readthedocs.io/en/latest/ -[pre-commit]: https://pre-commit.com/ -[anndata]: https://github.com/scverse/anndata -[mudata]: https://github.com/scverse/mudata -[pytest]: https://docs.pytest.org/ -[semver]: https://semver.org/ -[sphinx]: https://www.sphinx-doc.org/en/master/ -[myst]: https://myst-parser.readthedocs.io/en/latest/intro.html -[numpydoc-napoleon]: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html -[numpydoc]: https://numpydoc.readthedocs.io/en/latest/format.html -[sphinx autodoc typehints]: https://github.com/tox-dev/sphinx-autodoc-typehints -[pypi]: https://pypi.org/ +::::{group-tab} Pip + +```bash +source .venv/bin/activate +cd docs +sphinx-build -M html . _build -W +(xdg-)open _build/html/index.html +``` + +:::: +::::: diff --git a/docs/extensions/typed_returns.py b/docs/extensions/typed_returns.py index 1135204..0fbffef 100644 --- a/docs/extensions/typed_returns.py +++ b/docs/extensions/typed_returns.py @@ -12,7 +12,7 @@ def _process_return(lines: Iterable[str]) -> Generator[str, None, None]: for line in lines: if m := re.fullmatch(r"(?P\w+)\s+:\s+(?P[\w.]+)", line): - yield f'-{m["param"]} (:class:`~{m["type"]}`)' + yield f"-{m['param']} (:class:`~{m['type']}`)" else: yield line diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 954237b..0000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/template_usage.md b/docs/template_usage.md deleted file mode 100644 index 0022c98..0000000 --- a/docs/template_usage.md +++ /dev/null @@ -1,364 +0,0 @@ -# Using this template - -Welcome to the developer guidelines! This document is split into two parts: - -1. The [repository setup](#setting-up-the-repository). This section is relevant primarily for the repository maintainer and shows how to connect - continuous integration services and documents initial set-up of the repository. -2. The [contributor guide](contributing.md#contributing-guide). It contains information relevant to all developers who want to make a contribution. - -## Setting up the repository - -### First commit - -If you are reading this, you should have just completed the repository creation with : - -```bash -cruft create https://github.com/scverse/cookiecutter-scverse -``` - -and you should have - -``` -cd genomic-annotations -``` - -into the new project directory. Now that you have created a new repository locally, the first step is to push it to github. To do this, you'd have to create a **new repository** on github. -You can follow the instructions directly on [github quickstart guide][]. -Since `cruft` already populated the local repository of your project with all the necessary files, we suggest to _NOT_ initialize the repository with a `README.md` file or `.gitignore`, because you might encounter git conflicts on your first push. -If you are familiar with git and knows how to handle git conflicts, you can go ahead with your preferred choice. - -:::{note} -If you are looking at this document in the [cookiecutter-scverse-instance][] repository documentation, throughout this document the name of the project is `cookiecutter-scverse-instance`. Otherwise it should be replaced by your new project name: `genomic-annotations`. -::: - -Now that your new project repository has been created on github at `https://github.com/ivirshup/genomic-annotations` you can push your first commit to github. -To do this, simply follow the instructions on your github repository page or a more verbose walkthrough here: - -Assuming you are in `/your/path/to/genomic-annotations`. Add all files and commit. - -```bash -# stage all files of your new repo -git add --all -# commit -git commit -m "first commit" -``` - -You'll notice that the command `git commit` installed a bunch of packages and triggered their execution: those are pre-commit! To read more about what they are and what they do, you can go to the related section [Pre-commit checks](#pre-commit-checks) in this document. - -:::{note} -There is a chance that `git commit -m "first commit"` fails due to the `prettier` pre-commit formatting the file `.cruft.json`. No problem, you have just experienced what pre-commit checks do in action. Just go ahead and re-add the modified file and try to commit again: - -```bash - git add -u # update all tracked file - git commit -m "first commit" -``` - -::: - -Now that all the files of the newly created project have been committed, go ahead with the remaining steps: - -```bash -# update the `origin` of your local repo with the remote github link -git remote add origin https://github.com/ivirshup/genomic-annotations.git -# rename the default branch to main -git branch -M main -# push all your files to remote -git push -u origin main -``` - -Your project should be now available at `https://github.com/ivirshup/genomic-annotations`. While the repository at this point can be directly used, there are few remaining steps that needs to be done in order to achieve full functionality. - -### Coverage tests with _Codecov_ - -Coverage tells what fraction of the code is "covered" by unit tests, thereby encouraging contributors to -[write tests](contributing.md#writing-tests). -To enable coverage checks, head over to [codecov][] and sign in with your GitHub account. -You'll find more information in "getting started" section of the [codecov docs][]. - -In the `Actions` tab of your projects' github repository, you can see that the workflows are failing due to the **Upload coverage** step. The error message in the workflow should display something like: - -``` -... - Retrying 5/5 in 2s.. - {'detail': ErrorDetail(string='Could not find a repository, try using repo upload token', code='not_found')} -Error: 404 Client Error: Not Found for url: -... -``` - -While [codecov docs][] has a very extensive documentation on how to get started, _if_ you are using the default settings of this template we can assume that you are using [codecov][] in a github action workflow and hence you can make use of the [codecov bot][]. - -To set it up, simply go to the [codecov app][] page and follow the instructions to activate it for your repository. -Once the activation is completed, go back to the `Actions` tab and re-run the failing workflows. - -The workflows should now succeed and you will be able to find the code coverage at this link: `https://app.codecov.io/gh/ivirshup/genomic-annotations`. You might have to wait couple of minutes and the coverage of this repository should be ~60%. - -If your repository is private, you will have to specify an additional token in the repository secrets. In brief, you need to: - -1. Generate a Codecov Token by clicking _setup repo_ in the codecov dashboard. - - If you have already set up codecov in the repository by following the previous steps, you can directly go to the codecov repo webpage. -2. Go to _Settings_ and copy **only** the token `_______-____-...`. -3. Go to _Settings_ of your newly created repository on GitHub. -4. Go to _Security > Secrets > Actions_. -5. Create new repository secret with name `CODECOV_TOKEN` and paste the token generated by codecov. -6. Past these additional lines in `/.github/workflows.test.yaml` under the **Upload coverage** step: - ```bash - - name: Upload coverage - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - ``` -7. Go back to github `Actions` page an re-run previously failed jobs. - -### Documentation on _readthedocs_ - -We recommend using [readthedocs.org][] (RTD) to build and host the documentation for your project. -To enable readthedocs, head over to [their website][readthedocs.org] and sign in with your GitHub account. -On the RTD dashboard choose "Import a Project" and follow the instructions to add your repository. - -- Make sure to choose the correct name of the default branch. On GitHub, the name of the default branch should be `main` (it has - recently changed from `master` to `main`). -- We recommend to enable documentation builds for pull requests (PRs). This ensures that a PR doesn't introduce changes - that break the documentation. To do so, got to `Admin -> Advanced Settings`, check the - `Build pull requests for this projects` option, and click `Save`. For more information, please refer to - the [official RTD documentation](https://docs.readthedocs.io/en/stable/pull-requests.html). -- If you find the RTD builds are failing, you can disable the `fail_on_warning` option in `.readthedocs.yaml`. - -If your project is private, there are ways to enable docs rendering on [readthedocs.org][] but it is more cumbersome and requires a different subscription for read the docs. See a guide [here](https://docs.readthedocs.io/en/stable/guides/importing-private-repositories.html). - -### Pre-commit checks - -[Pre-commit][] checks are fast programs that -check code for errors, inconsistencies and code styles, before the code -is committed. - -This template uses a number of pre-commit checks. In this section we'll detail what is used, where they're defined, and how to modify these checks. - -#### Pre-commit CI - -We recommend setting up [pre-commit.ci][] to enforce consistency checks on every commit -and pull-request. - -To do so, head over to [pre-commit.ci][] and click "Sign In With GitHub". Follow -the instructions to enable pre-commit.ci for your account or your organization. You -may choose to enable the service for an entire organization or on a per-repository basis. - -Once authorized, pre-commit.ci should automatically be activated. - -#### Overview of pre-commit hooks used by the template - -The following pre-commit hooks are for code style and format: - -- [black](https://black.readthedocs.io/en/stable/): - standard code formatter in Python. -- [blacken-docs](https://github.com/asottile/blacken-docs): - black on Python code in docs. -- [prettier](https://prettier.io/docs/en/index.html): - standard code formatter for non-Python files (e.g. YAML). -- [ruff][] based checks: - - [isort](https://beta.ruff.rs/docs/rules/#isort-i) (rule category: `I`): - sort module imports into sections and types. - - [pydocstyle](https://beta.ruff.rs/docs/rules/#pydocstyle-d) (rule category: `D`): - pydocstyle extension of flake8. - - [flake8-tidy-imports](https://beta.ruff.rs/docs/rules/#flake8-tidy-imports-tid) (rule category: `TID`): - tidy module imports. - - [flake8-comprehensions](https://beta.ruff.rs/docs/rules/#flake8-comprehensions-c4) (rule category: `C4`): - write better list/set/dict comprehensions. - - [pyupgrade](https://beta.ruff.rs/docs/rules/#pyupgrade-up) (rule category: `UP`): - upgrade syntax for newer versions of the language. - -The following pre-commit hooks are for errors and inconsistencies: - -- [pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks): generic pre-commit hooks for text files. - - **detect-private-key**: checks for the existence of private keys. - - **check-ast**: check whether files parse as valid python. - - **end-of-file-fixer**: check files end in a newline and only a newline. - - **mixed-line-ending**: checks mixed line ending. - - **trailing-whitespace**: trims trailing whitespace. - - **check-case-conflict**: check files that would conflict with case-insensitive file systems. - - **forbid-to-commit**: Make sure that `*.rej` files cannot be commited. - These files are created by the [automated template sync](#automated-template-sync) - if there's a merge conflict and need to be addressed manually. -- [ruff][] based checks: - - [pyflakes](https://beta.ruff.rs/docs/rules/#pyflakes-f) (rule category: `F`): - various checks for errors. - - [pycodestyle](https://beta.ruff.rs/docs/rules/#pycodestyle-e-w) (rule category: `E`, `W`): - various checks for errors. - - [flake8-bugbear](https://beta.ruff.rs/docs/rules/#flake8-bugbear-b) (rule category: `B`): - find possible bugs and design issues in program. - - [flake8-blind-except](https://beta.ruff.rs/docs/rules/#flake8-blind-except-ble) (rule category: `BLE`): - checks for blind, catch-all `except` statements. - - [Ruff-specific rules](https://beta.ruff.rs/docs/rules/#ruff-specific-rules-ruf) (rule category: `RUF`): - - `RUF100`: remove unneccesary `# noqa` comments () - -#### How to add or remove pre-commit checks - -The [pre-commit checks](#pre-commit-checks) check for both correctness and stylistic errors. -In some cases it might overshoot and you may have good reasons to ignore certain warnings. -This section shows you where these checks are defined, and how to enable/ disable them. - -##### pre-commit - -You can add or remove pre-commit checks by simply deleting relevant lines in the `.pre-commit-config.yaml` file under the repository root. -Some pre-commit checks have additional options that can be specified either in the `pyproject.toml` (for `ruff` and `black`) or tool-specific -config files, such as `.prettierrc.yml` for **prettier**. - -##### Ruff - -This template configures `ruff` through the `[tool.ruff]` entry in the `pyproject.toml`. -For further information `ruff` configuration, see [the docs](https://beta.ruff.rs/docs/configuration/). - -Ruff assigns code to the rules it checks (e.g. `E401`) and groups them under a rule category (e.g. `E`). -Rule categories are selectively enabled by including them under the `select` key: - -```toml -[tool.ruff] -... - -select = [ - "F", # Errors detected by Pyflakes - "E", # Error detected by Pycodestyle - "W", # Warning detected by Pycodestyle - "I", # isort - ... -] -``` - -The `ignore` entry is used to disable specific rules for the entire project. -Add the rule code(s) you want to ignore and don't forget to add a comment explaining why. -You can find a long list of checks that this template disables by default sitting there already. - -```toml -ignore = [ - ... - # __magic__ methods are are often self-explanatory, allow missing docstrings - "D105", - ... -] -``` - -Checks can be ignored per-file (or glob pattern) with `[tool.ruff.per-file-ignores]`. - -```toml -[tool.ruff.per-file-ignores] -"docs/*" = ["I"] -"tests/*" = ["D"] -"*/__init__.py" = ["F401"] -``` - -To ignore a specific rule on a per-case basis, you can add a `# noqa: [, , …]` comment to the offending line. -Specify the rule code(s) to ignore, with e.g. `# noqa: E731`. Check the [Ruff guide][] for reference. - -```{note} -The `RUF100` check will remove rule codes that are no longer necessary from `noqa` comments. -If you want to add a code that comes from a tool other than Ruff, -add it to Ruff’s [`external = [...]`](https://beta.ruff.rs/docs/settings/#external) setting to prevent `RUF100` from removing it. -``` - -[ruff]: https://beta.ruff.rs/docs/ -[ruff guide]: https://beta.ruff.rs/docs/configuration/#suppressing-errors - -### API design - -Scverse ecosystem packages should operate on [AnnData][] and/or [MuData][] data structures and typically use an API -as originally [introduced by scanpy][scanpy-api] with the following submodules: - -- `pp` for preprocessing -- `tl` for tools (that, compared to `pp` generate interpretable output, often associated with a corresponding plotting - function) -- `pl` for plotting functions - -You may add additional submodules as appropriate. While we encourage to follow a scanpy-like API for ecosystem packages, -there may also be good reasons to choose a different approach, e.g. using an object-oriented API. - -[scanpy-api]: https://scanpy.readthedocs.io/en/stable/usage-principles.html - -### Using VCS-based versioning - -By default, the template uses hard-coded version numbers that are set in `pyproject.toml` and [managed with -bump2version](contributing.md#publishing-a-release). If you prefer to have your project automatically infer version numbers from git -tags, it is straightforward to switch to vcs-based versioning using [hatch-vcs][]. - -In `pyproject.toml` add the following changes, and you are good to go! - -```diff ---- a/pyproject.toml -+++ b/pyproject.toml -@@ -1,11 +1,11 @@ - [build-system] - build-backend = "hatchling.build" --requires = ["hatchling"] -+requires = ["hatchling", "hatch-vcs"] - - - [project] - name = "genomic-annotations" --version = "0.3.1dev" -+dynamic = ["version"] - -@@ -60,6 +60,9 @@ -+[tool.hatch.version] -+source = "vcs" -+ - [tool.coverage.run] - source = ["genomic-annotations"] - omit = [ -``` - -Don't forget to update the [Making a release section](contributing.md#publishing-a-release) in this document accordingly, after you are done! - -[hatch-vcs]: https://pypi.org/project/hatch-vcs/ - -### Automated template sync - -Automated template sync is enabled by default. This means that every night, a GitHub action runs [cruft][] to check -if a new version of the `scverse-cookiecutter` template got released. If there are any new changes, a pull request -proposing these changes is created automatically. This helps keeping the repository up-to-date with the latest -coding standards. - -It may happen that a template sync results in a merge conflict. If this is the case a `*.ref` file with the -diff is created. You need to manually address these changes and remove the `.rej` file when you are done. -The pull request can only be merged after all `*.rej` files have been removed. - -:::{tip} -The following hints may be useful to work with the template sync: - -- GitHub automatically disables scheduled actions if there has been not activity to the repository for 60 days. - You can re-enable or manually trigger the sync by navigating to `Actions` -> `Sync Template` in your GitHub repository. -- If you want to ignore certain files from the template update, you can add them to the `[tool.cruft]` section in the - `pyproject.toml` file in the root of your repository. More details are described in the - [cruft documentation][cruft-update-project]. -- To disable the sync entirely, simply remove the file `.github/workflows/sync.yaml`. - -::: - -[cruft]: https://cruft.github.io/cruft/ -[cruft-update-project]: https://cruft.github.io/cruft/#updating-a-project - -## Moving forward - -You have reached the end of this document. Congratulations! You have successfully set up your project and are ready to start. -For everything else related to documentation, code style, testing and publishing your project ot pypi, please refer to the [contributing docs](contributing.md#contributing-guide). - - - -[scanpy developer guide]: https://scanpy.readthedocs.io/en/latest/dev/index.html -[cookiecutter-scverse-instance]: https://cookiecutter-scverse-instance.readthedocs.io/en/latest/template_usage.html -[github quickstart guide]: https://docs.github.com/en/get-started/quickstart/create-a-repo?tool=webui -[codecov]: https://about.codecov.io/sign-up/ -[codecov docs]: https://docs.codecov.com/docs -[codecov bot]: https://docs.codecov.com/docs/team-bot -[codecov app]: https://github.com/apps/codecov -[pre-commit.ci]: https://pre-commit.ci/ -[readthedocs.org]: https://readthedocs.org/ -[myst-nb]: https://myst-nb.readthedocs.io/en/latest/ -[jupytext]: https://jupytext.readthedocs.io/en/latest/ -[pre-commit]: https://pre-commit.com/ -[anndata]: https://github.com/scverse/anndata -[mudata]: https://github.com/scverse/mudata -[pytest]: https://docs.pytest.org/ -[semver]: https://semver.org/ -[sphinx]: https://www.sphinx-doc.org/en/master/ -[myst]: https://myst-parser.readthedocs.io/en/latest/intro.html -[numpydoc-napoleon]: https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html -[numpydoc]: https://numpydoc.readthedocs.io/en/latest/format.html -[sphinx autodoc typehints]: https://github.com/tox-dev/sphinx-autodoc-typehints diff --git a/pyproject.toml b/pyproject.toml index 180bb94..ec4b159 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,133 +1,154 @@ [build-system] build-backend = "hatchling.build" -requires = ["hatchling"] +requires = [ "hatchling" ] [project] -name = "genomic-annotations" +name = "genomic-features" version = "0.0.1" description = "Genomic annotations using BioConductor resources in Python." readme = "README.md" -requires-python = ">=3.9" -license = {file = "LICENSE"} +license = { file = "LICENSE" } +maintainers = [ + { name = "Isaac Virshup", email = "ivirshup@gmail.com" }, +] authors = [ - {name = "Isaac Virshup"}, + { name = "Isaac Virshup" }, ] -maintainers = [ - {name = "Isaac Virshup", email = "ivirshup@gmail.com"}, +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] -urls.Documentation = "https://genomic-annotations.readthedocs.io/" -urls.Source = "https://github.com/ivirshup/genomic-annotations" -urls.Home-page = "https://github.com/ivirshup/genomic-annotations" dependencies = [ - "anndata", - # for debug logging (referenced from the issue template) - "session-info" + "anndata", + # for debug logging (referenced from the issue template) + "session-info2", ] - -[project.optional-dependencies] -dev = [ - # CLI for bumping the version number - "bump2version", - "pre-commit", - "twine>=4.0.2" +optional-dependencies.dev = [ + "pre-commit", + "twine>=4.0.2", ] -doc = [ - "sphinx>=4", - "sphinx-book-theme>=1.0.0", - "myst-nb", - "sphinxcontrib-bibtex>=1.0.0", - "sphinx-autodoc-typehints", - # For notebooks - "ipykernel", - "ipython", - "sphinx-copybutton", +optional-dependencies.doc = [ + "docutils>=0.8,!=0.18.*,!=0.19.*", + "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", + "sphinxext-opengraph", ] -test = [ - "pytest", - "pytest-cov", +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://genomic-features.readthedocs.io/" +urls.Homepage = "https://github.com/ivirshup/genomic-features" +urls.Source = "https://github.com/ivirshup/genomic-features" -[tool.coverage.run] -source = ["genomic_annotations"] -omit = [ - "**/test_*.py", -] +[tool.hatch.envs.default] +installer = "uv" +features = [ "dev" ] -[tool.pytest.ini_options] -testpaths = ["tests"] -xfail_strict = true -addopts = [ - "--import-mode=importlib", # allow using test files with same name -] +[tool.hatch.envs.docs] +features = [ "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}" + +# Test the lowest and highest supported Python versions with normal deps +[[tool.hatch.envs.hatch-test.matrix]] +deps = [ "stable" ] +python = [ "3.10", "3.13" ] + +# Test the newest supported Python version also with pre-release deps +[[tool.hatch.envs.hatch-test.matrix]] +deps = [ "pre" ] +python = [ "3.13" ] -[tool.black] -line-length = 88 -target-version = ["py39"] +[tool.hatch.envs.hatch-test] +features = [ "dev", "test" ] + +[tool.hatch.envs.hatch-test.overrides] +# If the matrix variable `deps` is set to "pre", +# set the environment variable `UV_PRERELEASE` to "allow". +matrix.deps.env-vars = [ + { key = "UV_PRERELEASE", value = "allow", if = [ "pre" ] }, +] [tool.ruff] -src = ["src"] -line-length = 88 -target-version = "py39" -select = [ - "F", # Errors detected by Pyflakes - "E", # Error detected by Pycodestyle - "W", # Warning detected by Pycodestyle - "I", # isort - "D", # pydocstyle - "B", # flake8-bugbear - "TID", # flake8-tidy-imports - "C4", # flake8-comprehensions - "BLE", # flake8-blind-except - "UP", # pyupgrade - "RUF100", # Report unused noqa directives +line-length = 120 +src = [ "src" ] +extend-include = [ "*.ipynb" ] + +format.docstring-code-format = true + +lint.select = [ + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "D", # pydocstyle + "E", # Error detected by Pycodestyle + "F", # Errors detected by Pyflakes + "I", # isort + "RUF100", # Report unused noqa directives + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # Warning detected by Pycodestyle ] -ignore = [ - # line too long -> we accept long comment lines; black gets rid of long code lines - "E501", - # 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 - "E741", - # Missing docstring in public package - "D104", - # Missing docstring in public module - "D100", - # Missing docstring in __init__ - "D107", - # Errors from function calls in argument defaults. These are fine when the result is immutable. - "B008", - # __magic__ methods are are often self-explanatory, allow missing docstrings - "D105", - # first line should end with a period [Bug: doesn't work with single-line docstrings] - "D400", - # First line should be in imperative mood; try rephrasing - "D401", - ## Disable one in each pair of mutually incompatible rules - # We don’t want a blank line before a class docstring - "D203", - # We want docstrings to start immediately after the opening triple quote - "D213", +lint.ignore = [ + "B008", # Errors from function calls in argument defaults. These are fine when the result is immutable. + "D100", # Missing docstring in public module + "D104", # Missing docstring in public package + "D105", # __magic__ methods are often self-explanatory, allow missing docstrings + "D107", # Missing docstring in __init__ + # Disable one in each pair of mutually incompatible rules + "D203", # We don’t want a blank line before a class docstring + "D213", # <> We want docstrings to start immediately after the opening triple quote + "D400", # first line should end with a period [Bug: doesn’t work with single-line docstrings] + "D401", # First line should be in imperative mood; try rephrasing + "E501", # line too long -> we accept long comment lines; formatter gets rid of long code lines + "E731", # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient + "E741", # allow I, O, l as variable names -> I is the identity matrix ] +lint.per-file-ignores."*/__init__.py" = [ "F401" ] +lint.per-file-ignores."docs/*" = [ "I" ] +lint.per-file-ignores."tests/*" = [ "D" ] +lint.pydocstyle.convention = "numpy" -[tool.ruff.pydocstyle] -convention = "numpy" - -[tool.ruff.per-file-ignores] -"docs/*" = ["I"] -"tests/*" = ["D"] -"*/__init__.py" = ["F401"] +[tool.pytest.ini_options] +testpaths = [ "tests" ] +xfail_strict = true +addopts = [ + "--import-mode=importlib", # allow using test files with same name +] -[tool.jupytext] -formats = "ipynb,md" +[tool.coverage.run] +source = [ "genomic_features" ] +patch = [ "subprocess" ] +omit = [ + "**/test_*.py", +] [tool.cruft] skip = [ - "tests", - "src/**/__init__.py", - "src/**/basic.py", - "docs/api.md", - "docs/changelog.md", - "docs/references.bib", - "docs/references.md", - "docs/notebooks/example.ipynb" + "tests", + "src/**/__init__.py", + "src/**/basic.py", + "docs/api.md", + "docs/changelog.md", + "docs/references.bib", + "docs/references.md", + "docs/notebooks/example.ipynb", ]