Skip to content

🐛 fix(quantity): stop _repr_latex_ corrupting units without _repr_latex_ - #870

Merged
nstarman merged 3 commits into
GalacticDynamics:mainfrom
nstarman:fix/repr-latex-non-astropy-unit
Aug 13, 2026
Merged

🐛 fix(quantity): stop _repr_latex_ corrupting units without _repr_latex_#870
nstarman merged 3 commits into
GalacticDynamics:mainfrom
nstarman:fix/repr-latex-non-astropy-unit

Conversation

@nstarman

Copy link
Copy Markdown
Contributor

Split out of #855's "bugs fixed in passing" so it can land and backport independently, ahead of that PR rebasing on top.

_repr_latex_ sliced [1:-1] off the unit's representation, assuming astropy's $...$ wrapping. A unit type with no _repr_latex_ (e.g. unxts.linalg.UnitsMatrix) falls back to plain __repr__(), which carries no such wrapping — the slice ate its first and last characters instead of just unwrapping $...$:

before: '$[1.,~2.,~3.] \; nitsMatrix("(m, s, kg)"$'
after:  '$[1.,~2.,~3.] \; UnitsMatrix("(m, s, kg)")$'

Only strip the $...$ wrapping when it's actually there, i.e. when _repr_latex_ exists; the plain __repr__() fallback is used unsliced. Nothing covered this before; a regression test is included in unxts.linalg (the concrete type that hits the fallback path) and unxt's own astropy-backed path is unaffected.

…latex_`

`_repr_latex_` sliced `[1:-1]` off the unit's representation, assuming
astropy's `$...$` wrapping. A unit type with no `_repr_latex_` (e.g.
`unxts.linalg.UnitsMatrix`) falls back to plain `__repr__()`, which carries
no such wrapping -- the slice ate its first and last characters instead:

    before: '$[1.,~2.,~3.] \; nitsMatrix("(m, s, kg)"$'
    after:  '$[1.,~2.,~3.] \; UnitsMatrix("(m, s, kg)")$'

Only strip the `$...$` wrapping when it's actually there, i.e. when
`_repr_latex_` exists; the plain `__repr__()` fallback is used unsliced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 13, 2026 11:35
@github-actions github-actions Bot added 🧩 unxts-linalg Issues/PRs affecting the unxts.linalg namespace package 🐛 Fix a bug Fix a bug. labels Aug 13, 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

Fixes a LaTeX representation bug in Quantity._repr_latex_() where unit rendering was being sliced incorrectly when the unit falls back to plain __repr__() (not wrapped in $...$), corrupting the displayed unit string.

Changes:

  • Update Quantity._repr_latex_() to avoid slicing the fallback unit __repr__() output.
  • Add a regression test in unxts.linalg covering the UnitsMatrix fallback path.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/unxt/_src/quantity/mixins.py Adjusts LaTeX unit repr handling to avoid corrupting units without _repr_latex_.
packages/unxts.linalg/tests/test_printing.py Adds regression test ensuring _repr_latex_() does not eat characters for UnitsMatrix.

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

Comment thread src/unxt/_src/quantity/mixins.py Outdated
@nstarman nstarman modified the milestones: v2.1.0, v2.0.x Aug 13, 2026
Fixes the `Format` job, which runs pylint:

    src/unxt/_src/quantity/mixins.py:189:17: C2801: Unnecessarily calls
    dunder method __repr__. Use repr built-in function. (unnecessary-dunder-call)

The fallback branch reached for `self.unit.__repr__()` to mirror the
`_repr_latex_` call above it. `repr(self.unit)` is the same call through the
built-in, and it fits on one line now that the conditional is shorter.

Behaviour is unchanged; the comment above still explains why this branch must
not slice its result.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the 🎨 Improve code structure / format Improve structure / format of the code. label Aug 13, 2026
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.97%. Comparing base (200b12e) to head (baa6a0b).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #870      +/-   ##
==========================================
+ Coverage   99.84%   99.97%   +0.12%     
==========================================
  Files          84       55      -29     
  Lines        3975     3338     -637     
  Branches      309      240      -69     
==========================================
- Hits         3969     3337     -632     
+ Misses          3        0       -3     
+ Partials        3        1       -2     

☔ 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.

Addresses Copilot's review: the previous version keyed the strip off the
*existence* of `_repr_latex_` rather than off the output being wrapped. A unit
whose `_repr_latex_` returns an unwrapped string would still be sliced --

    r"\mathrm{m}"  ->  "mathrm{m"

which is the same corruption this PR is for, one step removed. Check for the
delimiters instead.

The length guard matters: a lone `"$"` satisfies both `startswith` and
`endswith`, and would otherwise be sliced to nothing.

Four cases now pinned, exercising the mixin directly since no real unit type
takes the unwrapped branch: wrapped LaTeX is unwrapped exactly once, unwrapped
LaTeX is left intact, a unit without `_repr_latex_` falls back to `repr`, and a
lone `$` survives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the ✅ Add / update / pass tests Add, update, or pass tests. label Aug 13, 2026
@nstarman
nstarman merged commit 7d340b0 into GalacticDynamics:main Aug 13, 2026
40 checks passed
@nstarman
nstarman deleted the fix/repr-latex-non-astropy-unit branch August 13, 2026 13:02
nstarman added a commit to nstarman/unxt that referenced this pull request Aug 13, 2026
Carries across the hardening from GalacticDynamics#870, which this PR supersedes.

`pparts(AbstractUnit)` sliced `to_string("latex")[1:-1]` unconditionally. That
is safe for astropy units, which always wrap -- but it is the same shape as the
bug GalacticDynamics#870 fixed, and it would corrupt any fragment arriving unwrapped:
`\mathrm{m}` becomes `mathrm{m`. Since this engine is the extension point that
downstream packages register into, the assumption is worth removing before
someone else's unit type inherits it.

`unwrap_math` lives in the engine rather than at the call site: it is generic
markup handling with no unit knowledge, and it is the kind of rule a second
markup would otherwise re-derive.

The length guard is load-bearing -- a lone `"$"` satisfies both `startswith`
and `endswith` and would otherwise be sliced away entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nstarman added a commit to nstarman/unxt that referenced this pull request Aug 13, 2026
…ackport

The backport was cut from GalacticDynamics#870's first commit rather than its merged state, so
it missed the two changes that came out of review:

- `self.unit.__repr__()` -> `repr(self.unit)`. This is what fails the `Format`
  job here, which runs pylint: `C2801 unnecessary-dunder-call`.
- Strip `$...$` only when they are actually present, rather than keying off
  whether the unit implements `_repr_latex_` at all. That proxy reproduces this
  very defect one step removed, mangling `r"\mathrm{m}"` into `"mathrm{m"` for
  an implementation returning an unwrapped string. The length guard matters
  too: a lone `"$"` satisfies both `startswith` and `endswith`.

`mixins.py` is now byte-identical to `main`.

The four accompanying tests are lifted from `main`, but *only* those: `main`'s
copy of this file also carries `test_pdoc_chains_caller_custom_hook` from GalacticDynamics#869,
whose `_chain_custom` does not exist on this branch, so taking the file
wholesale would have tested absent code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nstarman added a commit that referenced this pull request Aug 13, 2026
…epr_latex_` corrupting units without `_repr_latex_`) (#872)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
nstarman added a commit to nstarman/unxt that referenced this pull request Aug 13, 2026
Carries across the hardening from GalacticDynamics#870, which this PR supersedes.

`pparts(AbstractUnit)` sliced `to_string("latex")[1:-1]` unconditionally. That
is safe for astropy units, which always wrap -- but it is the same shape as the
bug GalacticDynamics#870 fixed, and it would corrupt any fragment arriving unwrapped:
`\mathrm{m}` becomes `mathrm{m`. Since this engine is the extension point that
downstream packages register into, the assumption is worth removing before
someone else's unit type inherits it.

`unwrap_math` lives in the engine rather than at the call site: it is generic
markup handling with no unit knowledge, and it is the kind of rule a second
markup would otherwise re-derive.

The length guard is load-bearing -- a lone `"$"` satisfies both `startswith`
and `endswith` and would otherwise be sliced away entirely.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✅ Add / update / pass tests Add, update, or pass tests. 🐛 Fix a bug Fix a bug. 🎨 Improve code structure / format Improve structure / format of the code. 🧩 unxts-linalg Issues/PRs affecting the unxts.linalg namespace package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants