Skip to content

Commit 5b2d3b3

Browse files
ilevkivskyigvanrossum
authored andcommitted
Allow generics in runtime expressions (#2302)
As discussed in python/typing#303 generics in ``typing.py`` are now cached and are therefore much faster at runtime than before. Here I propose (and it looks like Guido is not against this) to allow generic types as runtime expressions.
1 parent 68c6e96 commit 5b2d3b3

File tree

3 files changed

+66
-18
lines changed

3 files changed

+66
-18
lines changed

mypy/checkexpr.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,8 +1373,8 @@ def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type:
13731373

13741374
def visit_type_application(self, tapp: TypeApplication) -> Type:
13751375
"""Type check a type application (expr[type, ...])."""
1376-
self.chk.fail(messages.GENERIC_TYPE_NOT_VALID_AS_EXPRESSION, tapp)
1377-
return AnyType()
1376+
tp = cast(CallableType, self.accept(tapp.expr))
1377+
return self.apply_generic_arguments(tp, tapp.types, tapp)
13781378

13791379
def visit_type_alias_expr(self, alias: TypeAliasExpr) -> Type:
13801380
return AnyType()

mypy/messages.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@
7777
READ_ONLY_PROPERTY_OVERRIDES_READ_WRITE = \
7878
'Read-only property cannot override read-write property'
7979
FORMAT_REQUIRES_MAPPING = 'Format requires a mapping'
80-
GENERIC_TYPE_NOT_VALID_AS_EXPRESSION = \
81-
"Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)"
8280
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT = "Cannot use a contravariant type variable as return type"
8381
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT = "Cannot use a covariant type variable as a parameter"
8482
INCOMPATIBLE_IMPORT_OF = "Incompatible import of"
@@ -681,7 +679,7 @@ def incompatible_type_application(self, expected_arg_count: int,
681679
actual_arg_count: int,
682680
context: Context) -> None:
683681
if expected_arg_count == 0:
684-
self.fail('Type application targets a non-generic function',
682+
self.fail('Type application targets a non-generic function or class',
685683
context)
686684
elif actual_arg_count > expected_arg_count:
687685
self.fail('Type application has too many types ({} expected)'

test-data/unit/check-generics.test

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -422,33 +422,83 @@ main: note: In member "f" of class "A":
422422
-- ----------------------------
423423

424424

425-
[case testInvalidTypeApplicationTarget]
425+
[case testTypeApplicationArgs]
426426
from typing import TypeVar, Generic
427427
T = TypeVar('T')
428-
class A(Generic[T]): pass
429-
A[A]() # E: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
430-
A[int, str]() # E: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
428+
class Node(Generic[T]):
429+
def __init__(self, x: T) -> None:
430+
...
431+
Node[int]() # E: Too few arguments for "Node"
432+
Node[int](1, 1, 1) # E: Too many arguments for "Node"
431433
[out]
432434

433-
[case testInvalidTypeApplicationTarget2]
435+
[case testTypeApplicationTvars]
434436
from typing import TypeVar, Generic
435437
T = TypeVar('T')
436438
S = TypeVar('S')
437439
class A(Generic[T, S]): pass
438-
A[int, str]() # E: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
439-
A[int]() # E: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
440+
A[int]() # E: Type application has too few types (2 expected)
441+
A[int, str, int]() # E: Type application has too many types (2 expected)
440442
[out]
441443

442-
[case testInvalidTypeApplicationTarget3]
443-
444+
[case testInvalidTypeApplicationType]
444445
a = None # type: A
445446
class A: pass
446-
a[A]() # Fail
447-
A[A]() # Fail
447+
a[A]() # E: Value of type "A" is not indexable
448+
A[A]() # E: Type application targets a non-generic function or class
448449
[out]
449-
main:4: error: Value of type "A" is not indexable
450-
main:5: error: Generic type is prohibited as a runtime expression (use a type alias or '# type:' comment)
451450

451+
[case testTypeApplicationArgTypes]
452+
from typing import TypeVar, Generic
453+
T = TypeVar('T')
454+
class Node(Generic[T]):
455+
def __init__(self, x: T) -> None:
456+
...
457+
458+
Node[int](1)
459+
Node[int]('a') # E: Argument 1 to "Node" has incompatible type "str"; expected "int"
460+
461+
class Dummy(Generic[T]):
462+
def meth(self, x: T) -> None:
463+
...
464+
def methout(self) -> T:
465+
...
466+
467+
Dummy[int]().meth(1)
468+
Dummy[int]().meth('a') # E: Argument 1 to "meth" of "Dummy" has incompatible type "str"; expected "int"
469+
reveal_type(Dummy[int]()) # E: Revealed type is '__main__.Dummy[builtins.int*]'
470+
reveal_type(Dummy[int]().methout()) # E: Revealed type is 'builtins.int*'
471+
[out]
472+
473+
[case testTypeApplicationArgTypesSubclasses]
474+
from typing import TypeVar, Generic
475+
T = TypeVar('T')
476+
S = TypeVar('S')
477+
class C(Generic[T, S]):
478+
def __init__(self, x: T, y: S) -> None:
479+
...
480+
481+
class D(C[int, T], Generic[T]): ...
482+
483+
D[str](1, 'a')
484+
D[str](1, 1) # E: Argument 2 to "D" has incompatible type "int"; expected "str"
485+
486+
class E(D[str]): ...
487+
E(1, 'a')
488+
E(1, 1) # E: Argument 2 to "E" has incompatible type "int"; expected "str"
489+
[out]
490+
491+
[case testTypeApplicationAlias]
492+
from typing import TypeVar, Generic
493+
T = TypeVar('T')
494+
class Node(Generic[T]):
495+
def __init__(self, x: T) -> None:
496+
...
497+
498+
Alias = Node
499+
Alias[int](1)
500+
Alias[int]("a") # E: Argument 1 to "Node" has incompatible type "str"; expected "int"
501+
[out]
452502

453503
-- Multiple assignment with lists
454504
-- ------------------------------

0 commit comments

Comments
 (0)