Code quality refactor + GitHub Actions CI#2
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
pyproject.toml(PEP 621) replacessetup.py, add.gitignore, untrack the stale committedbuild/output, add apy.typedmarker.paired_ranks_test's t-quantile branch built a result type literally namedBrunnerMunzelResult(copy-paste error). Replace per-callnamedtuple(...)construction with module-levelNamedTupleresult classes across all modules.twosample.py/pseudorank.pyvalidated arguments — a typo'dalternativeelsewhere could silently misbehave or crash with an unrelatedUnboundLocalError). Addtyping.Literaltypes for the string-enum parameters.hettmansperger_norton_testthat rebuilt a full list every loop iteration).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 .— cleanmypy PyNonpar— clean, 0 errors across all 7 source filespip install -e ".[dev]") reproduces all passing tests, clean ruff, clean mypypytest.approxtolerance)