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.
Refuse at the door what used to arrive as a NaN (#642)
Every entry point that takes a signal resolves it through one line, and that
line let three things past. A None became an array of one NaN, so
octave_filter(None, 8000) answered with eight bands of NaN and refused
nothing. A None inside a list became a NaN sample, and one poisoned sample in
a few thousand came back as a whole ruined spectrum with nothing said, which
a caller could not tell from a broken instrument. Anything else numpy could
not convert escaped in numpy's own words, or as a TypeError where the entry
points document a ValueError.
None of this is a new policy. The tree already refused a non-finite sample
nearly everywhere: two signal entry points and the calibration one say so in
their own words, and the filter family and broadcast were the outliers. The
resolver now says it once, for all of them, and the message names the
parameter that carried the value: 'ref_signal' where that is the name, not a
generic 'x'.
The suite decided the part that was in doubt. Refusing a non-finite sample
broke twelve tests and not one of them was a caller with a real NaN-bearing
signal: ten already refused such a sample and only disagreed about the
wording, one carried a written instruction to rewrite it if the library ever
validated the input itself, and the last asserted that a NaN propagates
through the filter bank, which is the defect recorded as expected behaviour.
It does propagate, which is exactly why it is not something to hand back.
Eleven call sites wrapped the resolver in np.asarray, which converted the
None to a NaN before either guard could see it; without dropping those the
naming would have been cosmetic at eleven of the entry points. Nine per-entry
guards became unreachable once the resolver refused first, and are gone
rather than left as dead lines.
One behaviour changes beyond the refusals: a masked array whose masked slot
hides a NaN is refused now. The mask never survived the conversion, so that
NaN was already reaching the computation; the refusal surfaces it.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.