Skip to content

perps: align list-envelope null-handling with prediction (NullableList)#420

Merged
TexasCoding merged 1 commit into
mainfrom
fix/perps-envelopes-nullable-consistency
Jun 5, 2026
Merged

perps: align list-envelope null-handling with prediction (NullableList)#420
TexasCoding merged 1 commit into
mainfrom
fix/perps-envelopes-nullable-consistency

Conversation

@TexasCoding

Copy link
Copy Markdown
Owner

Optional follow-up to #418 / #419: make perps and prediction list-envelope null-handling identical.

Why

This left the two surfaces inconsistent. This PR aligns perps to NullableList, which also restores the pre-#418 or [] null tolerance for these endpoints.

Change

GetMarginMarketsResponse.markets, GetMarginMarketCandlesticksResponse.candlesticks, GetMarginHistoricalFundingRatesResponse.funding_rates, GetMarginFundingHistoryResponse.funding_historyNullableList:

3.2.0 is unreleased, so this refines the envelopes before they ship.

Verification

  • Tests updated: the missing-key case still raises; the null case now returns [] (sync). Drift harness still passes (the field stays required).
  • mypy --strict + ruff clean; 1140 perps + contract tests pass.

🤖 Generated with Claude Code

Follow-up to #418/#419. The perps list-response envelopes (markets/funding) used
a non-nullable list — hard-failing on a null array per #404's strict wording —
while the prediction envelopes (#419) use NullableList (null -> []). This
restores the pre-#418 `or []` null tolerance and makes null-handling identical
across both surfaces.

`GetMarginMarketsResponse.markets`, `GetMarginMarketCandlesticksResponse.candlesticks`,
`GetMarginHistoricalFundingRatesResponse.funding_rates`, and
`GetMarginFundingHistoryResponse.funding_history` now use NullableList: a MISSING
key still hard-fails (surfacing drift), a NULL array coerces to []. 3.2.0 is
unreleased, so this refines the envelopes before release. Removed the now-unused
`import builtins` from both model files.

Tests updated: null -> [] instead of raising. mypy strict + ruff + 1140
perps/contract tests green.
@claude

claude Bot commented Jun 5, 2026

Copy link
Copy Markdown

Code Review — PR #420: perps list-envelope null-handling alignment

Overall verdict: LGTM with minor comments. The change is well-motivated, narrowly scoped, and the updated tests correctly verify the new semantics. Ready to merge modulo the nits below.


What this PR does

Switches four perps list-envelope fields from builtins.list[T] (missing or null → ValidationError) to NullableList[T] (missing key → ValidationError; null array → []). This aligns perps with the prediction API's null-handling convention and restores the pre-#418 null tolerance before 3.2.0 ships.


Code quality / conventions

  • Clean import hygiene. Removing import builtins after the type change is correct; nothing else in each file used it.
  • NullableList is the right tool. The BeforeValidator(_none_to_empty_list) in kalshi/types.py is Pydantic v2-idiomatic and exactly matches the stated semantics.
  • Docstrings are accurate. The updated docstrings clearly describe the missing-key vs. null-value distinction. The GetMarginMarketCandlesticksResponse docstring correctly calls out that ticker (plain str) still raises on null, while candlesticks gets the NullableList treatment.

Tests

All four modified fields now have explicit coverage for both paths:

  • missing key → ValidationError
  • null value → []

Two minor observations:

  1. Stale test name in test_markets.py:363. test_missing_ticker_or_candlesticks_raises was extended to also assert null → [], but the name wasn't updated. Consider renaming to test_missing_ticker_or_candlesticks_raises_but_null_coerces (or similar) so the name matches the full intent.

  2. Async path not explicitly covered for the null case. The PR body notes "(sync)" for verification. Since NullableList is a Pydantic-level BeforeValidator, the deserialization path is identical for sync and async, so this is functionally fine — but adding one async null smoke test (or a brief comment in the test noting the shared model path) would complete the coverage picture if you'd like belt-and-suspenders confidence.


Potential issues / risks

None. The spec-drift protection (missing key → ValidationError) is preserved, so the original #404 goal is maintained. The only relaxation is null array → [], which is the documented Kalshi convention.


Summary

Area Status
Correctness
Conventions
Test coverage (sync)
Test coverage (async) ⚠️ Not updated (functionally equivalent, but no explicit test)
mypy / ruff ✅ (per PR description)
Security ✅ No concerns

The async gap is very low risk given shared model deserialization. If you want to keep the async coverage consistent with the sync tests, a quick test_async_null_tolerated method in each test class would round it out — otherwise this is good to go.

🤖 Reviewed with Claude Code

@TexasCoding
TexasCoding merged commit 6b9228b into main Jun 5, 2026
5 checks passed
@TexasCoding
TexasCoding deleted the fix/perps-envelopes-nullable-consistency branch June 5, 2026 18:39
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