Skip to content

Clear code smell flagged by vulture, jscpd, and radon/xenon - #559

Merged
aarmey merged 1 commit into
mainfrom
cleanup/static-analysis-sweep
Sep 19, 2026
Merged

aarmey merged 1 commit into
mainfrom
cleanup/static-analysis-sweep

Conversation

@aarmey

@aarmey aarmey commented Sep 19, 2026

Copy link
Copy Markdown
Member

Ran the maintainability tools from CLAUDE.md across scrise/ and analysis/ and addressed what they flagged. (Note: CLAUDE.md refers to a cccrise/ package; the source actually lives in scrise/ and analysis/, and vulture/radon/xenon aren't in the dev group, so I ran them via uv run --with ... rather than touching pyproject.toml.)

Dead code — vulture

Most of the 37 hits were false positives (pytest fixtures, the fbuild entry point, figure helpers vulture can't trace across modules). Three were real:

  • Dropped analysis.gating.getHiResOldLupus and the cell_type_conv table it alone consumed — no caller, and no dataset in the repo carries the "Cell Type Old" column it reads.
  • Dropped figure3.plot_cell_perc_corr — no caller.
  • gateThomsonCellsLeiden, Thomson_Doublet and the marker_genes_* tables are the hand-run cache-builder/reference-data exception CLAUDE.md describes. Left in place, with a short note saying why they have no in-repo caller.

Duplication — jscpd

13 clones (1.02% of Python lines), 0 in analysis/. Three were genuine copy-paste and got shared helpers:

  • plotting/factors.py_draw_unit_scaled_heatmap backs plot_eigenstate_factors and plot_gene_factors (16-line clone).
  • plotting/pacmap.py_plot_continuous_pacmap backs plot_gene_pacmap and plot_wp_pacmap (11-line clone).
  • plotting/general.py_two_gene_frame backs gene_plot_cells and plot_cell_gene_corr (10-line clone).

Three others were left alone on purpose: the _pf2_utils.pyfactorization.py hit is a pass-through kwarg signature, and the other two are repeated docstring parameter blocks. Deduplicating those costs documentation without saving logic.

Complexity — radon / xenon

Maintainability index was rank A for every file. Seven functions ranked C; the three in library code are now below threshold:

  • rank_selection._resolve_bicv_inputs (C-14) → _resolve_dataset_alias, _validate_split_params, _ensure_condition_idxs.
  • annotation_alignment.score_cell_type_alignment (C-11) → per-component loop extracted as _component_metrics.
  • factor_io._attach_raw_data (C-11) → _subset_raw_cells / _subset_raw_genes.

Still rank C: three test functions and figureS9a_d.run_benchmarks. These are pre-existing, their complexity is assertion/parametrization chains where splitting hurts readability, and CLAUDE.md says to leave those.

Along the way

  • Docstring drift: an AST check of public signatures against their Parameters blocks found three functions with undocumented parameters — pf2 and bicv (condition_key, adata) and plot_condition_factors (control_pattern, control_conditions). Now documented.
  • Wrong annotations (ty): groupDrugs and plot_correlation_cmp_cell_count_perc annotated X as the anndata module rather than anndata.AnnData; figureS9e_f.plot_logistic_regression_component declared -> pd.DataFrame while returning a tuple. ty now reports zero diagnostics across scrise/.
  • 24 unused unpacked fig variables in the plotting tests, flagged by both vulture and ruff's RUF059. This was the highest-value item: it was drowning vulture's output, which drops from 37 hits to 11 — all 11 now genuine documented exceptions, so the report is worth reading again. Also removed one stale # noqa: E402.

Verification

ruff check and ruff format clean; ty check scrise/ clean. Full suite: 133 passed, 1 skipped. Every refactored function has existing smoke-test coverage.

Deliberately out of scope

Flagging these rather than folding them in, since each is a judgment call:

  • 45 NPY002 legacy np.random uses (mostly figure6.py and tests) — conftest.py already uses default_rng, so the codebase is split, but migrating changes the numbers drawn and would move the golden-regression baselines.
  • camelCase in the public API (calculateFMS; params cellType, doEmbedding, cbarMax, labelType, ThomsonNorm) — renaming is breaking.
  • The X/adata dual-alias params on pf2/bicv — part of why _resolve_bicv_inputs ranked C; worth retiring if one is a compatibility shim.
  • factor_io.py:110 catches ImportError, AttributeError, KeyError, ValueError, OSError for the vsparse→read_h5ad fallback. Only ImportError is the intended case; the rest silently turn genuine data errors into a path change.
  • 27 remaining ty diagnostics in analysis/ — 5 uninstalled optional deps (cupy, scib, scanorama, harmonypy), most of the rest anndata DataFrame | Dataset2D union noise.
  • 16 RET504 assign-then-return, mostly in figure scripts — churn.
  • Figure-script clones at lower jscpd thresholds — all the standard makeFigure preamble, i.e. the intended template.

🤖 Generated with Claude Code

Ran the maintainability tools from CLAUDE.md across scrise/ and analysis/
and addressed what they flagged.

Dead code (vulture):
- Drop analysis.gating.getHiResOldLupus and the cell_type_conv table it
  alone consumed; no caller, and no dataset in the repo carries the
  "Cell Type Old" column it reads.
- Drop figure3.plot_cell_perc_corr; no caller.
- Note in gateThomsonCellsLeiden/Thomson_Doublet docstrings, and beside the
  marker_genes_* tables, that these are hand-run cache builders and
  reference data, so their lack of an in-repo caller is deliberate.

Duplication (jscpd):
- plotting/factors.py: _draw_unit_scaled_heatmap now backs both
  plot_eigenstate_factors and plot_gene_factors.
- plotting/pacmap.py: _plot_continuous_pacmap now backs plot_gene_pacmap
  and plot_wp_pacmap.
- plotting/general.py: _two_gene_frame now backs gene_plot_cells and
  plot_cell_gene_corr.

Complexity (radon/xenon):
- rank_selection._resolve_bicv_inputs (C-14) splits into
  _resolve_dataset_alias, _validate_split_params, _ensure_condition_idxs.
- annotation_alignment.score_cell_type_alignment (C-11) hands its
  per-component loop to _component_metrics.
- factor_io._attach_raw_data (C-11) splits into _subset_raw_cells and
  _subset_raw_genes.
No function in scrise/ or analysis/ now ranks worse than B.

Along the way:
- Document pf2/bicv's condition_key and adata, and
  plot_condition_factors' control_pattern and control_conditions.
- groupDrugs and plot_correlation_cmp_cell_count_perc annotated X as the
  anndata module rather than anndata.AnnData; figureS9e_f's
  plot_logistic_regression_component declared -> pd.DataFrame while
  returning a tuple. ty now reports no diagnostics across scrise/.
- Drop 24 unused unpacked `fig` variables in the plotting tests, which
  were drowning vulture's output: it goes from 37 hits to 11, all of them
  now genuine documented exceptions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aarmey
aarmey merged commit 2f281c5 into main Sep 19, 2026
6 checks passed
@aarmey
aarmey deleted the cleanup/static-analysis-sweep branch September 19, 2026 14:06
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.

1 participant