-
I'm using pyright to check some code where I receive a dictionary from # client.py
import zmq
def main():
"""Run client."""
context = zmq.Context()
# Socket to talk to server
print("Connecting to server...")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
# Send request
socket.send(b"")
# Get dictionary
json_data: dict = socket.recv_json() # <-- pyright error
print(type(json_data))
print(json_data)
if __name__ == "__main__":
main() Here is the pyright error:
Also, here is the server code that I'm using with the client code shown above: # server.py
import zmq
def main():
"""Run server."""
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")
while True:
# Wait for request
socket.recv()
# Send a dictionary
d = {"one": 1, "two": 2, "names": ["bart", "homer"]}
socket.send_json(d)
if __name__ == "__main__":
main() |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
I'm no expert on best practices in typing, but I believe this is what typing.cast is for, because code-wise, there's no way for the type checker to know which of the several types json_data: dict = cast(dict, socket.recv_json()) which is equivalent to the (negligibly) more efficient: json_data: dict = socket.recv_json() # type: ignore Or you can use an json_data = socket.recv_json()
reveal_type(json_data) # dict or list or str, etc.
if not isinstance(json_data, dict):
raise ValueError(f"Invalid message: {json_data}")
reveal_type(json_data) # dict You might also be interested in TypedDict for your message schema, so you'll get the typed values of your fields you get out of messages, too, without having to cast again. |
Beta Was this translation helpful? Give feedback.
-
You still always need to json_data = cast(JSONDict, socket.recv_json())
# or
json_data: JSONDict = socket.recv_json() # type: ignore
Yes, this is precisely what TypedDict is for, to annotate a dict and all its keys and values with a single annotation.
Sorry, I think you understood the concept even if my words weren't clear. I only meant what you did in your example, which is to specify which fields are defined and what types they have in a TypedDict declaration. So in: class Message(TypedDict):
one: int
two: int
names: list[str]
json_data = cast(Message, socket.recv_json())
reveal_type(json_data['one']) # int
print(json_data['three']) # error, no such key 'three'
Yeah, typed code generally looks messier than untyped code. You kind of have to accept that if you want to use types in Python (I don't like typed Python very much, and don't use it in most of my projects). But if your objection is because of putting verbose type declarations in-line, remember that type annotations are regular Python objects, and you can store them in variables, consolidate them in utility modules, etc. like anything else in Python: _my_type = dict[str, int | list[str]]
...
result = cast(_my_type, something()) |
Beta Was this translation helpful? Give feedback.
You still always need to
cast
ortype:ignore
to convert from one type to another, so:Yes, this is precisely what TypedDict is for, to annotate a dict and all its keys and values with a single annotation.
Sorry, I think you understood the concept even if my words weren't clear. I only meant what you did in your example, which is to specify which fields are defined and what typ…