chore: keep what the pylint evaluation found, without keeping pylint - #399
Draft
thodson-usgs wants to merge 1 commit into
Draft
chore: keep what the pylint evaluation found, without keeping pylint#399thodson-usgs wants to merge 1 commit into
thodson-usgs wants to merge 1 commit into
Conversation
Pylint was evaluated as a periodic second opinion and not adopted: 87% of what it reports are checks ruff also implements, and its `unused-argument` check cannot see through the `locals()` forwarding the collection getters use. CONTRIBUTING records the measurement so nobody re-derives it, and the one check with no other home -- an `except` tuple whose members shadow one another, which ruff's B014 misses because it knows alias pairs rather than subclass relationships -- is written down as a one-line command instead of a dependency. Four things it found that are worth keeping: - `nwis._parse_json_or_raise` caught `(ValueError, JSONDecodeError)`, and `JSONDecodeError` subclasses `ValueError`, so the tuple advertised a distinction that cannot exist. - `tests/waterdata_progress_test.py` held an `async def parse_response` left from an earlier revision; the test passes `parse_sync`. - mypy's `possibly-undefined` catches a name bound on only some paths -- a runtime `NameError` on the branch nobody tested. mypy ships it but leaves it off even under `strict`, and nothing else in the stack models it. The package is clean today, so it lands as a ratchet. - The wheel smoke test named six imports and reached most of the tree by luck of what those names pull in. It now walks every module the installed wheel ships, and asserts the walk saw all of them -- `walk_packages` skips the subtree under a package it cannot import and says nothing when it does. It also pins the optional-dependency guard: without geopandas, importing `nldi` must fail with the message that says how to fix it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T6MVcko4gh68LieGWxUnrR
thodson-usgs
force-pushed
the
chore/pylint-findings
branch
from
August 30, 2026 21:55
b42e12e to
e1901c3
Compare
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.
The leftovers from the pylint evaluation behind #398. Recommendation: do not adopt pylint. This PR carries the four things it found that are worth keeping, none of which need the tool, plus a note in
CONTRIBUTING.mdso nobody re-derives the measurement.Independent of #398 — no overlapping hunks — though both touch
pyproject.tomlin different sections.Why not pylint
Measured at pylint 4.0.8 over
dataretrieval: 635 findings, of which 419 areunused-argument— the check cannot see through thelocals()forwarding the collection getters use, so it calls 419 documented, tested keyword arguments unused where ruff'sARG001finds ten. Fourteen findings have no ruff rule at all (too-few-public-methods×5,too-many-instance-attributes×2,too-many-lines×3,duplicate-code×3,unused-wildcard-import×1); duplication is alreadypyscn's job, and the rest are thresholds this package has no reason to want. It runs ~100× slower than ruff and has no per-file message control.The one capability ruff lacks is import resolution. That argument does not survive contact with the existing CI: the
package-artifactjob already installs the wheel with only its declared dependencies, outside the checkout. Strengthening that job — below — reaches more modules than pylint'simport-errorwould, and proves runtime importability rather than static resolution.One check genuinely has no other home: an
excepttuple whose members shadow one another. Ruff'sB014misses it because it knows alias pairs, not subclass relationships.CONTRIBUTING.mdnow records it as a one-line command rather than a dependency:I ran it against this branch: 10.00/10.
What it found that is worth keeping
nwis._parse_json_or_raisecaught(ValueError, JSONDecodeError).JSONDecodeErrorsubclassesValueError, so the tuple advertised a distinction that cannot exist. Behavior-identical; the now-orphaned import goes with it.async def parse_responseintests/waterdata_progress_test.py, left from an earlier revision — the test passesparse_sync. Ruff'sF841misses it because it covers unused variables, not nested function definitions.possibly-undefined. Catches a name bound on only some paths — a runtimeNameErroron the branch nobody tested. mypy ships the check but leaves it off even understrict, and neither ruff nor the suite models it. Clean on all 61 modules today, so it lands as a ratchet in a tool already in CI and pre-commit.walk_packagesskips the subtree under a package it cannot import and says nothing when it does, so without that assertion the loop can pass vacuously. It also pins the optional-dependency guard: without geopandas, importingnldimust fail with the message that says how to fix it, not a bareModuleNotFoundError.Honest limits
_version,ogc.interruptions,ogc.retry), and none of them imports a third-party package — so it does not advance the stated dependency-catching purpose right now. Its value is that it cannot go stale: a future module with a new import is covered without anyone remembering to edit the heredoc. Judge it on that, not on today's count.nwis.pyto "compatibility, security, and correctness fixes", and narrowing an except clause is a cleanup, none of the three. In practice the module has absorbed several refactor commits recently, so the freeze reads as "add no new capabilities" — but it deserves your explicit nod. TheCONTRIBUTINGone-liner also depends on this fix; without it the documented command reportsW0714.NEWS.mdentry: nothing here is user-visible.Noticed, not touched (pre-existing, adjacent)
[project.optional-dependencies].type-checkpins bare"mypy"while.pre-commit-config.yaml's comment claims it is "pinned to the same major as CI'smypy<2" — there is no<2anywhere. A one-token change would make that comment true. Relevant here because an opt-in error code is exactly the sort whose scope widens between releases.[[tool.mypy.overrides]]block for anyio justifiesfollow_imports = "skip"with "Under ourpython_version = "3.9"target"; the setting three lines above is3.10. I checked — it is clean without the override.Verification
The CI smoke script was extracted verbatim, run against a freshly built wheel in a throwaway venv with only declared dependencies: passed, 60 modules walked, nldi guard verified.
ruff check+format --checkclean ·mypy --strict+possibly-undefined62 files clean · 995 tests pass · import-linter 8/8. The commit passed the full pre-commit suite.🤖 Generated with Claude Code
https://claude.ai/code/session_01T6MVcko4gh68LieGWxUnrR