Thanks to visit codestin.com
Credit goes to github.com

Skip to content

PEP 655: Support Required[] inside TypedDict - #10370

Merged
JukkaL merged 22 commits into
python:masterfrom
davidfstr:f/required
Dec 7, 2021
Merged

PEP 655: Support Required[] inside TypedDict#10370
JukkaL merged 22 commits into
python:masterfrom
davidfstr:f/required

Conversation

@davidfstr

Copy link
Copy Markdown
Contributor

Description

Adds support for the Required[] syntax inside TypedDicts as specified by PEP 655. Note that the NotRequired[] syntax from that PEP is not in this initial draft, because I want to see first if there is any feedback on how I'm implementing Required[].

A separate PR will be made to the typing_extensions module to enable runtime support for Required[].

CC sponsor @gvanrossum

Test Plan

New tests exist in test-data/unit/check-typeddict.test after the -- Required[] line, and can be run individually with lines like:

  • pytest -n0 mypy/test/testcheck.py::TypeCheckSuite::testRequiredOnlyAllowsOneItem

@davidfstr

Copy link
Copy Markdown
Contributor Author

Looks like I have a CI failure to investigate...

This is the same approach used with TypeGuardType, which RequiredType is
modeled after.
@davidfstr

Copy link
Copy Markdown
Contributor Author

CI has passed. Ready for folks to take a look.

@davidfstr

Copy link
Copy Markdown
Contributor Author

Outline of changes, to hopefully make the review easier:

  • There are two kinds of TypedDict declarations mypy recognizes, a class-based form and an assignment-based form:
class Movie(TypedDict, total=False):  # class-based
    title: Required[str]
    year: int

Movie = TypedDict('Movie', {  # assignment-based
    'title': Required[str],
    'year': int,
}, total=False)
  • Those declarations are parsed by, respectively:
    • TypedDictAnalyzer.analyze_typeddict_classdef @ mypy/semanal_typeddict.py
    • SemanticAnalyzer.analyze_typeddict_assign @ mypy/semanal.py
  • This commit alters both of these functions to recognize Required[...] as valid wrapper around each key's type, and to treat such-marked keys as required.
  • Both of these functions obtain a Type for each key in the TypedDict by calling SemanticAnalyser.anal_type() on the key type.
  • anal_type() is changed by this commit to recognize Required[...] as a RequiredType in:
    • try_analyze_special_unbound_type @ mypy/typeanal.py
  • We only want to allow the Required[...] syntax in the very narrow situation of a TypedDict key definition. So the top call of SemanticAnalyser.anal_type() takes an allow_required=True parameter that can be used to enable recogition of Required[...] by the two TypedDict-parsing methods, but to disable recognition by default in other contexts.

@JukkaL

JukkaL commented Apr 30, 2021

Copy link
Copy Markdown
Collaborator

I did a quick review pass and overall looks good. It's okay to move forward with supporting NotRequired as an extension.

@davidfstr

Copy link
Copy Markdown
Contributor Author

Thanks for the review @JukkaL !

I'll convert this PR to draft for now and add additional commits for the NotRequired case. Separately, I'll make a PR for the typing_extensions module.

@davidfstr
davidfstr marked this pull request as draft May 1, 2021 16:56
@davidfstr
davidfstr marked this pull request as ready for review May 3, 2021 03:40
@davidfstr

Copy link
Copy Markdown
Contributor Author

PR is extended with support for NotRequired[] and is ready for another round of review.

@JukkaL

JukkaL commented Sep 21, 2021

Copy link
Copy Markdown
Collaborator

Would you be able to fix the merge conflict -- I can review this now. Apologies for the very slow response!

@davidfstr

Copy link
Copy Markdown
Contributor Author

I’ve been disconnected from open source for several months to take care of some life issues. I’m hoping I’ll have enough energy to reengage here in a month or so.

In the meantime I welcome any rebase/deconflict support from any observers who’d like this branch to be merged faster.

@JukkaL

JukkaL commented Sep 24, 2021

Copy link
Copy Markdown
Collaborator

No problem!

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Dec 5, 2021

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

steam.py (https://github.com/Gobot1234/steam.py)
- steam/iterators.py:70: error: Name "Required" is not defined  [name-defined]
- steam/iterators.py:71: error: Name "Required" is not defined  [name-defined]
- steam/trade.py:70: error: Variable "typing_extensions.Required" is not valid as a type  [valid-type]
- steam/trade.py:70: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- steam/trade.py:71: error: Variable "typing_extensions.Required" is not valid as a type  [valid-type]
- steam/trade.py:71: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- steam/trade.py:275: error: Argument 1 to "update" of "TypedDict" has incompatible type "AssetDict"; expected "TypedDict({'instanceid'?: Required?[builtins.str], 'classid'?: Required?[builtins.str], 'market_name'?: str, 'currency'?: int, 'name'?: str, 'market_hash_name'?: str, 'name_color'?: str, 'background_color'?: str, 'type'?: str, 'descriptions'?: Dict[str, str], 'market_actions'?: List[Dict[str, str]], 'tags'?: List[Dict[str, str]], 'actions'?: List[Dict[str, str]], 'icon_url'?: str, 'icon_url_large'?: str, 'tradable'?: bool, 'marketable'?: bool, 'commodity'?: int, 'fraudwarnings'?: List[str]})"  [typeddict-item]
+ steam/trade.py:275: error: Argument 1 to "update" of "TypedDict" has incompatible type "AssetDict"; expected "TypedDict({'instanceid'?: str, 'classid'?: str, 'market_name'?: str, 'currency'?: int, 'name'?: str, 'market_hash_name'?: str, 'name_color'?: str, 'background_color'?: str, 'type'?: str, 'descriptions'?: Dict[str, str], 'market_actions'?: List[Dict[str, str]], 'tags'?: List[Dict[str, str]], 'actions'?: List[Dict[str, str]], 'icon_url'?: str, 'icon_url_large'?: str, 'tradable'?: bool, 'marketable'?: bool, 'commodity'?: int, 'fraudwarnings'?: List[str]})"  [typeddict-item]
- steam/http.py:70: error: Name "Required" is not defined  [name-defined]
- steam/http.py:71: error: Name "Required" is not defined  [name-defined]
- steam/state.py:70: error: Name "Required" is not defined  [name-defined]
- steam/state.py:71: error: Name "Required" is not defined  [name-defined]

@JukkaL JukkaL left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now. Thank you @davidfstr and @97littleleaf11! This has been a frequently requested feature.

@97littleleaf11

Copy link
Copy Markdown
Collaborator

@JukkaL Thanks for your review! And this PR also looks good to me.

@JukkaL
JukkaL merged commit abb0bbb into python:master Dec 7, 2021
@davidfstr

davidfstr commented Dec 8, 2021

Copy link
Copy Markdown
Contributor Author

Thanks @97littleleaf11 for getting this branch finished while I've had to step back for a while! And thank you @JukkaL for getting it reviewed. Team work :)

tushar-deepsource pushed a commit to DeepSourceCorp/mypy that referenced this pull request Jan 20, 2022
Adds support for the Required[] syntax inside TypedDicts as specified by 
PEP 655 (draft). NotRequired[] is also supported.

Co-authored-by: 97littleleaf11 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants