Skip to content

Restore pyflakes in the ruff gate and correct the Python floor - #743

Open
bschwedler wants to merge 9 commits into
mainfrom
fix/lint-gate-and-python-floor
Open

Restore pyflakes in the ruff gate and correct the Python floor#743
bschwedler wants to merge 9 commits into
mainfrom
fix/lint-gate-and-python-floor

Conversation

@bschwedler

Copy link
Copy Markdown
Contributor

Ruff's select replaces the default rule set. It does not add to it. A configuration with only T201 therefore turns off every other rule. This is why a call to an _emit_summary() that did not exist passed the lint step and reached CI.

This change writes the rule set out in full. It does not use extend-select. Ruff's default enables about four hundred rules and gives 629 findings that are unrelated to this change. An explicit list also means that a ruff upgrade cannot change what CI enforces without a commit.

The 80 findings included two defects. The declared Python floor was wrong, because error.py cannot import on Python 3.10. Four tests never ran, because duplicate definitions shadowed them. Each shadowed test is handled on its own terms, not by deletion of the duplicate. In one case the shadowed test was the stricter test of the pair.

One finding stays open. wizcli/suite.py assigned a parse_err value that nothing reads. The suite logs a malformed report but does not surface it when wizcli exits with code 0. This change removes the dead variable only. A change to when scans fail does not belong in a lint change.

Closes #742

posit_bakery/error.py defines three exception classes that subclass the
builtin ExceptionGroup at import time, and ExceptionGroup only exists
from Python 3.11 (PEP 654). The package nonetheless declared
requires-python = ">=3.10" and advertised a 3.10 trove classifier, so
pip would happily install it on 3.10, where importing posit_bakery.error
raises NameError immediately -- and cli/build.py imports it, so the CLI
could not start at all.

Nothing here actually supported 3.10; the dev dependency on great-docs
was already gated on python_version >= '3.11'. Correct the declaration
rather than add a backport.

Also sets ruff's target-version to match, which is what let this go
unnoticed: at py310 ruff reports the ExceptionGroup references as
undefined names, and those reports were being suppressed by the lint
configuration fixed separately in this branch.
patch_version() was annotated `-> "ImageVersion"`, but ImageVersion was
never imported into config.py, so the forward reference pointed at
nothing: get_type_hints() on the method raised NameError, and neither
mypy nor an IDE could resolve it. Nothing evaluates the annotation at
runtime today -- BakeryConfig is a plain class, not a pydantic model --
so this was a wrong signature rather than a crash.

Other cross-module quoted annotations in this package are backed by a
TYPE_CHECKING block, but every one of those exists to break a specific
circular import. There is none here: nothing under config/image/ imports
config.config, and config.py already imports Image from that same
package, which loads ImageVersion as a side effect. Adding it to the
existing import binds an already-loaded name and executes no new module
code, so a deferred import would only imply a cycle that does not exist.
Nine top-level CLI handlers used a bare `except:`, which catches
BaseException -- so Ctrl-C during a create, remove, get or ci command
was swallowed and reported as "Failed to ..." with exit code 1, rather
than interrupting. SystemExit was caught the same way.

Narrowing to Exception leaves every ordinary failure on the existing
path: typer.Exit derives from RuntimeError, so the handlers that
re-raise it still behave identically, and so does every Bakery error.
Only KeyboardInterrupt, SystemExit and GeneratorExit change, and for
all three propagating is the correct behavior.
Regenerates uv.lock after requires-python moved to >=3.11: the
python_full_version < '3.11' resolution branch is dropped and the
remaining markers collapse accordingly. `uv lock --check` is clean
against the updated pyproject.
Twenty-seven string literals were marked as f-strings but interpolate
nothing (F541). Several are the opening line of an error message that
is appended to afterwards, so the prefix reads as intentional at a
glance and is not; the rest are plain leftovers.

No behavior change: an f-string with no replacement fields evaluates
to the same str.
Six names were defined twice in the same scope, so Python bound the
second and the first never ran. Each case differed, so each is handled
on its own terms rather than by deleting the duplicate:

- test_image_target.py: two unrelated tests both named test_build_args.
  The shadowed one is parametrized and covers dependency-to-build-arg
  resolution across matrix cases; the live one checks the build property
  dict. Renamed the former to test_build_args_from_dependencies, which
  restores roughly sixteen parameter cases that had never executed.
- test_spec.py: the shadowed test was the stricter of the pair, asserting
  the "version must not be empty" message where its replacement only
  asserted ValidationError. Removed the weaker later duplicate so the
  strict one runs again.
- test_bake.py: the shadowed test called get_expected_plan() with the
  pre-refactor single argument and would fail if run. Deleted.
- conftest.py: cli_tmpcontext was redefined as a byte-identical copy of
  cli_cwd_context, registering the same step string twice and hiding the
  real cli_tmpcontext. Deleted the copy.
- test_ci.py: three distinct @then steps all named
  check_log_metadata_targets. pytest-bdd resolves by step string so all
  three ran, but the names collided; renamed two to match their steps.

All previously shadowed assertions pass.
Twenty-three imports were dead. One was not: test/conftest.py imports
posit_bakery.image.image_target inside an autouse fixture purely so the
module is loaded before mocker.patch resolves its patch targets from
strings. That one keeps a noqa and a comment explaining why the name
looks unused.

Ruff declines to autofix inside __init__.py on the assumption that
imports there may be re-exports, so the unused `warnings` in the dgoss
plugin was removed by hand after confirming the module is a CLI entry
point rather than a re-export barrel.
Most of the twelve unused locals were assignments whose value was never
the point -- constructor calls inside pytest.raises blocks, or leftovers
from removed assertions (original_path duplicated old_path on the very
next line). Two were not cosmetic:

- test_oras.py: test_no_sources_is_success computed a result and never
  asserted on it, so it checked only that subprocess was not spawned,
  not the success its name claims. Added the missing assertion.
- wizcli/suite.py: parse_err captured a report-parse failure that nothing
  ever read. The failure is still logged, but when wizcli itself exits 0
  a malformed report is not surfaced as an error. Removing the dead
  variable does not change that; it is left as-is rather than fixed
  silently inside a lint cleanup.

For the two module-level imports outside the header: test/conftest.py's
is deliberate and already documented -- it must follow discover_plugins()
-- so it takes a noqa. test/plugins/test_registry.py's had no such
constraint or comment, and moving it to the header leaves the suite green.
ruff's `select` replaces its default rule set rather than adding to it,
so configuring only T201 disabled everything else -- including pyflakes.
That is why a call to an _emit_summary() that was never defined passed
lint and reached CI, and why several tests shadowed by duplicate
definitions went unnoticed for as long as they did.

The set is written out explicitly instead of switching to extend-select.
Inheriting ruff's default would enable roughly four hundred rules here,
producing several hundred findings unrelated to this change, and would
let a future ruff upgrade alter what CI enforces with no commit to point
at. Listing the rules means the gate only moves when someone moves it.

Verified both ends: a reference to an undefined name is now reported,
and print() is still reported.
@github-actions

Copy link
Copy Markdown

Test Results

2 087 tests  +8   2 087 ✅ +8   7m 13s ⏱️ - 2m 45s
    1 suites ±0       0 💤 ±0 
    1 files   ±0       0 ❌ ±0 

Results for commit 1f98930. ± Comparison against base commit 5693eac.

@ianpittwood ianpittwood left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are the key changes in posit-bakery/pyproject.toml?

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.

Ruff configuration disables pyflakes and hides real defects

2 participants