Skip to content

Commit c99a336

Browse files
Josverlstinos
authored andcommitted
extmod/modtyping: Add support for tytping aliases and typing.Literal in typing alias specifications.
Signed-off-by: Jos Verlinde <Jos_Verlinde@hotmail.com>
1 parent 5c5bc59 commit c99a336

File tree

1 file changed

+116
-1
lines changed

1 file changed

+116
-1
lines changed

extmod/modtyping.c

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@
2525
*/
2626

2727
#include "py/obj.h"
28+
#include "py/runtime.h"
2829

2930
#if MICROPY_PY_TYPING
3031

3132
// Implement roughly the equivalent of the following minimal Python typing module, meant to support the
3233
// typing syntax at runtime but otherwise ignoring any functionality:
3334
//
35+
// TYPE_CHECKING = False
3436
// class _AnyCall:
3537
// def __init__(*args, **kwargs):
3638
// pass
@@ -59,6 +61,78 @@ typedef struct _mp_obj_any_call_t
5961
} mp_obj_any_call_t;
6062

6163
static const mp_obj_type_t mp_type_any_call_t;
64+
static const mp_obj_type_t mp_type_typing_alias;
65+
66+
// Lightweight runtime representation for objects such as typing.List[int].
67+
// The alias keeps track of the original builtin type and the tuple of
68+
// parameters so that __origin__ and __args__ can be queried at runtime.
69+
typedef struct _mp_obj_typing_alias_t {
70+
mp_obj_base_t base;
71+
mp_obj_t origin;
72+
mp_obj_t args; // tuple or MP_OBJ_NULL when not parametrised
73+
} mp_obj_typing_alias_t;
74+
75+
// Maps a qstr name to the builtin type that should back the alias.
76+
typedef struct {
77+
qstr name;
78+
const mp_obj_type_t *type;
79+
} typing_alias_spec_t;
80+
81+
static mp_obj_t typing_alias_from_spec(const typing_alias_spec_t *spec_table, size_t spec_len, qstr attr);
82+
83+
static mp_obj_t typing_alias_new(mp_obj_t origin, mp_obj_t args) {
84+
mp_obj_typing_alias_t *self = mp_obj_malloc(mp_obj_typing_alias_t, &mp_type_typing_alias);
85+
self->origin = origin;
86+
self->args = args;
87+
return MP_OBJ_FROM_PTR(self);
88+
}
89+
90+
static void typing_alias_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
91+
// Only handle reads that we recognise: __origin__ and __args__. Anything
92+
// else is delegated back to the VM where it will fall through to the
93+
// generic AnyCall behaviour.
94+
if (dest[0] != MP_OBJ_NULL) {
95+
return;
96+
}
97+
98+
mp_obj_typing_alias_t *self = MP_OBJ_TO_PTR(self_in);
99+
if (attr == MP_QSTR___origin__) {
100+
dest[0] = self->origin;
101+
} else if (attr == MP_QSTR___args__) {
102+
dest[0] = self->args == MP_OBJ_NULL ? mp_const_empty_tuple : self->args;
103+
}
104+
}
105+
106+
static mp_obj_t typing_alias_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
107+
if (value != MP_OBJ_SENTINEL) {
108+
mp_raise_TypeError(MP_ERROR_TEXT("typing alias does not support assignment"));
109+
}
110+
111+
mp_obj_typing_alias_t *self = MP_OBJ_TO_PTR(self_in);
112+
mp_obj_t args_obj;
113+
if (mp_obj_is_type(index_in, &mp_type_tuple)) {
114+
args_obj = index_in;
115+
} else {
116+
mp_obj_t items[1] = { index_in };
117+
args_obj = mp_obj_new_tuple(1, items);
118+
}
119+
120+
return typing_alias_new(self->origin, args_obj);
121+
}
122+
123+
static mp_obj_t typing_alias_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
124+
mp_obj_typing_alias_t *self = MP_OBJ_TO_PTR(self_in);
125+
return mp_call_function_n_kw(self->origin, n_args, n_kw, args);
126+
}
127+
128+
static MP_DEFINE_CONST_OBJ_TYPE(
129+
mp_type_typing_alias,
130+
MP_QSTR_typing_alias,
131+
MP_TYPE_FLAG_NONE,
132+
attr, typing_alias_attr,
133+
subscr, typing_alias_subscr,
134+
call, typing_alias_call
135+
);
62136

63137

64138
// Can be used both for __new__ and __call__: the latter's prototype is
@@ -114,10 +188,39 @@ static void any_call_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
114188
}
115189
}
116190

191+
// Only a small subset of typing.* names need concrete runtime behaviour. The
192+
// table below lists those names together with the builtin type that should be
193+
// wrapped in a typing alias. Everything else continues to use the extremely
194+
// small AnyCall shim.
195+
static const typing_alias_spec_t typing_container_specs[] = {
196+
{ MP_QSTR_type, &mp_type_type },
197+
{ MP_QSTR_Type, &mp_type_type },
198+
{ MP_QSTR_List, &mp_type_list },
199+
{ MP_QSTR_Dict, &mp_type_dict },
200+
{ MP_QSTR_Tuple, &mp_type_tuple },
201+
{ MP_QSTR_Literal, &mp_type_any_call_t },
202+
#if MICROPY_PY_BUILTINS_SET
203+
{ MP_QSTR_Set, &mp_type_set },
204+
#endif
205+
#if MICROPY_PY_BUILTINS_FROZENSET
206+
{ MP_QSTR_FrozenSet, &mp_type_frozenset },
207+
#endif
208+
};
209+
117210
void any_call_module_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
118211
// Only loading is supported.
119212
if (dest[0] == MP_OBJ_NULL) {
120-
dest[0] = MP_OBJ_FROM_PTR(&mp_type_any_call_t);
213+
// First see if this attribute corresponds to a container alias that
214+
// needs a proper __getitem__ implementation.
215+
mp_obj_t alias = typing_alias_from_spec(typing_container_specs, MP_ARRAY_SIZE(typing_container_specs), attr);
216+
if (alias != MP_OBJ_NULL) {
217+
dest[0] = alias;
218+
} else {
219+
// Otherwise fall back to returning the singleton AnyCall object,
220+
// preserving the "typing ignores everything" behaviour used for
221+
// the majority of names.
222+
dest[0] = MP_OBJ_FROM_PTR(&mp_type_any_call_t);
223+
}
121224
}
122225
}
123226

@@ -131,9 +234,21 @@ static MP_DEFINE_CONST_OBJ_TYPE(
131234
call, any_call_call
132235
);
133236

237+
// Helper to look up a qstr in the alias specification table and lazily create
238+
// the corresponding typing alias object when a match is found.
239+
static mp_obj_t typing_alias_from_spec(const typing_alias_spec_t *spec_table, size_t spec_len, qstr attr) {
240+
for (size_t i = 0; i < spec_len; ++i) {
241+
if (spec_table[i].name == attr) {
242+
mp_obj_t origin = MP_OBJ_FROM_PTR(spec_table[i].type);
243+
return typing_alias_new(origin, MP_OBJ_NULL);
244+
}
245+
}
246+
return MP_OBJ_NULL;
247+
}
134248

135249
static const mp_rom_map_elem_t mp_module_typing_globals_table[] = {
136250
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_typing) },
251+
{ MP_ROM_QSTR(MP_QSTR_TYPE_CHECKING), MP_ROM_FALSE },
137252
};
138253

139254
static MP_DEFINE_CONST_DICT(mp_module_typing_globals, mp_module_typing_globals_table);

0 commit comments

Comments
 (0)