Skip to content

polish(resources): positions_all, lookback enum, multivariate @deprecated, event_ticker list helper - #290

Merged
TexasCoding merged 3 commits into
mainfrom
polish/269-resources
May 21, 2026
Merged

TexasCoding merged 3 commits into
mainfrom
polish/269-resources

Conversation

@TexasCoding

Copy link
Copy Markdown
Owner

Closes #269

Part of #273 (post-v2.4 audit), Wave 3.

…lidation

The spec marks GET /multivariate_event_collections/{ticker}/lookup,
PUT .../lookup, and POST .../{ticker} (create_market) as 'deprecated: true'
with the message 'This endpoint predates RFQs and should not be used for
new integrations.' Decorate the three sync + three async methods with
typing_extensions.deprecated so callers see a DeprecationWarning at
runtime and a type-checker hint (PEP 702).

Also reject lookback_seconds outside the spec enum {10, 60, 300, 3600}
client-side via _validate_lookback_seconds — server returns 400 for any
other value; failing before the GET mirrors the limit-bounds ergonomic
fix landed in #229.

Adds typing-extensions>=4.5 dep (3.12 lacks typing.deprecated which is
3.13+ only). Adds filterwarnings to silence the new DeprecationWarning
in existing tests that intentionally exercise these endpoints; user
callers still see it.

Bundled in this commit: drop the stale '{event_ticker} but spec uses
{ticker}' NOTE in tests/_contract_support.py — the SeriesResource rename
it forecast is long done.

Items 2, 3, 5 of #269 polish bundle.
Every other cursor-paginated endpoint on PortfolioResource (settlements,
deposits, withdrawals) and FcmResource (orders) ships a *_all() iterator
that wraps the cursor loop and exposes max_pages as a safety cap.
positions() did not, so callers walking the full book had to hand-roll
the loop and re-derive the _validate_max_pages guard.

Add positions_all() to PortfolioResource + AsyncPortfolioResource and
FcmResource + AsyncFcmResource, yielding MarketPosition. Reuses the
shared _list_all paginator with 'market_positions' as the items key.
EventPosition aggregates from the response envelope are intentionally
not surfaced — see docstring rationale (page boundaries split aggregates
arbitrarily so concatenating across pages would not reconstruct a
meaningful event-level total).

Register the four new methods in tests/_contract_support.py
(MethodEndpointEntry + cursor Exclusion + _MAX_PAGES_FQNS) so the
contract-drift suite covers them.

Item 1 of #269 polish bundle.
…-10 guard

