fix(review): address PR review findings for CORE-003

Critical fixes:
- Add math.isfinite() check to reject NaN/Infinity in _safe_quote_price
- Raise TypeError instead of silent 0.0 fallback in price_feed.py
- Use dict instead of Mapping for external data validation

Type improvements:
- Add PortfolioSnapshot TypedDict for type safety
- Add DisplayMode and EntryBasisMode Literal types
- Add explicit dict[str, Any] annotation in to_dict()
- Remove cast() in favor of type comment validation
This commit is contained in:
Bu5hm4nn
2026-03-30 00:39:02 +02:00
parent 1dce5bfd23
commit 98e3208b5e
4 changed files with 48 additions and 19 deletions

View File

@@ -7,7 +7,6 @@ import logging
import math
from dataclasses import dataclass
from datetime import datetime
from typing import Mapping
import yfinance as yf
@@ -52,15 +51,15 @@ class PriceFeed:
self._cache = get_cache()
@staticmethod
def _required_payload_value(payload: Mapping[str, object], key: str, *, context: str) -> object:
def _required_payload_value(payload: dict[str, object], key: str, *, context: str) -> object:
if key not in payload:
raise TypeError(f"{context} is missing required field: {key}")
return payload[key]
@classmethod
def _normalize_cached_price_payload(cls, payload: object, *, expected_symbol: str) -> PriceData:
if not isinstance(payload, Mapping):
raise TypeError("cached price payload must be an object")
if not isinstance(payload, dict):
raise TypeError("cached price payload must be a plain dict")
payload_symbol = str(payload.get("symbol", expected_symbol)).strip().upper()
normalized_symbol = expected_symbol.strip().upper()
if payload_symbol != normalized_symbol:
@@ -69,7 +68,9 @@ class PriceFeed:
if not isinstance(timestamp, str) or not timestamp.strip():
raise TypeError("cached timestamp must be a non-empty ISO string")
price_val = cls._required_payload_value(payload, "price", context="cached price payload")
price = float(price_val) if isinstance(price_val, (int, float)) else 0.0
if not isinstance(price_val, (int, float)):
raise TypeError(f"cached price must be numeric, got {type(price_val).__name__}")
price = float(price_val)
return PriceData(
symbol=payload_symbol,
price=price,
@@ -80,8 +81,8 @@ class PriceFeed:
@classmethod
def _normalize_provider_price_payload(cls, payload: object, *, expected_symbol: str) -> PriceData:
if not isinstance(payload, Mapping):
raise TypeError("provider price payload must be an object")
if not isinstance(payload, dict):
raise TypeError("provider price payload must be a plain dict")
payload_symbol = str(payload.get("symbol", expected_symbol)).strip().upper()
normalized_symbol = expected_symbol.strip().upper()
if payload_symbol != normalized_symbol:
@@ -90,7 +91,9 @@ class PriceFeed:
if not isinstance(timestamp, datetime):
raise TypeError("provider timestamp must be a datetime")
price_val = cls._required_payload_value(payload, "price", context="provider price payload")
price = float(price_val) if isinstance(price_val, (int, float)) else 0.0
if not isinstance(price_val, (int, float)):
raise TypeError(f"provider price must be numeric, got {type(price_val).__name__}")
price = float(price_val)
return PriceData(
symbol=payload_symbol,
price=price,