Skip to content

Commit fbdca67

Browse files
Josverlstinos
authored andcommitted
tests/extmod: Add tests for typing PEPs.
PEP 484 Type Hints Python 3.5 PEP 526 Syntax for Variable Annotations Python 3.6 PEP 544 Protocols: Structural subtyping (static duck typing) Python 3.8 PEP 560 Core support for typing module and generic types Python 3.7 PEP 586 Literal Types Python 3.8 PEP 589 TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys Python 3.8 PEP 591 Adding a final qualifier to typing Python 3.8 Signed-off-by: Jos Verlinde <[email protected]>
1 parent 5734680 commit fbdca67

File tree

7 files changed

+1400
-0
lines changed

7 files changed

+1400
-0
lines changed

tests/extmod/typing_pep_0484.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
try:
2+
from typing import TYPE_CHECKING
3+
except ImportError:
4+
print("SKIP")
5+
raise SystemExit
6+
7+
print("# Python 3.5")
8+
print("### PEP 484")
9+
10+
# https://peps.python.org/topic/typing/
11+
# https://peps.python.org/pep-0484/
12+
13+
# Currently excludes tests using `Generic[T]` due to MicroPython runtime limitations
14+
15+
16+
print("Running PEP 484 example-based test")
17+
18+
19+
print("Type Definition Syntax")
20+
21+
22+
def greeting(name: str) -> str:
23+
return "Hello " + name
24+
25+
26+
print("greeting:", greeting("world"))
27+
28+
from typing import List
29+
30+
l: List[int]
31+
32+
33+
print("Type aliases")
34+
35+
Url = str
36+
37+
38+
def retry(url: Url, retry_count: int) -> None:
39+
print("retry", url, retry_count)
40+
41+
42+
retry("http://example", 3)
43+
44+
45+
print("Callable example")
46+
from typing import Callable, TypeVar, Union
47+
48+
49+
def feeder(get_next_item: Callable[[], str]) -> None:
50+
try:
51+
v = get_next_item()
52+
print("feeder got", v)
53+
except Exception as e:
54+
print("feeder runtime exception:", e)
55+
56+
57+
def get_const():
58+
return "x"
59+
60+
61+
feeder(get_const)
62+
63+
64+
print("TypeVar constrained example")
65+
AnyStr = TypeVar("AnyStr", str, bytes)
66+
67+
68+
def concat(x: AnyStr, y: AnyStr) -> AnyStr:
69+
return x + y
70+
71+
72+
print("concat:", concat("a", "b"))
73+
74+
# Generic user-defined class
75+
from typing import Generic, TypeVar
76+
77+
T = TypeVar("T")
78+
# FIXME: Crash - inheriting from typing.Generic[T] unsupported at runtime
79+
# try:
80+
#
81+
# class LoggedVar(Generic[T]):
82+
# pass
83+
#
84+
# def __init__(self, value: T, name: str) -> None:
85+
# self.name = name
86+
# self.value = value
87+
#
88+
# def set(self, new: T) -> None:
89+
# self.value = new
90+
#
91+
# def get(self) -> T:
92+
# return self.value
93+
#
94+
# except Exception as e:
95+
# print("-[ ] FIXME: Difference - Generic[T] base class unsupported:", e)
96+
97+
98+
# Union/Optional examples
99+
def handle_employee(e: Union[str, None]) -> None:
100+
print("handle_employee called with", e)
101+
102+
103+
handle_employee("John")
104+
handle_employee(None)
105+
106+
107+
# Any example
108+
def use_map(m: dict) -> None:
109+
print("use_map keys:", list(m.keys()))
110+
111+
112+
use_map({"a": 1})
113+
114+
# NewType example: at runtime NewType returns identity function
115+
try:
116+
from typing import NewType
117+
118+
UserId = NewType("UserId", int)
119+
v = UserId(5)
120+
print("NewType UserId runtime:", v, type(v))
121+
except Exception as e:
122+
print("-[ ] FIXME: Difference or Crash - NewType runtime issue:", e)
123+
124+
print("TYPE_CHECKING guard")
125+
126+
from typing import TYPE_CHECKING
127+
128+
# TYPE_CHECKING guard
129+
if TYPE_CHECKING:
130+
# This block is for type checkers only
131+
pass
132+
print("typing.TYPE_CHECKING is True at runtime. ERROR")
133+
else:
134+
print("typing.TYPE_CHECKING is False at runtime as expected")
135+
136+
137+
print("Forward reference example")
138+
139+
140+
class Tree:
141+
def __init__(self, left: "Tree" = None, right: "Tree" = None): # type: ignore
142+
self.left = left
143+
self.right = right
144+
145+
146+
tr = Tree()
147+
print("Tree forward refs OK")
148+
149+
# NoReturn example
150+
from typing import NoReturn
151+
152+
153+
def stop() -> NoReturn:
154+
raise RuntimeError("stop")
155+
156+
157+
try:
158+
stop()
159+
except RuntimeError:
160+
print("stop() raised RuntimeError as expected (NoReturn at runtime)")
161+
162+
# Overload example (runtime @overload should not be called directly)
163+
from typing import overload
164+
165+
166+
@overload
167+
def func(x: int) -> int:
168+
...
169+
170+
171+
@overload
172+
def func(x: str) -> str:
173+
...
174+
175+
176+
def func(x):
177+
return x
178+
179+
180+
print("overload func for int:", func(1))
181+
182+
# Cast example: at runtime cast returns the value
183+
from typing import cast
184+
185+
print("cast runtime identity:", cast(str, 123))
186+
187+
print("-----")

tests/extmod/typing_pep_0526.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
try:
2+
from typing import TYPE_CHECKING
3+
except Exception:
4+
print("SKIP")
5+
raise SystemExit
6+
7+
print("# Python 3.6")
8+
print("### PEP 526 - Syntax for variable annotations")
9+
10+
# https://peps.python.org/pep-0526/
11+
# Currently excludes tests using `Generic[T]` due to MicroPython runtime limitations
12+
13+
14+
print("Specification")
15+
16+
my_var: int
17+
my_var = 5 # Passes type check.
18+
other_var: int = "a" # Flagged as error by type checker, # type: ignore
19+
# but OK at runtime.
20+
21+
22+
print("Global and local variable annotations")
23+
from typing import List, Tuple, Optional
24+
25+
some_number: int # variable without initial value
26+
some_list: List[int] = [] # variable with initial value
27+
28+
29+
sane_world: bool
30+
if 2 + 2 == 4:
31+
sane_world = True
32+
else:
33+
sane_world = False
34+
35+
# Tuple packing with variable annotation syntax
36+
t: Tuple[int, ...] = (1, 2, 3)
37+
# or
38+
t: Tuple[int, ...] = 1, 2, 3 # This only works in Python 3.8+
39+
40+
# Tuple unpacking with variable annotation syntax
41+
header: str
42+
kind: int
43+
body: Optional[List[str]]
44+
45+
a: int # type: ignore
46+
try:
47+
print(a) # raises NameError # type: ignore
48+
49+
except NameError:
50+
print("Expected NameError")
51+
52+
53+
def f_1():
54+
a: int
55+
try:
56+
print(a) # raises UnboundLocalError # type: ignore
57+
except UnboundLocalError:
58+
print("Expected UnboundLocalError")
59+
60+
61+
a: int # type: ignore
62+
a: str # Static type checker may or may not warn about this.
63+
64+
65+
print("Class and instance variable annotations")
66+
from typing import ClassVar, Dict
67+
68+
69+
class BasicStarship:
70+
captain: str = "Picard" # instance variable with default
71+
damage: int # instance variable without default
72+
stats: ClassVar[Dict[str, int]] = {} # class variable
73+
74+
75+
class Starship_1:
76+
captain = "Picard"
77+
stats = {}
78+
79+
def __init__(self, damage, captain=None):
80+
self.damage = damage
81+
if captain:
82+
self.captain = captain # Else keep the default
83+
84+
def hit(self):
85+
Starship.stats["hits"] = Starship.stats.get("hits", 0) + 1
86+
87+
88+
class Starship:
89+
captain: str = "Picard"
90+
damage: int
91+
stats: ClassVar[Dict[str, int]] = {}
92+
93+
def __init__(self, damage: int, captain: str = None): # type: ignore
94+
self.damage = damage
95+
if captain:
96+
self.captain = captain # Else keep the default
97+
98+
def hit(self):
99+
Starship.stats["hits"] = Starship.stats.get("hits", 0) + 1
100+
101+
102+
enterprise_d = Starship(3000)
103+
enterprise_d.stats = {} # Flagged as error by a type checker # type: ignore
104+
Starship.stats = {} # This is OK
105+
106+
107+
# FIXME: - cpy_diff - User Defined Generic Classes unsupported
108+
# from typing import Generic, TypeVar
109+
# T = TypeVar("T")
110+
# class Box(Generic[T]):
111+
# def __init__(self, content):
112+
# self.content: T = content
113+
114+
115+
print("Annotating expressions")
116+
117+
118+
class Cls:
119+
pass
120+
121+
122+
c = Cls()
123+
c.x: int = 0 # Annotates c.x with int. # type: ignore
124+
c.y: int # Annotates c.y with int.# type: ignore
125+
126+
d = {}
127+
d["a"]: int = 0 # Annotates d['a'] with int.# type: ignore
128+
d["b"]: int # Annotates d['b'] with int.# type: ignore
129+
130+
(x): int # Annotates x with int, (x) treated as expression by compiler.# type: ignore
131+
(y): int = 0 # Same situation here.
132+
133+
134+
# print("Where annotations aren’t allowed")
135+
# The Examples crash both CPython and MicroPython at runtime.
136+
137+
print("Runtime Effects of Type Annotations")
138+
139+
140+
def f():
141+
x: NonexistentName # No RUNTIME error. # type: ignore
142+
143+
144+
# FIXME: cpy_diff - MicroPython does not raise NameError at runtime
145+
# try:
146+
# x: NonexistentName # Error!
147+
# print("-[ ] FIXME: Expected NameError")
148+
# except NameError:
149+
# print("Expected NameError:")
150+
151+
# try:
152+
153+
# class X:
154+
# var: NonexistentName # Error!
155+
# except NameError:
156+
# print("Expected NameError:")
157+
158+
159+
# FIXME: cpy_diff - MicroPython does not provide the ``__annotations__`` dict at runtime
160+
# print(__annotations__)
161+
# __annotations__["s"] = str
162+
163+
164+
alice: "well done" = "A+" # type: ignore
165+
bob: "what a shame" = "F-" # type: ignore
166+
167+
print("-----")

0 commit comments

Comments
 (0)