Skip to content

Commit c496cfb

Browse files
authored
Merge pull request #19 from tekktrik/doc/add-docstrings
Add docstrings
2 parents dbbfcb3 + 1ff8313 commit c496cfb

File tree

7 files changed

+276
-10
lines changed

7 files changed

+276
-10
lines changed

asyncio/core.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
# pylint or black.
1111
# pylint: skip-file
1212
# fmt: off
13+
"""
14+
Core
15+
====
16+
"""
1317

1418
from adafruit_ticks import ticks_ms as ticks, ticks_diff, ticks_add
1519
import sys, select, traceback
@@ -26,10 +30,14 @@
2630

2731

2832
class CancelledError(BaseException):
33+
"""Injected into a task when calling `Task.cancel()`"""
34+
2935
pass
3036

3137

3238
class TimeoutError(Exception):
39+
"""Raised when waiting for a task longer than the specified timeout."""
40+
3341
pass
3442

3543

@@ -65,13 +73,23 @@ def __next__(self):
6573
# Pause task execution for the given time (integer in milliseconds, uPy extension)
6674
# Use a SingletonGenerator to do it without allocating on the heap
6775
def sleep_ms(t, sgen=SingletonGenerator()):
76+
"""Sleep for *t* milliseconds.
77+
78+
This is a coroutine, and a MicroPython extension.
79+
"""
80+
6881
assert sgen.state is None, "Check for a missing `await` in your code"
6982
sgen.state = ticks_add(ticks(), max(0, t))
7083
return sgen
7184

7285

7386
# Pause task execution for the given time (in seconds)
7487
def sleep(t):
88+
"""Sleep for *t* seconds
89+
90+
This is a coroutine.
91+
"""
92+
7593
return sleep_ms(int(t * 1000))
7694

7795

@@ -152,6 +170,11 @@ def _promote_to_task(aw):
152170

153171
# Create and schedule a new task from a coroutine
154172
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+
155178
if not hasattr(coro, "send"):
156179
raise TypeError("coroutine expected")
157180
t = Task(coro, globals())
@@ -161,6 +184,8 @@ def create_task(coro):
161184

162185
# Keep scheduling tasks until there are none left to schedule
163186
def run_until_complete(main_task=None):
187+
"""Run the given *main_task* until it completes."""
188+
164189
global cur_task
165190
excs_all = (CancelledError, Exception) # To prevent heap allocation in loop
166191
excs_stop = (CancelledError, StopIteration) # To prevent heap allocation in loop
@@ -232,6 +257,11 @@ def run_until_complete(main_task=None):
232257

233258
# Create a new task from a coroutine and run it until it finishes
234259
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+
235265
return run_until_complete(create_task(coro))
236266

237267

@@ -247,54 +277,92 @@ async def _stopper():
247277

248278

249279
class Loop:
280+
"""Class representing the event loop"""
281+
250282
_exc_handler = None
251283

252284
def create_task(coro):
285+
"""Create a task from the given *coro* and return the new `Task` object."""
286+
253287
return create_task(coro)
254288

255289
def run_forever():
290+
"""Run the event loop until `Loop.stop()` is called."""
291+
256292
global _stop_task
257293
_stop_task = Task(_stopper(), globals())
258294
run_until_complete(_stop_task)
259295
# TODO should keep running until .stop() is called, even if there're no tasks left
260296

261297
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+
262302
return run_until_complete(_promote_to_task(aw))
263303

264304
def stop():
305+
"""Stop the event loop"""
306+
265307
global _stop_task
266308
if _stop_task is not None:
267309
_task_queue.push_head(_stop_task)
268310
# If stop() is called again, do nothing
269311
_stop_task = None
270312

271313
def close():
314+
"""Close the event loop."""
315+
272316
pass
273317

274318
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+
275323
Loop._exc_handler = handler
276324

277325
def get_exception_handler():
326+
"""Get the current exception handler. Returns the handler, or ``None`` if no
327+
custom handler is set.
328+
"""
329+
278330
return Loop._exc_handler
279331

280332
def default_exception_handler(loop, context):
333+
"""The default exception handler that is called."""
334+
281335
exc = context["exception"]
282336
traceback.print_exception(None, exc, exc.__traceback__)
283337

