Skip to content

Commit ebebed2

Browse files
authored
Merge branch 'builtintypingmodule' into builtintypingmodule
2 parents 83c1a6f + c99a336 commit ebebed2

File tree

1 file changed

+242
-0
lines changed

1 file changed

+242
-0
lines changed

tests/extmod/typing_pep_0589.py

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
try:
2+
import typing
3+
except ImportError:
4+
print("SKIP")
5+
raise SystemExit
6+
7+
print("# Python 3.8")
8+
print("### PEP 589")
9+
10+
# https://peps.python.org/topic/typing/
11+
# https://peps.python.org/pep-0589/
12+
# https://typing.python.org/en/latest/spec/typeddict.html#typeddict
13+
14+
15+
print("Class-based Syntax")
16+
17+
from typing import TypedDict
18+
from typing import NotRequired, ReadOnly, Annotated
19+
20+
21+
class Movie(TypedDict):
22+
name: str
23+
year: int
24+
25+
26+
class EmptyDict(TypedDict):
27+
pass
28+
29+
30+
# ------------------------------------------------------------------------
31+
print("Using TypedDict Types")
32+
33+
movie: Movie = {"name": "Blade Runner", "year": 1982}
34+
35+
36+
def record_movie(movie: Movie) -> None:
37+
...
38+
39+
40+
record_movie({"name": "Blade Runner", "year": 1982})
41+
42+
movie: Movie
43+
movie = {"name": "Blade Runner", "year": 1982}
44+
45+
# ------------------------------------------------------------------------
46+
print("Totality and optional keys")
47+
48+
try:
49+
# FIXME cpy_diff: runtime typing does not accept 'total' argument
50+
class MovieTotal(TypedDict, total=True):
51+
name: str
52+
year: int
53+
except TypeError as e:
54+
print(
55+
"-[ ] FIXME: cpy_diff : `total` parameter not supported by runtime TypedDict implementation:",
56+
e,
57+
)
58+
59+
try:
60+
61+
class MoviePartial(TypedDict, total=False):
62+
name: str
63+
year: int
64+
except TypeError as e:
65+
print(
66+
"-[ ] FIXME: cpy_diff : `total` parameter not supported by runtime TypedDict implementation:",
67+
e,
68+
)
69+
70+
71+
mt: MovieTotal = {"name": "Alien", "year": 1979} # type : ignore
72+
mp: MoviePartial = {"name": "Alien"} # year is optional # type : ignore
73+
74+
assert mt["year"] == 1979
75+
assert "year" not in mp or isinstance(mp.get("year"), (int, type(None)))
76+
77+
# ------------------------------------------------------------------------
78+
print("Inheritance and required/optional mix")
79+
80+
81+
class Point2D(TypedDict):
82+
x: int
83+
y: int
84+
85+
86+
try:
87+
88+
class Point3D(Point2D, total=False):
89+
z: int
90+
except TypeError as e:
91+
print(
92+
"FIXME: cpy_diff : `total` parameter not supported by runtime TypedDict implementation for Point3D:",
93+
e,
94+
)
95+
96+
97+
p2: Point2D = {"x": 1, "y": 2}
98+
p3: Point3D = {"x": 1, "y": 2}
99+
assert p2["x"] == 1
100+
assert "z" not in p3
101+
102+
print("Runtime checks: TypedDict cannot be used with isinstance/class checks")
103+
try:
104+
if isinstance(movie, Movie): # type: ignore
105+
pass
106+
print("-[ ] FIXME: TypedDict class allowed in isinstance (unexpected)")
107+
except TypeError:
108+
print("TypedDict class not allowed for isinstance/class checks")
109+
110+
print("Alternative functional syntax and constructors")
111+
112+
MovieAlt = TypedDict("MovieAlt", {"name": str, "year": int})
113+
MovieAlt2 = TypedDict("MovieAlt2", {"name": str, "year": int}, total=False)
114+
115+
m_alt: MovieAlt = {"name": "Blade Runner", "year": 1982}
116+
117+
# FIXME: Difference or Crash - calling the functional TypedDict constructor with kwargs
118+
try:
119+
ma = MovieAlt(name="Blade Runner", year=1982)
120+
print(type(ma)) # should be dict at runtime
121+
except TypeError as e:
122+
print(
123+
"-[ ] FIXME: cpy_diff Functional TypedDict constructor call failed at runtime (expected):",
124+
e,
125+
)
126+
127+
128+
print("Inheritance examples")
129+
130+
try:
131+
132+
class BookBasedMovie(Movie):
133+
based_on: str
134+
except TypeError as e:
135+
print(
136+
"Inheritance from TypedDicts not supported by runtime implementation for BookBasedMovie:",
137+
e,
138+
)
139+
140+
# KNOWN limitation - no multiple inheritance in MicroPython
141+
# class X(TypedDict):
142+
# x: int
143+
# class Y(TypedDict):
144+
# y: str
145+
# try:
146+
# class XYZ(X, Y):
147+
# z: bool
148+
# xyz: XYZ = {"x": 1, "y": "a", "z": True}
149+
# except TypeError as e:
150+
# print("Multiple inheritance for TypedDicts not supported at runtime (XYZ):", e)
151+
152+
print("Totality and mixing with Required/NotRequired")
153+
154+
155+
class _MovieBase(TypedDict):
156+
title: str
157+
158+
159+
try:
160+
161+
class MovieMix(_MovieBase, total=False):
162+
year: int
163+
except TypeError as e:
164+
print(
165+
"FIXME: cpy_diff - total parameter not supported by runtime TypedDict implementation for MovieMix:",
166+
e,
167+
)
168+
MovieMix = dict # fallback for runtime operations # type: ignore
169+
170+
171+
# equivalent to marking year as NotRequired
172+
class MovieMix2(_MovieBase):
173+
year: NotRequired[int]
174+
175+
176+
# Do not try to execute known runtime errors:
177+
try:
178+
m1: MovieMix = {} # type: ignore
179+
m2: MovieMix = {"year": 2015} # type: ignore
180+
except TypeError as e:
181+
print("Assigning to MovieMix failed at runtime (expected for missing required fields):", e)
182+
183+
print("Required/NotRequired with Annotated/ReadOnly examples")
184+
185+
from typing import NotRequired, ReadOnly, Annotated
186+
187+
188+
class Band(TypedDict):
189+
name: str
190+
members: ReadOnly[list[str]]
191+
192+
193+
blur: Band = {"name": "blur", "members": []}
194+
blur["name"] = "Blur"
195+
# the following would be a type-checker error (but allowed at runtime):
196+
blur["members"] = ["Daemon Albarn"] # type: ignore
197+
blur["members"].append("Daemon Albarn")
198+
199+
print("extra_items and closed examples")
200+
201+
try:
202+
203+
class MovieExtra(TypedDict, extra_items=int):
204+
name: str
205+
206+
# FIXME: Difference - constructor with kwargs
207+
extra_ok: MovieExtra = {"name": "BR", "year": 1982}
208+
except TypeError as e:
209+
print("-[ ] FIXME: extra_items not supported by runtime typing implementation:", e)
210+
211+
try:
212+
213+
class MovieClosed(TypedDict, closed=True):
214+
name: str
215+
216+
try:
217+
# FIXME: Difference or Crash - constructor with kwargs
218+
MovieClosed(
219+
name="No Country for Old Men", year=2007
220+
) # Should be runtime error per ctor semantics
221+
print("Constructed ClosedMovie with extra item (may be allowed at runtime)")
222+
except TypeError:
223+
print("-[ ] FIXME: Closed Movie rejected extra kwargs at construction")
224+
except TypeError as e:
225+
print("-[ ] FIXME: closed parameter not supported by runtime typing implementation:", e)
226+
227+
print("Interaction with Mapping and dict conversions")
228+
229+
try:
230+
# FIXME:
231+
class IntDict(TypedDict, extra_items=int):
232+
pass
233+
234+
not_required_num_dict: IntDict = {"num": 1, "bar": 2}
235+
except TypeError as e:
236+
print("-[ ] FIXME: extra_items not supported by runtime typing implementation", e)
237+
# Fall back to plain dict to exercise runtime operations
238+
not_required_num_dict = {"num": 1, "bar": 2}
239+
# at runtime this is a dict; operations like clear/popitem are available
240+
not_required_num_dict.clear()
241+
242+
print("-----")

0 commit comments

Comments
 (0)