diff --git a/.cruft.json b/.cruft.json index 3f646eb8..a76b0561 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,7 +1,7 @@ { "template": "https://github.com/scverse/cookiecutter-scverse", - "commit": "87a407a65408d75a949c0b54b19fd287475a56f8", - "checkout": "v0.4.0", + "commit": "94ef9fb6f9ad8cfe65a3d9575679c03c80c49cd1", + "checkout": "v0.5.0", "context": { "cookiecutter": { "project_name": "scverse-tutorials", @@ -10,19 +10,33 @@ "author_full_name": "scverse team", "author_email": "core-team@scverse.org", "github_user": "scverse", - "project_repo": "https://github.com/scverse/scverse-tutorials", + "github_repo": "scverse-tutorials", "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" + "_template": "https://github.com/scverse/cookiecutter-scverse", + "_commit": "94ef9fb6f9ad8cfe65a3d9575679c03c80c49cd1" } }, "directory": null diff --git a/.editorconfig b/.editorconfig index 050f9118..66678e37 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,10 +8,7 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[*.{yml,yaml}] -indent_size = 2 - -[.cruft.json] +[{*.{yml,yaml,toml},.cruft.json}] indent_size = 2 [Makefile] diff --git a/.github/workflows/execute-nbs.yaml b/.github/workflows/execute-nbs.yaml index 2f766949..31da05ae 100644 --- a/.github/workflows/execute-nbs.yaml +++ b/.github/workflows/execute-nbs.yaml @@ -48,7 +48,7 @@ jobs: activate-environment: tutorials channel-priority: flexible environment-file: environment.yml - miniforge-variant: Mambaforge + miniforge-variant: Miniforge3 miniforge-version: latest use-mamba: true # some important packages are not available as .tar.bz2 anymore @@ -83,6 +83,6 @@ jobs: jcache notebook merge $f $f done - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: Render notebooks diff --git a/.gitignore b/.gitignore index 78455d2a..cb7fe93a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,18 +8,21 @@ __pycache__/ # Temp files .DS_Store *~ +buck-out/ -# Virtual environments +# Compiled files .venv/ -# Build artifacts +# Distribution / packaging /dist/ + +# Tests and coverage +/data/ +/node_modules/ + +# docs /docs/generated/ /docs/_build/ # User configuration /hatch.toml - -# IDEs -/.idea/ -/.vscode/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f02e0354..f4d58d10 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,10 +6,15 @@ default_stages: - pre-push minimum_pre_commit_version: 2.16.0 repos: - - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.5.3 + - repo: https://github.com/biomejs/pre-commit + rev: v1.9.4 hooks: - - id: prettier + - 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.5.1 + hooks: + - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.11.5 hooks: diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..caaeb4f7 --- /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 00000000..36d18746 --- /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 00000000..e034b91f --- /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 00000000..2175c16e --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,16 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "formatter": { "useEditorconfig": true }, + "overrides": [ + { + "include": ["./.vscode/*.json", "**/*.jsonc"], + "json": { + "formatter": { "trailingCommas": "all" }, + "parser": { + "allowComments": true, + "allowTrailingCommas": true, + }, + }, + }, + ], +} diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst index e4665dfc..7b4a0cf8 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 6564e89e..6bd70d48 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,7 @@ needs_sphinx = "4.0" html_context = { - "display_github": True, + "display_github": True, # Integrate GitHub "github_user": "scverse", "github_repo": project_name, "github_version": "main", @@ -89,14 +89,20 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "anndata": ("https://anndata.readthedocs.io/en/stable/", None), - "numpy": ("https://numpy.org/doc/stable/", None), "scanpy": ("https://scanpy.readthedocs.io/en/stable/", None), + "numpy": ("https://numpy.org/doc/stable/", None), } # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**.ipynb_checkpoints", ".jupyter_cache"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "**.ipynb_checkpoints", + ".jupyter_cache", +] # -- Options for HTML output ------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index d5186f98..2983aa04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,136 +1,132 @@ [build-system] build-backend = "hatchling.build" -requires = ["hatchling"] +requires = [ "hatchling" ] [project] name = "scverse-tutorials" version = "0.0.1" description = "Tutorials for single-cell analysis with scverse packages" readme = "README.md" -requires-python = ">=3.10" -license = {file = "LICENSE"} -authors = [ - {name = "scverse team"}, -] +license = "BSD-3-Clause" maintainers = [ - {name = "scverse team", email = "core-team@scverse.org"}, + { name = "scverse team", email = "core-team@scverse.org" }, ] -urls.Documentation = "https://scverse.org/scverse-tutorials" -urls.Source = "https://github.com/scverse/scverse-tutorials" -urls.Home-page = "https://github.com/scverse/scverse-tutorials" +authors = [ + { name = "scverse team" }, +] +requires-python = ">=3.10" classifiers = [ - "Private :: Do Not Upload", # Prevent uploading to PyPI + "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", ] - -[project.optional-dependencies] -dev = ["pre-commit"] -registry = [ - "jsonschema", - "pillow", - "httpx", - "pyyaml", +dependencies = [ + "anndata", + # for debug logging (referenced from the issue template) + "session-info2", ] -docs = [ - "sphinx>=7", - "sphinx-book-theme>=1.1.0", - "myst-nb>=1.1.0", - "sphinxcontrib-bibtex>=1.0.0", - "sphinx-autodoc-typehints", - "sphinxext-opengraph", - # For notebooks - "ipykernel", - "ipython", - "sphinx-copybutton", +optional-dependencies.dev = [ "pre-commit" ] +optional-dependencies.docs = [ + "ipykernel", # For notebooks + "ipython", + "myst-nb>=1.1", + "sphinx>=7", + "sphinx-autodoc-typehints", + "sphinx-book-theme>=1.1", + "sphinx-copybutton", + "sphinxcontrib-bibtex>=1", + "sphinxext-opengraph", +] +optional-dependencies.registry = [ + "httpx", + "jsonschema", + "pillow", + "pyyaml", ] +# https://docs.pypi.org/project_metadata/#project-urls +urls.Documentation = "https://scverse-tutorials.readthedocs.io/" +urls.Homepage = "https://github.com/scverse/scverse-tutorials" +urls.Source = "https://github.com/scverse/scverse-tutorials" + +[tool.hatch.build.targets.wheel] +bypass-selection = true # This is not a package + [tool.hatch.envs.default] installer = "uv" +features = [ "dev" ] [tool.hatch.envs.registry] -features = ["registry"] +features = [ "registry" ] [tool.hatch.envs.registry.scripts] validate = "python tutorial-registry/validate.py {args}" [tool.hatch.envs.docs] -features = ["docs"] +features = [ "docs" ] extra-dependencies = [ - "setuptools", # undeclared dependency in pybtex - # fix from here: https://github.com/executablebooks/MyST-NB/pull/597 - "myst-nb @ git+https://github.com/flying-sheep/MyST-NB.git@eval-metadata", + "setuptools", # undeclared dependency in pybtex + # fix from here: https://github.com/executablebooks/MyST-NB/pull/597 + "myst-nb @ git+https://github.com/flying-sheep/MyST-NB.git@eval-metadata", ] -[tool.hatch.envs.docs.scripts] -build = "sphinx-build -M html docs docs/_build {args}" - -[tool.hatch.build.targets.wheel] -bypass-selection = true # This is not a package +scripts.build = "sphinx-build -M html docs docs/_build {args}" +scripts.open = "python -m webbrowser -t docs/_build/html/index.html" +scripts.clean = "git clean -fdX -- {args:docs}" [tool.ruff] line-length = 120 -src = ["src"] -extend-include = ["*.ipynb"] +src = [ "src" ] +extend-include = [ "*.ipynb" ] -[tool.ruff.lint] -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 +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; formatter 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 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 ] - -[tool.ruff.lint.pydocstyle] -convention = "numpy" - -[tool.ruff.lint.per-file-ignores] -"docs/*" = [ - "B018", # Trailing expressions in notebooks are not “useless” - "D103", # No need for docstrings in functions, we use literate programming - "E402", # Imports in non-top cells are fine +lint.per-file-ignores."*/__init__.py" = [ "F401" ] +lint.per-file-ignores."docs/*" = [ + "B018", # Trailing expressions in notebooks are not “useless” + "D103", # No need for docstrings in functions, we use literate programming + "E402", # Imports in non-top cells are fine ] -"tests/*" = ["D"] -"*/__init__.py" = ["F401"] +lint.per-file-ignores."tests/*" = [ "D" ] + +lint.pydocstyle.convention = "numpy" [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", ]