284338
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+
"""
285343
(Loop._exc_handler or Loop.default_exception_handler)(Loop, context)
286344

287345

288346
# The runq_len and waitq_len arguments are for legacy uasyncio compatibility
289347
def get_event_loop(runq_len=0, waitq_len=0):
348+
"""Return the event loop used to schedule and run tasks. See `Loop`."""
349+
290350
return Loop
291351

292352

293353
def current_task():
354+
"""Return the `Task` object associated with the currently running task."""
355+
294356
return cur_task
295357

296358

297359
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+
298366
global _task_queue, _io_queue
299367
# TaskQueue of Task instances
300368
_task_queue = TaskQueue()

asyncio/event.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,34 @@
1010
# pylint or black.
1111
# pylint: skip-file
1212
# fmt: off
13+
"""
14+
Events
15+
======
16+
"""
1317

1418
from . import core
1519

1620
# Event class for primitive events that can be waited on, set, and cleared
1721
class Event:
22+
"""Create a new event which can be used to synchronize tasks. Events
23+
start in the cleared state.
24+
"""
25+
1826
def __init__(self):
1927
self.state = False # False=unset; True=set
2028
self.waiting = (
2129
core.TaskQueue()
2230
) # Queue of Tasks waiting on completion of this event
2331

2432
def is_set(self):
33+
"""Returns ``True`` if the event is set, ``False`` otherwise."""
34+
2535
return self.state
2636

2737
def set(self):
38+
"""Set the event. Any tasks waiting on the event will be scheduled to run.
39+
"""
40+
2841
# Event becomes set, schedule any tasks waiting on it
2942
# Note: This must not be called from anything except the thread running
3043
# the asyncio loop (i.e. neither hard or soft IRQ, or a different thread).
@@ -33,15 +46,23 @@ def set(self):
3346
self.state = True
3447

3548
def clear(self):
49+
"""Clear the event."""
50+
3651
self.state = False
3752

3853
async def wait(self):
54+
"""Wait for the event to be set. If the event is already set then it returns
55+
immediately.
56+
57+
This is a coroutine.
58+
"""
59+
3960
if not self.state:
4061
# Event not set, put the calling task on the event's waiting queue
4162
self.waiting.push_head(core.cur_task)
4263
# Set calling task's data to the event's queue so it can be removed if needed
4364
core.cur_task.data = self.waiting
44-
yield
65+
await core.sleep(0)
4566
return True
4667

4768

asyncio/funcs.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,33 @@
1010
# pylint or black.
1111
# pylint: skip-file
1212
# fmt: off
13+
"""
14+
Functions
15+
=========
16+
"""
17+
1318

1419
from . import core
1520

1621

1722
async def wait_for(aw, timeout, sleep=core.sleep):
23+
"""Wait for the *aw* awaitable to complete, but cancel if it takes longer
24+
than *timeout* seconds. If *aw* is not a task then a task will be created
25+
from it.
26+
27+
If a timeout occurs, it cancels the task and raises ``asyncio.TimeoutError``:
28+
this should be trapped by the caller.
29+
30+
Returns the return value of *aw*.
31+
32+
This is a coroutine.
33+
"""
34+
1835
aw = core._promote_to_task(aw)
1936
if timeout is None:
2037
return await aw
2138

22-
def runner(waiter, aw):
39+
async def runner(waiter, aw):
2340
nonlocal status, result
2441
try:
2542
result = await aw
@@ -60,10 +77,23 @@ def runner(waiter, aw):
6077

6178

6279
def wait_for_ms(aw, timeout):
80+
"""Similar to `wait_for` but *timeout* is an integer in milliseconds.
81+
82+
This is a coroutine, and a MicroPython extension.
83+
"""
84+
6385
return wait_for(aw, timeout, core.sleep_ms)
6486

6587

6688
async def gather(*aws, return_exceptions=False):
89+
"""Run all *aws* awaitables concurrently. Any *aws* that are not tasks
90+
are promoted to tasks.
91+
92+
Returns a list of return values of all *aws*
93+
94+
This is a coroutine.
95+
"""
96+
6797
ts = [core._promote_to_task(aw) for aw in aws]
6898
for i in range(len(ts)):
6999
try:

asyncio/lock.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@
1010
# pylint or black.
1111
# pylint: skip-file
1212
# fmt: off
13+
"""
14+
Locks
15+
=====
16+
"""
1317

1418
from . import core
1519

1620
# Lock class for primitive mutex capability
1721
class Lock:
22+
"""Create a new lock which can be used to coordinate tasks. Locks start in
23+
the unlocked state.
24+
25+
In addition to the methods below, locks can be used in an ``async with``
26+
statement.
27+
"""
28+
1829
def __init__(self):
1930
# The state can take the following values:
2031
# - 0: unlocked
@@ -25,9 +36,16 @@ def __init__(self):
2536
self.waiting = core.TaskQueue()
2637

2738
def locked(self):
39+
"""Returns ``True`` if the lock is locked, otherwise ``False``."""
40+
2841
return self.state == 1
2942

3043
def release(self):
44+
"""Release the lock. If any tasks are waiting on the lock then the next
45+
one in the queue is scheduled to run and the lock remains locked. Otherwise,
46+
no tasks are waiting and the lock becomes unlocked.
47+
"""
48+
3149
if self.state != 1:
3250
raise RuntimeError("Lock not acquired")
3351
if self.waiting.peek():
@@ -39,13 +57,19 @@ def release(self):
3957
self.state = 0
4058

4159
async def acquire(self):
60+
"""Wait for the lock to be in the unlocked state and then lock it in an
61+
atomic way. Only one task can acquire the lock at any one time.
62+
63+
This is a coroutine.
64+
"""
65+
4266
if self.state != 0:
4367
# Lock unavailable, put the calling Task on the waiting queue
4468
self.waiting.push_head(core.cur_task)
4569
# Set calling task's data to the lock's queue so it can be removed if needed
4670
core.cur_task.data = self.waiting
4771
try:
48-
yield
72+
await core.sleep(0)
4973
except core.CancelledError as er:
5074
if self.state == core.cur_task:
5175
# Cancelled while pending on resume, schedule next waiting Task

0 commit comments

Comments
 (0)