Skip to content

Code quality refactor + GitHub Actions CI#2

Merged
happma merged 10 commits into
masterfrom
code-quality-refactor
Jul 13, 2026
Merged

Code quality refactor + GitHub Actions CI#2
happma merged 10 commits into
masterfrom
code-quality-refactor

Conversation

@happma

@happma happma commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replace Travis CI (defunct for OSS) with GitHub Actions: a Python 3.9-3.12 test matrix plus a blocking lint job (ruff + mypy). Modernize packaging: pyproject.toml (PEP 621) replaces setup.py, add .gitignore, untrack the stale committed build/ output, add a py.typed marker.
  • Fix a real bug where paired_ranks_test's t-quantile branch built a result type literally named BrunnerMunzelResult (copy-paste error). Replace per-call namedtuple(...) construction with module-level NamedTuple result classes across all modules.
  • Add consistent input validation everywhere (previously only twosample.py/pseudorank.py validated arguments — a typo'd alternative elsewhere could silently misbehave or crash with an unrelated UnboundLocalError). Add typing.Literal types for the string-enum parameters.
  • Vectorize the manual O(N) accumulation loops with numpy (also fixes an accidental O(N²) in hettmansperger_norton_test that rebuilt a full list every loop iteration).
  • Rewrite tests to idiomatic pytest style (plain assert, named attribute access instead of positional indexing) and add negative-path test coverage for the new validation.

All changes were verified to produce numerically identical results to the pre-refactor implementation — diffed against a pinned baseline (exact match within 1e-10), and every pre-existing pinned-reference-value test still passes with its original numeric constants unchanged.

Test plan

  • pytest --cov=./ — 19 passed (10 original + 9 new negative-path tests)
  • ruff check . — clean
  • mypy PyNonpar — clean, 0 errors across all 7 source files
  • Fresh-venv install from scratch (pip install -e ".[dev]") reproduces all passing tests, clean ruff, clean mypy
  • Numeric outputs diffed against pre-refactor baseline for exact equivalence (not just pytest.approx tolerance)
  • CI green on this PR (Python 3.9-3.12 matrix + lint job)

happma and others added 10 commits July 13, 2026 01:18
Travis is defunct for open-source projects. Add a GitHub Actions
workflow testing Python 3.9-3.12 plus a blocking lint job (ruff +
mypy). Replace setup.py with pyproject.toml (PEP 621), add
.gitignore, untrack the stale committed build/ output, and add a
py.typed marker so downstream type checkers can use PyNonpar's hints.
- Replace per-call namedtuple(...) construction with module-level
  NamedTuple result classes; fixes a real bug in twosample_paired.py
  where the t-quantile branch of paired_ranks_test built a result
  type literally named 'BrunnerMunzelResult' (copy-paste error).
- Add typing.Literal aliases (PyNonpar/_types.py) for the alternative/
  quantile/method/ties_method/distribution string options, and
  consistent input validation across all modules (previously only
  twosample.py and pseudorank.py validated their arguments; a typo'd
  alternative elsewhere could silently misbehave or crash with an
  unrelated UnboundLocalError). Replaces a bare AssertionError() for
  mismatched paired-sample lengths with a descriptive ValueError.
- Vectorize the manual O(N) accumulation loops (variance estimators
  in brunner_munzel_test, wilcoxon_mann_whitney_test, paired_ranks_test,
  kruskal_wallis_test, hettmansperger_norton_test, kepner_robinson_test)
  with numpy; also fixes an accidental O(N^2) in
  hettmansperger_norton_test that rebuilt df['data'].tolist() every
  loop iteration.

All changes verified to produce numerically identical results to the
pre-refactor implementation (diffed against a pinned baseline, exact
match within 1e-10); the full pinned-reference-value test suite passes
unchanged, and mypy/ruff are clean across the package.

Adds CLAUDE.md with repo-specific guidance for future work.
Replace the `if cond: raise AssertionError()` pattern with plain
assert statements, and switch from positional result indexing
(result[-1], result[2]) to named attribute access (result.pvalue,
result.statistic) now that results are typed NamedTuples with stable
field names. Removes a stray module-level print() that ran on every
test collection, and renames a test that was checking the Kepner-
Robinson test but was named test_wilcoxon_mann_whitney_test_exact
(copy-paste leftover).

Adds negative-path tests for the validation added in the previous
commit: invalid alternative/quantile/method/distribution/ties_method
values, mismatched paired-sample lengths, wrong custom-trend length,
and non-list inputs, across all five modules.

All pinned reference numeric constants are unchanged from before this
refactor (verified via diff); only assertion style and test coverage
changed. 19 tests pass (10 original + 9 new negative-path tests).
CI's ruff check . lints the whole repo tree, which includes
source/conf.py (Sphinx config) - out of scope for this refactor per
the original plan (only PyNonpar/ and tests/ were meant to change).
Locally this was masked by an earlier incidental fix to conf.py that
was later reverted to keep the diff scoped, so the lint job only
failed once it ran clean in CI.
…overage

- Remove CLAUDE.md from the repo and gitignore CLAUDE.md/.claude/ so
  AI assistant configuration/context files aren't published.
- Bump package version 0.2.0 -> 0.2.1 in pyproject.toml.
- Add coverage for brunner_munzel_test(quantile="normal") and
  paired_ranks_test(var_equal=True), for both quantile="t" and
  quantile="normal", closing gaps identified in a test coverage audit
  (previously only the default var_equal=False / quantile="t" paths
  were exercised).
actions/checkout@v4 and actions/setup-python@v5 still bundle a Node 20
runtime, which GitHub Actions is deprecating in favor of Node 24.
Bump to checkout@v7, setup-python@v6, and codecov-action@v7 to clear
the deprecation warning.
- Add PyNonpar._types.as_list(), a shared coercion helper accepting
  list/tuple/numpy.ndarray/pandas.Series (anything numpy.asarray can
  handle), matching scipy's "array_like" calling convention. Every
  public function's isinstance(x, list) validation is replaced with
  this helper; internals are untouched and keep operating on plain
  lists exactly as before, so no numeric output changes.
- Update parameter type hints from List[float]/List to
  numpy.typing.ArrayLike, and docstrings from "list(float)" to
  "array_like" to match. Also modernize typing.List/typing.Tuple to
  builtin list[...]/tuple[...] package-wide (PEP 585, py39+), and add
  ruff's UP (pyupgrade) rule group so this is enforced going forward.
- Add array/tuple/pandas.Series parity tests (identical results to
  list input) and 2-D-array/dict rejection tests for every public
  function, plus a few small negative-path tests closing gaps a
  coverage pass surfaced.
- Bump version 0.2.1 -> 0.3.0 (new public-facing input-handling
  capability warrants a minor bump).

28 tests pass, mypy and ruff clean, 100% coverage on every
instrumentable module.
Triggered on GitHub Release publish (matching how 0.1.0/0.2.0 were
actually released). Builds sdist+wheel, runs twine check, then
publishes via pypa/gh-action-pypi-publish against the "pypi"
GitHub Environment - no stored PyPI API token, and the environment's
required-reviewer protection rule gates the actual publish behind a
manual approval.
@happma
happma merged commit 0c5a40b into master Jul 13, 2026
5 checks passed
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