Skip to content

Implement support for closed TypedDicts (PEP 728)#21382

Draft
alicederyn wants to merge 21 commits intopython:masterfrom
alicederyn:closed.keyword
Draft

Implement support for closed TypedDicts (PEP 728)#21382
alicederyn wants to merge 21 commits intopython:masterfrom
alicederyn:closed.keyword

Conversation

@alicederyn
Copy link
Copy Markdown

Implement support for the closed keyword on TypedDicts (part of PEP 728).

Additionally, fix some preexisting issues with ReadOnly keys.

PEP 728 allows multiple special forms to be applied to a key when a
TypedDict is created using functional syntax, but the logic was only
checking and stripping a single instance. This prevents overriding
the overall total status on a ReadOnly field.
Fix a bug where a non-required key was considered consistent with a
required key if the latter was marked as read-only.
Fix a bug where the meet of a mutable and readonly key was incorrectly
set as readonly. This bug was caused by mistakenly unioning the
readonly key sets, which happened to work for the tested case where
readonly keys never appeared in the other type.
The TypedDict meet is returning Never whenever inputs have mismatched
keys (value types or requiredness). However:

* when a key is readonly, the meet value type can be a subtype of it
* when a key is readonly and not required, the meet key can be required
* when both keys are readonly and not required, the meet value type can
  be uninhabited (absent)
A typo was causing the readonly state of keys on the LHS of a TypedDict
join to be ignored.
Improve TypedDict joins where keys mismatch by returning a readonly key
with the joined type instead of discarding it.
Allow readonly keys to be refined in subclasses, in line with PEP 705.
For cases where refinement is not permitted by spec, provide a more
detailed error message.

This closes python#7435
The fallback path in TypeAnalyser::visit_typeddict_type is copying
the required keys from the original type, but not the readonly keys.
This is resulting in incorrect subtyping analysis for type variables
with TypedDict bounds with readonly keys.
`NotRequired[Never]` can be used to indicate that a single key in a
TypedDict will not be present.
Begin implementing PEP 728 support by adding an is_closed field to
TypedDictType. This is filled out from the closed keyword, and displayed
in reveal_type, but not otherwise supported yet.
Continue implementing PEP 728 support with updates to the subtyping
logic. Drop the previous use of 'names_are_wider_than', which will
be difficult to extend to cover `extra_items`, and instead use zipall
to check for key addition/removal alongside the other key-based checks.
Propagate closed from subclasses. Verify that a subclass of a closed
TypedDict does not add keys, nor override the closed status.
Close the join of two closed TypedDicts, and treat a missing key in
a closed TypedDict as a `NotRequired[Never]` rather than the
`ReadOnly[NotRequired[object]]` of an open TypedDict.
The meet of a closed TypedDict and another TypedDict must be closed.
The implicit type of a missing key in a closed TypedDict is
`ReadOnly[NotRequired[Never]]`.
Narrowing a union already treats final TypedDicts as closed; use the
same logic if the closed keyword is used.
If a TypeVar with a TypedDict upper bound is encountered while
narrowing, narrow based on the upper bound.
Propagate closed when analysing a TypedDictType with a fallback. This
ensures subtype checks work correctly for a TypeVar with a closed
TypedDict upper bound.
Unpacking a TypedDict with an undeclared key to a TypedDict with that
key declared as not required is acceptable if the former is closed.
Unpacking an open TypedDict into a closed TypedDict is never safe.
@github-actions
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/types/trade.py:69: error: Overwriting TypedDict field "instanceid" while merging  [misc]
- steam/types/trade.py:69: error: Overwriting TypedDict field "classid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "assetid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "amount" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "appid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "contextid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "instanceid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "classid" while merging  [misc]
- steam/types/trade.py:112: error: Overwriting TypedDict field "missing" while merging  [misc]

