10
10
# pylint or black.
11
11
# pylint: skip-file
12
12
# fmt: off
13
+ """
14
+ Core
15
+ ====
16
+ """
13
17
14
18
from adafruit_ticks import ticks_ms as ticks , ticks_diff , ticks_add
15
19
import sys , select , traceback
26
30
27
31
28
32
class CancelledError (BaseException ):
33
+ """Injected into a task when calling `Task.cancel()`"""
34
+
29
35
pass
30
36
31
37
32
38
class TimeoutError (Exception ):
39
+ """Raised when waiting for a task longer than the specified timeout."""
40
+
33
41
pass
34
42
35
43
@@ -65,13 +73,23 @@ def __next__(self):
65
73
# Pause task execution for the given time (integer in milliseconds, uPy extension)
66
74
# Use a SingletonGenerator to do it without allocating on the heap
67
75
def sleep_ms (t , sgen = SingletonGenerator ()):
76
+ """Sleep for *t* milliseconds.
77
+
78
+ This is a coroutine, and a MicroPython extension.
79
+ """
80
+
68
81
assert sgen .state is None , "Check for a missing `await` in your code"
69
82
sgen .state = ticks_add (ticks (), max (0 , t ))
70
83
return sgen
71
84
72
85
73
86
# Pause task execution for the given time (in seconds)
74
87
def sleep (t ):
88
+ """Sleep for *t* seconds
89
+
90
+ This is a coroutine.
91
+ """
92
+
75
93
return sleep_ms (int (t * 1000 ))
76
94
77
95
@@ -152,6 +170,11 @@ def _promote_to_task(aw):
152
170
153
171
# Create and schedule a new task from a coroutine
154
172
def create_task (coro ):
173
+ """Create a new task from the given coroutine and schedule it to run.
174
+
175
+ Returns the corresponding `Task` object.
176
+ """
177
+
155
178
if not hasattr (coro , "send" ):
156
179
raise TypeError ("coroutine expected" )
157
180
t = Task (coro , globals ())
@@ -161,6 +184,8 @@ def create_task(coro):
161
184
162
185
# Keep scheduling tasks until there are none left to schedule
163
186
def run_until_complete (main_task = None ):
187
+ """Run the given *main_task* until it completes."""
188
+
164
189
global cur_task
165
190
excs_all = (CancelledError , Exception ) # To prevent heap allocation in loop
166
191
excs_stop = (CancelledError , StopIteration ) # To prevent heap allocation in loop
@@ -232,6 +257,11 @@ def run_until_complete(main_task=None):
232
257
233
258
# Create a new task from a coroutine and run it until it finishes
234
259
def run (coro ):
260
+ """Create a new task from the given coroutine and run it until it completes.
261
+
262
+ Returns the value returned by *coro*.
263
+ """
264
+
235
265
return run_until_complete (create_task (coro ))
236
266
237
267
@@ -247,54 +277,92 @@ async def _stopper():
247
277
248
278
249
279
class Loop :
280
+ """Class representing the event loop"""
281
+
250
282
_exc_handler = None
251
283
252
284
def create_task (coro ):
285
+ """Create a task from the given *coro* and return the new `Task` object."""
286
+
253
287
return create_task (coro )
254
288
255
289
def run_forever ():
290
+ """Run the event loop until `Loop.stop()` is called."""
291
+
256
292
global _stop_task
257
293
_stop_task = Task (_stopper (), globals ())
258
294
run_until_complete (_stop_task )
259
295
# TODO should keep running until .stop() is called, even if there're no tasks left
260
296
261
297
def run_until_complete (aw ):
298
+ """Run the given *awaitable* until it completes. If *awaitable* is not a task then
299
+ it will be promoted to one.
300
+ """
301
+
262
302
return run_until_complete (_promote_to_task (aw ))
263
303
264
304
def stop ():
305
+ """Stop the event loop"""
306
+
265
307
global _stop_task
266
308
if _stop_task is not None :
267
309
_task_queue .push_head (_stop_task )
268
310
# If stop() is called again, do nothing
269
311
_stop_task = None
270
312
271
313
def close ():
314
+ """Close the event loop."""
315
+
272
316
pass
273
317
274
318
def set_exception_handler (handler ):
319
+ """Set the exception handler to call when a Task raises an exception that is not
320
+ caught. The *handler* should accept two arguments: ``(loop, context)``
321
+ """
322
+
275
323
Loop ._exc_handler = handler
276
324
277
325
def get_exception_handler ():
326
+ """Get the current exception handler. Returns the handler, or ``None`` if no
327
+ custom handler is set.
328
+ """
329
+
278
330
return Loop ._exc_handler
279
331
280
332
def default_exception_handler (loop , context ):
333
+ """The default exception handler that is called."""
334
+
281
335
exc = context ["exception" ]
282
336
traceback .print_exception (None , exc , exc .__traceback__ )
283
337
284
338
def call_exception_handler (context ):
339
+ """Call the current exception handler. The argument *context* is passed through
340
+ and is a dictionary containing keys:
341
+ ``'message'``, ``'exception'``, ``'future'``
342
+ """
285
343
(Loop ._exc_handler or Loop .default_exception_handler )(Loop , context )
286
344
287
345
288
346
# The runq_len and waitq_len arguments are for legacy uasyncio compatibility
289
347
def get_event_loop (runq_len = 0 , waitq_len = 0 ):
348
+ """Return the event loop used to schedule and run tasks. See `Loop`."""
349
+
290
350
return Loop
291
351
292
352
293
353
def current_task ():
354
+ """Return the `Task` object associated with the currently running task."""
355
+
294
356
return cur_task
295
357
296
358
297
359
def new_event_loop ():
360
+ """Reset the event loop and return it.
361
+
362
+ **NOTE**: Since MicroPython only has a single event loop, this function just resets
363
+ the loop's state, it does not create a new one
364
+ """
365
+
298
366
global _task_queue , _io_queue
299
367
# TaskQueue of Task instances
300
368
_task_queue = TaskQueue ()
0 commit comments