Skip to content

Modernize pre-commit hooks & linting (PR 1/9)#1282

Merged
Marius1311 merged 17 commits intomainfrom
modernize/pre-commit-linting
Feb 20, 2026
Merged

Modernize pre-commit hooks & linting (PR 1/9)#1282
Marius1311 merged 17 commits intomainfrom
modernize/pre-commit-linting

Conversation

@Marius1311
Copy link
Copy Markdown
Collaborator

Summary

Replace the legacy linting/formatting stack (black, isort, flake8, doc8, pyupgrade) with ruff + biome + pyproject-fmt, require Python ≥3.12, and apply bulk reformatting.

This is the first in a series of modernization PRs to align CellRank with the scverse cookiecutter template.

Changes

Pre-commit hooks

  • Replace black + isort with ruff-format (single formatter)
  • Remove redundant hooks: flake8, doc8, pyupgrade, rst-backticks, yesqa (all subsumed by ruff)
  • Add biome (JSON formatting), pyproject-fmt, and useful pre-commit-hooks (check-ast, check-merge-conflict, check-added-large-files, etc.)
  • Bump all hook versions to latest

Python version & ruff config

  • Require python_requires = ">=3.12" — drop 3.10/3.11 classifiers
  • Consolidate ruff config: remove target-version (inferred from requires-python), add isort rules ("I"), enable docstring-code-format, add B905 to ignore (67 pre-existing zip() without strict= — to be addressed separately)

Code quality

  • Move 16 local imports to top level where they were only local for historical reasons; annotate all remaining ~21 local imports with # circular import comments
  • Fix bare # noqa comments — replaced with explicit rule codes (e.g., # noqa: BLE001) or removed where unnecessary (5 removed, 19 annotated)
  • Fix np.arraynp.ndarray in 6 type annotations across 3 files — np.array is a function, not a type, so np.array | None raises TypeError at runtime (pre-existing bug exposed by ruff UP007/UP045 auto-fixes)

Bulk reformat

  • Ran pre-commit run --all-files to convergence — reformats ~67 files
  • Added .git-blame-ignore-revs so git blame (and GitHub blame UI) skips the reformat commit

Housekeeping

  • Updated .gitignore (add uv.lock, .github/prompts/)

Commits

# Commit Description
1 91a3bdfd Replace black + isort with ruff-format
2 edd7a132 Remove redundant pre-commit hooks
3 7ac027e7 Add modern hooks: biome, pyproject-fmt; bump versions
4 5e687f0f Require Python ≥3.12, update ruff config
5 1dd1e667 Move local imports to top level
6 313b30a4 Fix bare # noqa comments with explicit rule codes
7 0a8f50a9 Bulk reformat: pre-commit run --all-files
8 e9bbac22 Add .git-blame-ignore-revs for bulk reformat commit
9 b3b8bd63 Fix np.array type annotations → np.ndarray
10 63cddb85 Update .gitignore

Notes

  • All 14 pre-commit hooks pass cleanly
  • The bulk reformat commit (0a8f50a9) is listed in .git-blame-ignore-revs — GitHub will automatically ignore it in blame views
  • B905 (zip() without strict=) is intentionally deferred — 67 call sites need individual review for correctness

- Remove black and isort pre-commit hooks
- Remove pretty-format-yaml hook (replaced by biome in 1c)
- Rename ruff hook to ruff-check, add ruff-format hook
- Add types_or: [python, pyi, jupyter] to both ruff hooks
- Remove [tool.black] and [tool.isort] sections from pyproject.toml
- Add I (isort) to [tool.ruff.lint] select
- Remove pyupgrade hook (ruff UP rules cover this)
- Remove blacken-docs hook (ruff-format handles code blocks)
- Remove rstcheck hook (CI lint-docs is more thorough)
- Remove doc8 hook and [tool.doc8] from pyproject.toml
- Add biomejs/pre-commit v2.2.4 (biome-format) + biome.jsonc config
- Add tox-dev/pyproject-fmt v2.6.0
- Bump pre-commit-hooks v4.6.0 → v6.0.0
- Bump ruff-pre-commit v0.6.5 → v0.13.2
- requires-python: '>=3.10' → '>=3.12'
- Remove Python 3.10/3.11 classifiers
- Remove target-version (inferred from requires-python)
- Add src = ['src'] for first-party import detection
- Add format.docstring-code-format = true
Move 16 safe-to-move local imports to module top level:
- scanpy.plotting.palettes in _colors.py
- numpy.linalg.norm, scipy.spatial.distance.jensenshannon,
  sklearn.feature_selection.mutual_info_regression in _lineage.py
- networkx, cellrank._utils._parallelize.parallelize in _utils.py
- cellrank._utils._key.Key in _base_kernel.py
- scvelo.plotting.utils in _random_walk.py
- cellrank.kernels._utils._ensure_numeric_ordered in _log_odds.py
- mpl_toolkits.axes_grid1.Divider/Size in _heatmap.py
- sklearn.svm.SVR in pl/_utils.py
- cellrank._utils._linear_solver._is_petsc_slepc_available in _schur.py
- cellrank._utils.Lineage, sklearn.utils.sparsefuncs in _cytotrace_kernel.py

