A catalogue row is completed by one path, and a banded row reads at any array of bands (#870)
* A catalogue row is completed by one path, and a banded row reads at any array of bands
io.CatalogueRow.from_printed(**cells) is now the one place that works anything out of a row's printed cells. It checks the cells the way the constructor does, converts a figure written under a unit the row class takes as an alias of its own, builds the row, fills what follows from the printed cells and names each filled value in derived. Every packaged loader builds through it: the completions of the solids and of the porous materials are hooks it calls, with the same arithmetic in the same order, and the sabins of Long Table 7.1 are an alias of AbsorptionAreaSpectrum rather than a step of the loader. Which of the two absorption classes a row is stays the loader's choice. Building with Cls(...) stays literal.
row.printed_fields() gives back the cells the page prints, the converted and the carried ones included and the derived ones left out, so type(row).from_printed(**row.printed_fields()) is the row again for every published row. Changing a cell there and building again is how a row is edited; dataclasses.replace copies derived values that no longer follow from the cell that changed, and the docstrings say so.
A figure in another unit is converted on its digits with the exact factor and rounded once. For Long Table 7.1 that moves four cells by one binary digit, the musician's 500, 2000 and 4000 Hz areas and the air's 2000 Hz area (1.0683849600000002 becomes 1.06838496, 0.007545931758530183 becomes 0.007545931758530184), and nothing a page prints.
When the printed cells a derived value rests on, followed back through the values derived first, do not all have one basis, the derived text names the basis of each. In the published catalogues that is the nineteen rows of Hopkins Table A2 whose Poisson ratio the page marks as an estimate: each of their five derived texts now says the value rests on that estimate and on a plate speed and a density whose basis the page does not state. Both published changes are steps of the catalogue fingerprint, which lists every cell they move, and a test checks that the loaders build what they built before apart from those cells.
io.BandedRow.values_at(frequencies_hz) reads a banded row at an array of frequencies, for the functions that take one value per band by position. Each frequency matches the band whose centre is nearest on a logarithmic scale, within a sixth of the spacing between bands, so nominal, exact base-ten and exact base-two centres read the same band, and a band the row does not print is refused with what the page had there. It answers what the per-band accessors answer for every published banded row. The refusal of a band no table prints now reads "an octave band that absorption tables print".
* Cells the completion cannot take are refused by name, and an empty per survives printed_fields
from_printed now refuses printed cells its arithmetic cannot take, with an io.CatalogueError that names the value, the printed cells it would rest on and their values. A modulus of 1 GPa beside a shear modulus of 0.1 GPa gives a Poisson ratio of 4, which no isotropic solid has: without a density it used to be stored as a derived Poisson ratio, and with one it surfaced as a ValueError about a cell nobody printed. A printed Poisson ratio of 0.7 gives a plate speed but no speed in an unbounded solid, and the refusal says which. The arithmetic runs only for a cell it fills, so a row whose cells are not meant to give a value says so in not_derivable and loads.
printed_fields() leaves a field out only when it holds nothing and nothing is also its default. An absorption area whose per is empty used to come back as an area per person after the round trip; it now comes back as it was. A derived passed to the literal constructor is the caller's, and the docstrings and the API table now say that printed_fields() leaves it out with its value.
A figure left empty under one unit alias still speaks for its cell, so a second alias for the same cell is refused as the cell given twice.
The changelog and the fingerprint step now say that Hopkins Table A2 marks twenty-one Poisson ratios as estimates, and that nineteen of those rows derive from them: aircrete and brick print their density as a range. The docstrings of convert_figure and of the figure a float is written as say what repr gives rather than that a float is its decimal.
Tests pin each of these: the Poisson ratio from two moduli against 21/79 worked by hand, every fill site with one estimated input at a time, the refusals, not_derivable, the empty per and the empty alias figure.
* The alias checks of the completion read as two small steps, and a finite number is named once
* An empty figure in a unit alias needs no text field to explain it
* The Spanish catalogues page says which estimate the Hopkins derived values rest on
Give the tree a formatter, and a gate that keeps it (#602)
* Give the tree a formatter, and a gate that keeps it
Ruff was the linter and nothing was the formatter. There was no
`[tool.ruff]`, no `ruff.toml`, no pre-commit and no `.editorconfig`, and CI
ran `ruff check .` on ruff's default rules alone, which are four families of
outright errors. Nothing looked at shape, so 673 of the 812 Python files had
drifted from any single style.
The line length is 88 because that is what the tooling defaults to and,
measured, what moves this tree least: 100 rewrites 708 files and 120 rewrites
717, since a longer limit rejoins lines that were split by hand. Only 0,8 %
of the lines here were over 88 to begin with.
Markdown stays out of it. Ruff formats fenced Python inside `.md` and does
not recognise `.mdx` at all, and every snippet in `docs/` is mirrored by hand
into the site in English and Spanish, so formatting one copy would drift the
three apart with nothing to notice. Those snippets also align their trailing
comments to show what each line prints, which is a teaching device.
The linter now selects the families this tree already satisfied or nearly
did, and the fifty-odd findings they raised are fixed. Two of those fixes are
worth naming because the formatter caused them: reflowing a call moves a
`# type: ignore` onto a line that no longer holds the error it silenced, and
mypy caught four such cases. One rename is reverted with the reason written
down, because renaming an unused binding moves the fingerprint the clip
freshness gate reads, and that is not worth re-rendering an FDTD animation.
* Keep the reformat out of git blame
A tree-wide reformat stands in front of every line it touched. This file
names it so `git blame` walks past, which GitHub honours without any
local configuration and which `git config blame.ignoreRevsFile` turns on
for a checkout.
* Stop selecting the rule that reads a constant as a literal
SIM300 calls any upper-case name a literal, and on that reading it gets
three idioms in this tree backwards.
`spectrum[OCTAVE_BANDS == f]` is a numpy mask, not a condition.
`res.transmission_loss[BANDS <= f0]` reads "the bands at or below f0" until
it becomes `f0 >= BANDS`. And `value == pytest.approx(x)` is how pytest is
written here: 1755 of the 2065 `pytest.raises`-adjacent assertions in the
suite already put the expected value on the right.
It is right about two of the nineteen it finds and wrong about the other
seventeen, and SonarCloud caught the result as `python:S3415`, assertions
whose argument order no longer agrees within a file. The nineteen are
reverted by rebuilding those files as they would have been had the rule
never been selected, which keeps the `C420` and formatting changes two of
them also carry.
Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and complete a catalogue file in your editor with its JSON Schema (#891)
* Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and write the JSON Schema of a catalogue file
The elastic FDTD takes a solid's catalogue row wherever it takes a Material: scholte_speed and ElasticFDTD2D.from_regions read its bulk longitudinal speed, its transverse speed and its density through printed(), never its plate or bar speed, and refuse a cell the page does not print in the page's terms.
GroundSurface.medium turns a ground row into the porous half-space ground_effect, barrier_insertion_loss and atmospheric_parabolic_equation take, through the Delany and Bazley or the Miki model. A row Cox and D'Antonio mark as fitted with one model goes into that model only.
HomogeneousElement.from_solid builds the ISO 12354 element a layer of a solid's row makes, with m' = rho t and fc = c0^2 / (1.8 cL t) from the row's density and plate speed, and never picks one of the row's loss factors: the internal loss factor is the caller's to give.
io.catalogue_schema writes the JSON Schema (2020-12) of a catalogue document for the row classes it is given, and the site publishes the one of every published row class as schemas/phonometry-catalogue-1.json, held current by CI. A document may name it in a top-level $schema, which the reader takes and never reads.
* A dollar sign inside inline code opens no maths in the markdown check
The unclosed-maths rule counted every unescaped $ on a line, so the $schema key of a JSON document written as inline code in a table cell read as maths cut off by the next row. CommonMark reads a code span before any maths could open in it, so the rule now leaves code spans out of the count, and still finds maths that wraps onto a block marker, on a line with a code span or without one.
* The catalogues guide hands a solid, a ground and a wall of your own to their models, and completes a catalogue file in your editor
The guide on catalogues of your own shows a solid's row read from a data sheet going into the elastic simulation by its bulk speeds, a ground fitted to a site going into the outdoor models through medium(), with the refusal of a resistivity fitted with another model, and a block's density and plate speed building an ISO 12354 element with the internal loss factor you give. A new section writes the JSON Schema of a catalogue file with io.catalogue_schema, points a document at it with $schema, and sets an editor to the one the site publishes; the document's keys now list $schema. The elastic waves, ground effect and detailed prediction guides each take a row from a catalogue in their own terms, the published catalogues page draws its ground example through medium() and links the schema, and the Files overview and the guide indexes name it, in English, in Spanish and in the docs mirror.
* The catalogue schema describes a row class by the first line of its docstring, or by its name when it has none
* The JSON Schema writer is held to its place in io's public names
* The elastic FDTD reads a row's fields through the dataclasses module, which no local name of the module shadows
* A wetted sand of Cox Table 6.7 goes into neither outdoor ground model, as the text beside the table says
GroundSurface.medium took the eight rows of the two sands Cox and D'Antonio print at four water contents into the Delany and Bazley and the Miki models, because no footnote marks their fit. The text of Sect. 6.6.3 (PDF page 272, printed p. 215) says their parameters are those of the two-parameter model of Attenborough, so a row the text ties to that model is now refused by both, naming the page, as a row the footnotes mark with another model already was. The notes of the eight rows say so on the catalogues page, in both languages, and the docstring no longer says Cox names the fit on every row.
* The schema never marks a catalogue file the reader reads, a null in an optional key included
The reader took a null for basis, $schema and phonometry_version, and a $schema or a version of any length, all of which the schema refuses. It now refuses a null there as it does under every other key, and holds the two texts to the length of any other text. The schema holds the standards a row's own provenance names to the fields of its class, as it already held the document's. Each refused case is now checked against the place and the first words of the reader's refusal, so a case refused for another reason than its name fails; the one that was, a negative thickness written beside the same cell in another unit, now gives the negative thickness alone. A CSV file's header that names the schema is read as one that does not.
* The oracles of a solid's row in the building and elastic models cite the page each number is printed on
The detailed-model test took 600 kg/m3 for autoclaved aerated concrete from ISO 12354-1 Table B.3, which prints 400 to 800; it now reads the three materials off the Annex L element block (PDF page 84, printed p. 78), which prints the density and velocity of each, and says Annex L takes 600 from that range. The elastic test writes the modulus, Poisson ratio and density Bies Table C.1 prints for mild steel (PDF page 747, printed p. 718) as literals and holds the row's bulk and transverse speeds to their closed forms, rather than reading them back from the row under test. The elastic FDTD's docstrings say a row class is refused when it lacks any of the three cells, not only all of them.
* The catalogues guide cites ISO 12354-1 and JSON Schema 2020-12, and says why an outdoor ground raises a fit-range warning
The guide's references now carry ISO 12354-1:2017, whose Formula (B.2), Table B.3 and Annex L the wall section rests on, and the two JSON Schema 2020-12 drafts the schema is written in. After the ground example it says the call raises a PorousAbsorberWarning because an outdoor ground lies below the fit range of both models, and links to where the ground guide explains it. The catalogues page says the schema covers every row class the library publishes, not only the ones its tables use. The Spanish pages name the elastic FDTD rather than a solver object and call the bulk speed that of an extended solid, as the solids guides do.
* The catalogue schema takes the table's credit a document or a CSV header gives at its top, as one text that is not blank
* The Annex L test holds the element's constants on the left, as the rest of the file does
* The catalogue schema's patterns read as the reader does under every validator
The schema said a filled text with \S, which Python's re matches against
U+FEFF and ECMA-262 does not: a name or a credit that is only U+FEFF was read
by the reader and accepted by Python's jsonschema, and refused by an
editor's validator. The reader tells a blank text by str.strip, so the
schema moves to it: every class shorthand of a pattern the schema takes
from the reader is spelled out as the characters it matches under that
pattern's own flags, the white space str.strip strips for a filled text and
the ASCII letters and digits for a key, a column of your own and a reserved
name. Python's re and ECMA-262, with the u flag and without it, now read
each pattern alike, over every character of the basic plane.
* The catalogue schema's patterns that hold a whole text end where it ends, under Python's re and ECMA-262 alike
The seven patterns of the schema that hold a whole text were anchored with
a closing $, which Python's re and ECMA-262 read apart: jsonschema searches
with re.search, whose $ also matches before a line feed that ends the
text, so Python's validator accepted "k\n" as a row's key, "x-a\n" as a
column of your own, "mine\n" as a catalogue's name, and a day, a date or a
digest followed by a line feed, all of which the reader and an editor's
JavaScript validator refuse. Each of the seven now ends with (?![\s\S]),
which no character follows in either engine, with the u flag or without
it. The other two, the test of a text that is not blank and the reserved
form of a name, match a part of the text and are not anchored at its end,
as a test now holds. A validator built on RE2 compiles no lookahead, and so
none of the seven, which the comments and the entry now say instead of
claiming every validator. The comparison of Python and ECMA-262 covers every
pattern the schema holds, over texts that end in a line feed after a whole
key, day and digest, and the six texts are refused by both the schema and
the reader. The catalogues guide says a file the reader reads is never
marked by an editor whose validator reads regular expressions as JavaScript
or Python does, rather than by any editor.
* A catalogue is read as its reader reads the file before a byte of it is written, so nothing the reader refuses is written
write_catalogue held a document to none of the reader's limits: more than
50 000 rows, a JSON file or a CSV file past 16 MiB, a CSV header past
64 KiB, or a text longer than the reader takes (2 000 characters, 20 000
for an about or a note, from a row built in Python or an about passed in)
was written, and with overwrite=True replaced a good file with one that
read_catalogue then refused. The JSON writer now reads the text it is
about to write as parse_catalogue reads one, its UTF-8 bytes held to the
file's limit first, and the CSV writer reads the sheet and its header as
read_catalogue reads the two files, each held to its own limit first, so
whatever the reader refuses, for its size, its rows, the length of a text
or anything else, is refused before anything is written or replaced, in
the reader's own words and naming the file. The reader looks its size
limits up when it runs, and the CSV reader reads the bytes of the two files
through one function the writer shares. The calibration sidecar writer
already refused a sidecar past the 1 MiB its reader takes, before it
touched the file.
For each writer, a file of exactly the most rows, the most bytes of the
file and of a CSV header, and the longest text and prose the reader takes
is written and read back, and one more row, one more byte or one more
character is refused over it, the good file left byte for byte and nothing
beside it; the limits of rows and bytes are lowered for the test. Each of
the nine fails on the code before this change.
* A code span in the markdown check closes only on a run of backticks as long as its opener, and the size refusals share the name of a catalogue file
CommonMark closes a code span only on a whole run of backticks of the opener's length. The check took one tick of a longer run as the closer, so a dollar sign after an unmatched backtick was read as code and an unclosed maths delimiter went unreported; each run is now read whole. The three size refusals name the file with one constant.
Keep a catalogue of your own in a spreadsheet: read it from the CSV file the sheet saves, with its JSON header beside it, and write one back (#889)
* Read a catalogue of your own from the CSV file a spreadsheet saves, with its JSON header beside it, and write one back
io.read_catalogue reads a .csv file whose header, the catalogue's JSON document without its rows, sits beside it as <name>.phonometry.json (or where header_path says) and declares the delimiter and the decimal mark; nothing about the dialect is guessed. A cell holds one value, bound, range or word in a closed grammar (0.85, ~0.85, <=30, >=5, 0.30..0.50, 0.85±0.05, [AFr5], true or false in a column of flags), the columns are the row class's fields in any unit of their family, basis, the provenance.* columns, attributed_to.row and columns of your own named x-, and the lines go through the same pass as a JSON document's rows, so every problem is raised at once and placed at its line and its column as a spreadsheet letters them. Text where a number goes is never read as a word, a NaN or zero, a thousands separator is never read, and when every failing number is written with the other decimal mark, or the first line splits at another delimiter, the refusal says which to declare. A cell typed the way a spreadsheet or a data sheet writes it (a range with a dash, +- for a plus-or-minus, a unit after the number, a number grouped in thousands) is told how to write it, and a control character in it is named, never shown. A line break inside a quoted cell reads as a line feed in every column, written CRLF or LF, and a carriage return alone is refused there.
io.write_catalogue writes a .csv file with a byte order mark and its header, in the delimiter and decimal mark asked for, CRLF at the end of every line, refuses what one cell cannot hold at the pointer a JSON document would write it at, and puts an apostrophe before any text a spreadsheet would run as a formula, which the reader takes off again. Catalogue.header_sha256 is the hash of the header a CSV file was read with.
A row's credit travels in the attributed_to.row column, and the credit of the whole table is the document's own attributed_to, one text beside the provenance, which a JSON document may now write as well. Rossing's tables of B/A credit each paper to its row, or to the whole table on Table 8.2, so they go into a sheet whole; only a credit for one cell stays in a JSON document.
The guide Your own catalogues gains a section, in both languages and in docs/, that writes a published table as a CSV file and its header, reads a sheet in the closed grammar of cells, shows refusals at their lines and columns, and says what only a JSON document can hold.
* A CSV row is said to start on a line of its own, since a quoted line break carries it over the next
* A CSV file and its header are read as a JSON catalogue is, from a regular file only and never past their limit, and named by their escapes
* The read_catalogue row says again that a catalogue is read only from a regular file, and the CSV wording is reflowed
The row of read_catalogue in the API table said, before the CSV file came,
that the file is a regular file of 16 MiB at most and that a pipe, a device
or a directory at the name is refused before it is opened. It says so again,
for the JSON file and for the CSV file with its header, which is read the
same way. The paragraphs that now say a CSV row starts on a line of its own
are wrapped again, in the guide in both languages, its mirror, the
CHANGELOG and the docstrings, and the test of a file name that is not UTF-8
pins the message each of its two cases writes.
* Each step of reading and writing a CSV catalogue is a function of its own
A cell's grammar reads a bound, a range and a value in helpers, and the
diagnosis of a cell that is none hands a miswritten plus-or-minus or range
to one. A column's role is its own column, a misplaced one or a field's; a
cell goes into the row as a word or as a text; the writer spells a word, a
range and a bound in helpers, and a row's texts, hedges and provenance in
methods of their own. The CSV branch of write_catalogue is a helper, and the
credit's pointer is named once. Nothing any of them reads, writes or refuses
changes.
* A CSV row that empties an entry of its document's provenance is refused, and goes into a JSON catalogue
A row writes a provenance entry only where it says other than its
document, so an empty one clears what the document fills. The CSV writer
wrote it as an empty cell, which the reader takes for the document's own:
a row read from a JSON catalogue with "page": "", or written against a
page given by provenance=, read back from the sheet citing the document's
page, and nothing said so. It is now refused at the entry's pointer, as an
empty text whose default says something is, with the words that the row is
written only in a JSON catalogue, and nothing is written. A test for each
of the two ways a row comes to empty the page fails on the code before this
change, and the same rows written as JSON read back with the empty page.
* The test of a symbolic link at a CSV header skips where the system makes no link
Windows makes a symbolic link only for a process that holds the privilege,
and without it Path.symlink_to raised OSError before the writer ran, so the
test failed rather than skipped there. It now skips when the link cannot be
made, as the tests of a hard link to a sidecar do and as the test of a
symbolic link at a JSON catalogue now does.
* A CSV file holds an empty text only where an empty cell reads back as one
An empty cell reads as nothing written: a text field takes its default, a
provenance entry the document's, and a column of the caller's is left out
of the row. The writer refused an empty text whose default says something
and, since the change before this one, a provenance entry a row empties,
but two more went into the sheet and came back as something else. An empty
text in a column of the caller's was written and read back as a row
without the column, so the catalogue's extras lost the entry, and one in a
text field of the caller's row class that has no default was written and
then refused on every line by the reader, as a row without a field its
class needs, so the sheet could not be read back at all. The writer now
writes an empty text only in a field whose default is empty and refuses one
anywhere else at its pointer, with the words that a row holding it is
written only in a JSON catalogue, and nothing is written.
Every value a sheet writes as a cell was emptied in turn in a one-row
catalogue of each published row class: each text field, a column of the
caller's, the row's credit, each provenance entry against a document that
fills it and one that does not, the basis, a word for a number and a number
left out. Each now reads back or is refused, the JSON reader refusing the
empty credit, basis and word and an empty name. A test for the column of
the caller's and one for the field with no default, from a document and
from rows built in Python, fail on the code before this change, and the
same rows written as JSON keep the empty text. A guard empties every text
field, a column of the caller's and every provenance entry of every
published row class, and fails on the code before this change for each of
them.
Keep a data sheet's absorption and a laboratory's insulation as rows of their own, each rated again from its bands, and a guide to reading a material datasheet (#890)
* Keep a data sheet's practical and one-third-octave absorption, a laboratory's sound reduction index and a covering's impact improvement as rows of their own, each rated again from its bands
PracticalAbsorptionSpectrum holds the practical coefficients of ISO 11654 from 125 Hz to 4 kHz and ThirdOctaveAbsorptionSpectrum the ISO 354 coefficients from 50 Hz to 10 kHz, both with the weighted coefficient, shape indicators, class, NRC, mounting, thickness and depth of construction the sheet prints. rating() works the weighted coefficient out again from the bands, and practical() turns one-third octaves into practical coefficients, each marked as derived and never borrowed from a neighbouring band.
SoundReductionSpectrum holds the ISO 10140-2 sound reduction index in 21 one-third octaves with Rw, C, Ctr and the Annex B terms of ISO 717-1; ImpactImprovementSpectrum holds the ISO 10140-3 reduction of impact level with Delta Lw, CI,r and CI,Delta. Their rating() calls weighted_rating_extended and the ISO 717-2 improvement functions; the new ImpactImprovementRatingResult carries Delta Lw with both adaptation terms and plots the spectrum.
Reading a catalogue file notes a practical coefficient above 1.00 or off the steps of 0.05, and a printed rating the bands do not give, without changing any cell. weighted_rating, weighted_impact_rating, weighted_impact_improvement and impact_improvement_adaptation_term take a mapping of band to value and refuse a missing band.
* A guide to reading a material datasheet, and the catalogues guide names the row classes that now hold a sheet's absorption and a laboratory's insulation
"Reading a material datasheet" (EN, ES and the docs mirror) sits first among the materials guides and says what each number on a data sheet or a test report is before it is typed: the practical coefficient of ISO 11654 against the one-third-octave coefficient of ISO 354, the steps of 0.05, the cap of 1.00 and the 125 Hz band that is held and not rated; a level declared under CE marking, read from EN 13162, as a bound and never a value; measured against declared, calculated or estimated; the mounting and the depth of construction; the revision and the day it was read; the decimal comma and a corrupt text layer; and the sound reduction index and the impact improvement of a laboratory report. Its examples turn a test report into practical coefficients, read a sheet's octave table from CSV with the notes it earns, and rate a wall and refuse a declared impact improvement a prediction cannot take.
The catalogues guide, the Files overview, the materials overview and the published catalogues page link it, and no longer say that no row class holds these quantities. The guides index counts 146.
* A one-third-octave row's practical coefficients keep a derived text the row already carried on a shared field
* Six coefficients ending in 0 or 5 look like practical ones, and only a sheet that names them says they are
* A negative absorption coefficient is refused rather than read as zero, and a covering's improvement plot keeps a negative band in view
ThirdOctaveAbsorptionSpectrum.rating() refuses a negative one-third-octave
coefficient, naming the row, the band and the value, as
practical_absorption_coefficient does; practical() leaves the octave it
falls in empty and says why in not_derivable, so practical().rating()
refuses the same rating band and a negative band below 200 Hz only empties
the unrated 125 Hz octave. Nothing is floored to 0.00 any more.
A printed absorption class is compared with the one the bands give on its
casefolded word, so "not classified", "c" and "Class C" agree with the
bands that give them. A coefficient above 1 is explained as ISO 354 3.9
NOTE 2 does, by diffraction effects.
ImpactImprovementSpectrum cites the definition of Delta L where it is, ISO
10140-1 Annex H, Equation (H.1), with the levels measured by ISO 10140-3.
The ISO 717-2 improvement plot starts its axis at 0 dB only when no band is
below it, and its title reads "ISO 717-2 Delta Lw (CI,Delta=-9; CI,r=-2) =
15 dB" with the typographic minus of the other rating plots, short enough
for the default figure in both languages. The enlarged range of the ISO
717-1 and ISO 717-2 extended rating plots is shaded with an opaque wash
that stays visible on a dark page.
* The datasheet guide draws each rating it works out, rates a floor covering's report, and cites ISO 10140-1 for the impact improvement
The guide on reading a material datasheet now shows the figure of every
rating its examples work out: the ISO 11654 rating of a test report's
one-third octaves, the ISO 717-1 rating of a laboratory's sound reduction
index over 50 Hz to 5 kHz, and a new example that reads a floor covering's
Delta L from its report and rates it to Delta Lw 15 dB with CI,Delta -9 dB
and CI,r -2 dB. Each figure is the result's own plot() with the code behind
it, in English and Spanish.
Delta L is cited to ISO 10140-1 Annex H, which defines it, with ISO 10140-3
as the measurement; a coefficient above 1 is explained by diffraction, as
ISO 354 does; the glazed partition of the example is a 4 mm pane, which is
where its coincidence dip at 3.15 kHz puts it; the guide says what happens
to a negative coefficient; and two Spanish sentences read as the English
ones do.
* The search check weighs a plain search on two copies of the index that have read the same chunks
On this branch the Spanish search for "ruido" failed the check as if the
standards field had reordered it. Nothing in the field matches "ruido";
the reorder came from how much of the index each side had read. Pagefind
fetches the word index in chunks and keeps them, and a search ranks every
word that begins with what was typed among the chunks it holds, while it
fetches only the chunk that takes in the typed word or its stem. The pages
this branch adds moved a chunk boundary between "ruid" and "ruidos", the
stem of "ruidoso". The weighted copy the check compared against had already
run every designation and held both chunks; the unweighted copy was fresh
and held one, so the two pages whose image text says "ruidoso" rose only on
the weighted side. Two fresh copies at weight 3 and weight 0 return the
same top five, and so do two copies that have both read the second chunk.
The comparison now runs on two fresh copies that make the same searches in
the same order, and a search that returns different pages from them fails
as a difference in what they read, not as a reorder by the field.
loadSearchIndex returns a fresh copy on every call, as it said it did:
Node keeps one module per address, so a second call for the same language
and weight used to get the copy already in use.
* The refusal of a band set and the annex of the enlarged ranges are each named once
The refusal ratings.py gives a band set it does not read is one constant for
its three uses, and the clause the sound reduction sheet cites for its five
enlarged adaptation terms is one. The test of the one-third-octave rating
holds the rating it worked out on the left, as the rest of the file does.
Keep a catalogue of your own in a spreadsheet: read it from the CSV file the sheet saves, with its JSON header beside it, and write one back (#889)
* Read a catalogue of your own from the CSV file a spreadsheet saves, with its JSON header beside it, and write one back
io.read_catalogue reads a .csv file whose header, the catalogue's JSON document without its rows, sits beside it as <name>.phonometry.json (or where header_path says) and declares the delimiter and the decimal mark; nothing about the dialect is guessed. A cell holds one value, bound, range or word in a closed grammar (0.85, ~0.85, <=30, >=5, 0.30..0.50, 0.85±0.05, [AFr5], true or false in a column of flags), the columns are the row class's fields in any unit of their family, basis, the provenance.* columns, attributed_to.row and columns of your own named x-, and the lines go through the same pass as a JSON document's rows, so every problem is raised at once and placed at its line and its column as a spreadsheet letters them. Text where a number goes is never read as a word, a NaN or zero, a thousands separator is never read, and when every failing number is written with the other decimal mark, or the first line splits at another delimiter, the refusal says which to declare. A cell typed the way a spreadsheet or a data sheet writes it (a range with a dash, +- for a plus-or-minus, a unit after the number, a number grouped in thousands) is told how to write it, and a control character in it is named, never shown. A line break inside a quoted cell reads as a line feed in every column, written CRLF or LF, and a carriage return alone is refused there.
io.write_catalogue writes a .csv file with a byte order mark and its header, in the delimiter and decimal mark asked for, CRLF at the end of every line, refuses what one cell cannot hold at the pointer a JSON document would write it at, and puts an apostrophe before any text a spreadsheet would run as a formula, which the reader takes off again. Catalogue.header_sha256 is the hash of the header a CSV file was read with.
A row's credit travels in the attributed_to.row column, and the credit of the whole table is the document's own attributed_to, one text beside the provenance, which a JSON document may now write as well. Rossing's tables of B/A credit each paper to its row, or to the whole table on Table 8.2, so they go into a sheet whole; only a credit for one cell stays in a JSON document.
The guide Your own catalogues gains a section, in both languages and in docs/, that writes a published table as a CSV file and its header, reads a sheet in the closed grammar of cells, shows refusals at their lines and columns, and says what only a JSON document can hold.
* A CSV row is said to start on a line of its own, since a quoted line break carries it over the next
* A CSV file and its header are read as a JSON catalogue is, from a regular file only and never past their limit, and named by their escapes
* The read_catalogue row says again that a catalogue is read only from a regular file, and the CSV wording is reflowed
The row of read_catalogue in the API table said, before the CSV file came,
that the file is a regular file of 16 MiB at most and that a pipe, a device
or a directory at the name is refused before it is opened. It says so again,
for the JSON file and for the CSV file with its header, which is read the
same way. The paragraphs that now say a CSV row starts on a line of its own
are wrapped again, in the guide in both languages, its mirror, the
CHANGELOG and the docstrings, and the test of a file name that is not UTF-8
pins the message each of its two cases writes.
* Each step of reading and writing a CSV catalogue is a function of its own
A cell's grammar reads a bound, a range and a value in helpers, and the
diagnosis of a cell that is none hands a miswritten plus-or-minus or range
to one. A column's role is its own column, a misplaced one or a field's; a
cell goes into the row as a word or as a text; the writer spells a word, a
range and a bound in helpers, and a row's texts, hedges and provenance in
methods of their own. The CSV branch of write_catalogue is a helper, and the
credit's pointer is named once. Nothing any of them reads, writes or refuses
changes.
* A CSV row that empties an entry of its document's provenance is refused, and goes into a JSON catalogue
A row writes a provenance entry only where it says other than its
document, so an empty one clears what the document fills. The CSV writer
wrote it as an empty cell, which the reader takes for the document's own:
a row read from a JSON catalogue with "page": "", or written against a
page given by provenance=, read back from the sheet citing the document's
page, and nothing said so. It is now refused at the entry's pointer, as an
empty text whose default says something is, with the words that the row is
written only in a JSON catalogue, and nothing is written. A test for each
of the two ways a row comes to empty the page fails on the code before this
change, and the same rows written as JSON read back with the empty page.
* The test of a symbolic link at a CSV header skips where the system makes no link
Windows makes a symbolic link only for a process that holds the privilege,
and without it Path.symlink_to raised OSError before the writer ran, so the
test failed rather than skipped there. It now skips when the link cannot be
made, as the tests of a hard link to a sidecar do and as the test of a
symbolic link at a JSON catalogue now does.
* A CSV file holds an empty text only where an empty cell reads back as one
An empty cell reads as nothing written: a text field takes its default, a
provenance entry the document's, and a column of the caller's is left out
of the row. The writer refused an empty text whose default says something
and, since the change before this one, a provenance entry a row empties,
but two more went into the sheet and came back as something else. An empty
text in a column of the caller's was written and read back as a row
without the column, so the catalogue's extras lost the entry, and one in a
text field of the caller's row class that has no default was written and
then refused on every line by the reader, as a row without a field its
class needs, so the sheet could not be read back at all. The writer now
writes an empty text only in a field whose default is empty and refuses one
anywhere else at its pointer, with the words that a row holding it is
written only in a JSON catalogue, and nothing is written.
Every value a sheet writes as a cell was emptied in turn in a one-row
catalogue of each published row class: each text field, a column of the
caller's, the row's credit, each provenance entry against a document that
fills it and one that does not, the basis, a word for a number and a number
left out. Each now reads back or is refused, the JSON reader refusing the
empty credit, basis and word and an empty name. A test for the column of
the caller's and one for the field with no default, from a document and
from rows built in Python, fail on the code before this change, and the
same rows written as JSON keep the empty text. A guard empties every text
field, a column of the caller's and every provenance entry of every
published row class, and fails on the code before this change for each of
them.
Search a catalogue of your own with every lookup by name, draw a panel's plateau from its row, and a guide to keeping catalogues of your own (#888)
* Search a catalogue of your own with every lookup by name, and draw a panel's plateau from its row
Every *_named lookup of a published catalogue takes catalogue=, the rows to
search in place of its own PUBLISHED_*: a catalogue read from a file, the
published one joined with it by |, or any mapping of key to row. Each keeps
its own way of matching a name, answers every match in the order the mapping
holds them and never picks one, and refuses a row of another class by its
key. The join of two mappings of one row class is typed as a mapping of that
class. building.plateau_transmission_loss takes a solids.PlateauMaterial row
as its material and reads its three numbers through printed(), so a bound is
refused in the sheet's own words, and a number passed explicitly replaces the
row's without reading it.
* A guide to keeping catalogues of your own, and the published catalogues page says where they go
"Your own catalogues" (EN and ES, with its docs/ twin) takes a data sheet
from a published table written out as a template to the rows in the models:
the document and its provenance, the refusals that point at a cell, a porous
core in the transfer-matrix model, a spectrum in a reverberation time, a
board in the plateau method, a floating floor's s' and s't, and every lookup
searching yours and the published ones at once. It shows how to write a
bound, a word, a range and a figure in another unit, lists the units a cell
takes, says which row class holds which quantity, and what the library never
does with the data. The guide is registered in the Files topic and the guides
index (145), and the published catalogues page gains a section on why it
never shows a manufacturer's table and where your own go.
* A refused catalogue row or plateau material is named by its class in words that read right for every class
A lookup given a catalogue that holds a row of another class now says
"holds a row of class AbsorptionSpectrum under 'mine/one'", and one given
something that is not a mapping, or a plateau material that is neither a name
nor a PlateauMaterial, ends with "got list" the way the other type refusals
of the library do, instead of putting "a" before a class name that needs
"an". The three lookups whose row class starts with a vowel say so in their
docstrings too.
* The contributing guide sends a manufacturer's data to the guide on catalogues of your own
The paragraph that says a data sheet, a declaration of performance or a test
report is never merged now links "Your own catalogues", which shows how to
keep such data in a file of your own and read it into the same rows.
* Every lookup by name finds a name however its accented letters are stored
A name pasted from a document can store an accented letter as the plain
letter followed by a combining accent. It prints the same as the one typed,
and a lookup that only folded case answered it with nothing. The twenty
lookups now compare the name asked for and each row's text in one Unicode
form, through one shared function, so both forms find the same rows.
Their summaries and return lines describe the catalogue searched, not only
the published one, and the curated API table says the same. The plateau
tests cover an explicit mass per area and frequency ratio beside a row as
well as the height, the lookup guard holds every public *_named to the shape
of a lookup instead of skipping one annotated otherwise, and the type check
covers a new tests/static_typing directory, where a published catalogue
joined with yours is checked to be accepted by a lookup's catalogue=.
* The catalogues guide says what a laboratory's R is, which kinds a provenance takes and what a row's source cites
The guide on catalogues of your own no longer calls every
TransmissionLossSpectrum row a field-incidence value or says a laboratory's
R comes with its own flanking: ISO 10140-2 measures R in one-third-octave
bands on one specimen, where flanking has been shown to be negligible, and
rates it with ISO 717-1, while a book compiles octave bands from tests it
seldom names. The document table names the seven provenance kinds, the date
format and phonometry_version; the source is said to leave the test date and
the standards in the provenance; a temperature in a row class of your own
takes only its own unit; the floating-floor example states s't to the whole
MN/m3 a test report prints; and the template is the one foam of Allard &
Atalla's Table 13.1. The Spanish page says módulo de cizalla, as the rest of
the site does.
* A plateau panel reads its material and the constants it gives in two steps of their own
The name or row is held to what it may be, and each number the caller leaves
out is then taken from the material, as before.
* A lookup searches the very rows it held to their class, the mapping read once
A lookup given catalogue= held every row to its class over the mapping's
items and then read the rows again from its values, so a mapping that
changed between the two readings could hand the lookup a row of another
class without the TypeError it documents. The rows held to the class are
now the rows searched, in the order the mapping gave them. A test with a
mapping whose values are not the rows its items give fails on the code
before this change, for each of the twenty lookups.
Read a catalogue of your own from a JSON file into the rows every catalogue hands out, and write one back (#887)
* Read a catalogue of your own from a JSON file into the rows every catalogue hands out, and write one back
io.read_catalogue and io.parse_catalogue read one table of your own (a data sheet, a declaration of performance, a test report, a measurement) into rows of the classes the published catalogues use, built through CatalogueRow.from_printed like the packaged rows. Each row carries an io.Provenance: the kind of document, its version, the day it was consulted, the page, the laboratory and the report, and its source is composed from it. A figure written in another unit of the same kind (thickness_m, flow_resistivity_kpa_s_m2) is converted on its digits and recorded in converted, whether it is a value, the end of a range or a reading. Every problem in a file is raised at once in one io.CatalogueError whose issues name the JSON pointer, the row and the field; what is only worth a second look is kept as a note with one io.CatalogueWarning. io.write_catalogue writes rows, a published table among them, as a file that reads back into the same rows.
* The io section is Files, and documents the catalogue reader and writer
The section that held measurement audio now also holds the catalogues of materials a caller keeps in files of their own, so it is labelled Files (Archivos) on the site, in the guides index, the API reference, the llms shards and the READMEs, in the same place in the topic order. Its overview says what a catalogue file is for and that no manufacturer's data ships with the library, CONTRIBUTING says such data is never merged from an issue or a pull request, the API reference gains a row for each of the eight new names of phonometry.io, and the reference pages of every catalogue row show the provenance field.
* A catalogue file names every row's broken rule at once, keeps each field's test standard, and never scales a compound unit by its last word
The unit families apply to every field a class of the library declares, each
read for the unit its name carries. A field of a row class of your own takes
another unit only when its name leaves no doubt: a stiffness in N/m, a
conductivity in W/(m K) or a temperature rise in degrees now takes no alias,
where it used to be scaled as a length or moved by the kelvin offset. A
figure the reader cannot convert gets the message about units only when the
name differs from a field in its unit alone, and a misspelt field gets the
name most like it.
The rows a file writes correctly are built even when other rows or keys are
wrong, so one refusal names every problem of form and the first rule of the
row contract each row breaks. A row that narrows the standards of single
fields adds to the document's instead of replacing them, and the writer
writes only the ones that differ. Conventions are held to 2000 characters,
and a converted figure in a unit no family holds, such as sabins, is quoted
with the row's own unit beside it.
* The Files overview says how a catalogue file's problems are reported, and the icon sheet names the section Files
The overview, the API reference and the changelog say what one refusal of a
catalogue file holds, the Spanish overview calls a manufacturer's sheet a
hoja de características and speaks to the reader as the rest of the site
does, and the icon sheet's caption follows the section's new name.
* A catalogue text nested too deep is refused the same way on every Python
The reader counted on the JSON decoder raising RecursionError for a text
nested past what it follows, and from Python 3.14 the decoder follows as
deep as the stack of the thread allows: with a large stack a text of
100 000 nested brackets was decoded, and then refused for not being an
object instead of for its nesting.
The brackets are now counted in the text before it is decoded, outside
its strings and honouring their escapes, and a text nested past 64
levels is refused with the line and column of the bracket that goes one
level too far. A document nested from 7 to 64 levels is still decoded
and told where it nests too deep, and RecursionError stays as a second
line for a caller already deep in its own stack.
* A catalogue file that would hang or crash the reader is refused where it goes wrong
A figure written in another unit is read exactly, as a fraction, and a
text such as 1e-999999999, which a float reads as zero and so passed the
check of a finite number, made that fraction build a power of ten a
billion digits long: minutes of work and hundreds of megabytes before
any answer. A figure of more than 4 300 digits raised a ValueError past
the refusal, and one whose exponent is too long for a Decimal raised a
decimal error from the decoder itself. Such a figure is now refused
before it is read, at its place in the file and under its field: one
longer than 640 characters, the fewest digits Python can be set to read
as one integer, or whose decimal exponent lies more than 400 powers of
ten from one, past every float and every factor of a unit. A figure a
Decimal cannot hold is the float it reads as, and the writer keeps a
converted record it cannot read back as a record.
A file is opened once and read to one byte past 16 MiB at most, so a
pipe or a link to /dev/zero behind the name, whose size says nothing, is
refused as soon as that byte arrives; a regular file past the limit is
still refused before a byte of it is read. The nesting of a text is
counted only to the end of the value it opens, since a decoder reads
nothing past it: sixteen mebibytes of [] were counted for seconds and
are now refused at the third character. The count lives in a module of
its own, for every reader of JSON a caller hands in.
A key that is not text in the provenance of the document or of a row, a
lone surrogate in text handed in, an integer or a fraction of a mapping
past every float, and containers a mapping nests without end are refused
with the rest of the document's problems, instead of raising TypeError,
ValueError, OverflowError or RecursionError.
* A calibration sidecar no reader can take is refused as a ValueError
io.read_sidecar, which io.read and io.read_blocks call for every audio
file they open, read the file at the sidecar's name whole and handed it
to the decoder as it was. A text of 100 000 nested brackets escaped as
a RecursionError, an integer too large for a float as an OverflowError,
one longer than Python reads as a ValueError that did not name the file,
and a link to /dev/zero was read until memory ran out.
A sidecar is now read to 1 MiB at most, with the catalogue reader's
bound on what is read; its brackets are counted before it is decoded and
refused past 64 levels with the line and column where they go too deep;
and a number past every float, or longer than Python reads, is refused
by name. Each is the ValueError the function documents, and a malformed
optional number names the file once, not twice.
* A pipe at a sidecar's name no longer hangs the read of the audio, and neither reader keeps what it cannot write back
io.read_catalogue and io.read_sidecar opened the file at the name before
asking what it was, and opening a named pipe waits for a writer: a pipe no
program writes to, or one whose writer stopped, hung the catalogue reader,
and at a sidecar's name it hung io.read and io.read_blocks on the audio
beside it. A link to /dev/zero was read to the size limit before it was
refused. Both readers now look at the name first, following links, and
refuse a pipe, a character or block device, a socket or a directory with a
message that says which it is; the file is then opened without waiting and
asked again, so that a pipe put in its place in between is refused rather
than waited on. The bounded read stays behind both, for a regular file
that holds more than its size says.
A JSON escape such as \ud800 decodes to half a UTF-16 pair, which the
catalogue reader kept in a text and write_catalogue then failed to encode.
It is now refused beside the control characters, at the cell that holds
it, and a JSON pointer to a name that holds one, or a control character,
writes its escape, so that the refusal can be printed and logged. The
sidecar kept the same text in a calibrator model or a channel label, and
write_sidecar failed on it only after it had emptied the sidecar already
there; the record now refuses it, read or written.
The sidecar took NaN and Infinity, which the decoder reads and JSON does
not have, as the reference level and the calibrator frequency, and
write_sidecar wrote them. Each is now refused by the name of its key, and
never written; the factor and the schema version already refused them.
* A sidecar is written beside its name and renamed into place, never into a pipe at it, and the version it records is text or nothing
* A catalogue text holds neither DEL nor a C1 control, and a file whose name is not UTF-8 is named by its escapes
* Each check of a catalogue document is a step of its own, and the patterns spell an ASCII digit by its escape
The header, the provenance, a cell's kind, the pass over the whole text,
the pieces of a converted cell and the heading of a written document are
read in named helpers, one step each, with no change to what any of them
accepts or refuses. The patterns of a key, a reserved name, a JSON number
and a figure write their digits and word characters as \d and \w under
re.ASCII, and the bracket scanner takes a run of opening or of closing
brackets in one branch each.
* A file written again keeps its permission bits, a sidecar's refusal names a file that is not UTF-8 by its escapes, and a sidecar record holds text where it holds text
A sidecar or a catalogue file written over an old one is a new file renamed
into place, so it came back with the mode a new file takes: a sidecar kept
at 0600 was 0644 after a recalibration. The new file now takes the
permission bits of the one it replaces before the rename, and the
docstrings and the CHANGELOG say that a hard link to the old file keeps the
old content.
A refusal of io.read_sidecar, of io.read beside a sidecar it cannot take and
of io.write_sidecar beside a pipe printed the audio's name as Python holds
it, with a lone surrogate for each byte that is not UTF-8, which no UTF-8
stream can write. The name is escaped as the catalogue reader escapes it,
with the helper both now share.
io.CalibrationSidecar took a version, a calibrator model or a channel label
that was not text, such as nan or 4.0, and a writer then wrote it as a
number no reader takes. It refuses each as a ValueError.
* A sidecar takes the numbers Signal takes and writes nothing a reader refuses, and a read-only file is replaced on Windows as on POSIX
A calibration sidecar's numeric fields took whatever float() reads:
write_sidecar(audio, 2.5, reference_spl="94"), a reference level of True or
a calibrator frequency of "1000" wrote a sidecar over the good one, and
read_sidecar then refused it, so io.read of the audio beside it failed from
then on. An integer past every float escaped as an OverflowError where a
ValueError is documented, labels given as 5 escaped as a TypeError, and
"LR" was taken as two labels. CalibrationSidecar now takes a number where
Signal takes a calibration factor and it is a real scalar: an int or a
float, a NumPy integer or floating-point number, or a 0-d array of one,
kept as a float, and a Signal calibrated by a 0-d array still writes its
sidecar. A bool, a text, a Decimal, a Fraction, a complex number, a NumPy
timedelta, an array that is not 0-d, a masked value and an integer past 64
bits are refused as a ValueError naming the field, a masked value as Signal
refuses it rather than read as the number NumPy finds under its mask. The
labels are a tuple or a list of texts, kept as a tuple, and write_sidecar
is annotated to take either, as it documents. write_sidecar also refuses
fields whose sidecar would pass the 1 MiB read_sidecar takes, and labels
whose count is not the channel count of the audio file beside it, each
before the file is touched, since either stopped every read of the audio.
io.write with sidecar=True makes the sidecar's bytes from the Signal before
a sample is written, so a Signal whose sidecar cannot be written leaves no
audio behind. io.read and io.read_blocks refuse labels that do not fit the
audio naming the sidecar, where they failed building the signal in a
message that named neither, and io.convert refuses them before it writes.
A file written beside its name was given the old file's mode before the
rename. On Windows the mode is the read-only flag: a read-only sidecar
or catalogue file made the new file read-only too, the rename onto the
read-only file failed, and the clean-up could not remove the new file,
which stayed beside it while the clean-up's PermissionError was raised. The
mode is now given before the rename on POSIX only; on Windows the old
file's flag is cleared for the rename, set back if the rename fails and
set on the new file once it is in place, and a flag that cannot be set
then is not raised, since the file is replaced. The flag belongs to the
file, so a hard link to the old file keeps its bytes and loses the flag,
as the docstrings now say. A new file that cannot be removed after a
failure is named in a note on the error that stopped the write.
* A catalogue written without overwrite keeps a file another program makes at its name while it is written
write_catalogue looked at the name before it wrote a byte and then renamed
the new file into place, and a POSIX rename replaces what is at the name:
a file another program made there in between was replaced without a word,
where a FileExistsError is documented. Without overwrite, the new file is
now put at the name in a step that refuses one already there: a hard link,
whose temporary name is then removed, on POSIX, and the rename itself on
Windows, which refuses a name that holds a file. A file system that makes
no hard link, such as FAT, is asked once more whether the name is free just
before the rename, so only a file made in that instant is replaced there.
The look before the write is kept, so a file found at the name is still
refused before anything is written, and a file made in between is refused
in the same words. The new file is removed whichever step fails.
A test that makes a file at the name once the new file's bytes are on the
disk fails on the code before this change, and so do the two that run the
same with no hard links and with Windows's rename. The test of a failed
write now fails whichever step puts the file in place, with and without
overwrite, and the test of a symbolic link skips where the system does not
let it make one, rather than failing before the code runs.
* Where no hard link is made, the name is taken by an empty file before the catalogue is renamed over it
On a file system that makes no hard link, such as FAT, write_catalogue
without overwrite looked at the name once more and then renamed the new
file into place, so a file another program made at the name in the instant
between the two was replaced, while the docstring's Raises entry and the
CHANGELOG said a file made while the catalogue is written is kept. The name
is now taken first by an empty file of the writer's own, made only where
nothing is at the name, a link that names nothing among them, in the one
step that looks and makes, and the new file is renamed over it. A file made
at the name before is kept and refused, and none can be made there after.
What remains is said in the docstrings, the Raises entry and the CHANGELOG:
a reader of the name may find the empty file for the instant between the
two. When anything fails after the name is taken, the empty file is removed
while it is still the one the writer made, a failure to remove it is noted
on the error, and the new file is removed as before.
Without hard links, a file made at the name before it is taken and a link
that names nothing are refused and kept; another program that makes its
file just before the rename finds the name taken, which fails on the code
before this change; a failed rename leaves neither the empty file nor the
new one; and a file another program renamed over the empty one by then is
left as it is.
* The reserving fallback's link test runs where the fallback runs, and Windows's own rename is tested with a link at the name
The fallback that takes the name with an exclusive create runs on POSIX, whose O_EXCL never follows a symbolic link. Forcing it on Windows, where an open follows the link, made its test fail for a path the code never takes there. The test now runs off Windows, and a test of its own checks on Windows that the real rename refuses a symbolic link that names nothing and keeps it.
* Each writer test that expects a refusal builds its catalogue before the call it checks
Seven tests of the no-replace install built the catalogue inside the pytest.raises block, so a refusal from building it would have passed for the writer's. The catalogue is now built first and only the write is inside the block.
Keep a catalogue of your own in a spreadsheet: read it from the CSV file the sheet saves, with its JSON header beside it, and write one back (#889)
* Read a catalogue of your own from the CSV file a spreadsheet saves, with its JSON header beside it, and write one back
io.read_catalogue reads a .csv file whose header, the catalogue's JSON document without its rows, sits beside it as <name>.phonometry.json (or where header_path says) and declares the delimiter and the decimal mark; nothing about the dialect is guessed. A cell holds one value, bound, range or word in a closed grammar (0.85, ~0.85, <=30, >=5, 0.30..0.50, 0.85±0.05, [AFr5], true or false in a column of flags), the columns are the row class's fields in any unit of their family, basis, the provenance.* columns, attributed_to.row and columns of your own named x-, and the lines go through the same pass as a JSON document's rows, so every problem is raised at once and placed at its line and its column as a spreadsheet letters them. Text where a number goes is never read as a word, a NaN or zero, a thousands separator is never read, and when every failing number is written with the other decimal mark, or the first line splits at another delimiter, the refusal says which to declare. A cell typed the way a spreadsheet or a data sheet writes it (a range with a dash, +- for a plus-or-minus, a unit after the number, a number grouped in thousands) is told how to write it, and a control character in it is named, never shown. A line break inside a quoted cell reads as a line feed in every column, written CRLF or LF, and a carriage return alone is refused there.
io.write_catalogue writes a .csv file with a byte order mark and its header, in the delimiter and decimal mark asked for, CRLF at the end of every line, refuses what one cell cannot hold at the pointer a JSON document would write it at, and puts an apostrophe before any text a spreadsheet would run as a formula, which the reader takes off again. Catalogue.header_sha256 is the hash of the header a CSV file was read with.
A row's credit travels in the attributed_to.row column, and the credit of the whole table is the document's own attributed_to, one text beside the provenance, which a JSON document may now write as well. Rossing's tables of B/A credit each paper to its row, or to the whole table on Table 8.2, so they go into a sheet whole; only a credit for one cell stays in a JSON document.
The guide Your own catalogues gains a section, in both languages and in docs/, that writes a published table as a CSV file and its header, reads a sheet in the closed grammar of cells, shows refusals at their lines and columns, and says what only a JSON document can hold.
* A CSV row is said to start on a line of its own, since a quoted line break carries it over the next
* A CSV file and its header are read as a JSON catalogue is, from a regular file only and never past their limit, and named by their escapes
* The read_catalogue row says again that a catalogue is read only from a regular file, and the CSV wording is reflowed
The row of read_catalogue in the API table said, before the CSV file came,
that the file is a regular file of 16 MiB at most and that a pipe, a device
or a directory at the name is refused before it is opened. It says so again,
for the JSON file and for the CSV file with its header, which is read the
same way. The paragraphs that now say a CSV row starts on a line of its own
are wrapped again, in the guide in both languages, its mirror, the
CHANGELOG and the docstrings, and the test of a file name that is not UTF-8
pins the message each of its two cases writes.
* Each step of reading and writing a CSV catalogue is a function of its own
A cell's grammar reads a bound, a range and a value in helpers, and the
diagnosis of a cell that is none hands a miswritten plus-or-minus or range
to one. A column's role is its own column, a misplaced one or a field's; a
cell goes into the row as a word or as a text; the writer spells a word, a
range and a bound in helpers, and a row's texts, hedges and provenance in
methods of their own. The CSV branch of write_catalogue is a helper, and the
credit's pointer is named once. Nothing any of them reads, writes or refuses
changes.
* A CSV row that empties an entry of its document's provenance is refused, and goes into a JSON catalogue
A row writes a provenance entry only where it says other than its
document, so an empty one clears what the document fills. The CSV writer
wrote it as an empty cell, which the reader takes for the document's own:
a row read from a JSON catalogue with "page": "", or written against a
page given by provenance=, read back from the sheet citing the document's
page, and nothing said so. It is now refused at the entry's pointer, as an
empty text whose default says something is, with the words that the row is
written only in a JSON catalogue, and nothing is written. A test for each
of the two ways a row comes to empty the page fails on the code before this
change, and the same rows written as JSON read back with the empty page.
* The test of a symbolic link at a CSV header skips where the system makes no link
Windows makes a symbolic link only for a process that holds the privilege,
and without it Path.symlink_to raised OSError before the writer ran, so the
test failed rather than skipped there. It now skips when the link cannot be
made, as the tests of a hard link to a sidecar do and as the test of a
symbolic link at a JSON catalogue now does.
* A CSV file holds an empty text only where an empty cell reads back as one
An empty cell reads as nothing written: a text field takes its default, a
provenance entry the document's, and a column of the caller's is left out
of the row. The writer refused an empty text whose default says something
and, since the change before this one, a provenance entry a row empties,
but two more went into the sheet and came back as something else. An empty
text in a column of the caller's was written and read back as a row
without the column, so the catalogue's extras lost the entry, and one in a
text field of the caller's row class that has no default was written and
then refused on every line by the reader, as a row without a field its
class needs, so the sheet could not be read back at all. The writer now
writes an empty text only in a field whose default is empty and refuses one
anywhere else at its pointer, with the words that a row holding it is
written only in a JSON catalogue, and nothing is written.
Every value a sheet writes as a cell was emptied in turn in a one-row
catalogue of each published row class: each text field, a column of the
caller's, the row's credit, each provenance entry against a document that
fills it and one that does not, the basis, a word for a number and a number
left out. Each now reads back or is refused, the JSON reader refusing the
empty credit, basis and word and an empty name. A test for the column of
the caller's and one for the field with no default, from a document and
from rows built in Python, fail on the code before this change, and the
same rows written as JSON keep the empty text. A guard empties every text
field, a column of the caller's and every provenance entry of every
published row class, and fails on the code before this change for each of
them.
Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and complete a catalogue file in your editor with its JSON Schema (#891)
* Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and write the JSON Schema of a catalogue file
The elastic FDTD takes a solid's catalogue row wherever it takes a Material: scholte_speed and ElasticFDTD2D.from_regions read its bulk longitudinal speed, its transverse speed and its density through printed(), never its plate or bar speed, and refuse a cell the page does not print in the page's terms.
GroundSurface.medium turns a ground row into the porous half-space ground_effect, barrier_insertion_loss and atmospheric_parabolic_equation take, through the Delany and Bazley or the Miki model. A row Cox and D'Antonio mark as fitted with one model goes into that model only.
HomogeneousElement.from_solid builds the ISO 12354 element a layer of a solid's row makes, with m' = rho t and fc = c0^2 / (1.8 cL t) from the row's density and plate speed, and never picks one of the row's loss factors: the internal loss factor is the caller's to give.
io.catalogue_schema writes the JSON Schema (2020-12) of a catalogue document for the row classes it is given, and the site publishes the one of every published row class as schemas/phonometry-catalogue-1.json, held current by CI. A document may name it in a top-level $schema, which the reader takes and never reads.
* A dollar sign inside inline code opens no maths in the markdown check
The unclosed-maths rule counted every unescaped $ on a line, so the $schema key of a JSON document written as inline code in a table cell read as maths cut off by the next row. CommonMark reads a code span before any maths could open in it, so the rule now leaves code spans out of the count, and still finds maths that wraps onto a block marker, on a line with a code span or without one.
* The catalogues guide hands a solid, a ground and a wall of your own to their models, and completes a catalogue file in your editor
The guide on catalogues of your own shows a solid's row read from a data sheet going into the elastic simulation by its bulk speeds, a ground fitted to a site going into the outdoor models through medium(), with the refusal of a resistivity fitted with another model, and a block's density and plate speed building an ISO 12354 element with the internal loss factor you give. A new section writes the JSON Schema of a catalogue file with io.catalogue_schema, points a document at it with $schema, and sets an editor to the one the site publishes; the document's keys now list $schema. The elastic waves, ground effect and detailed prediction guides each take a row from a catalogue in their own terms, the published catalogues page draws its ground example through medium() and links the schema, and the Files overview and the guide indexes name it, in English, in Spanish and in the docs mirror.
* The catalogue schema describes a row class by the first line of its docstring, or by its name when it has none
* The JSON Schema writer is held to its place in io's public names
* The elastic FDTD reads a row's fields through the dataclasses module, which no local name of the module shadows
* A wetted sand of Cox Table 6.7 goes into neither outdoor ground model, as the text beside the table says
GroundSurface.medium took the eight rows of the two sands Cox and D'Antonio print at four water contents into the Delany and Bazley and the Miki models, because no footnote marks their fit. The text of Sect. 6.6.3 (PDF page 272, printed p. 215) says their parameters are those of the two-parameter model of Attenborough, so a row the text ties to that model is now refused by both, naming the page, as a row the footnotes mark with another model already was. The notes of the eight rows say so on the catalogues page, in both languages, and the docstring no longer says Cox names the fit on every row.
* The schema never marks a catalogue file the reader reads, a null in an optional key included
The reader took a null for basis, $schema and phonometry_version, and a $schema or a version of any length, all of which the schema refuses. It now refuses a null there as it does under every other key, and holds the two texts to the length of any other text. The schema holds the standards a row's own provenance names to the fields of its class, as it already held the document's. Each refused case is now checked against the place and the first words of the reader's refusal, so a case refused for another reason than its name fails; the one that was, a negative thickness written beside the same cell in another unit, now gives the negative thickness alone. A CSV file's header that names the schema is read as one that does not.
* The oracles of a solid's row in the building and elastic models cite the page each number is printed on
The detailed-model test took 600 kg/m3 for autoclaved aerated concrete from ISO 12354-1 Table B.3, which prints 400 to 800; it now reads the three materials off the Annex L element block (PDF page 84, printed p. 78), which prints the density and velocity of each, and says Annex L takes 600 from that range. The elastic test writes the modulus, Poisson ratio and density Bies Table C.1 prints for mild steel (PDF page 747, printed p. 718) as literals and holds the row's bulk and transverse speeds to their closed forms, rather than reading them back from the row under test. The elastic FDTD's docstrings say a row class is refused when it lacks any of the three cells, not only all of them.
* The catalogues guide cites ISO 12354-1 and JSON Schema 2020-12, and says why an outdoor ground raises a fit-range warning
The guide's references now carry ISO 12354-1:2017, whose Formula (B.2), Table B.3 and Annex L the wall section rests on, and the two JSON Schema 2020-12 drafts the schema is written in. After the ground example it says the call raises a PorousAbsorberWarning because an outdoor ground lies below the fit range of both models, and links to where the ground guide explains it. The catalogues page says the schema covers every row class the library publishes, not only the ones its tables use. The Spanish pages name the elastic FDTD rather than a solver object and call the bulk speed that of an extended solid, as the solids guides do.
* The catalogue schema takes the table's credit a document or a CSV header gives at its top, as one text that is not blank
* The Annex L test holds the element's constants on the left, as the rest of the file does
* The catalogue schema's patterns read as the reader does under every validator
The schema said a filled text with \S, which Python's re matches against
U+FEFF and ECMA-262 does not: a name or a credit that is only U+FEFF was read
by the reader and accepted by Python's jsonschema, and refused by an
editor's validator. The reader tells a blank text by str.strip, so the
schema moves to it: every class shorthand of a pattern the schema takes
from the reader is spelled out as the characters it matches under that
pattern's own flags, the white space str.strip strips for a filled text and
the ASCII letters and digits for a key, a column of your own and a reserved
name. Python's re and ECMA-262, with the u flag and without it, now read
each pattern alike, over every character of the basic plane.
* The catalogue schema's patterns that hold a whole text end where it ends, under Python's re and ECMA-262 alike
The seven patterns of the schema that hold a whole text were anchored with
a closing $, which Python's re and ECMA-262 read apart: jsonschema searches
with re.search, whose $ also matches before a line feed that ends the
text, so Python's validator accepted "k\n" as a row's key, "x-a\n" as a
column of your own, "mine\n" as a catalogue's name, and a day, a date or a
digest followed by a line feed, all of which the reader and an editor's
JavaScript validator refuse. Each of the seven now ends with (?![\s\S]),
which no character follows in either engine, with the u flag or without
it. The other two, the test of a text that is not blank and the reserved
form of a name, match a part of the text and are not anchored at its end,
as a test now holds. A validator built on RE2 compiles no lookahead, and so
none of the seven, which the comments and the entry now say instead of
claiming every validator. The comparison of Python and ECMA-262 covers every
pattern the schema holds, over texts that end in a line feed after a whole
key, day and digest, and the six texts are refused by both the schema and
the reader. The catalogues guide says a file the reader reads is never
marked by an editor whose validator reads regular expressions as JavaScript
or Python does, rather than by any editor.
* A catalogue is read as its reader reads the file before a byte of it is written, so nothing the reader refuses is written
write_catalogue held a document to none of the reader's limits: more than
50 000 rows, a JSON file or a CSV file past 16 MiB, a CSV header past
64 KiB, or a text longer than the reader takes (2 000 characters, 20 000
for an about or a note, from a row built in Python or an about passed in)
was written, and with overwrite=True replaced a good file with one that
read_catalogue then refused. The JSON writer now reads the text it is
about to write as parse_catalogue reads one, its UTF-8 bytes held to the
file's limit first, and the CSV writer reads the sheet and its header as
read_catalogue reads the two files, each held to its own limit first, so
whatever the reader refuses, for its size, its rows, the length of a text
or anything else, is refused before anything is written or replaced, in
the reader's own words and naming the file. The reader looks its size
limits up when it runs, and the CSV reader reads the bytes of the two files
through one function the writer shares. The calibration sidecar writer
already refused a sidecar past the 1 MiB its reader takes, before it
touched the file.
For each writer, a file of exactly the most rows, the most bytes of the
file and of a CSV header, and the longest text and prose the reader takes
is written and read back, and one more row, one more byte or one more
character is refused over it, the good file left byte for byte and nothing
beside it; the limits of rows and bytes are lowered for the test. Each of
the nine fails on the code before this change.
* A code span in the markdown check closes only on a run of backticks as long as its opener, and the size refusals share the name of a catalogue file
CommonMark closes a code span only on a whole run of backticks of the opener's length. The check took one tick of a longer run as the closer, so a dollar sign after an unmatched backtick was read as code and an unclosed maths delimiter went unreported; each run is now read whole. The three size refusals name the file with one constant.
A catalogue row checks itself when it is built (#869)
* A catalogue row checks itself when it is built
Every io.CatalogueRow now holds itself, in its constructor, to one
contract, whoever builds it: a packaged loader or a caller writing a row by
hand. A name and a source that are not empty; a numeric cell that is None
or a finite number, never a bool, a text or a NaN; a whole-number field that
holds one, a flag that is True or False, a text field that is text; hedges
keyed only by the fields they can hedge; bounds only on fields with a range;
ranges that are finite, ordered and hold the end the page printed; readings
and uncertainties that are finite, the second not below zero; no value beside
a hedge that says there is none, and no conversion or carried cell with
nothing behind it; the basis vocabulary; and the physical limits by unit
suffix, a porosity from 0 to 1 and a share of a whole from 0 to 100 per cent.
A refusal is an io.CatalogueError naming the row and the field.
The fields are classified once per class from their resolved annotations,
and every set and mapping is frozen from its annotation all the way down.
That replaces the fifteen per-module lists of set fields and the frozen=
argument of take(). The solids' list had fallen behind its dataclass and
never froze bounded_below, and a loader with no list, or a row built by
hand such as Carpet(approximate=[...]), kept the list it was given. A
subclass field annotated with a type the contract has no check for raises
TypeError the first time the class is built.
The packaged data files are read as strict JSON: a NaN or an infinity, a
name written twice in one object, a slash in a row key or an unknown
top-level key is refused naming the file and the row.
PorousMaterial.frame_constants asks for the structural loss factor through
printed() instead of reading an empty cell as a lossless frame. Every
published porous row with both moduli prints one, so no result moves.
Every packaged row already met the contract except six ground surfaces of
Cox & D'Antonio Table 6.7, whose porosity the page prints in per cent in a
column that holds fractions everywhere else and states no unit. Their digits
move from GroundSurface.porosity to a new GroundSurface.porosity_percent, so
porosity is empty on those six rows; the catalogue page shows them in a
column of their own, the defect is in the errata registry, and the
fingerprint names the six cells that moved. No digit changes.
* A misprinted porosity says what the page prints, and an empty hedge is refused
The six porosities Cox & D'Antonio Table 6.7 prints in per cent, 26.9 to
58.1 in a column of fractions that states no unit, no longer get a field of
their own. GroundSurface.porosity_percent would have been a second spelling
of one quantity, and a field every user catalogue file would carry from
4.0. The six cells leave porosity empty and hold the defect as misprinted,
as every other cell the errata registry calls wrong does, so
why_missing("porosity") and printed("porosity") quote the figure the page
prints instead of saying the page gives nothing, which is also what they
answer for a row that prints a dash. The catalogue page shows the six cells
empty with that note, and the fingerprint step names exactly that move.
The contract also refuses three things it let through: a hedge whose text
is empty, which made why_missing answer "" for a misprinted cell; a
reported list with nothing in it, which read "the page lists and no single
value" and counted as something behind a converted or carried cell; and an
interval among the reported readings that runs backwards. A subclass field
annotated as a bare float or int raises TypeError, because every cell of a
row may be missing, and so does an annotation that is not valid Python,
which escaped as a SyntaxError. A negative value in a field such as
surface_density_g_m2 is refused naming the suffix it ends in, _m2, rather
than calling it a quantity in m2.
The fluid tables read their validity from the same strict parse as their
rows instead of a second json.loads, and the io package docstring says what
CatalogueError covers now. No other published cell changes.
* A set of field names is read only after its type is known
_names iterated a value the checker still typed as a plain object, because the
refusal before it returns nothing the analysis can follow. The branch that
reads the items now sits under the type test itself. Behaviour is unchanged.
* The frozenset fast path of a field-name set narrows by isinstance
A type() comparison does not tell a static checker the value is iterable, so
the generator over it read as iterating a plain object. isinstance narrows it
and admits the same frozensets.
Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and complete a catalogue file in your editor with its JSON Schema (#891)
* Take a solid's row into the elastic simulation and the detailed building model, a ground's into the outdoor models, and write the JSON Schema of a catalogue file
The elastic FDTD takes a solid's catalogue row wherever it takes a Material: scholte_speed and ElasticFDTD2D.from_regions read its bulk longitudinal speed, its transverse speed and its density through printed(), never its plate or bar speed, and refuse a cell the page does not print in the page's terms.
GroundSurface.medium turns a ground row into the porous half-space ground_effect, barrier_insertion_loss and atmospheric_parabolic_equation take, through the Delany and Bazley or the Miki model. A row Cox and D'Antonio mark as fitted with one model goes into that model only.
HomogeneousElement.from_solid builds the ISO 12354 element a layer of a solid's row makes, with m' = rho t and fc = c0^2 / (1.8 cL t) from the row's density and plate speed, and never picks one of the row's loss factors: the internal loss factor is the caller's to give.
io.catalogue_schema writes the JSON Schema (2020-12) of a catalogue document for the row classes it is given, and the site publishes the one of every published row class as schemas/phonometry-catalogue-1.json, held current by CI. A document may name it in a top-level $schema, which the reader takes and never reads.
* A dollar sign inside inline code opens no maths in the markdown check
The unclosed-maths rule counted every unescaped $ on a line, so the $schema key of a JSON document written as inline code in a table cell read as maths cut off by the next row. CommonMark reads a code span before any maths could open in it, so the rule now leaves code spans out of the count, and still finds maths that wraps onto a block marker, on a line with a code span or without one.
* The catalogues guide hands a solid, a ground and a wall of your own to their models, and completes a catalogue file in your editor
The guide on catalogues of your own shows a solid's row read from a data sheet going into the elastic simulation by its bulk speeds, a ground fitted to a site going into the outdoor models through medium(), with the refusal of a resistivity fitted with another model, and a block's density and plate speed building an ISO 12354 element with the internal loss factor you give. A new section writes the JSON Schema of a catalogue file with io.catalogue_schema, points a document at it with $schema, and sets an editor to the one the site publishes; the document's keys now list $schema. The elastic waves, ground effect and detailed prediction guides each take a row from a catalogue in their own terms, the published catalogues page draws its ground example through medium() and links the schema, and the Files overview and the guide indexes name it, in English, in Spanish and in the docs mirror.
* The catalogue schema describes a row class by the first line of its docstring, or by its name when it has none
* The JSON Schema writer is held to its place in io's public names
* The elastic FDTD reads a row's fields through the dataclasses module, which no local name of the module shadows
* A wetted sand of Cox Table 6.7 goes into neither outdoor ground model, as the text beside the table says
GroundSurface.medium took the eight rows of the two sands Cox and D'Antonio print at four water contents into the Delany and Bazley and the Miki models, because no footnote marks their fit. The text of Sect. 6.6.3 (PDF page 272, printed p. 215) says their parameters are those of the two-parameter model of Attenborough, so a row the text ties to that model is now refused by both, naming the page, as a row the footnotes mark with another model already was. The notes of the eight rows say so on the catalogues page, in both languages, and the docstring no longer says Cox names the fit on every row.
* The schema never marks a catalogue file the reader reads, a null in an optional key included
The reader took a null for basis, $schema and phonometry_version, and a $schema or a version of any length, all of which the schema refuses. It now refuses a null there as it does under every other key, and holds the two texts to the length of any other text. The schema holds the standards a row's own provenance names to the fields of its class, as it already held the document's. Each refused case is now checked against the place and the first words of the reader's refusal, so a case refused for another reason than its name fails; the one that was, a negative thickness written beside the same cell in another unit, now gives the negative thickness alone. A CSV file's header that names the schema is read as one that does not.
* The oracles of a solid's row in the building and elastic models cite the page each number is printed on
The detailed-model test took 600 kg/m3 for autoclaved aerated concrete from ISO 12354-1 Table B.3, which prints 400 to 800; it now reads the three materials off the Annex L element block (PDF page 84, printed p. 78), which prints the density and velocity of each, and says Annex L takes 600 from that range. The elastic test writes the modulus, Poisson ratio and density Bies Table C.1 prints for mild steel (PDF page 747, printed p. 718) as literals and holds the row's bulk and transverse speeds to their closed forms, rather than reading them back from the row under test. The elastic FDTD's docstrings say a row class is refused when it lacks any of the three cells, not only all of them.
* The catalogues guide cites ISO 12354-1 and JSON Schema 2020-12, and says why an outdoor ground raises a fit-range warning
The guide's references now carry ISO 12354-1:2017, whose Formula (B.2), Table B.3 and Annex L the wall section rests on, and the two JSON Schema 2020-12 drafts the schema is written in. After the ground example it says the call raises a PorousAbsorberWarning because an outdoor ground lies below the fit range of both models, and links to where the ground guide explains it. The catalogues page says the schema covers every row class the library publishes, not only the ones its tables use. The Spanish pages name the elastic FDTD rather than a solver object and call the bulk speed that of an extended solid, as the solids guides do.
* The catalogue schema takes the table's credit a document or a CSV header gives at its top, as one text that is not blank
* The Annex L test holds the element's constants on the left, as the rest of the file does
* The catalogue schema's patterns read as the reader does under every validator
The schema said a filled text with \S, which Python's re matches against
U+FEFF and ECMA-262 does not: a name or a credit that is only U+FEFF was read
by the reader and accepted by Python's jsonschema, and refused by an
editor's validator. The reader tells a blank text by str.strip, so the
schema moves to it: every class shorthand of a pattern the schema takes
from the reader is spelled out as the characters it matches under that
pattern's own flags, the white space str.strip strips for a filled text and
the ASCII letters and digits for a key, a column of your own and a reserved
name. Python's re and ECMA-262, with the u flag and without it, now read
each pattern alike, over every character of the basic plane.
* The catalogue schema's patterns that hold a whole text end where it ends, under Python's re and ECMA-262 alike
The seven patterns of the schema that hold a whole text were anchored with
a closing $, which Python's re and ECMA-262 read apart: jsonschema searches
with re.search, whose $ also matches before a line feed that ends the
text, so Python's validator accepted "k\n" as a row's key, "x-a\n" as a
column of your own, "mine\n" as a catalogue's name, and a day, a date or a
digest followed by a line feed, all of which the reader and an editor's
JavaScript validator refuse. Each of the seven now ends with (?![\s\S]),
which no character follows in either engine, with the u flag or without
it. The other two, the test of a text that is not blank and the reserved
form of a name, match a part of the text and are not anchored at its end,
as a test now holds. A validator built on RE2 compiles no lookahead, and so
none of the seven, which the comments and the entry now say instead of
claiming every validator. The comparison of Python and ECMA-262 covers every
pattern the schema holds, over texts that end in a line feed after a whole
key, day and digest, and the six texts are refused by both the schema and
the reader. The catalogues guide says a file the reader reads is never
marked by an editor whose validator reads regular expressions as JavaScript
or Python does, rather than by any editor.
* A catalogue is read as its reader reads the file before a byte of it is written, so nothing the reader refuses is written
write_catalogue held a document to none of the reader's limits: more than
50 000 rows, a JSON file or a CSV file past 16 MiB, a CSV header past
64 KiB, or a text longer than the reader takes (2 000 characters, 20 000
for an about or a note, from a row built in Python or an about passed in)
was written, and with overwrite=True replaced a good file with one that
read_catalogue then refused. The JSON writer now reads the text it is
about to write as parse_catalogue reads one, its UTF-8 bytes held to the
file's limit first, and the CSV writer reads the sheet and its header as
read_catalogue reads the two files, each held to its own limit first, so
whatever the reader refuses, for its size, its rows, the length of a text
or anything else, is refused before anything is written or replaced, in
the reader's own words and naming the file. The reader looks its size
limits up when it runs, and the CSV reader reads the bytes of the two files
through one function the writer shares. The calibration sidecar writer
already refused a sidecar past the 1 MiB its reader takes, before it
touched the file.
For each writer, a file of exactly the most rows, the most bytes of the
file and of a CSV header, and the longest text and prose the reader takes
is written and read back, and one more row, one more byte or one more
character is refused over it, the good file left byte for byte and nothing
beside it; the limits of rows and bytes are lowered for the test. Each of
the nine fails on the code before this change.
* A code span in the markdown check closes only on a run of backticks as long as its opener, and the size refusals share the name of a catalogue file
CommonMark closes a code span only on a whole run of backticks of the opener's length. The check took one tick of a longer run as the closer, so a dollar sign after an unmatched backtick was read as code and an unclosed maths delimiter went unreported; each run is now read whole. The three size refusals name the file with one constant.
Keep a data sheet's absorption and a laboratory's insulation as rows of their own, each rated again from its bands, and a guide to reading a material datasheet (#890)
* Keep a data sheet's practical and one-third-octave absorption, a laboratory's sound reduction index and a covering's impact improvement as rows of their own, each rated again from its bands
PracticalAbsorptionSpectrum holds the practical coefficients of ISO 11654 from 125 Hz to 4 kHz and ThirdOctaveAbsorptionSpectrum the ISO 354 coefficients from 50 Hz to 10 kHz, both with the weighted coefficient, shape indicators, class, NRC, mounting, thickness and depth of construction the sheet prints. rating() works the weighted coefficient out again from the bands, and practical() turns one-third octaves into practical coefficients, each marked as derived and never borrowed from a neighbouring band.
SoundReductionSpectrum holds the ISO 10140-2 sound reduction index in 21 one-third octaves with Rw, C, Ctr and the Annex B terms of ISO 717-1; ImpactImprovementSpectrum holds the ISO 10140-3 reduction of impact level with Delta Lw, CI,r and CI,Delta. Their rating() calls weighted_rating_extended and the ISO 717-2 improvement functions; the new ImpactImprovementRatingResult carries Delta Lw with both adaptation terms and plots the spectrum.
Reading a catalogue file notes a practical coefficient above 1.00 or off the steps of 0.05, and a printed rating the bands do not give, without changing any cell. weighted_rating, weighted_impact_rating, weighted_impact_improvement and impact_improvement_adaptation_term take a mapping of band to value and refuse a missing band.
* A guide to reading a material datasheet, and the catalogues guide names the row classes that now hold a sheet's absorption and a laboratory's insulation
"Reading a material datasheet" (EN, ES and the docs mirror) sits first among the materials guides and says what each number on a data sheet or a test report is before it is typed: the practical coefficient of ISO 11654 against the one-third-octave coefficient of ISO 354, the steps of 0.05, the cap of 1.00 and the 125 Hz band that is held and not rated; a level declared under CE marking, read from EN 13162, as a bound and never a value; measured against declared, calculated or estimated; the mounting and the depth of construction; the revision and the day it was read; the decimal comma and a corrupt text layer; and the sound reduction index and the impact improvement of a laboratory report. Its examples turn a test report into practical coefficients, read a sheet's octave table from CSV with the notes it earns, and rate a wall and refuse a declared impact improvement a prediction cannot take.
The catalogues guide, the Files overview, the materials overview and the published catalogues page link it, and no longer say that no row class holds these quantities. The guides index counts 146.
* A one-third-octave row's practical coefficients keep a derived text the row already carried on a shared field
* Six coefficients ending in 0 or 5 look like practical ones, and only a sheet that names them says they are
* A negative absorption coefficient is refused rather than read as zero, and a covering's improvement plot keeps a negative band in view
ThirdOctaveAbsorptionSpectrum.rating() refuses a negative one-third-octave
coefficient, naming the row, the band and the value, as
practical_absorption_coefficient does; practical() leaves the octave it
falls in empty and says why in not_derivable, so practical().rating()
refuses the same rating band and a negative band below 200 Hz only empties
the unrated 125 Hz octave. Nothing is floored to 0.00 any more.
A printed absorption class is compared with the one the bands give on its
casefolded word, so "not classified", "c" and "Class C" agree with the
bands that give them. A coefficient above 1 is explained as ISO 354 3.9
NOTE 2 does, by diffraction effects.
ImpactImprovementSpectrum cites the definition of Delta L where it is, ISO
10140-1 Annex H, Equation (H.1), with the levels measured by ISO 10140-3.
The ISO 717-2 improvement plot starts its axis at 0 dB only when no band is
below it, and its title reads "ISO 717-2 Delta Lw (CI,Delta=-9; CI,r=-2) =
15 dB" with the typographic minus of the other rating plots, short enough
for the default figure in both languages. The enlarged range of the ISO
717-1 and ISO 717-2 extended rating plots is shaded with an opaque wash
that stays visible on a dark page.
* The datasheet guide draws each rating it works out, rates a floor covering's report, and cites ISO 10140-1 for the impact improvement
The guide on reading a material datasheet now shows the figure of every
rating its examples work out: the ISO 11654 rating of a test report's
one-third octaves, the ISO 717-1 rating of a laboratory's sound reduction
index over 50 Hz to 5 kHz, and a new example that reads a floor covering's
Delta L from its report and rates it to Delta Lw 15 dB with CI,Delta -9 dB
and CI,r -2 dB. Each figure is the result's own plot() with the code behind
it, in English and Spanish.
Delta L is cited to ISO 10140-1 Annex H, which defines it, with ISO 10140-3
as the measurement; a coefficient above 1 is explained by diffraction, as
ISO 354 does; the glazed partition of the example is a 4 mm pane, which is
where its coincidence dip at 3.15 kHz puts it; the guide says what happens
to a negative coefficient; and two Spanish sentences read as the English
ones do.
* The search check weighs a plain search on two copies of the index that have read the same chunks
On this branch the Spanish search for "ruido" failed the check as if the
standards field had reordered it. Nothing in the field matches "ruido";
the reorder came from how much of the index each side had read. Pagefind
fetches the word index in chunks and keeps them, and a search ranks every
word that begins with what was typed among the chunks it holds, while it
fetches only the chunk that takes in the typed word or its stem. The pages
this branch adds moved a chunk boundary between "ruid" and "ruidos", the
stem of "ruidoso". The weighted copy the check compared against had already
run every designation and held both chunks; the unweighted copy was fresh
and held one, so the two pages whose image text says "ruidoso" rose only on
the weighted side. Two fresh copies at weight 3 and weight 0 return the
same top five, and so do two copies that have both read the second chunk.
The comparison now runs on two fresh copies that make the same searches in
the same order, and a search that returns different pages from them fails
as a difference in what they read, not as a reorder by the field.
loadSearchIndex returns a fresh copy on every call, as it said it did:
Node keeps one module per address, so a second call for the same language
and weight used to get the copy already in use.
* The refusal of a band set and the annex of the enlarged ranges are each named once
The refusal ratings.py gives a band set it does not read is one constant for
its three uses, and the clause the sound reduction sheet cites for its five
enlarged adaptation terms is one. The test of the one-third-octave rating
holds the rating it worked out on the left, as the rest of the file does.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Hold the reading and measurement chain to values other people published (#578)
* Version real measurement audio with published values as oracle data
Five licence-verified third-party sources under tests/data/audio/, each
redistributable and each carrying a value its authors published: the NTi
XL2 calibration take and five pass-by excerpts of the Antwerp campaign
(CC BY 4.0), a Sound Devices 702T BWF from the wavinfo project (MIT), a
genuine RF64 from EBU libbw64 (Apache-2.0), the omnidirectional channel
of a St. Nicholas Chapel impulse response (CC BY 4.0), and the 2015
five-minute LAeq series of two Dublin City Council monitors (CC BY 4.0).
The folder README states per file the source, the licence, the required
attribution and the published values, and marks what was changed (cuts,
repackaging) versus what is byte-identical.
* Hold the reading and measurement chain to values other people published
One oracle module per source: the XL2 calibration take must land on its
published 113.7 dB through the bext-declared full scale; the five
pass-by excerpts must reproduce the campaign's LA,eq, LA,max, L10
within 0.3 dB and its median loudness N50 within 5 percent; the Sound
Devices bext and the EBU RF64 are transcription oracles for the chunk
parser, asserted by exact equality; the chapel impulse response must
agree with the deposit's ISO 3382-1 parameter table within the
standard's own just noticeable differences; and the Dublin monitoring
year must average to the annual report's night-time values before
feeding lden, ldn and composite_rating_level. Each module states its
source, the transcribed published row and why each tolerance is what
it is; L90 and the pass-by level rise stay unasserted because they pin
a windowing convention rather than a measurement.
* Close the calibration chain in the io guide on a real meter's file
The guide derived the sensitivity from a synthesized calibrator take;
the new worked example reads the repository's committed NTi XL2
calibration recording, takes the full scale the instrument itself
declared in its bext chunk, and lands on the 113.7 dB the dataset
publishes, with no hand-typed sensitivity anywhere. Both language
mirrors carry the example and the llms dumps are regenerated.
* State the audio oracle folder's real size in the data index
* Record the committed audio oracles in the changelog
* Date the retrievals in the audio oracle README
* Let the calibration example stand alone when copied
The worked example opens with its own imports, which promises the block is
self-contained, and then leans on names two earlier blocks brought in. On
the page it runs; pasted alone it raises NameError on the first real line.
The two missing imports join the block's own, in all three mirrors, and the
llms copies follow. Verified by running the block exactly as a reader would
paste it: Leq = 113.7 dB.
Hold the reading and measurement chain to values other people published (#578)
* Version real measurement audio with published values as oracle data
Five licence-verified third-party sources under tests/data/audio/, each
redistributable and each carrying a value its authors published: the NTi
XL2 calibration take and five pass-by excerpts of the Antwerp campaign
(CC BY 4.0), a Sound Devices 702T BWF from the wavinfo project (MIT), a
genuine RF64 from EBU libbw64 (Apache-2.0), the omnidirectional channel
of a St. Nicholas Chapel impulse response (CC BY 4.0), and the 2015
five-minute LAeq series of two Dublin City Council monitors (CC BY 4.0).
The folder README states per file the source, the licence, the required
attribution and the published values, and marks what was changed (cuts,
repackaging) versus what is byte-identical.
* Hold the reading and measurement chain to values other people published
One oracle module per source: the XL2 calibration take must land on its
published 113.7 dB through the bext-declared full scale; the five
pass-by excerpts must reproduce the campaign's LA,eq, LA,max, L10
within 0.3 dB and its median loudness N50 within 5 percent; the Sound
Devices bext and the EBU RF64 are transcription oracles for the chunk
parser, asserted by exact equality; the chapel impulse response must
agree with the deposit's ISO 3382-1 parameter table within the
standard's own just noticeable differences; and the Dublin monitoring
year must average to the annual report's night-time values before
feeding lden, ldn and composite_rating_level. Each module states its
source, the transcribed published row and why each tolerance is what
it is; L90 and the pass-by level rise stay unasserted because they pin
a windowing convention rather than a measurement.
* Close the calibration chain in the io guide on a real meter's file
The guide derived the sensitivity from a synthesized calibrator take;
the new worked example reads the repository's committed NTi XL2
calibration recording, takes the full scale the instrument itself
declared in its bext chunk, and lands on the 113.7 dB the dataset
publishes, with no hand-typed sensitivity anywhere. Both language
mirrors carry the example and the llms dumps are regenerated.
* State the audio oracle folder's real size in the data index
* Record the committed audio oracles in the changelog
* Date the retrievals in the audio oracle README
* Let the calibration example stand alone when copied
The worked example opens with its own imports, which promises the block is
self-contained, and then leans on names two earlier blocks brought in. On
the page it runs; pasted alone it raises NameError on the first real line.
The two missing imports join the block's own, in all three mirrors, and the
llms copies follow. Verified by running the block exactly as a reader would
paste it: Leq = 113.7 dB.
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.
Read a catalogue of your own from a JSON file into the rows every catalogue hands out, and write one back (#887)
* Read a catalogue of your own from a JSON file into the rows every catalogue hands out, and write one back
io.read_catalogue and io.parse_catalogue read one table of your own (a data sheet, a declaration of performance, a test report, a measurement) into rows of the classes the published catalogues use, built through CatalogueRow.from_printed like the packaged rows. Each row carries an io.Provenance: the kind of document, its version, the day it was consulted, the page, the laboratory and the report, and its source is composed from it. A figure written in another unit of the same kind (thickness_m, flow_resistivity_kpa_s_m2) is converted on its digits and recorded in converted, whether it is a value, the end of a range or a reading. Every problem in a file is raised at once in one io.CatalogueError whose issues name the JSON pointer, the row and the field; what is only worth a second look is kept as a note with one io.CatalogueWarning. io.write_catalogue writes rows, a published table among them, as a file that reads back into the same rows.
* The io section is Files, and documents the catalogue reader and writer
The section that held measurement audio now also holds the catalogues of materials a caller keeps in files of their own, so it is labelled Files (Archivos) on the site, in the guides index, the API reference, the llms shards and the READMEs, in the same place in the topic order. Its overview says what a catalogue file is for and that no manufacturer's data ships with the library, CONTRIBUTING says such data is never merged from an issue or a pull request, the API reference gains a row for each of the eight new names of phonometry.io, and the reference pages of every catalogue row show the provenance field.
* A catalogue file names every row's broken rule at once, keeps each field's test standard, and never scales a compound unit by its last word
The unit families apply to every field a class of the library declares, each
read for the unit its name carries. A field of a row class of your own takes
another unit only when its name leaves no doubt: a stiffness in N/m, a
conductivity in W/(m K) or a temperature rise in degrees now takes no alias,
where it used to be scaled as a length or moved by the kelvin offset. A
figure the reader cannot convert gets the message about units only when the
name differs from a field in its unit alone, and a misspelt field gets the
name most like it.
The rows a file writes correctly are built even when other rows or keys are
wrong, so one refusal names every problem of form and the first rule of the
row contract each row breaks. A row that narrows the standards of single
fields adds to the document's instead of replacing them, and the writer
writes only the ones that differ. Conventions are held to 2000 characters,
and a converted figure in a unit no family holds, such as sabins, is quoted
with the row's own unit beside it.
* The Files overview says how a catalogue file's problems are reported, and the icon sheet names the section Files
The overview, the API reference and the changelog say what one refusal of a
catalogue file holds, the Spanish overview calls a manufacturer's sheet a
hoja de características and speaks to the reader as the rest of the site
does, and the icon sheet's caption follows the section's new name.
* A catalogue text nested too deep is refused the same way on every Python
The reader counted on the JSON decoder raising RecursionError for a text
nested past what it follows, and from Python 3.14 the decoder follows as
deep as the stack of the thread allows: with a large stack a text of
100 000 nested brackets was decoded, and then refused for not being an
object instead of for its nesting.
The brackets are now counted in the text before it is decoded, outside
its strings and honouring their escapes, and a text nested past 64
levels is refused with the line and column of the bracket that goes one
level too far. A document nested from 7 to 64 levels is still decoded
and told where it nests too deep, and RecursionError stays as a second
line for a caller already deep in its own stack.
* A catalogue file that would hang or crash the reader is refused where it goes wrong
A figure written in another unit is read exactly, as a fraction, and a
text such as 1e-999999999, which a float reads as zero and so passed the
check of a finite number, made that fraction build a power of ten a
billion digits long: minutes of work and hundreds of megabytes before
any answer. A figure of more than 4 300 digits raised a ValueError past
the refusal, and one whose exponent is too long for a Decimal raised a
decimal error from the decoder itself. Such a figure is now refused
before it is read, at its place in the file and under its field: one
longer than 640 characters, the fewest digits Python can be set to read
as one integer, or whose decimal exponent lies more than 400 powers of
ten from one, past every float and every factor of a unit. A figure a
Decimal cannot hold is the float it reads as, and the writer keeps a
converted record it cannot read back as a record.
A file is opened once and read to one byte past 16 MiB at most, so a
pipe or a link to /dev/zero behind the name, whose size says nothing, is
refused as soon as that byte arrives; a regular file past the limit is
still refused before a byte of it is read. The nesting of a text is
counted only to the end of the value it opens, since a decoder reads
nothing past it: sixteen mebibytes of [] were counted for seconds and
are now refused at the third character. The count lives in a module of
its own, for every reader of JSON a caller hands in.
A key that is not text in the provenance of the document or of a row, a
lone surrogate in text handed in, an integer or a fraction of a mapping
past every float, and containers a mapping nests without end are refused
with the rest of the document's problems, instead of raising TypeError,
ValueError, OverflowError or RecursionError.
* A calibration sidecar no reader can take is refused as a ValueError
io.read_sidecar, which io.read and io.read_blocks call for every audio
file they open, read the file at the sidecar's name whole and handed it
to the decoder as it was. A text of 100 000 nested brackets escaped as
a RecursionError, an integer too large for a float as an OverflowError,
one longer than Python reads as a ValueError that did not name the file,
and a link to /dev/zero was read until memory ran out.
A sidecar is now read to 1 MiB at most, with the catalogue reader's
bound on what is read; its brackets are counted before it is decoded and
refused past 64 levels with the line and column where they go too deep;
and a number past every float, or longer than Python reads, is refused
by name. Each is the ValueError the function documents, and a malformed
optional number names the file once, not twice.
* A pipe at a sidecar's name no longer hangs the read of the audio, and neither reader keeps what it cannot write back
io.read_catalogue and io.read_sidecar opened the file at the name before
asking what it was, and opening a named pipe waits for a writer: a pipe no
program writes to, or one whose writer stopped, hung the catalogue reader,
and at a sidecar's name it hung io.read and io.read_blocks on the audio
beside it. A link to /dev/zero was read to the size limit before it was
refused. Both readers now look at the name first, following links, and
refuse a pipe, a character or block device, a socket or a directory with a
message that says which it is; the file is then opened without waiting and
asked again, so that a pipe put in its place in between is refused rather
than waited on. The bounded read stays behind both, for a regular file
that holds more than its size says.
A JSON escape such as \ud800 decodes to half a UTF-16 pair, which the
catalogue reader kept in a text and write_catalogue then failed to encode.
It is now refused beside the control characters, at the cell that holds
it, and a JSON pointer to a name that holds one, or a control character,
writes its escape, so that the refusal can be printed and logged. The
sidecar kept the same text in a calibrator model or a channel label, and
write_sidecar failed on it only after it had emptied the sidecar already
there; the record now refuses it, read or written.
The sidecar took NaN and Infinity, which the decoder reads and JSON does
not have, as the reference level and the calibrator frequency, and
write_sidecar wrote them. Each is now refused by the name of its key, and
never written; the factor and the schema version already refused them.
* A sidecar is written beside its name and renamed into place, never into a pipe at it, and the version it records is text or nothing
* A catalogue text holds neither DEL nor a C1 control, and a file whose name is not UTF-8 is named by its escapes
* Each check of a catalogue document is a step of its own, and the patterns spell an ASCII digit by its escape
The header, the provenance, a cell's kind, the pass over the whole text,
the pieces of a converted cell and the heading of a written document are
read in named helpers, one step each, with no change to what any of them
accepts or refuses. The patterns of a key, a reserved name, a JSON number
and a figure write their digits and word characters as \d and \w under
re.ASCII, and the bracket scanner takes a run of opening or of closing
brackets in one branch each.
* A file written again keeps its permission bits, a sidecar's refusal names a file that is not UTF-8 by its escapes, and a sidecar record holds text where it holds text
A sidecar or a catalogue file written over an old one is a new file renamed
into place, so it came back with the mode a new file takes: a sidecar kept
at 0600 was 0644 after a recalibration. The new file now takes the
permission bits of the one it replaces before the rename, and the
docstrings and the CHANGELOG say that a hard link to the old file keeps the
old content.
A refusal of io.read_sidecar, of io.read beside a sidecar it cannot take and
of io.write_sidecar beside a pipe printed the audio's name as Python holds
it, with a lone surrogate for each byte that is not UTF-8, which no UTF-8
stream can write. The name is escaped as the catalogue reader escapes it,
with the helper both now share.
io.CalibrationSidecar took a version, a calibrator model or a channel label
that was not text, such as nan or 4.0, and a writer then wrote it as a
number no reader takes. It refuses each as a ValueError.
* A sidecar takes the numbers Signal takes and writes nothing a reader refuses, and a read-only file is replaced on Windows as on POSIX
A calibration sidecar's numeric fields took whatever float() reads:
write_sidecar(audio, 2.5, reference_spl="94"), a reference level of True or
a calibrator frequency of "1000" wrote a sidecar over the good one, and
read_sidecar then refused it, so io.read of the audio beside it failed from
then on. An integer past every float escaped as an OverflowError where a
ValueError is documented, labels given as 5 escaped as a TypeError, and
"LR" was taken as two labels. CalibrationSidecar now takes a number where
Signal takes a calibration factor and it is a real scalar: an int or a
float, a NumPy integer or floating-point number, or a 0-d array of one,
kept as a float, and a Signal calibrated by a 0-d array still writes its
sidecar. A bool, a text, a Decimal, a Fraction, a complex number, a NumPy
timedelta, an array that is not 0-d, a masked value and an integer past 64
bits are refused as a ValueError naming the field, a masked value as Signal
refuses it rather than read as the number NumPy finds under its mask. The
labels are a tuple or a list of texts, kept as a tuple, and write_sidecar
is annotated to take either, as it documents. write_sidecar also refuses
fields whose sidecar would pass the 1 MiB read_sidecar takes, and labels
whose count is not the channel count of the audio file beside it, each
before the file is touched, since either stopped every read of the audio.
io.write with sidecar=True makes the sidecar's bytes from the Signal before
a sample is written, so a Signal whose sidecar cannot be written leaves no
audio behind. io.read and io.read_blocks refuse labels that do not fit the
audio naming the sidecar, where they failed building the signal in a
message that named neither, and io.convert refuses them before it writes.
A file written beside its name was given the old file's mode before the
rename. On Windows the mode is the read-only flag: a read-only sidecar
or catalogue file made the new file read-only too, the rename onto the
read-only file failed, and the clean-up could not remove the new file,
which stayed beside it while the clean-up's PermissionError was raised. The
mode is now given before the rename on POSIX only; on Windows the old
file's flag is cleared for the rename, set back if the rename fails and
set on the new file once it is in place, and a flag that cannot be set
then is not raised, since the file is replaced. The flag belongs to the
file, so a hard link to the old file keeps its bytes and loses the flag,
as the docstrings now say. A new file that cannot be removed after a
failure is named in a note on the error that stopped the write.
* A catalogue written without overwrite keeps a file another program makes at its name while it is written
write_catalogue looked at the name before it wrote a byte and then renamed
the new file into place, and a POSIX rename replaces what is at the name:
a file another program made there in between was replaced without a word,
where a FileExistsError is documented. Without overwrite, the new file is
now put at the name in a step that refuses one already there: a hard link,
whose temporary name is then removed, on POSIX, and the rename itself on
Windows, which refuses a name that holds a file. A file system that makes
no hard link, such as FAT, is asked once more whether the name is free just
before the rename, so only a file made in that instant is replaced there.
The look before the write is kept, so a file found at the name is still
refused before anything is written, and a file made in between is refused
in the same words. The new file is removed whichever step fails.
A test that makes a file at the name once the new file's bytes are on the
disk fails on the code before this change, and so do the two that run the
same with no hard links and with Windows's rename. The test of a failed
write now fails whichever step puts the file in place, with and without
overwrite, and the test of a symbolic link skips where the system does not
let it make one, rather than failing before the code runs.
* Where no hard link is made, the name is taken by an empty file before the catalogue is renamed over it
On a file system that makes no hard link, such as FAT, write_catalogue
without overwrite looked at the name once more and then renamed the new
file into place, so a file another program made at the name in the instant
between the two was replaced, while the docstring's Raises entry and the
CHANGELOG said a file made while the catalogue is written is kept. The name
is now taken first by an empty file of the writer's own, made only where
nothing is at the name, a link that names nothing among them, in the one
step that looks and makes, and the new file is renamed over it. A file made
at the name before is kept and refused, and none can be made there after.
What remains is said in the docstrings, the Raises entry and the CHANGELOG:
a reader of the name may find the empty file for the instant between the
two. When anything fails after the name is taken, the empty file is removed
while it is still the one the writer made, a failure to remove it is noted
on the error, and the new file is removed as before.
Without hard links, a file made at the name before it is taken and a link
that names nothing are refused and kept; another program that makes its
file just before the rename finds the name taken, which fails on the code
before this change; a failed rename leaves neither the empty file nor the
new one; and a file another program renamed over the empty one by then is
left as it is.
* The reserving fallback's link test runs where the fallback runs, and Windows's own rename is tested with a link at the name
The fallback that takes the name with an exclusive create runs on POSIX, whose O_EXCL never follows a symbolic link. Forcing it on Windows, where an open follows the link, made its test fail for a path the code never takes there. The test now runs off Windows, and a test of its own checks on Windows that the real rename refuses a symbolic link that names nothing and keeps it.
* Each writer test that expects a refusal builds its catalogue before the call it checks
Seven tests of the no-replace install built the catalogue inside the pytest.raises block, so a refusal from building it would have passed for the writer's. The catalogue is now built first and only the write is inside the block.
Results keep their own copy of the arrays they were given (#939)
np.asarray hands back the very array it is given when that array is already
float64, and so does every validation helper built on it, so a result built
from such an array kept the caller's array, or a view of it: changing that
array afterwards changed a result already computed, and nothing raised. Every
array a function or a method of the library keeps from its arguments is now a
read-only copy of its own, through the private helper read_only_copy, and the
objects that keep arrays to hand out later (SilencerChain, AnpDatabase,
FDTD2D, ElasticFDTD2D, window_metrics) do the same; a Signal keeps a writeable
copy of its samples. The values do not move. scripts/check_array_aliasing.py
(make array-aliasing and a CI job of its own) follows each parameter of every
function of the package to the places an array is kept and fails on one kept
without a copy of its own.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.