Step 8 - Extend the Loop¶
As a next step we want to extend the loop so that we can get the current loop.
from typing import Any, Generator
class Loop:
"""Loop v2"""
_instance: "Loop" = None
def __init__(self):
self._running = False
@classmethod
def get_current_loop(cls) -> "Loop":
if not cls._instance:
cls._instance = Loop()
return cls._instance
def run(self, coroutine: Generator[Any, None, Any]) -> Any:
"""Run a coroutine"""
self._running = True
step = 1
while self._running:
print("Loop step", step)
try:
next(coroutine)
step += 1
except StopIteration as e:
self._running = False
return e.value
def stop(self) -> None:
"""Stop running the loop"""
self._running = False
The loop has been converted into a class that uses a singleton pattern to return
the current loop. The current loop is now accessible via Loop.get_current_loop()
.
A coroutine can be started by calling the run
method on the loop instance. The
state of the loop (running
/not running
) is tracked in the private _running
property.
from future import Future
from loop import Loop
def some_result(value):
future = Future()
future.set_result(value)
return (yield from future)
def add(coroutine1, coroutine2):
x = yield from coroutine1
y = yield from coroutine2
return x + y
def main():
return (yield from add(some_result(1), some_result(2)))
loop = Loop.get_current_loop()
result = loop.run(main())
print("Loop finished with result", result)
Output:
Loop step 1
Loop step 2
Loop step 3
Loop finished with result 3
Summary
The loop will not be instantiated by the application code directly.
Instead the current loop is requested at a single API point.
The loop could decide whether it returns a new instance or not.