GET /portfolio/orders is the one route whose event_ticker param refs
MultipleEventTickerQuery in the spec (openapi.yaml:3961-3967): comma-
joined string, server max 10. The SDK accepted only str, forcing
callers either to hand-join the list (and forget the max-10 cap until
the server 400's) or to fall back to client-side filtering.

Widen OrdersResource.list / list_all + AsyncOrdersResource.list /
list_all kwarg to list[str] | str | None and run it through
_join_tickers(..., max_items=10). _join_tickers grows a keyword-only
max_items cap that rejects oversize lists before the wire (pre-joined
strings opt out — caller owns wire format).

Not widened on HistoricalResource.markets or FcmResource.orders despite
the issue suggesting otherwise: their spec refs are SingleEventTickerQuery
(openapi.yaml:3769, :3448) — passing multiple is undefined behaviour
server-side. Item 4 stays narrow to /portfolio/orders.

Item 4 of #269 polish bundle.

Closes #269
@claude

claude Bot commented May 21, 2026

Copy link
Copy Markdown

Code Review — PR #290

Summary: Wave 3 of the post-v2.4 audit. Adds positions_all() pagination helpers to PortfolioResource and FcmResource (sync + async), applies @deprecated to spec-marked multivariate endpoints, adds lookback_seconds enum validation, and extends orders.list/list_all event_ticker to accept list[str] per MultipleEventTickerQuery. Also includes a substantial formatting-only cleanup pass across the test suite.


✅ What's done well

  • Pattern fidelity. positions_all() on both resources follows the existing settlements_all / orders_all template exactly — _require_auth() + _validate_max_pages() before _list_all(), cursor excluded via PAGINATOR_SKIP_LIST, and a clear docstring explaining why event_positions are intentionally dropped.
  • @deprecated placement. Applying the decorator to the implementation function (not the @overload stubs) is correct per PEP 702 — type checkers propagate the warning through overload resolution.
  • filterwarnings in pyproject.toml. Suppressing the noisy DeprecationWarning in test output while still letting it surface in user code is the right trade-off, and the comment explains the rationale.
  • _join_tickers max_items. Clean keyword-only addition that enforces the spec cap at the right layer. Skipping the cap for pre-joined strings is well-documented.
  • Contract support. positions_all entries are correctly wired into METHOD_ENDPOINT_MAP, cursor exclusions, and PAGINATOR_SKIP_LIST for both sync and async variants.

🐛 Bugs / correctness issues

None found. The async positions_all() methods are correctly def (not async def) — consistent with the established pattern where _list_all returns an AsyncIterator directly.


🔍 Issues / suggestions

1. Missing async deprecation-warning tests (gap)

test_multivariate.py tests all three deprecation warnings on the sync class but has no equivalent tests for AsyncMultivariateCollectionsResource. The @deprecated decorator is present on the async implementations, but it's untested. A brief async test class (or parametrized approach) would close the gap symmetrically.

class TestAsyncMultivariateDeprecationWarnings:
    @pytest.mark.asyncio
    async def test_lookup_history_emits_deprecation_warning(self, async_mv): ...
    @pytest.mark.asyncio
    async def test_lookup_tickers_emits_deprecation_warning(self, async_mv): ...
    @pytest.mark.asyncio
    async def test_create_market_emits_deprecation_warning(self, async_mv): ...

2. test_positions_all_forwards_filters in fcm doesn't verify limit

TestPositionsAll.test_positions_all_forwards_filters passes limit=50 but the assertion block doesn't check that url.params["limit"] == "50". Compare with TestPortfolioPositionsAll.test_positions_all_forwards_filters_and_omits_cursor, which does assert params["limit"] == "100". Minor inconsistency worth fixing for parity.

3. test_list_all_rejects_event_ticker_list_over_spec_max — comment says "eager" but doesn't explicitly verify it

The test comment says "Eager validation — must fire at call time, not on iteration", but the test just calls orders.list_all(...) without asserting the ValueError fires before any HTTP is dispatched. Adding a respx.mock route and asserting route.call_count == 0 after the raise would make the eagerness claim machine-verified rather than just documented.

4. typing-extensions lower bound

>=4.5 is correct — deprecated was introduced in 4.5.0. Worth a quick check that the project's minimum Python version aligns (4.5 requires Python ≥ 3.8, which should be fine).


📋 Nit / style

  • The formatting-only churn across test files (collapsing parenthesized strings, trailing commas) is functionally harmless but makes the diff harder to review. Worth splitting into a separate polish commit or PR to keep functional changes readable — though this may already be intentional given the PR title.
  • _validate_lookback_seconds returns the validated int value, which is a clean pattern. Consistent with how _validate_limit works elsewhere.

🔒 Security

No concerns. No new endpoints, no user-input paths, no serialization changes outside the established patterns.


Overall

The functional additions are correct, well-tested (sync side), and follow conventions closely. The two coverage gaps (async deprecation tests, fcm limit assertion) are minor but worth addressing before merge. Everything else is ✅.

@TexasCoding
TexasCoding merged commit 0454b66 into main May 21, 2026
5 checks passed
@TexasCoding
TexasCoding deleted the polish/269-resources branch May 21, 2026 13:55
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.

Polish bundle: REST resource small items (positions *_all(), enum validation, deprecation, list helpers, stale comment)

1 participant