Add '# circular import' comments to all remaining local imports
that must stay local to avoid circular import chains.
- Remove 5 unnecessary noqa comments (no rules triggered)
- Add specific codes to 19 remaining noqa comments:
  D400/D401 for docrep %(copy)s templates
  D400 for multi-line docstring endings
  F821 for forward-reference type annotations
  F401 for unused petsc4py/slepc4py imports
  C408 for dict() call
  NPY001 for deprecated np.int alias
  B006 for mutable default argument
Run all pre-commit hooks on every file to apply consistent formatting:
- ruff format: reformat all Python files
- ruff check --fix: auto-fix UP007/UP045 (Union→X|Y), C420 (dict.fromkeys),
  UP042/UP047 (type params), remove unused Union/Optional imports
- pyproject-fmt: normalize pyproject.toml
- biome: format JSON files
- Ignore B905 (zip without strict=) for now (67 pre-existing violations)
np.array is a function, not a type — using it in X | Y union syntax
causes TypeError at runtime. These were pre-existing annotation bugs
exposed by the ruff UP007/UP045 auto-fixes.
RTD was using Python 3.10, which now fails because
requires-python >= 3.12.
@Marius1311 Marius1311 requested a review from WeilerP February 13, 2026 22:50
Aligns with requires-python >= 3.12 set in commit 5e687f0.
- lint.yml: 3.10 → 3.12
- deployment.yml: 3.10 → 3.12
- docs/installation.rst: >= 3.8 → >= 3.12
Ruff UP017 auto-converted datetime.timezone.utc → datetime.UTC (the
modern Python 3.11+ alias). The test's IncTime monkeypatch only exposed
a 'timezone' property, not 'UTC'. Add the missing property to the mock.
@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 93.96985% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.37%. Comparing base (be09075) to head (9c77017).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/cellrank/_utils/_lineage.py 82.60% 4 Missing ⚠️
.../cellrank/estimators/mixins/_fate_probabilities.py 75.00% 0 Missing and 4 partials ⚠️
src/cellrank/_utils/_utils.py 93.54% 2 Missing ⚠️
src/cellrank/estimators/mixins/_lineage_drivers.py 83.33% 0 Missing and 2 partials ⚠️
src/cellrank/logging/_logging.py 77.77% 2 Missing ⚠️
src/cellrank/_utils/_docs.py 0.00% 0 Missing and 1 partial ⚠️
src/cellrank/_utils/_enum.py 75.00% 1 Missing ⚠️
src/cellrank/_utils/_linear_solver.py 87.50% 1 Missing ⚠️
...cellrank/estimators/mixins/decomposition/_eigen.py 83.33% 0 Missing and 1 partial ⚠️
...cellrank/estimators/mixins/decomposition/_schur.py 95.00% 0 Missing and 1 partial ⚠️
... and 5 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1282      +/-   ##
==========================================
- Coverage   79.38%   79.37%   -0.01%     
==========================================
  Files          51       51              
  Lines        8624     8627       +3     
  Branches     1492     1492              
==========================================
+ Hits         6846     6848       +2     
  Misses       1198     1198              
- Partials      580      581       +1     
Files with missing lines Coverage Δ
src/cellrank/_utils/_colors.py 81.34% <100.00%> (ø)
src/cellrank/_utils/_key.py 94.80% <100.00%> (+0.06%) ⬆️
src/cellrank/_utils/_parallelize.py 73.33% <100.00%> (ø)
src/cellrank/datasets.py 44.44% <100.00%> (ø)
src/cellrank/estimators/_base_estimator.py 80.00% <100.00%> (ø)
src/cellrank/estimators/mixins/_kernel.py 100.00% <100.00%> (ø)
src/cellrank/estimators/mixins/_utils.py 89.18% <100.00%> (ø)
src/cellrank/estimators/terminal_states/_cflare.py 67.77% <100.00%> (ø)
src/cellrank/estimators/terminal_states/_gpcca.py 78.04% <100.00%> (ø)
...timators/terminal_states/_term_states_estimator.py 77.39% <100.00%> (ø)
... and 38 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Member

@Zethson Zethson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love these kind of PRs. Sorry, just saw this while passing by

Comment thread .github/workflows/deployment.yml
Comment thread .github/workflows/test.yml
Comment thread docs/installation.rst
Comment thread .git-blame-ignore-revs
Comment thread .pre-commit-config.yaml
Comment thread .readthedocs.yaml
Comment thread .readthedocs.yaml Outdated
Comment thread pyproject.toml
Comment thread pyproject.toml
@Marius1311
Copy link
Copy Markdown
Collaborator Author

Hi @Zethson, thanks a lot for your review, that's great! I'll go over it step by step, but for context, I planned a series of PRs to update CellRank, this is just the first one. I wanted to do the more "radical" changes, like switching to hatch, on a separate PR, that's why it's not included here.

@Marius1311
Copy link
Copy Markdown
Collaborator Author

comments

  • Can use from numpy.typing import NDarray for type hints.

@Marius1311
Copy link
Copy Markdown
Collaborator Author

comments

  • Can use from numpy.typing import NDarray for type hints.

Actually I wouldn't do it - that's only really useful if you want to be really strict about typing, i.e. use mypy. We're more leaninant on types, so I think it's fine.

@Marius1311 Marius1311 merged commit c8f1975 into main Feb 20, 2026
6 of 7 checks passed
@Marius1311 Marius1311 deleted the modernize/pre-commit-linting branch February 20, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants