Skip to content

API polish: type-stub CI, dunder sweep, kwarg hardening, doc fixes#122

Merged
ssmichael1 merged 5 commits into
mainfrom
ci-stubtest
Jul 7, 2026
Merged

API polish: type-stub CI, dunder sweep, kwarg hardening, doc fixes#122
ssmichael1 merged 5 commits into
mainfrom
ci-stubtest

Conversation

@ssmichael1

@ssmichael1 ssmichael1 commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Non-breaking API polish plus a new type-stub CI gate. All changes are additive or bugfix-class; existing call sites keep working (the one behavior change — rejecting typo'd kwargs — is a bugfix). Originally opened for the mypy.stubtest CI; now also carries the Tier A/B API-polish work.

Added

  • mypy.stubtest in CI — the python bindings test job now verifies the hand-written .pyi stubs match the compiled PyO3 bindings.
  • time and duration are now hashable — they defined __eq__ but not __hash__, which (under PyO3 0.29) made them unhashable, so they couldn't be dict keys or set members. Hash derives from the exact microsecond count, consistent with equality.
  • __eq__ / __repr__ on more value types__eq__ on TLE, kepler, itrfcoord; __repr__ (delegating to str) on TLE, kepler, satstate, propsettings. The float-backed __eq__ types are intentionally left unhashable (a clear TypeError beats silently-wrong float-keyed lookups).
  • SGP4InitArgs::from_mean_elements — shared constructor so TLE and OMM use one rev/day→rad/min conversion.

Changed

  • itrfcoord(...), sgp4(...), satstate.propagate(...) now reject unknown kwargs instead of silently ignoring them (closes the alttiude= → 0 m-altitude footgun). Callers passing only documented keywords are unaffected.
  • Type-stub fixes: filled gaps, removed a phantom time.as_gregorian(scale=) param, and renamed several kwargs so the accepted name matches its documentation (see CHANGELOG).

Fixed

  • Doc nits: GMST doc 0.9831040.093104, itrfcoord.latitude_deg range [-180,180][-90,90], disable_eop_time_warning docstring + un-stuck EOP doc from to_gcrf. Deleted dead Py2 duration.__div__.

Validation

206 Rust tests, 114 Python tests, clippy -D warnings, cargo fmt --check, and mypy.stubtest all clean.

🤖 Generated with Claude Code

ssmichael1 and others added 5 commits July 3, 2026 22:05
The hand-written .pyi stubs had never been type-checked and had drifted
from the compiled bindings — e.g. `quaternion.rotz(theta=0.5)` (what the
stub advertised) raised TypeError at runtime because PyO3 named the arg
`theta_rad`. Add a stubtest gate to the `python bindings test` CI job and
bring the stubs back into agreement with the runtime.

CI check:
- New stubtest step + `mypy` in the `[test]` extra. Run with
  `--ignore-positional-only --ignore-disjoint-bases` plus
  python/stubtest_allowlist.txt, which suppresses only systematic PyO3
  idioms (constructors, pickle dunders, native submodules, final classes,
  stub-only type aliases, __all__). No real drift is allowlisted.

Stub build fixes (stubs now type-check):
- Removed 22 illegal overload-implementation blocks across 5 .pyi files.
- Reunited the `time.__add__` overload group split by other methods.
- Added `_Time` alias to dodge the `time`-property annotation shadowing in
  satstate/propresult.

Drift fixes (stub-only, no runtime change):
- rotx/y/z theta->theta_rad, from_gps_week_and_second sec->seconds,
  sgp4 tm->time, satstate.propagate time->timedur,
  from_rotation_matrix mat->dcm; removed phantom as_gregorian(scale=);
  fixed sgp4_opsmode.improved; added itrfcoord.height, time.add_utc_days,
  weekday.Invalid.

Keyword renames (public API — positional calls unaffected):
- duration.from_hours/from_minutes/from_seconds: d -> hours/minutes/seconds
- time.from_string s->string, from_unixtime t->unixtime,
  from_rfc3339 s->rfc3339, from_datetime tm->dt,
  strftime fmt->format, strptime (s,fmt)->(date_string,format)
- kepler.from_pv (r,v)->(pos,vel)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- frametransform GMST doc: 0.983104 -> 0.093104 (matches code)
- itrfcoord latitude_deg doc range: [-180,180] -> [-90,90]
- move orphaned EOP-warning doc block off to_gcrf; give
  disable_eop_time_warning its own docstring
- delete dead Python 2 duration.__div__ alias

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- time, duration: add __hash__ (were unhashable due to __eq__ without
  __hash__); hash the exact microsecond count so it agrees with __eq__
- TLE, kepler, itrfcoord: add __eq__/__ne__ (float-backed, so left
  unhashable rather than risk silent float-keyed lookup misses)
- TLE, kepler, satstate, propsettings: add __repr__ delegating to __str__

satstate.__eq__ intentionally deferred (no PartialEq; equality across
float pv + covariance + maneuvers is a design decision, not a sweep).

114 Python tests pass; stubtest clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Typo'd keywords were silently dropped — e.g. itrfcoord(alttiude=100)
left the ground station at 0 m. These three sites now call
reject_unused_kwargs after consuming all documented keywords, matching
the pattern already used elsewhere in the bindings.

114 Python tests pass; stubtest clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The rev/day->rad/min and degree->radian conversion was copy-pasted in
TLE::sgp4_init_args and OMM::sgp4_init_args. Extract it into a single
SGP4InitArgs::from_mean_elements constructor; both sources now marshal
their fields through it. Behavior-preserving — 206 lib tests + Vallado
SGP4 vectors unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ssmichael1 ssmichael1 changed the title ci: verify type stubs match PyO3 bindings with mypy.stubtest API polish: type-stub CI, dunder sweep, kwarg hardening, doc fixes Jul 5, 2026
@ssmichael1
ssmichael1 merged commit fc980b3 into main Jul 7, 2026
16 checks passed
@ssmichael1
ssmichael1 deleted the ci-stubtest branch July 7, 2026 23:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant