-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
final TypedDict #7981
Comments
The proposed semantics seem to be that a final TypedDict object doesn't have any extra keys beyond those included in the definition. Also, a final TypedDict can't be used as a base to define derived TypedDicts. I haven't thought about this carefully, but it may be possible to define this in a sound fashion. A final TypedDict would only be compatible with another final TypedDict, and they must have the same keys and required keys, and the key types must be compatible. Since this seems to work at runtime, this could be implemented without changes to |
Exactly, it looks like this is the only way to make this sound. This however may be not very useful, as one would need to have same typed dict all the way down the call stack. On the other hand this may be OK for new codebases. |
The same problem is on the keys. Real life use case: from typing_extensions import TypedDict, final
@final
class Counters(TypedDict):
counter_1: int
blah_blah: int
something_else: int
and_another: int
one_more: int
def reset_counters(c: Counters) -> None:
for k in c:
c[k] = 0 # error: TypedDict key must be a string literal; expected one of ('counter_1', 'blah_blah', 'something_else', 'and_another', 'one_more') |
I would find the implementation propsed by @JukkaL quite useful. I have a TypedDict whose all values are |
I would also find this useful in annotating real world code, as indicated in the linked closed issue. My actual usecase is a Computer Vision type problem where we're using TypedDict to package together corresponding images. For example, there might be an RGB image and a binary mask indicating the foreground of that image.
A particularly elegant construction that might be enabled by marking a
|
I've also just run into this issue -- use case is I have a number of TypedDicts that are all Mapping[str, T], but for each TypedDict I'd like to restrict the keys to a known set. Besides declaring
This would I think solve the use cases in this thread, without disallowing extension |
I just hit a case where it would be useful to have a final TypedDict. I have a utility function that accepts a I suppose I could use a dataclass, but one thing that is useful about using TypedDicts this way is that I can use keys that are not valid Python identifiers (e.g. containing spaces), which I can directly serialize as a header row of the CSV, making it easier for non-technical users to read the CSVs. |
I think most use cases mentioned here would be better served by intersection types: python/typing#213 So if you want to express that all values of a dict are float and it also conforms to a TypedDict, you would write |
This now also makes mypy diverge with pylance - microsoft/pyright#1899 Which is problematic for projects that use pylance for IDE/intellisense and mypy in CI. |
Jukka‘s comment about this: #12266 (comment) |
I find myself in exactly this case. Imagine I'm hitting an API that returns a JSON response. If my request succeeded, the service responds with something like {"transactionDetails": {"transactionId": "1234-56-7890"}} but if my request failed the service response with something like {"error": {"code": 1234, "description": "Oops!"}} I can annotate each of those possibilities as a class TransactionDetails(TypedDict):
transactionId: str
class TransactionDetailsResponse(TypedDict):
transactionDetails: TransactionDetails
class Error(TypedDict):
code: int
description: str
class ErrorResponse(TypedDict):
error: Error
Response = ErrorResponse | TransactionDetailsResponse At this point, what I'd like mypy to understand is that something typed as Unfortunately, I'm instead stuck with casts: def handle_response(response: Response):
if "transactionDetails" in response:
print(f"Transaction {cast(TransactionDetailsResponse, response)['transactionDetails']['transactionId']} submitted!")
else:
print(f"Got error {cast(ErrorResponse, response)['error']['code']}: {cast(ErrorResponse, response)['error']['description']}") I really don't want that cast. I want to say that |
Allow a `TypedDict` to be decorated with `@final`. Like a regular class, it is a type error to subclass from a final `TypedDict`. Relates-to: python#7981
Allow a `TypedDict` to be decorated with `@final`. Like a regular class, mypy will emit an error if a final `TypedDict` is subclassed. Relates-to: python#7981
Allow a `TypedDict` to be decorated with `@final`. Like a regular class, mypy will emit an error if a final `TypedDict` is subclassed. Relates-to: python#7981
Allow a `TypedDict` to be decorated with `@final`. Like a regular class, mypy will emit an error if a final `TypedDict` is subclassed. Allow `@final` to be applied to a `TypedDict`, and have mypy emit an error if class is derived from a final `TypedDict`. This goes some way towards closing #7981 and closing a feature gap with pyright, though not the whole way, as #7981 also asks for additional type narrowing for a final `TypedDict`.
I ran into this limitation and realized mypy's behavior (with assuming `object) is consistent with PEP 589 - TypedDict and the TypedDict Typing Spec.
Currently the above Typing Spec page, and the one for @final don't clarify the interaction between these two features. |
The What you're looking for is a new concept referred to as a "closed" |
Reopened from #7845 as requested
mypy seems to ignore the
@final
decorator when it is applied to a TypedDict. This issue becomes material when one invokes the items() or values() methods, or passes a generic string to get(); then the output will be incorrectly upcast to object since mypy thinks it may be dealing with a subclass.Output:
Expected output:
The text was updated successfully, but these errors were encountered: