Skip to content

♻️ refactor(matplotlib): replace the one-key options dict with a typed field - #862

Merged
nstarman merged 2 commits into
GalacticDynamics:mainfrom
nstarman:claude/matplotlib-shim-cleanup
Aug 8, 2026
Merged

♻️ refactor(matplotlib): replace the one-key options dict with a typed field#862
nstarman merged 2 commits into
GalacticDynamics:mainfrom
nstarman:claude/matplotlib-shim-cleanup

Conversation

@nstarman

@nstarman nstarman commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

From a ponytail-audit of the shim packages. One of 3 independent PRs (with #863, #864).

The change

UnxtConverter.axisinfo_kw was a dict[str, Any] behind a default_factory:

axisinfo_kw: dict[str, Any] = field(default_factory=lambda: {"format": "latex_inline"})
...
fmt = self.axisinfo_kw.get("format", "latex_inline")

A dict, a factory and an untyped signature to carry a single string — whose only key defaults to the same value the .get already falls back to.

The extension point is worth keeping: choosing the axis label format is a plausible thing for a downstream user to want. So only the indirection goes, and it becomes the field it stood for:

unit_format: str = "latex_inline"
"""`astropy` unit format for the axis label (see ``Unit.to_string``)."""

Same capability, now typed and discoverable in the signature.

−7/+4 lines, one file.

API note

The spelling changes from UnxtConverter(axisinfo_kw={"format": "latex"}) to UnxtConverter(unit_format="latex"). That's the only externally visible difference — happy to keep the old name if you'd rather not touch it.

Verification

  • pytest packages/unxts.interop.matplotlib/tests — 8 passed
  • Both branches of axisinfo plus an end-to-end plot:
    default:     $\mathrm{m\,s^{-1}}$
    custom:      $\mathrm{\frac{m}{s}}$      <- extension point intact
    no unit:     None                        <- short-circuit intact
    ax.plot:     xlabel $\mathrm{s}$ | ylabel $\mathrm{m}$
    
  • Full pre-commit suite passes (pyright / ty / mypy typing guards included)

Audited and not changed

Two findings from the audit were dropped rather than shipped:

  • zeroth stays. The audit flagged it as a dependency whose body is one next(iter(x)) call. Maintainer's call: keep it. This PR touches no pyproject.toml and no uv.lock.
  • convert()'s isinstance(obj, AbstractQuantity) fast path stays. I'd flagged it as duplicating _convert_value(); that was wrong. An AbstractQuantity is Iterable with ndim == 1, so removing it would send a 1-D quantity into the element-wise recursion branch below. It's a guard, not a duplicate.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings August 8, 2026 16:01
@github-actions github-actions Bot added 🧩 unxts-interop-matplotlib Issues/PRs affecting the unxts.interop.matplotlib namespace package ➖ Remove a dependency Remove a dependency. labels Aug 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR simplifies unxts.interop.matplotlib by removing the zeroth dependency and replacing an unused dict-based axis label formatting configuration with a typed unit_format field in the Matplotlib converter.

Changes:

  • Dropped the zeroth dependency and inlined next(iter(x)) at the single call site.
  • Replaced axisinfo_kw={"format": ...} with a typed unit_format: str field used by axisinfo().
  • Updated lockfile metadata accordingly.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 2 comments.

File Description
uv.lock Removes zeroth from resolved dependencies and deletes its locked package entry.
packages/unxts.interop.matplotlib/src/unxts/interop/matplotlib/_src/converter.py Removes zeroth import/usage; replaces axisinfo_kw dict indirection with unit_format and uses it for axis labeling.
packages/unxts.interop.matplotlib/pyproject.toml Removes zeroth from package dependencies.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

…d field

`axisinfo_kw` was a `dict[str, Any]` behind a `default_factory`, read in one
place as `.get("format", "latex_inline")` -- a dict, a factory and an untyped
signature to carry a single string, whose only key defaults to the same value
the `.get` already falls back to.

The extension point is worth keeping: choosing the axis label format is a
plausible thing for a downstream user to want. So only the indirection goes,
and it becomes the field it stood for:

    unit_format: str = "latex_inline"

Same capability, now typed and discoverable. The spelling changes from
`UnxtConverter(axisinfo_kw={"format": "latex"})` to
`UnxtConverter(unit_format="latex")`.

Verified beyond the suite: default and custom formats both render
(`$\mathrm{m\,s^{-1}}$` / `$\mathrm{\frac{m}{s}}$`), `axisinfo(None, ...)`
still short-circuits, `default_units` still resolves a list of quantities, and
an end-to-end `ax.plot` of two quantities still labels both axes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nstarman
nstarman force-pushed the claude/matplotlib-shim-cleanup branch from 6f93b83 to 893aad7 Compare August 8, 2026 16:13
@nstarman nstarman changed the title ➖ dep-rm(matplotlib): drop zeroth, and the dict wrapping one option ♻️ refactor(matplotlib): replace the one-key options dict with a typed field Aug 8, 2026
@nstarman nstarman added this to the v2.1.0 milestone Aug 8, 2026
@github-actions github-actions Bot added the ♻️ Refactor code Refactor code. label Aug 8, 2026
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (c91f1a2) to head (3a61e84).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main      #862      +/-   ##
===========================================
+ Coverage   99.84%   100.00%   +0.15%     
===========================================
  Files          84        48      -36     
  Lines        3969      2734    -1235     
  Branches      307       169     -138     
===========================================
- Hits         3963      2734    -1229     
+ Misses          3         0       -3     
+ Partials        3         0       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- Add axisinfo_kw as deprecated field with DeprecationWarning
- Map axisinfo_kw['format'] to unit_format in __post_init__
- Add comprehensive tests for unit_format with real unit formatting
- Add tests for deprecated axisinfo_kw backward compatibility
- All tests pass (14/14) and linting clean

Addresses PR review comments on GalacticDynamics#862:
- Preserves backward compatibility for downstream users
- Provides clear migration path with deprecation warning
- Tests verify AxisInfo.label respects unit_format for real units
@nstarman
nstarman requested a lite review from Copilot August 8, 2026 20:02
@github-actions github-actions Bot added the ✨ Introduce new features Introduce new features. label Aug 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Suppressed comments (5)

packages/unxts.interop.matplotlib/tests/test_converter.py:93

  • This test already checks equality against unit.to_string('latex'); asserting that the string contains frac is redundant and can break if Astropy chooses a different (but still correct) LaTeX representation for the same unit.
    # latex format should produce something like $\mathrm{\frac{m}{s}}$
    assert info.label == unit.to_string("latex")
    assert "frac" in info.label

packages/unxts.interop.matplotlib/tests/test_converter.py:123

  • This test already asserts equality to unit.to_string('latex'); the extra frac substring assertion is redundant and may fail if Astropy renders the unit differently (but still correctly).
    # Should use the format from axisinfo_kw
    assert info.label == unit.to_string("latex")
    assert "frac" in info.label

packages/unxts.interop.matplotlib/src/unxts/interop/matplotlib/_src/converter.py:38

  • UnxtConverter’s dataclass fields are positional by default; with the new first field unit_format: str, any existing positional usage like UnxtConverter({'format': 'latex'}) will now bind the dict to unit_format and fail later in unit.to_string(...). Making the dataclass keyword-only avoids this silent API break and provides an immediate, clear error for positional calls.
    unit_format: str = "latex_inline"
    """`astropy` unit format for the axis label (see ``Unit.to_string``)."""

    axisinfo_kw: dict[str, Any] | None = field(default=None, init=True, repr=False)
    """Deprecated: use ``unit_format`` instead.

packages/unxts.interop.matplotlib/tests/test_converter.py:83

  • This test already asserts info.label == unit.to_string('latex_inline'), which is the source of truth. The additional checks for $ / mathrm assert on internal formatting details that can change across Astropy versions and may cause spurious failures.

This issue also appears in the following locations of the same file:

  • line 91
  • line 121
    # latex_inline format should produce something like $\mathrm{m\,s^{-1}}$
    assert info.label == unit.to_string("latex_inline")
    assert "$" in info.label
    assert "mathrm" in info.label

packages/unxts.interop.matplotlib/tests/test_converter.py:103

  • Since the label is already asserted equal to unit.to_string('console'), checking for the absence of $ is redundant and may be brittle if upstream formatting changes. Prefer relying on the equality assertion alone.
    # console format should produce plain text like "m / s"
    assert info.label == unit.to_string("console")
    assert "$" not in info.label

@nstarman
nstarman merged commit c511a3d into GalacticDynamics:main Aug 8, 2026
40 checks passed
@nstarman
nstarman deleted the claude/matplotlib-shim-cleanup branch August 8, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Introduce new features Introduce new features. ♻️ Refactor code Refactor code. ➖ Remove a dependency Remove a dependency. 🧩 unxts-interop-matplotlib Issues/PRs affecting the unxts.interop.matplotlib namespace package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants