massive-com / massive-com/client-python
Inconsistent typing for WebSocketMessage and WebSocketClient.run() callback in v2.8.0
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 1.5k
- Fork
- 362
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
Hi Massive team,
I noticed an inconsistency in the websocket typing in Massive Python SDK v2.8.0.
WebSocketMessage is currently defined as a NewType over a list of parsed event models:
WebSocketMessage = NewType(
"WebSocketMessage",
List[
Union[
EquityAgg,
CurrencyAgg,
EquityTrade,
...
]
],
)
However, WebSocketClient.run() is typed as accepting a callback of:
Callable[[List[WebSocketMessage]], None]
This effectively makes the callback parameter type a nested list-like structure:
List[WebSocketMessage], while WebSocketMessage itself already represents a list
of parsed websocket events.
This causes type checkers such as Pyright/Pylance to reject handlers like this:
def handle_msg(messages: WebSocketMessage) -> None:
for message in messages:
if isinstance(message, EquityAgg):
...
with an error similar to:
Argument of type "(messages: WebSocketMessage) -> None" cannot be assigned to parameter "handle_msg"
Type "(messages: WebSocketMessage) -> None" is not assignable to type "(List[WebSocketMessage]) -> None"
There also seems to be a related inconsistency in the parser:
def parse_single(...) -> Optional[WebSocketMessage]:
parsed = model_class.from_dict(data)
return cast(WebSocketMessage, parsed)
At runtime, parsed is a single event object such as EquityAgg, not a list. Then
parse() returns List[WebSocketMessage].
So there appear to be two possible fixes:
- If
WebSocketMessageis intended to mean a single parsed event, redefine it as
a union of event model types, not a list. - If
WebSocketMessageis intended to mean a batch of parsed events, thenrun()
should acceptCallable[[WebSocketMessage], None], andparse_single()should
not cast individual event objects toWebSocketMessage.
Based on the current runtime behavior, option 1 seems more natural:
WebSocketMessage = NewType(
"WebSocketMessage",
Union[
EquityAgg,
CurrencyAgg,
EquityTrade,
...
],
)
or alternatively, using a type alias instead of NewType:
WebSocketMessage = Union[
EquityAgg,
CurrencyAgg,
EquityTrade,
...
]
Then these signatures would make sense:
def parse_single(...) -> Optional[WebSocketMessage]: ...
def parse(...) -> List[WebSocketMessage]: ...
def run(
self,
handle_msg: Callable[[List[WebSocketMessage]], None] | Callable[[str | bytes], None],
close_timeout: int = 1,
**kwargs: Any,
) -> None: ...
One smaller typing improvement: run() currently leaves **kwargs unknown for
Pyright/Pylance. Annotating it as **kwargs: Any and adding -> None would avoid
"partially unknown" warnings.
Thanks.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia con le definizioni di WebSocketMessage, WebSocketClient.run(), parse_single() e parse() menzionate nell’issue, quindi esamina i relativi valori a runtime e le firme dei tipi. Verifica se WebSocketMessage rappresenta un singolo evento analizzato o un batch, adegua di conseguenza le annotazioni del callback e del parser e includi le annotazioni proposte per run(), in modo che Pyright/Pylance accetti un handler tipizzato correttamente.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- python
- Ambito
- api, networking
- Tipo di issue
- Bug
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Stato di attività
- Tranquilla
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 55/100