A Spanish figure writes its decimal comma on every panel it draws, and two guards keep it that way (#777)
Every helper that writes text takes a language and defaults to English, so a call that forgets to pass it on raises nothing and puts an English decimal point into a Spanish figure. The plotting modules, the figure generators and the fiches now hand the caller's language to every helper that takes one, and localize_axes is called once per axes a plot builds rather than once per figure: a twin axis, a colorbar and the z of a 3-D panel each carry a formatter of their own, and each was shipping in English beside a panel already in Spanish. What decides whether an axis is localised is its formatter and not its scale, so a log axis of distances still writing its own numbers is reached and a pinned frequency axis is left to format_frequency_axis. A zoom inset and a contour colorbar, which the save-time pass of the figure generators cannot reach, are localised where they are built. The band column of the room criterion sheet and of the whole sound-power fiche family is written in the sheet's language too.
scripts/check_language_forwarding.py fails on any call to a helper that takes a language, made from anywhere a language is in scope, that does not pass it on. It reads module and class bodies as well as functions, follows a helper through a local alias and a functools.partial, names the shapes it cannot reach, keys an exemption by its line so one approved call cannot cover another, and reports a method name only some of whose namesakes take the language, which silently switched the untyped-receiver check off.
scripts/check_figure_decimal_point.py reads the committed Spanish figures and fails on a tick label that is a number with a point in it, whichever pass was meant to write the comma. It found six figures, regenerated here. Both run from the Makefile and in CI.
Say which error each test is waiting for (#610)
* Say which error each test is waiting for
`PT` is selected. The 241 `pytest.raises(ValueError)` calls that named no
message now name one, and the fragment they name is the part that does not
move: the parameter the guard is about, not a bound, not the list of allowed
values, not wording that belongs to numpy.
Reading the guards turned up two `match=` that already existed and pinned
nothing, neither of them reported by the linter because both do pass a
`match=`: one waited on `frequencies`, a word three messages in its module
share, and one waited on `must be positive`, which every positivity guard in
its module shares.
One test could only pin numpy's shape-mismatch text because
`reception_plate_power` broadcast two of its arguments without checking them
first. It has its own guard now, through a new shared helper, so the caller is
told which argument was the wrong length.
Alongside it, 44 `parametrize` decorators take the shape the other 467 use,
and ten `assert a is not None and b is not None` became two assertions.
* Regenerate the reference page for the guard that now names its argument
* Refuse the one-element array the band axis would have stretched
A scalar means the same value in every band and a band-long array means one
value per band. Broadcasting alone also accepted a one-element array and
repeated it across every band, which is the shape a caller lands on after
computing one value where the bands needed several, and four copies of one
measurement read exactly like a measurement of four bands.
The changelog's counts were measured before earlier work moved them: the
suite holds 2075 `pytest.raises`, of which 1795 already named a message. The
39 that still name none are the classes that are their own assertion.
Let imports say what they are for, and names say what they are (#603)
Three rules that the tree nearly satisfied already, and one repair to the
gate that had to come first.
`TC` moves the 313 imports that only a type checker reads under
`if TYPE_CHECKING`. `ICN` imports a well-known package under the alias
everyone writes, 190 of them. `N812` retires an alias that disguised what a
name was: 83 places wrote `from ._layout import fiche_paragraph as
Paragraph`, which made a helper of this package read as the class reportlab
publishes under that name. Four conformance domains imported numpy a second
time inside a function that already had it at module scope.
`INP` and `T20` are selected and find nothing. Both are already clean in
`src` and are here to hold it; a print is what the scripts are for, so they
and the tests are exempt.
The clip freshness gate had to be repaired before any of this could land.
It hashes the AST of the code that draws each clip, and its own preamble
says an alarm nobody believes would be worse than no alarm, because a false
positive costs a several-minute re-render. It hashed each reached module
whole, so moving an import under `if TYPE_CHECKING` read as a change to the
drawing even though nothing in that block exists while a frame renders: it
marked most of the forty-two clips stale. It skips those blocks now, and
hashes the remaining imports as a sorted set so that sorting them is not a
change either. It still hashes them, because the walk stops at the package
boundary and for `import numpy as np` the statement is all it has.
`TC006` is not selected. It quotes the first argument of `typing.cast`,
which is never evaluated for its value, so the rewrite is inert; it moved
two clips through `figures.media._save_animation`. Reverting it needs care:
an unquoted cast does evaluate the name, so where a cast names an import
that lives only under `if TYPE_CHECKING`, the quotes are what keep it from
raising. Ruff says so itself as TC004, on seven sites here.