Structural reform baseline#
This page records the reviewed Phase 0 characterization of the pre-reform implementation. It is intentionally limited to public-safe, reproducible facts. It is not a progress diary and it does not preserve raw terminal output.
Snapshot#
The baseline was measured from the clean main revision used to start the
Issue #165 reform branch. At that revision the package was the flat-layout 0.3.x
implementation with gsplot/ at the repository root, setup.py, and a
generated-version workflow. The exact public root names, signatures, defaults,
annotations, lazy targets, and compatibility modules are frozen in
tests/fixtures/reform/public-api-v1.json; the reviewed mapping and update
procedure are in the API migration matrix.
That historical snapshot remains useful evidence. The current concise
publication reform starts from the accepted Issue #181 parity baseline at
main commit 782117f and is specified by
Issue #183. The newer
baseline below is normative for that reform.
The repository contained:
30 Python source files under the package directory;
11 Python test files;
13 demo scripts in the numbered demo tree; and
96 top-level function or class definitions found by the Phase 0 inventory.
These counts are orientation data, not acceptance thresholds. The reform must measure behavior and public contracts rather than optimize for file counts.
Concise publication baselines#
The versioned machine-readable style and series baseline is
tests/fixtures/reform/publication-style-v1.json. It freezes only fields that
gsplot deliberately owns; every unlisted Matplotlib field remains the ambient
Matplotlib default. The owned paper profile is:
Area |
Frozen contract |
|---|---|
Figure |
white face only when |
Axes |
white face, round-number autolimits, zero margins, no grid |
Spines |
visible black, 0.8 pt |
Major ticks |
inward on all four sides, 3.5 pt long, 0.8 pt wide, 6 pt pad |
Minor ticks |
inward on all four sides, 2 pt long, 0.6 pt wide |
Typography |
10 pt DejaVu Sans baseline, 6 pt axis-label padding |
Property cycle |
five frozen viridis-derived RGBA values in the fixture |
Legend operation |
best placement, frameless, non-fancy, inherited edge, automatic frame alpha, label spacing 0.3 |
Series identity |
ten frozen colors, ten line styles, and ten markers |
paper() owns the Axes fields and native property cycle only. Matching legend
defaults belong to the explicit legend() operation. Type 42 PDF/PS fonts
belong to the bounded save() operation. Neither setting is a process-global
side effect of paper() or package import.
The publication-example source metrics are measured by
tools/maintenance/check_example_metrics.py. Comments are removed with
Python’s tokenizer, so # inside strings remains source. Empty lines and
trailing whitespace are excluded from comment-free counts. Module, class, and
function docstrings identified by the AST are excluded from executable counts.
Lexical characters exclude comments, indentation, dedentation, newlines, and
encoding/end markers but include every other token spelling. API-call counts
include calls made through an imported gsplot module alias or a direct
from gsplot import ... binding; explicit Matplotlib cleanup such as
plt.close(fig) is required but is not counted as a plotting/output API call.
The frozen source baselines, implemented final example, and final budgets are:
Measure |
0.3 reference |
Selected prototype |
Issue #181 repair |
Implemented final |
Final maximum |
|---|---|---|---|---|---|
Physical lines |
98 |
81 |
265 |
74 |
98 |
Comment-free lines |
74 |
71 |
230 |
64 |
74 |
Comment-free characters |
2487 |
2323 |
7182 |
2172 |
2400 |
Executable lines |
74 |
68 |
223 |
62 |
70 |
Executable characters |
2487 |
2135 |
6714 |
2038 |
2200 |
Lexical characters |
2099 |
1649 |
4628 |
1618 |
1700 |
gsplot API calls |
19 |
12 |
19 |
11 |
15 |
The final example must also reduce executable lines and executable characters by at least 60 percent from the Issue #181 repair while preserving its scientific content and accepted visual meaning. The accepted prototype remains a design fixture; the final row is enforced against executable documentation.
Reproduce the tracked prototype, final, and historical measurements without creating a repository file:
python tools/maintenance/check_example_metrics.py \
tools/maintenance/fixtures/publication_tuple_prototype.py \
--manifest tools/maintenance/example-metrics.json \
--expect selected_tuple_prototype --check-budgets
python tools/maintenance/check_example_metrics.py \
examples/publication/publication.py \
--manifest tools/maintenance/example-metrics.json \
--expect final_example --check-budgets
git show v0.3.0:demo/4_paper_plot/paper_plot.py | \
python tools/maintenance/check_example_metrics.py - \
--manifest tools/maintenance/example-metrics.json \
--expect historical_v0_3
Validation baseline#
The following checks were run in the locked development environment:
Check |
Result |
|---|---|
|
27 passed |
|
passed; 74 files unchanged |
|
passed |
|
passed; 30 files checked |
|
passed; 0 errors and 0 warnings |
|
passed |
|
passed; no known vulnerabilities reported |
|
passed; Poetry emitted existing metadata deprecation warnings |
coverage report |
unavailable; |
The unavailable coverage report is a recorded limitation, not a passing quality gate. Coverage configuration and the target thresholds belong to the implementation slices that add the new test layout.
Documentation and package-artifact validation remain required for the packaging and documentation slices; they are not inferred from the checks above.
Documentation/example output manifest#
Issue #206 replaced the numbered demo/ source tree with semantic executable
examples. The docs build runs every manifest-covered example in an isolated
headless subprocess. The only files an example may create or modify are the
declared image outputs below; source data, configuration, and Python files are
inputs and must remain unchanged.
Example |
Allowed outputs |
|---|---|
|
none |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
examples/manifest.json is the source of truth and is enforced through
tools/maintenance/example_runner.py by both Sphinx and the standalone image
builder. An undeclared or missing script/page, unsafe or duplicate path,
unexpected output, missing output, or stale declared output fails the build.
Subprocesses use Python isolated mode, bytecode suppression, temporary user and
Matplotlib directories, and a credential-free environment. The compatibility
example’s non-interactive display warning is expected under the Agg backend.
Reform validation snapshot#
The canonical implementation now reports 88.51 percent coverage across the
_core, _config, _figure, _plot, _style, and _io modules, while the
pure _core modules report 98.97 percent. These are enforced as CI minimums
of 85 percent and 95 percent respectively; historical compatibility modules
remain covered by their characterization tests but are not part of the
canonical implementation threshold.
The revision-pair benchmark compares 782117f with a committed candidate in
the same isolated environment. It uses 20 fresh imports, one warm-up plus 10
timed repetitions for each plotting workload, and three clean Sphinx builds
including all declared examples. A result is material only when it exceeds both
the 15 percent relative threshold and the absolute threshold of 10 ms for
import, 5 ms for plotting, or one second for documentation. The command is:
poetry run python tools/maintenance/benchmark_reform.py \
--baseline 782117f --candidate HEAD
The Phase 10 candidate f2dba80 used CPython 3.12.13, Matplotlib 3.10.9,
NumPy 2.2.6, the Agg backend, and Linux x86-64. Baseline/candidate medians were
42.917/43.310 ms for import, 2.636/5.735 ms for an ordinary line,
2.962/6.557 ms for an ordinary scatter, 3.183/3.818 ms for a colored line,
and 14.434/16.943 seconds for a clean documentation/example build. Import and all
plotting workloads remained below their material absolute thresholds after
paper styling stopped materializing tick labels eagerly.
Documentation crossed the total-build investigation threshold. A separate clean build counted 98 baseline HTML pages and 124 candidate pages; total time rose 17.4 percent while time per HTML page improved from 147.3 to 136.6 ms. The additional canonical APIs, type aliases, migration material, and guides therefore explain the aggregate increase without showing a per-page slowdown. This expected content-growth cost is explicitly accepted for Issue #183; future unchanged-content comparisons must still investigate the same thresholds. The memory-retention integration test separately creates and closes repeated Figures and confirms that the canonical package owns no Figure/Axes registry or cache. Measurements are environment-dependent review evidence, not machine-specific performance promises.
Import and state characterization#
The pre-cutover Phase 0 isolated-process probe observed the following behavior when importing the historical package:
matplotlib.pyplotis imported eagerly;several Matplotlib
rcParams, including the x and y margins, are modified during import;the probe did not observe a backend change when
MPLBACKEND=Aggwas set;importing can create project logging/configuration output and Matplotlib or font caches in the user cache/config locations; and
no figure existed immediately after the import probe.
The completed structural reform removed application file writes, eager
pyplot imports, implicit configuration loading, root-logger setup, backend
selection, and rcParams mutation from ordinary package imports. Their
absence is validated by isolated-process tests.
Performance reference points#
The same pre-cutover isolated probes produced these reference points:
fresh-process
import gsplot: median approximately 546 ms across 30 samples;warmed ordinary
lineplotting call: median approximately 0.596 ms across 30 calls, with each temporary figure closed after the call.
These numbers are environment-dependent and are used only as historical references. Final performance validation uses the revision-pair protocol recorded above and publishes only aggregate medians, commit identifiers, and generic software/platform dimensions.
Phase 0 acceptance#
Phase 0 is complete when the following remain true in its merged PR:
every current root export has a canonical target or an explicit compatibility-only classification;
every documented pre-cutover module page has a forwarding-shim decision;
the public requirements record the target ownership, configuration, dependency, packaging, and compatibility decisions;
the inventory command is read-only and reproducible; and
the baseline checks and unavailable checks are recorded without leaking private paths, credentials, or raw environment output.