-
-
Notifications
You must be signed in to change notification settings - Fork 16.7k
Allow TypedDict as a ResponseValue #4695
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
Conversation
|
The python 3.7 tests fail because that version doesn't have |
|
This seems like a bug in MyPy or TypedDict. It doesn't make sense that we'd add what should be a subclass of dict to the union which already has dict. Please report this to MyPy and/or Python first. Seems like this is relevant in MyPy: python/mypy#12934 |
|
We use |
|
|
CI reports
I don't believe it's a bug in MyPy or TypedDict per se. According to its typing interface, a TypedDict exposes fewer methods than a dict does, even though at runtime it's really just a dict. So from a typing perspective it makes sense to not allow a TypedDict to be accepted by a function that accepts a Dict. Otherwise, this would be valid: class DefinitelyContainsStatus(TypedDict):
status: str
def delete_status(d: dict):
del d['status']
d: DefinitelyContainsStatus = {'status': 'ok'}
delete_status(d)Now you'd have a Flask doesn't do anything of the sort; it doesn't modify the dict at all. So it should be able to accept dicts typed as TypedDict. However, you are right about the ambiguity. Perhaps the right approach is not to add |
|
No, we specifically accept dict, not anything that satisfies the mapping protocol. See my subsequent comments. This is an issue with MyPy or TypedDict, there is nothing that can be done in Flask. Either it's an issue because |
|
In my testing However Python 3.7 still poses a problem, there's no TypedDict in the 3.7 standard library and I don't see a way to get it working with the one provided by typing_extensions. Would it be agreeable to add a version check, so that on 3.7 the current union is used and for 3.8+ |
|
typing_extensions imports fine: I've already tried all this, it makes other things fail, which also fail with |
|
That doesn't import fine, because the import is done within the Can you tell me more about the failures you're referring to? Aside from the Python 3.7 based environments nothing appears to break in CI. It doesn't seem to fail for our Flask application either which uses TypedDicts all over the place. |
|
You need to put it in quotes if it's conditionally imported. |
|
I'm not sure what CI you're looking at. The CI for this PR has failures everywhere. When I fix the TypedDict import, it causes more failures, which then cascade to other areas. |
|
I'm looking at https://github.com/pallets/flask/actions/runs/2670388875 (which I believe is 370b0cb, it doesn't say on that page), which does not use typing_extensions. |
|
I've added the version check in 261c002, but since the PR is closed CI doesn't run. However I believe all tests should pass, since they did before for 3.8+ and the version check fixes the 3.7 case. |
|
Meanwhile, if you don't see a way forward for this particular PR, what other approaches would you consider acceptable for solving this regression? |
|
Report the issue to MyPy (or lend your feedback to the issue I already linked). It's an issue with MyPy or typing in general, not with Flask. |
|
It's not a issue from MyPy's point of view, as I've detailed above in #4695 (comment). Being able to pass a TypedDict to an interface that accepts dict, means that this interface can modify the dict without regard to the type invariants afforded by TypedDict, so any remaining references to that TypedDict cannot rely on its type invariants still being held. Therefore, the MyPy error is correct and this is not an issue with the MyPy project. Also, the MyPy project will tell you that Mapping is the appropriate type to accept, since Flask does not modify the dict and TypedDicts do satisfy the Mapping protocol. |
|
|
Do you suppose that type checking is the right place to enforce this, at the cost of not being able to solve real-world problems? You can already return any non-dict Mapping if you simply don't use type checkers and it will still break visibly. Right now, Flask's typing.py rejects valid real-world usage. Adding |
Yes, given that the entire point of type checking is to describe the correct types and catch things before runtime. If they're not able to do that, that's a design issue that needs to be fixed with typing or type checkers.
But with |
In that case, I would suggest to revert #4579.
But in 2.1.2, literally every invalid type was accepted. Yes, being able to reject all invalid types before runtime is ideal, but not at the cost of also rejecting valid types. With Surely that is still a significant improvement over 2.1.2. In 2.1.3, where TypedDicts are not accepted, we would lose the ability to strongly type check our JSON-based API endpoints. |
|
I've pushed a change to use The fact that it fails with MyPy in 3.8 mode suggests that the If this cannot be solved without using |
|
Well, I definitely agree there's a contravariance problem when sending a TypedDict to a dict receiver, but I'm not sure what the typing or MyPy projects could theoretically do to resolve this. Suppose they could define a Still, I shall open a issue there and link it here. |
|
For Flask's purpose though, it doesn't matter what the contents are. You want to check the return value of your own function, where it does matter. But |
|
They could also add an "immutable dict" to be able to say "any dict type, and I won't change the contents so it's valid for typed dict too". Basically mapping but specifically for dict. |
|
Thanks. I suppose mapping is ok since there's still an error at runtime, although hopefully typing can address it eventually. I'll clean this up in a bit. |
When using a
TypedDictas the return type for a route, this is rejected by mypy with the following error:The problem first appeared in 2.1.3 due to #4579, which changed the route decorator's callable to
ViewCallable, which requires the return type to beResponseValue.ResponseValueis a union that already includesdict[str, Any], however it does not includeTypedDict, which would behave the same asdict[str, Any]at runtime.This PR adds
TypedDictto theResponseValueunion.Checklist:
CHANGES.rstsummarizing the change and linking to the issue... versionchanged::entries in any relevant code docs.pre-commithooks and fix any issues.pytestandtox, no tests failed.