Skip to content

Basic support for typing_extensions.Annotated. #7292

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

Merged
merged 2 commits into from
Aug 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions mypy/newsemanal/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'typing.Union',
'typing.Literal',
'typing_extensions.Literal',
'typing_extensions.Annotated',
} # type: Final

ARG_KINDS_BY_CONSTRUCTOR = {
Expand Down Expand Up @@ -302,6 +303,12 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt
return UninhabitedType(is_noreturn=True)
elif fullname in ('typing_extensions.Literal', 'typing.Literal'):
return self.analyze_literal_type(t)
elif fullname == 'typing_extensions.Annotated':
if len(t.args) < 2:
self.fail("Annotated[...] must have exactly one type argument"
" and at least one annotation", t)
return AnyType(TypeOfAny.from_error)
return self.anal_type(t.args[0])
return None

def get_omitted_any(self, typ: Type, fullname: Optional[str] = None) -> AnyType:
Expand Down
1 change: 1 addition & 0 deletions mypy/test/testcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'check-newsemanal.test',
'check-inline-config.test',
'check-reports.test',
'check-annotated.test',
]

# Tests that use Python 3.8-only AST features (like expression-scoped ignores):
Expand Down
7 changes: 7 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
'typing.Union',
'typing.Literal',
'typing_extensions.Literal',
'typing_extensions.Annotated',
} # type: Final

ARG_KINDS_BY_CONSTRUCTOR = {
Expand Down Expand Up @@ -312,6 +313,12 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Opt
return UninhabitedType(is_noreturn=True)
elif fullname in ('typing_extensions.Literal', 'typing.Literal'):
return self.analyze_literal_type(t)
elif fullname == 'typing_extensions.Annotated':
if len(t.args) < 2:
self.fail("Annotated[...] must have exactly one type argument"
" and at least one annotation", t)
return AnyType(TypeOfAny.from_error)
return self.anal_type(t.args[0])
return None

def analyze_unbound_type_with_type_info(self, t: UnboundType, info: TypeInfo) -> Type:
Expand Down
65 changes: 65 additions & 0 deletions test-data/unit/check-annotated.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[case testAnnotated0]
from typing_extensions import Annotated
x: Annotated[int, ...]
reveal_type(x) # N: Revealed type is 'builtins.int'

[case testAnnotated1]
from typing import Union
from typing_extensions import Annotated
x: Annotated[Union[int, str], ...]
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str]'

[case testAnnotated2]
from typing_extensions import Annotated
x: Annotated[int, THESE, ARE, IGNORED, FOR, NOW]
reveal_type(x) # N: Revealed type is 'builtins.int'

[case testAnnotated3]
from typing_extensions import Annotated
x: Annotated[int, -+~12.3, "som"[e], more(anno+a+ions, that=[are]), (b"ignored",), 4, N.O.W, ...]
reveal_type(x) # N: Revealed type is 'builtins.int'

[case testAnnotatedBadType]
from typing_extensions import Annotated
x: Annotated[XXX, ...] # E: Name 'XXX' is not defined
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedBadNoArgs]
from typing_extensions import Annotated
x: Annotated # E: Annotated[...] must have exactly one type argument and at least one annotation
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedBadOneArg]
from typing_extensions import Annotated
x: Annotated[int] # E: Annotated[...] must have exactly one type argument and at least one annotation
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedNested0]
from typing_extensions import Annotated
x: Annotated[Annotated[int, ...], ...]
reveal_type(x) # N: Revealed type is 'builtins.int'

[case testAnnotatedNested1]
from typing import Union
from typing_extensions import Annotated
x: Annotated[Annotated[Union[int, str], ...], ...]
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str]'

[case testAnnotatedNestedBadType]
from typing_extensions import Annotated
x: Annotated[Annotated[XXX, ...], ...] # E: Name 'XXX' is not defined
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedNestedBadNoArgs]
from typing_extensions import Annotated
x: Annotated[Annotated, ...] # E: Annotated[...] must have exactly one type argument and at least one annotation
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedNestedBadOneArg]
from typing_extensions import Annotated
x: Annotated[Annotated[int], ...] # E: Annotated[...] must have exactly one type argument and at least one annotation
reveal_type(x) # N: Revealed type is 'Any'

[case testAnnotatedNoImport]
x: Annotated[int, ...] # E: Name 'Annotated' is not defined
reveal_type(x) # N: Revealed type is 'Any'
2 changes: 2 additions & 0 deletions test-data/unit/lib-stub/typing_extensions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def final(x: _T) -> _T: pass

Literal: _SpecialForm = ...

Annotated: _SpecialForm = ...


# Fallback type for all typed dicts (does not exist at runtime).
class _TypedDict(Mapping[str, object]):
Expand Down