Skip to content

Make INTERNAL output models robust to server-side columns filtering #34

@MarketDataDev03

Description

@MarketDataDev03

Summary

Follow-up to #27 (which fixed #23). The columns parameter on
options.expirations is applied server-side, so the API can omit any
field from the response — not just updated. PR #27 fixed the specific
columns=["expirations"] case and made updated optional, but the underlying
class of bug remains for other column combinations.

This is a low-priority hardening issue: in practice the API almost certainly
always returns expirations as the core payload, so these paths are unlikely
to trigger today. Filing to track the root cause rather than leave it implicit.

Root cause

The INTERNAL output format builds the dataclass via output_model(**data).
Any field that is not present in a filtered response (because the user
excluded it via columns) will raise TypeError if it's a required field.

Gaps still present

  1. columns=["updated"] (or any filter excluding expirations) crashes the
    INTERNAL path.
    OptionsExpirations.expirations is still a required field:

    @dataclass
    class OptionsExpirations:
        s: str
        expirations: list[datetime.datetime]      # required
        updated: datetime.datetime | None = None  # optional (fixed in #27)
    
    A response without expirationsTypeErrorMarketDataClientErrorResultthe same failure class as #23, just a different column.
  2. OptionsExpirationsHumanReadable got no equivalent treatment. Both
    Expirations and Date are required, and post_init calls
    format_timestamp(self.Date) unconditionally. A partial human-readable
    INTERNAL response missing Date would crash:

@DataClass
class OptionsExpirationsHumanReadable:
Expirations: list[datetime.datetime] # required
Date: datetime.datetime # required; format_timestamp called unconditionally

Proposed fix

Treat every server-side-filterable field as optional in both INTERNAL models
(default None + guarded conversion in post_init), consistent with how
updated was handled in #27. Decide explicitly whether expirations /
Expirations should be optional or whether an empty payload should be rejected
with a clearer error — a response with no expirations data is arguably
meaningless, so a deliberate validation error may be preferable to silent None.

Acceptance criteria

  • INTERNAL path does not raise TypeError for any valid columns filter
    combination on options.expirations.
  • OptionsExpirationsHumanReadable handles partial responses consistently
    with OptionsExpirations.
  • Regression tests covering columns=["updated"] and a partial
    human-readable response.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions