-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code Idea: Move away from TypedDict #12
Comments
@leycec, master of type hints, do you have any input on this? |
@leycec suddenly erupts from the floor! I have been summoned. Seriously, though. Thanks for summoning me. Let's see here... dataclass IPC shenanigans, huh? from typing import TypedDict, Unpack
class _MuhSpecialKwargs(TypedDict):
timeless_kwarg: int
priceless_kwarg: str
def i_heart_kwargs(**kwargs: Unpack[_MuhSpecialKwargs]): ... Will anybody ever actually do that, though? Precisely typing variadic keyword arguments is something nobody cares that much about – especially when doing so requires an unhealthy amount of Which leaves our world with few to no valid use cases for I Get It: IPC, Huh?I suspect I know why you're using So, you started pickling dictionaries back and forth. But plain dictionaries suck. So, you promoted the core But now you're feeling the growing pains.
My favourite usage of from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class Message(object):
"""Base class for messages in and out of the server"""
id: int
type: str # Can be "request" or "response" That said, you seem to "need" the ability to monkey-patch in additional instance variables to dataclass instances:
Seems kinda suss, bro. But who am I to squint at another man's suspicious monkey-patch? We all go there when we need to go there. In that case, just drop the In theory, you shouldn't even need to write your own All you'll need to do then is heavily refactor your entire codebase to leverage dataclasses rather than typed dictionaries. What could be simpler!? 🤣 I acknowledge this is an extreme pain point. Thus, this may never happen. Or perhaps there was a valid reason to prefer typed dictionaries over dataclasses during your initial design process? No idea. I know little about many things. My ignorance is as vast as Wikipedia and just as deep. |
@leycec He's alive!!!
So I think in order to understand the way collegamento is set up its important to understand its history. Originally, this was a sub project in Salve that used
The reason I never used from dataclasses import dataclass
@dataclass
class Message:
"""Base class for messages in and out of the server"""
id: int
type: str # Can be "request" or "response"
def __post_init__(self) -> None:
self.items = ["id", "type"]
def update(self, update_items: dict):
for key, value in update_items.items():
setattr(self, key, value)
self.items.append(key)
m = Message(0, "Test")
m.update({"x": 20})
print(m)
# Message(id=0, type='Test')
print(m.items)
# ['id', 'type', 'x']
if "x" in m.items:
# Still requires type: ignore
print(m.x) # type: ignore
# 20 SadlyI don't see many elegant ways to access the extra elements that don't still add these
I'm glad we can both agree this is a poor option 😄. The only redeeming quality seems to be that it allows default values. Unfortunately it seems that there is truly no good solution for something like this 😕. I will probably stick with Sigh |
Hah-ah! Everything sucks, huh?
Here's how I'd manually define your class Message:
"""Base class for messages in and out of the server"""
# Don't declare these attributes to be class attributes.
# Do, however, tell mypy and pyright to politely shutup.
if TYPE_CHECKING:
id: int
kind: str # Can be "request" or "response"
# @beartype type-checks this on instantiation. Good.
def __init__(self, id: int, kind: str) -> None:
self.id = id
self.kind = kind
# @beartype type-checks these on property reads. Good.
@property
def id(self) -> int: return self.id
@property
def kind(self) -> str: return self.kind
# @beartype type-checks these on property writes. Good.
@id.setter
def id(self, id: int) -> None: self.id = id
@kind.setter
def kind(self, kind: str) -> None: self.kind = kind
def __eq__(self, other: object) -> bool:
return (
isinstance(other, Message) and
self.id == other.id and
self.kind == other.kind
)
def __hash__(self) -> int:
return hash((self.id, self.kind))
def __repr__(self) -> str:
return (
f'{Message.__module__}.Message('
f'id={repr(self.id)}, kind={repr(self.kind)}'
f')'
)
def __getstate__(self) -> dict:
return self.__dict__
def __setstate__(self, d: dict[str, object]) -> None:
return self.__dict__ = d That sorta thing, right? Things suddenly explode with boilerplate, which is bad. But you actually control the horizontal and the vertical, which is far more important. Object attribute syntax (e.g., |
Code Idea:
The idea behind using the
TypedDict
's was to give some structure and show what can always be expected in theRequest
's andResponse
's. Unfortunately there will almost always be extra values added that the type checkers dislike which requires a# type: ignore
. Honestly, this is not a great solution. There has to be a better way. The problem is that its impossible to give good type hints withoutTypedDict
and extra values won't be added until 3.12 if I remember correctly.The text was updated successfully, but these errors were encountered: