Skip to content

[python-types] router.py: _parse_market and _parse_event have dishonest return type annotations #692

@realfishsam

Description

@realfishsam

File

sdks/python/pmxt/router.py

Imprecise Return Types (Type Lies)

Line 28 — _parse_market

def _parse_market(raw: Any) -> UnifiedMarket:
    """Best-effort parse of a raw market dict into UnifiedMarket."""
    if isinstance(raw, UnifiedMarket):
        return raw
    if isinstance(raw, dict):
        return _convert_market(raw)
    return raw  # ← returns Any here, annotation says UnifiedMarket

The else branch returns raw unchanged, which is typed Any. The declared return type -> UnifiedMarket is false: callers believe they receive a UnifiedMarket but may receive anything at runtime.

Line 37 — _parse_event

def _parse_event(raw: Any) -> UnifiedEvent:
    """Best-effort parse of a raw event dict into UnifiedEvent."""
    if isinstance(raw, UnifiedEvent):
        return raw
    if isinstance(raw, dict):
        return _convert_event(raw)
    return raw  # ← returns Any here, annotation says UnifiedEvent

Same issue: the fallthrough return raw can return a non-UnifiedEvent value.

Impact

  • MEDIUM: Both helpers are private (not public API), but they are called in every match-related public method on Router (fetch_market_matches, fetch_event_matches, fetch_matched_market_clusters, fetch_matched_event_clusters, compare_market_prices, fetch_hedges, fetch_arbitrage). Static analysis treats their return values as UnifiedMarket / UnifiedEvent with no warning, masking possible runtime errors when raw is neither a dict nor the expected type.
  • With warn_return_any = true already set in pyproject.toml, enabling disallow_untyped_defs = true (issue [python-types] pyproject.toml: disallow_untyped_defs = false allows all type gaps to silently pass #246) will surface this as a mypy error.

Suggested Fix

Option A — honest fallback with Union:

def _parse_market(raw: Any) -> Union[UnifiedMarket, Any]:
    ...

Option B — assert well-formedness and raise on unexpected input:

def _parse_market(raw: Any) -> UnifiedMarket:
    if isinstance(raw, UnifiedMarket):
        return raw
    if isinstance(raw, dict):
        return _convert_market(raw)
    raise TypeError(f"Cannot parse {type(raw)} as UnifiedMarket")

Option C — return Any and let callers narrow:

def _parse_market(raw: Any) -> Any:
    ...

Option B is safest for production correctness; Option A is the minimal change to fix the type lie.


Found by automated Python type hints audit

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions