blob: a9f25c1954e2bcde91bec6887974eaf32b50fded [file] [log] [blame]
Damien George6c9c7bc2014-08-21 15:12:51 +01001/*
Alexander Steffen55f33242017-06-30 09:22:17 +02002 * This file is part of the MicroPython project, http://micropython.org/
Damien George6c9c7bc2014-08-21 15:12:51 +01003 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2014 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Paul Sokolovsky8f5bc3f2016-11-20 23:49:45 +030027#include "py/mpconfig.h"
28#if MICROPY_PY_USELECT
29
Damien George6c9c7bc2014-08-21 15:12:51 +010030#include <stdio.h>
31
Damien Georgea2bfcbe2016-10-07 13:58:25 +110032#include "py/runtime.h"
Damien George2cf6dfa2015-01-01 21:06:20 +000033#include "py/obj.h"
34#include "py/objlist.h"
Damien George304cfda2016-12-02 16:37:29 +110035#include "py/stream.h"
Damien George5ab98d52016-05-10 23:22:54 +010036#include "py/mperrno.h"
Damien George731f3592015-10-30 23:03:58 +000037#include "py/mphal.h"
Damien George6c9c7bc2014-08-21 15:12:51 +010038
Paul Sokolovskybe3ae9d2015-12-13 21:05:17 +020039// Flags for poll()
40#define FLAG_ONESHOT (1)
41
Damien George013d53c2014-08-26 17:18:12 +010042/// \module select - Provides select function to wait for events on a stream
43///
44/// This module provides the select function.
Damien George6c9c7bc2014-08-21 15:12:51 +010045
Damien George6c9c7bc2014-08-21 15:12:51 +010046typedef struct _poll_obj_t {
Damien Georgee2a61862014-08-22 21:49:08 +010047 mp_obj_t obj;
Paul Sokolovskyf4a6a572014-11-17 00:16:14 +020048 mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, mp_uint_t arg, int *errcode);
Damien George6c9c7bc2014-08-21 15:12:51 +010049 mp_uint_t flags;
50 mp_uint_t flags_ret;
51} poll_obj_t;
52
53STATIC void poll_map_add(mp_map_t *poll_map, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t flags, bool or_flags) {
54 for (mp_uint_t i = 0; i < obj_len; i++) {
Damien Georgee2a61862014-08-22 21:49:08 +010055 mp_map_elem_t *elem = mp_map_lookup(poll_map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
Damien George6c9c7bc2014-08-21 15:12:51 +010056 if (elem->value == NULL) {
57 // object not found; get its ioctl and add it to the poll list
Damien Georgeebc8d732016-12-02 16:39:50 +110058 const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL);
Damien George6c9c7bc2014-08-21 15:12:51 +010059 poll_obj_t *poll_obj = m_new_obj(poll_obj_t);
Damien Georgee2a61862014-08-22 21:49:08 +010060 poll_obj->obj = obj[i];
Paul Sokolovsky07209f82016-06-18 18:19:24 +030061 poll_obj->ioctl = stream_p->ioctl;
Damien George6c9c7bc2014-08-21 15:12:51 +010062 poll_obj->flags = flags;
63 poll_obj->flags_ret = 0;
64 elem->value = poll_obj;
65 } else {
66 // object exists; update its flags
67 if (or_flags) {
68 ((poll_obj_t*)elem->value)->flags |= flags;
69 } else {
70 ((poll_obj_t*)elem->value)->flags = flags;
71 }
72 }
73 }
74}
75
76// poll each object in the map
77STATIC mp_uint_t poll_map_poll(mp_map_t *poll_map, mp_uint_t *rwx_num) {
78 mp_uint_t n_ready = 0;
79 for (mp_uint_t i = 0; i < poll_map->alloc; ++i) {
80 if (!MP_MAP_SLOT_IS_FILLED(poll_map, i)) {
81 continue;
82 }
83
84 poll_obj_t *poll_obj = (poll_obj_t*)poll_map->table[i].value;
Damien Georgea2f55fe2014-08-21 22:48:23 +010085 int errcode;
Damien George304cfda2016-12-02 16:37:29 +110086 mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj->flags, &errcode);
Damien George6c9c7bc2014-08-21 15:12:51 +010087 poll_obj->flags_ret = ret;
88
89 if (ret == -1) {
90 // error doing ioctl
Damien Georgea2bfcbe2016-10-07 13:58:25 +110091 mp_raise_OSError(errcode);
Damien George6c9c7bc2014-08-21 15:12:51 +010092 }
93
94 if (ret != 0) {
95 // object is ready
96 n_ready += 1;
97 if (rwx_num != NULL) {
Damien George304cfda2016-12-02 16:37:29 +110098 if (ret & MP_STREAM_POLL_RD) {
Damien George6c9c7bc2014-08-21 15:12:51 +010099 rwx_num[0] += 1;
100 }
Damien George304cfda2016-12-02 16:37:29 +1100101 if (ret & MP_STREAM_POLL_WR) {
Damien George6c9c7bc2014-08-21 15:12:51 +0100102 rwx_num[1] += 1;
103 }
Damien George304cfda2016-12-02 16:37:29 +1100104 if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
Damien George6c9c7bc2014-08-21 15:12:51 +0100105 rwx_num[2] += 1;
106 }
107 }
108 }
109 }
110 return n_ready;
111}
112
113/// \function select(rlist, wlist, xlist[, timeout])
114STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) {
115 // get array data from tuple/list arguments
Damien Georgea8a3ab42017-03-26 17:19:08 +1100116 size_t rwx_len[3];
Damien George6c9c7bc2014-08-21 15:12:51 +0100117 mp_obj_t *r_array, *w_array, *x_array;
118 mp_obj_get_array(args[0], &rwx_len[0], &r_array);
119 mp_obj_get_array(args[1], &rwx_len[1], &w_array);
120 mp_obj_get_array(args[2], &rwx_len[2], &x_array);
121
122 // get timeout
123 mp_uint_t timeout = -1;
124 if (n_args == 4) {
125 if (args[3] != mp_const_none) {
Dave Hylands39296b42014-09-26 09:04:05 -0700126 #if MICROPY_PY_BUILTINS_FLOAT
Damien George6c9c7bc2014-08-21 15:12:51 +0100127 float timeout_f = mp_obj_get_float(args[3]);
128 if (timeout_f >= 0) {
129 timeout = (mp_uint_t)(timeout_f * 1000);
130 }
Dave Hylands39296b42014-09-26 09:04:05 -0700131 #else
132 timeout = mp_obj_get_int(args[3]) * 1000;
133 #endif
Damien George6c9c7bc2014-08-21 15:12:51 +0100134 }
135 }
136
137 // merge separate lists and get the ioctl function for each object
138 mp_map_t poll_map;
139 mp_map_init(&poll_map, rwx_len[0] + rwx_len[1] + rwx_len[2]);
Damien George304cfda2016-12-02 16:37:29 +1100140 poll_map_add(&poll_map, r_array, rwx_len[0], MP_STREAM_POLL_RD, true);
141 poll_map_add(&poll_map, w_array, rwx_len[1], MP_STREAM_POLL_WR, true);
142 poll_map_add(&poll_map, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true);
Damien George6c9c7bc2014-08-21 15:12:51 +0100143
Paul Sokolovsky19b671c2015-10-29 20:42:12 +0300144 mp_uint_t start_tick = mp_hal_ticks_ms();
Damien George6c9c7bc2014-08-21 15:12:51 +0100145 rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
146 for (;;) {
147 // poll the objects
148 mp_uint_t n_ready = poll_map_poll(&poll_map, rwx_len);
149
Paul Sokolovsky19b671c2015-10-29 20:42:12 +0300150 if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
Damien George6c9c7bc2014-08-21 15:12:51 +0100151 // one or more objects are ready, or we had a timeout
152 mp_obj_t list_array[3];
153 list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
154 list_array[1] = mp_obj_new_list(rwx_len[1], NULL);
155 list_array[2] = mp_obj_new_list(rwx_len[2], NULL);
156 rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
157 for (mp_uint_t i = 0; i < poll_map.alloc; ++i) {
158 if (!MP_MAP_SLOT_IS_FILLED(&poll_map, i)) {
159 continue;
160 }
161 poll_obj_t *poll_obj = (poll_obj_t*)poll_map.table[i].value;
Damien George304cfda2016-12-02 16:37:29 +1100162 if (poll_obj->flags_ret & MP_STREAM_POLL_RD) {
Damien Georgee2a61862014-08-22 21:49:08 +0100163 ((mp_obj_list_t*)list_array[0])->items[rwx_len[0]++] = poll_obj->obj;
Damien George6c9c7bc2014-08-21 15:12:51 +0100164 }
Damien George304cfda2016-12-02 16:37:29 +1100165 if (poll_obj->flags_ret & MP_STREAM_POLL_WR) {
Damien Georgee2a61862014-08-22 21:49:08 +0100166 ((mp_obj_list_t*)list_array[1])->items[rwx_len[1]++] = poll_obj->obj;
Damien George6c9c7bc2014-08-21 15:12:51 +0100167 }
Damien George304cfda2016-12-02 16:37:29 +1100168 if ((poll_obj->flags_ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
Damien Georgee2a61862014-08-22 21:49:08 +0100169 ((mp_obj_list_t*)list_array[2])->items[rwx_len[2]++] = poll_obj->obj;
Damien George6c9c7bc2014-08-21 15:12:51 +0100170 }
171 }
172 mp_map_deinit(&poll_map);
173 return mp_obj_new_tuple(3, list_array);
174 }
Damien George2d329c42016-12-02 16:40:39 +1100175 MICROPY_EVENT_POLL_HOOK
Damien George6c9c7bc2014-08-21 15:12:51 +0100176 }
177}
178MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
179
Damien George28429452014-09-17 23:27:42 +0000180/// \class Poll - poll class
Damien George6c9c7bc2014-08-21 15:12:51 +0100181
182typedef struct _mp_obj_poll_t {
183 mp_obj_base_t base;
184 mp_map_t poll_map;
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300185 short iter_cnt;
186 short iter_idx;
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300187 int flags;
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300188 // callee-owned tuple
189 mp_obj_t ret_tuple;
Damien George6c9c7bc2014-08-21 15:12:51 +0100190} mp_obj_poll_t;
191
192/// \method register(obj[, eventmask])
193STATIC mp_obj_t poll_register(uint n_args, const mp_obj_t *args) {
194 mp_obj_poll_t *self = args[0];
195 mp_uint_t flags;
196 if (n_args == 3) {
197 flags = mp_obj_get_int(args[2]);
198 } else {
Damien George304cfda2016-12-02 16:37:29 +1100199 flags = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR;
Damien George6c9c7bc2014-08-21 15:12:51 +0100200 }
201 poll_map_add(&self->poll_map, &args[1], 1, flags, false);
202 return mp_const_none;
203}
204MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
205
206/// \method unregister(obj)
207STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
208 mp_obj_poll_t *self = self_in;
Damien Georgee2a61862014-08-22 21:49:08 +0100209 mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
Damien George6c9c7bc2014-08-21 15:12:51 +0100210 // TODO raise KeyError if obj didn't exist in map
211 return mp_const_none;
212}
213MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
214
215/// \method modify(obj, eventmask)
216STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
217 mp_obj_poll_t *self = self_in;
Damien Georgee2a61862014-08-22 21:49:08 +0100218 mp_map_elem_t *elem = mp_map_lookup(&self->poll_map, mp_obj_id(obj_in), MP_MAP_LOOKUP);
Damien George6c9c7bc2014-08-21 15:12:51 +0100219 if (elem == NULL) {
Damien Georgea2bfcbe2016-10-07 13:58:25 +1100220 mp_raise_OSError(MP_ENOENT);
Damien George6c9c7bc2014-08-21 15:12:51 +0100221 }
222 ((poll_obj_t*)elem->value)->flags = mp_obj_get_int(eventmask_in);
223 return mp_const_none;
224}
225MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
226
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300227STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
Damien George6c9c7bc2014-08-21 15:12:51 +0100228 mp_obj_poll_t *self = args[0];
229
230 // work out timeout (its given already in ms)
231 mp_uint_t timeout = -1;
Paul Sokolovskybe3ae9d2015-12-13 21:05:17 +0200232 int flags = 0;
233 if (n_args >= 2) {
Damien George6c9c7bc2014-08-21 15:12:51 +0100234 if (args[1] != mp_const_none) {
235 mp_int_t timeout_i = mp_obj_get_int(args[1]);
236 if (timeout_i >= 0) {
237 timeout = timeout_i;
238 }
239 }
Paul Sokolovskybe3ae9d2015-12-13 21:05:17 +0200240 if (n_args >= 3) {
241 flags = mp_obj_get_int(args[2]);
242 }
Damien George6c9c7bc2014-08-21 15:12:51 +0100243 }
244
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300245 self->flags = flags;
246
Paul Sokolovsky19b671c2015-10-29 20:42:12 +0300247 mp_uint_t start_tick = mp_hal_ticks_ms();
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300248 mp_uint_t n_ready;
Damien George6c9c7bc2014-08-21 15:12:51 +0100249 for (;;) {
250 // poll the objects
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300251 n_ready = poll_map_poll(&self->poll_map, NULL);
Paul Sokolovsky19b671c2015-10-29 20:42:12 +0300252 if (n_ready > 0 || (timeout != -1 && mp_hal_ticks_ms() - start_tick >= timeout)) {
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300253 break;
Damien George6c9c7bc2014-08-21 15:12:51 +0100254 }
Damien George2d329c42016-12-02 16:40:39 +1100255 MICROPY_EVENT_POLL_HOOK
Damien George6c9c7bc2014-08-21 15:12:51 +0100256 }
Paul Sokolovskyedc0dcb2017-04-29 13:05:20 +0300257
258 return n_ready;
259}
260
261STATIC mp_obj_t poll_poll(uint n_args, const mp_obj_t *args) {
262 mp_obj_poll_t *self = args[0];
263 mp_uint_t n_ready = poll_poll_internal(n_args, args);
264
265 // one or more objects are ready, or we had a timeout
266 mp_obj_list_t *ret_list = mp_obj_new_list(n_ready, NULL);
267 n_ready = 0;
268 for (mp_uint_t i = 0; i < self->poll_map.alloc; ++i) {
269 if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
270 continue;
271 }
272 poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
273 if (poll_obj->flags_ret != 0) {
274 mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret)};
275 ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
276 if (self->flags & FLAG_ONESHOT) {
277 // Don't poll next time, until new event flags will be set explicitly
278 poll_obj->flags = 0;
279 }
280 }
281 }
282 return ret_list;
Damien George6c9c7bc2014-08-21 15:12:51 +0100283}
Paul Sokolovskybe3ae9d2015-12-13 21:05:17 +0200284MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
Damien George6c9c7bc2014-08-21 15:12:51 +0100285
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300286STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
287 mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
288
289 if (self->ret_tuple == MP_OBJ_NULL) {
290 self->ret_tuple = mp_obj_new_tuple(2, NULL);
291 }
292
293 int n_ready = poll_poll_internal(n_args, args);
294 self->iter_cnt = n_ready;
295 self->iter_idx = 0;
296
297 return args[0];
298}
299MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
300
301STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
302 mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
303
304 if (self->iter_cnt == 0) {
305 return MP_OBJ_STOP_ITERATION;
306 }
307
308 self->iter_cnt--;
309
310 for (mp_uint_t i = self->iter_idx; i < self->poll_map.alloc; ++i) {
311 self->iter_idx++;
312 if (!MP_MAP_SLOT_IS_FILLED(&self->poll_map, i)) {
313 continue;
314 }
315 poll_obj_t *poll_obj = (poll_obj_t*)self->poll_map.table[i].value;
316 if (poll_obj->flags_ret != 0) {
317 mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
318 t->items[0] = poll_obj->obj;
319 t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj->flags_ret);
320 if (self->flags & FLAG_ONESHOT) {
321 // Don't poll next time, until new event flags will be set explicitly
322 poll_obj->flags = 0;
323 }
324 return MP_OBJ_FROM_PTR(t);
325 }
326 }
327
328 assert(!"inconsistent number of poll active entries");
329 self->iter_cnt = 0;
330 return MP_OBJ_STOP_ITERATION;
331}
332
Paul Sokolovskyb0828692017-04-29 11:03:46 +0300333STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = {
334 { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
335 { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
336 { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
337 { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300338 { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
Damien George6c9c7bc2014-08-21 15:12:51 +0100339};
340STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
341
342STATIC const mp_obj_type_t mp_type_poll = {
343 { &mp_type_type },
344 .name = MP_QSTR_poll,
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300345 .getiter = mp_identity_getiter,
346 .iternext = poll_iternext,
Paul Sokolovskyb0828692017-04-29 11:03:46 +0300347 .locals_dict = (void*)&poll_locals_dict,
Damien George6c9c7bc2014-08-21 15:12:51 +0100348};
349
350/// \function poll()
351STATIC mp_obj_t select_poll(void) {
352 mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
353 poll->base.type = &mp_type_poll;
354 mp_map_init(&poll->poll_map, 0);
Paul Sokolovskyde3a96b2017-04-29 13:05:44 +0300355 poll->iter_cnt = 0;
356 poll->ret_tuple = MP_OBJ_NULL;
Damien George6c9c7bc2014-08-21 15:12:51 +0100357 return poll;
358}
359MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
Damien George013d53c2014-08-26 17:18:12 +0100360
Paul Sokolovskyb0828692017-04-29 11:03:46 +0300361STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = {
362 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) },
363 { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) },
364 { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
Damien Georgebbced3b2017-07-31 13:00:34 +1000365 { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) },
366 { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) },
367 { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(MP_STREAM_POLL_ERR) },
368 { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(MP_STREAM_POLL_HUP) },
Damien George013d53c2014-08-26 17:18:12 +0100369};
370
Damien George3b603f22014-11-29 14:39:27 +0000371STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
Damien George013d53c2014-08-26 17:18:12 +0100372
Damien George0107e902014-10-12 20:23:47 +0100373const mp_obj_module_t mp_module_uselect = {
Damien George013d53c2014-08-26 17:18:12 +0100374 .base = { &mp_type_module },
Damien George013d53c2014-08-26 17:18:12 +0100375 .globals = (mp_obj_dict_t*)&mp_module_select_globals,
376};
Paul Sokolovsky8f5bc3f2016-11-20 23:49:45 +0300377
Damien George0d56c652016-11-21 15:47:48 +1100378#endif // MICROPY_PY_USELECT