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 subplots(style="paper") creates it

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

MPLBACKEND=Agg poetry run pytest -q

27 passed

poetry run black --check gsplot tests scripts docs/conf.py

passed; 74 files unchanged

poetry run isort --check-only --profile black gsplot tests scripts docs/conf.py

passed

poetry run mypy --config-file .mypy.ini gsplot

passed; 30 files checked

poetry run pyright gsplot

passed; 0 errors and 0 warnings

python -m compileall -q gsplot tests scripts

passed

poetry run pip-audit --local

passed; no known vulnerabilities reported

poetry check --lock

passed; Poetry emitted existing metadata deprecation warnings

coverage report

unavailable; pytest-cov is not installed in the baseline environment

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

paths/explicit_paths.py

none

layouts/mosaic.py

axes.png

plotting/lines_and_labels.py

line_and_label.png

configuration/configuration.py

config.png, config.pdf

publication/publication.py

SC_cal.png, SC_cal.pdf

plotting/scatter.py

scatter.png

plotting/colored_lines.py

line_colormap.png

themes/white.py

graph_white.png

themes/transparent.py

graph_transparent.png

compatibility/legacy_v0.py

compatibility.png, compatibility.pdf

layouts/matplotlib_interoperability.py

subplots.png

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.pyplot is 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=Agg was 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 line plotting 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:

  1. every current root export has a canonical target or an explicit compatibility-only classification;

  2. every documented pre-cutover module page has a forwarding-shim decision;

  3. the public requirements record the target ownership, configuration, dependency, packaging, and compatibility decisions;

  4. the inventory command is read-only and reproducible; and

  5. the baseline checks and unavailable checks are recorded without leaking private paths, credentials, or raw environment output.