altair (https://github.com/vega/altair)
+ altair/vegalite/v6/schema/_config.py:6593: error: Unused "type: ignore" comment  [unused-ignore]
+ altair/vegalite/v6/schema/_config.py:6618: error: Unused "type: ignore" comment  [unused-ignore]
+ altair/vegalite/v6/api.py:699: error: Unused "type: ignore" comment  [unused-ignore]
+ altair/vegalite/v6/api.py:725: error: Unused "type: ignore" comment  [unused-ignore]
+ altair/vegalite/v6/api.py:774: error: Unused "type: ignore" comment  [unused-ignore]

hydra-zen (https://github.com/mit-ll-responsible-ai/hydra-zen)
- src/hydra_zen/typing/_implementations.py:606: error: Overwriting TypedDict field "module" while extending  [misc]

discord.py (https://github.com/Rapptz/discord.py)
- discord/types/emoji.py:42: error: Overwriting TypedDict field "animated" while extending  [misc]
+ discord/types/scheduled_event.py:84: error: Field "user_count" is required in base class "_WithUserCount" but not in base class "StageInstanceScheduledEvent"  [misc]
+ discord/types/scheduled_event.py:87: error: Field "user_count" is required in base class "_WithUserCount" but not in base class "VoiceScheduledEvent"  [misc]
+ discord/types/scheduled_event.py:90: error: Field "user_count" is required in base class "_WithUserCount" but not in base class "ExternalScheduledEvent"  [misc]
+ discord/types/interactions.py:214: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:220: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:226: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:232: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:234: error: Field "id" is not required and not readonly in base class "ComponentBase"  [misc]
+ discord/types/interactions.py:239: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:241: error: Field "id" is not required and not readonly in base class "ComponentBase"  [misc]
+ discord/types/interactions.py:246: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:248: error: Field "id" is not required and not readonly in base class "ComponentBase"  [misc]
+ discord/types/interactions.py:268: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/interactions.py:273: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:54: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:59: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:87: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:92: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:97: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:102: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:107: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:113: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:125: error: Definition of field "type" incompatible with base class "SelectComponent"  [misc]
+ discord/types/components.py:133: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:139: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:156: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:169: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:174: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:182: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:188: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:195: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:202: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:210: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:220: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
+ discord/types/components.py:232: error: Definition of field "type" incompatible with base class "ComponentBase"  [misc]
- discord/types/scheduled_event.py:84: error: Overwriting TypedDict field "user_count" while merging  [misc]
- discord/types/scheduled_event.py:87: error: Overwriting TypedDict field "user_count" while merging  [misc]
- discord/types/scheduled_event.py:90: error: Overwriting TypedDict field "user_count" while merging  [misc]
- discord/types/interactions.py:214: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:220: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:226: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:232: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:234: error: Overwriting TypedDict field "id" while extending  [misc]
- discord/types/interactions.py:239: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:241: error: Overwriting TypedDict field "id" while extending  [misc]
- discord/types/interactions.py:246: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:248: error: Overwriting TypedDict field "id" while extending  [misc]
- discord/types/interactions.py:268: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/interactions.py:273: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/guild.py:142: error: Overwriting TypedDict field "stickers" while extending  [misc]
- discord/types/components.py:54: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:59: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:87: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:92: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:97: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:102: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:107: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:113: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:125: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:133: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:139: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:156: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:169: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:174: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:182: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:188: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:195: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:202: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:210: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:220: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/components.py:232: error: Overwriting TypedDict field "type" while extending  [misc]
- discord/types/gateway.py:180: error: Overwriting TypedDict field "newly_created" while extending  [misc]
- discord/channel.py:144: error: Overwriting TypedDict field "bitrate" while extending  [misc]
- discord/channel.py:145: error: Overwriting TypedDict field "user_limit" while extending  [misc]
- discord/channel.py:146: error: Overwriting TypedDict field "rtc_region" while extending  [misc]
- discord/channel.py:147: error: Overwriting TypedDict field "video_quality_mode" while extending  [misc]
- discord/channel.py:148: error: Overwriting TypedDict field "overwrites" while extending  [misc]
- discord/channel.py:151: error: Overwriting TypedDict field "topic" while extending  [misc]
- discord/channel.py:152: error: Overwriting TypedDict field "slowmode_delay" while extending  [misc]
- discord/channel.py:153: error: Overwriting TypedDict field "nsfw" while extending  [misc]
- discord/channel.py:154: error: Overwriting TypedDict field "overwrites" while extending  [misc]
- discord/channel.py:155: error: Overwriting TypedDict field "default_auto_archive_duration" while extending  [misc]
- discord/channel.py:156: error: Overwriting TypedDict field "default_thread_slowmode_delay" while extending  [misc]
+ discord/ext/commands/hybrid.py:61: error: Definition of field "description" incompatible with base class "_HybridCommandKwargs"  [misc]
+ discord/ext/commands/hybrid.py:69: error: Definition of field "description" incompatible with base class "_HybridCommandDecoratorKwargs"  [misc]
+ discord/ext/commands/hybrid.py:73: error: Definition of field "description" incompatible with base class "_HybridGroupKwargs"  [misc]
- discord/ext/commands/hybrid.py:61: error: Overwriting TypedDict field "description" while extending  [misc]
- discord/ext/commands/hybrid.py:64: error: Overwriting TypedDict field "with_app_command" while extending  [misc]
- discord/ext/commands/hybrid.py:65: error: Overwriting TypedDict field "guild_ids" while extending  [misc]
- discord/ext/commands/hybrid.py:66: error: Overwriting TypedDict field "guild_only" while extending  [misc]
- discord/ext/commands/hybrid.py:67: error: Overwriting TypedDict field "default_permissions" while extending  [misc]
- discord/ext/commands/hybrid.py:68: error: Overwriting TypedDict field "nsfw" while extending  [misc]
- discord/ext/commands/hybrid.py:69: error: Overwriting TypedDict field "description" while extending  [misc]
- discord/ext/commands/hybrid.py:73: error: Overwriting TypedDict field "description" while extending  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "max_messages" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "proxy" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "proxy_auth" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "shard_id" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "shard_count" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "application_id" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "member_cache_flags" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "chunk_guilds_at_startup" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "status" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "activity" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "allowed_mentions" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "heartbeat_timeout" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "guild_ready_timeout" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "assume_unsync_clock" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "enable_debug_events" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "enable_raw_presences" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "http_trace" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "max_ratelimit_timeout" while merging  [misc]
- discord/ext/commands/bot.py:96: error: Overwriting TypedDict field "connector" while merging  [misc]

pydantic (https://github.com/pydantic/pydantic)
- pydantic/fields.py:99: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict"  [call-arg]

bokeh (https://github.com/bokeh/bokeh)
+ src/bokeh/models/annotations/html/labels.pyi:78: error: INTERNAL ERROR -- Please try using mypy master on GitHub:
+ https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
+ Please report a bug at https://github.com/python/mypy/issues
+ version: 2.1.0+dev.a9cbc85274b37cc8a6bc7bef4885d624ef8c622d
+ src/bokeh/models/annotations/html/labels.pyi:78: note: use --pdb to drop into pdb
- src/bokeh/util/serialization.py: note: In function "convert_datetime_type":
- src/bokeh/util/serialization.py:171:1: error: Argument 1 to "convert_datetime_type" becomes "Any | Any | Any | datetime | date | time | datetime64[date | int | None]" due to an unfollowed import  [no-any-unimported]
- src/bokeh/util/serialization.py: note: In function "transform_series":
- src/bokeh/util/serialization.py:373:1: error: Argument 1 to "transform_series" becomes "Any | Any | Any" due to an unfollowed import  [no-any-unimported]
- src/bokeh/io/export.py: note: In function "get_screenshot_as_png":
- src/bokeh/io/export.py:221:1: error: Return type becomes "Any" due to an unfollowed import  [no-any-unimported]
- src/bokeh/core/property/data_frame.py: note: In class "EagerDataFrame":
- src/bokeh/core/property/data_frame.py:50:22: error: Base type becomes "Property[Any]" due to an unfollowed import  [no-any-unimported]
- src/bokeh/core/property/data_frame.py: note: At top level:
- src/bokeh/core/property/data_frame.py: note: In class "EagerSeries":
- src/bokeh/core/property/data_frame.py:68:19: error: Base type becomes "Property[Any]" due to an unfollowed import  [no-any-unimported]
- src/bokeh/core/property/data_frame.py: note: In class "PandasDataFrame":
- src/bokeh/core/property/data_frame.py:86:23: error: Base type becomes "Property[Any]" due to an unfollowed import  [no-any-unimported]
- src/bokeh/core/property/data_frame.py: note: In class "PandasGroupBy":
- src/bokeh/core/property/data_frame.py:109:21: error: Base type becomes "Property[Any]" due to an unfollowed import  [no-any-unimported]
+ Traceback (most recent call last):
+   File "", line 6, in <module>
+     sys.exit(console_entry())
+   File "/__main__.py", line 16, in console_entry
+     main()
+   File "/main.py", line 150, in main
+     res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
+   File "/main.py", line 240, in run_build
+     res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
+   File "/build.py", line 422, in build
+     result = build_inner(
+   File "/build.py", line 532, in build_inner
+     graph = dispatch(sources, manager, stdout, connect_threads)
+   File "/build.py", line 4145, in dispatch
+     process_graph(graph, manager)
+   File "/build.py", line 4613, in process_graph
+     done, still_working, results = manager.wait_for_done(graph)
+   File "/build.py", line 1487, in wait_for_done
+     process_stale_scc(graph, next_scc, self)
+   File "/build.py", line 4780, in process_stale_scc
+     mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors)
+   File "/semanal_main.py", line 91, in semantic_analysis_for_scc
+     process_top_levels(graph, scc, patches)
+   File "/semanal_main.py", line 200, in process_top_levels
+     deferred, incomplete, progress = semantic_analyze_target(
+   File "/semanal_main.py", line 380, in semantic_analyze_target
+     analyzer.refresh_partial(
+   File "/semanal.py", line 701, in refresh_partial
+     self.refresh_top_level(node)
+   File "/semanal.py", line 728, in refresh_top_level
+     self.accept(d)
+   File "/semanal.py", line 7705, in accept
+     node.accept(self)
+     ~~~~~~~~~~~^^^^^^
+   File "/nodes.py", line 1721, in accept
+     return visitor.visit_class_def(self)
+            ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/semanal.py", line 1840, in visit_class_def
+     self.analyze_class(defn)
+     ~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/semanal.py", line 2023, in analyze_class
+     if self.analyze_typeddict_classdef(defn):
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/semanal.py", line 2115, in analyze_typeddict_classdef
+     is_typeddict, info = self.typed_dict_analyzer.analyze_typeddict_classdef(defn)
+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/semanal_typeddict.py", line 185, in analyze_typeddict_classdef
+     field_types, required_keys, readonly_keys, is_closed = self.resolve_field_inheritance(
+                                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
+         bases_info, new_field_sources, is_closed, defn
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     )
+     ^
+   File "/semanal_typeddict.py", line 395, in resolve_field_inheritance
+     self.verify_compatibility(
+     ~~~~~~~~~~~~~~~~~~~~~~~~~^
+         field_name,
+         ^^^^^^^^^^^
+     ...<5 lines>...
+         primary_source.child_field_ctx or ctx,
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     )
+     ^
+   File "/semanal_typeddict.py", line 285, in verify_compatibility
+     is_type_compatible = is_equivalent(field_type, source.field_type)
+   File "/subtypes.py", line 240, in is_equivalent
+     return is_subtype(
+            ~~~~~~~~~~^
+         a,
+         ^^
+     ...<4 lines>...
+         subtype_context=subtype_context,
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     ) and is_subtype(
+     ^
+   File "/subtypes.py", line 189, in is_subtype
+     return _is_subtype(left, right, subtype_context, proper_subtype=False)
+   File "/subtypes.py", line 363, in _is_subtype
+     return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
+            ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   File "/types.py", line 1673, in accept
+     return visitor.visit_instance(self)
+            ~~~~~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/subtypes.py", line 599, in visit_instance
+     if not check_type_parameter(
+            ~~~~~~~~~~~~~~~~~~~~^
+         lefta, righta, variance, self.proper_subtype, self.subtype_context
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     ):
+     ^
+   File "/subtypes.py", line 397, in check_type_parameter
+     return is_equivalent(left, right, subtype_context=subtype_context)
+   File "/subtypes.py", line 240, in is_equivalent
+     return is_subtype(
+            ~~~~~~~~~~^
+         a,
+         ^^
+     ...<4 lines>...
+         subtype_context=subtype_context,
+         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     ) and is_subtype(
+     ^
+   File "/subtypes.py", line 189, in is_subtype
+     return _is_subtype(left, right, subtype_context, proper_subtype=False)
+   File "/subtypes.py", line 363, in _is_subtype
+     return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
+            ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   File "/types.py", line 3393, in accept
+     return visitor.visit_union_type(self)
+            ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
+   File "/subtypes.py", line 1102, in visit_union_type
+     if not self._is_subtype(item, self.orig_right):
+            ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
+   File "/subtypes.py", line 435, in _is_subtype
+     return is_subtype(left, right, subtype_context=self.subtype_context)
+   File "/subtypes.py", line 189, in is_subtype
+     return _is_subtype(left, right, subtype_context, proper_subtype=False)
+   File "/subtypes.py", line 325, in _is_subtype
+     is_subtype_of_item = any(
+                          ^^^
+   File "/subtypes.py", line 326, in <genexpr>
+     is_subtype(orig_left, item, subtype_context=subtype_context)
+     ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   File "/subtypes.py", line 189, in is_subtype
+     return _is_subtype(left, right, subtype_context, proper_subtype=False)
+   File "/subtypes.py", line 363, in _is_subtype
+     return left.accept(SubtypeVisitor(orig_right, subtype_context, proper_subtype))
+            ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   File "/types.py", line 3622, in accept
+     assert isinstance(visitor, SyntheticTypeVisitor)
+            ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ AssertionError
+ 

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.

Cannot combine special types in alternative TypedDict syntax TypedDict does not support type refinement in subclasses

1 participant