← Back to index

annotations_forward_refs.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 17
FP: 4
FN: 2
Optional: 3 / 3
1"""
2Tests the handling of forward references in type annotations.
3"""
4
5# > When a type hint contains names that have not been defined yet, that
6# > definition may be expressed as a string literal, to be resolved later.
7
8
9import types
10from typing import assert_type
13def func1(
14 p1: "ClassA", p2: "list[ClassA]", p3: list["ClassA"], p4: list["int | ClassA"]
15) -> None:
16 assert_type(p1, ClassA)
17 assert_type(p2, list[ClassA])
18 assert_type(p3, list[ClassA])
19 assert_type(p4, list[ClassA | int])
22bad1: ClassA # E?: Runtime error prior to 3.14: requires quotes
[unresolved-reference] Name `ClassA` used when not defined
23bad2: list[ClassA] # E?: Runtime error prior to 3.14: requires quotes
[unresolved-reference] Name `ClassA` used when not defined
24bad3: "ClassA" | int # E: Runtime error
Expected a ty diagnostic for this line
25bad4: int | "ClassA" # E: Runtime error
Expected a ty diagnostic for this line
28class ClassA:
29 ...
32# > The string literal should contain a valid Python expression
33# > should be a valid code object).
35var1 = 1
38# The following should all generate errors because they are not legal type
39# expressions, despite being enclosed in quotes.
40def invalid_annotations(
41 p1: "eval(''.join(map(chr, [105, 110, 116])))", # E
[invalid-type-form] Function calls are not allowed in type expressions
42 p2: "[int, str]", # E
[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
43 p3: "(int, str)", # E
[invalid-type-form] Tuple literals are not allowed in this context in a type expression: Did you mean `tuple[int, str]`?
44 p4: "[int for i in range(1)]", # E
[invalid-type-form] List comprehensions are not allowed in type expressions
45 p5: "{}", # E
[invalid-type-form] Dict literals are not allowed in type expressions
46 p6: "(lambda : int)()", # E
[invalid-type-form] Function calls are not allowed in type expressions
47 p7: "[int][0]", # E
[invalid-type-form] Invalid subscript of object of type `list[Unknown | <class 'int'>]` in type expression [invalid-type-form] Int literals are not allowed in this context in a type expression
48 p8: "int if 1 < 3 else str", # E
[invalid-type-form] `if` expressions are not allowed in type expressions
49 p9: "var1", # E
[invalid-type-form] Variable of type `Literal[1]` is not allowed in a type expression
50 p10: "True", # E
[invalid-type-form] Boolean literals are not allowed in this context in a type expression
51 p11: "1", # E
[invalid-type-form] Int literals are not allowed in this context in a type expression
52 p12: "-1", # E
[invalid-type-form] Unary operations are not allowed in type expressions
53 p13: "int or str", # E
[invalid-type-form] Boolean operations are not allowed in type expressions
54 p14: 'f"int"', # E
[fstring-type-annotation] Type expressions cannot use f-strings
55 p15: "types", # E
[invalid-type-form] Module `types` is not valid in a type expression
56):
57 pass
60# > It should evaluate without errors once the module has been fully loaded.
61# > The local and global namespace in which it is evaluated should be the same
62# > namespaces in which default arguments to the same function would be evaluated.
65class ClassB:
66 def method1(self) -> ClassB: # E?: Runtime error prior to 3.14
[unresolved-reference] Name `ClassB` used when not defined
67 return ClassB()
69 def method2(self) -> "ClassB": # OK
70 return ClassB()
73class ClassC:
74 ...
77class ClassD:
78 ClassC: "ClassC" # OK
80 ClassF: "ClassF" # E: circular reference
[unresolved-reference] Name `ClassF` used when not defined
82 str: "str" = "" # OK
Unexpected error [invalid-type-form] Variable of type `Literal[""]` is not allowed in a type expression
84 def int(self) -> None: # OK
85 ...
87 x: "int" = 0 # OK
Unexpected error [invalid-type-form] Function `int` is not valid in a type expression
89 y: int = 0 # E: Refers to local int, which isn't a legal type expression
[invalid-type-form] Function `int` is not valid in a type expression
91 def __init__(self) -> None:
92 self.ClassC = ClassC()
95assert_type(ClassD.str, str)
Unexpected error [type-assertion-failure] Type `str` does not match asserted type `Unknown`
96assert_type(ClassD.x, int)
Unexpected error [type-assertion-failure] Type `int` does not match asserted type `Unknown`
99# > If a triple quote is used, the string should be parsed as though it is implicitly
100# > surrounded by parentheses. This allows newline characters to be
101# > used within the string literal.
103value: """
104 int |
105 str |
106 list[int]
107"""