Last active
December 1, 2024 00:10
-
-
Save BeautyyuYanli/0a6891d8959bcf06b5cd2e5bd30ef3d1 to your computer and use it in GitHub Desktop.
Revisions
-
BeautyyuYanli revised this gist
Nov 30, 2024 . 1 changed file with 86 additions and 48 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,9 +1,13 @@ import asyncio from typing import ( Any, Awaitable, Generator, Generic, Tuple, TypeVar, Union, ) """ We have invented a new type of function, called `CWaitable` function, @@ -15,18 +19,29 @@ The yellow function can call all colors of functions. To call a red (async) function, it will yield the Awaitable[T] to the top-level event loop, and get the result T to continue. To call a blue (sync) function, it will simply call and return, with a Generator wrapper. To call a yellow function, it will simply call and, pass the yield to the top-level event loop, then/or get the result T. The difference between yellow and red/async functions, is that red/async functions are always submitted to the event loop. But yellow functions will only be submitted when encountering `yield Awaitable`. Otherwise, it simply call yellow functions in the stack. To convert a blue/sync function to a yellow function, we need to wrap the return value with `CWaitValue`, and find the caller chain to use `yield from` to call the yellow function. This still introduces the problem of coloring: just use yellow instead of red. """ T = TypeVar("T") CWaitable = Generator[Awaitable[Any], Any, T] class CWaitValue(Generator[Any, Any, T], Generic[T]): @@ -40,64 +55,87 @@ def send(self, value: Any) -> T: raise StopIteration(self.value) def throw(self, typ: Any, val: Any = None, tb: Any = None): raise RuntimeError("CWaitValue should not be thrown") async def await_c(cwaitable: Union[CWaitable[T], CWaitValue[T]]) -> T: print("await_c start") try: x = next(cwaitable) except StopIteration as e: return e.value cnt = 1 while True: try: print(f"await_c submit async func {cnt} times") result = await x # type: ignore x = cwaitable.send(result) cnt += 1 except StopIteration as e: print("await_c ends") return e.value async def red_task(x: int): print("red_task start") await asyncio.sleep(x) print("red_task ends") return x def blue_task(x: int): return x # Yellow function to call red function # The keyword is `yield` def yellow_call_red(x: int) -> CWaitable[int]: print("yellow_call_red start") res1 = yield red_task(x) print("yellow_call_red ends") return res1 # Yellow function to call blue function. Using CWaitValue to wrap the result. def yellow_call_blue(x: int): print("yellow_call_blue") ans = blue_task(x) return CWaitValue(ans) # Yellow function to call yellow function in sync manner # The keyword is `yield from` def yellow_call_yellow(x: int, y: int, z: int) -> CWaitable[int]: print("yellow_call_yellow start") res1 = yield from yellow_call_red(x) res2 = yield from yellow_call_red(y) res3 = yield from yellow_call_blue(z) print("yellow_call_yellow ends") return res1 + res2 + res3 # Yellow function to call yellow function in async manner # The keyword is `yield await_c(...)` def yellow_call_yellow_async(x: int, y: int, z: int) -> CWaitable[int]: print("yellow_call_yellow_async start") res: Tuple[int, int, int] = yield asyncio.gather( await_c(yellow_call_red(x)), await_c(yellow_call_red(y)), await_c(yellow_call_blue(z)), ) print("yellow_call_yellow_async ends") return res[0] + res[1] + res[2] if __name__ == "__main__": from time import time t0 = time() print(asyncio.run(await_c(yellow_call_yellow(1, 2, 3)))) t1 = time() print(f"====== yellow_call_yellow cost {t1 - t0} seconds ======") print(asyncio.run(await_c(yellow_call_yellow_async(1, 2, 3)))) print(f"====== yellow_call_yellow_async cost {time() - t1} seconds ======") -
BeautyyuYanli revised this gist
Nov 30, 2024 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -49,14 +49,14 @@ def throw(self, typ: Any, val: Any = None, tb: Any = None): # Yellow function to call red function # The keyword is `yield` def yellow_call_red(x: int, y: int) -> CWaitable[int]: async def red_task(x: int): print("red_task start") await asyncio.sleep(x) print("red_task ends") return x print("yellow_call_red start") res1 = yield red_task(x) res2 = yield red_task(y) -
BeautyyuYanli revised this gist
Nov 30, 2024 . 1 changed file with 22 additions and 24 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,8 +4,6 @@ from queue import Queue T = TypeVar("T") """ We have invented a new type of function, called `CWaitable` function, @@ -29,7 +27,23 @@ Otherwise, it simply call yellow functions in the stack. """ class CWaitValue(Generator[Any, Any, T], Generic[T]): def __init__(self, value: T): self.value = value def __next__(self) -> T: raise StopIteration(self.value) def send(self, value: Any) -> T: raise StopIteration(self.value) def throw(self, typ: Any, val: Any = None, tb: Any = None): raise StopIteration(self.value) CWaitable = Generator[Awaitable[Any], Any, T] # Yellow function to call red function @@ -50,43 +64,27 @@ async def red_task(x: int): return res1 + res2 # Yellow function to call blue function. Using CWaitValue to wrap the result. def yellow_call_blue(x: int, y: int): def blue_task(x: int, y: int): return x + y ans = blue_task(x, y) return CWaitValue(ans) # Yellow function to call yellow function # The keyword is `yield from` def yellow_call_yellow(x: int, y: int) -> CWaitable[int]: print("yellow_call_yellow start") res1 = yield from yellow_call_red(x, y) res2 = yield from yellow_call_blue(x, y) print("yellow_call_yellow ends") return res1 + res2 async def a_wait_c(cwaitable: CWaitable[T]) -> T: print("a_wait_c start") x = next(cwaitable) cnt = 1 -
BeautyyuYanli created this gist
Nov 30, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,105 @@ import asyncio from concurrent.futures import ThreadPoolExecutor from typing import Optional, Generic, TypeVar, Awaitable, Generator, Any, Union from queue import Queue T = TypeVar("T") T1 = TypeVar("T1") T2 = TypeVar("T2") """ We have invented a new type of function, called `CWaitable` function, or "yellow function". Yellow functions must be runned in an async event loop environment. Or we say, the async event loop is the runtime of yellow functions. The yellow function can call all colors of functions. To call a red (async) function, it will yield the Awaitable[T] until the top-level event loop, and get the result T to continue. To call a blue (sync) function, it will yield the result T directly. To call a yellow function, it choose to yield or not, by the return type. The difference between yellow and red/async functions, is that red/async functions always be submitted to the event loop. But yellow functions will only submit when encountering `yield Awaitable`. Otherwise, it simply call yellow functions in the stack. """ CWaitable = Union[Generator[Awaitable[Any], Any, T], T] # Yellow function to call red function # The keyword is `yield` def yellow_call_red(x: int, y: int) -> CWaitable[int]: print("yellow_call_red start") async def red_task(x: int): print("red_task start") await asyncio.sleep(x) print("red_task ends") return x res1 = yield red_task(x) res2 = yield red_task(y) print("yellow_call_red ends") return res1 + res2 # Yellow function to call blue function def yellow_call_blue(x: int, y: int) -> CWaitable[int]: print("yellow_call_blue start") def blue_task(x: int, y: int): return x + y ans = blue_task(x, y) print("yellow_call_blue ends") return ans # Yellow function to call yellow function # The keyword is `yield from` def yellow_call_yellow(x: int, y: int) -> CWaitable[int]: print("yellow_call_yellow start") tmp1 = yellow_call_red(x, y) if isinstance(tmp1, Generator): res1 = yield from tmp1 else: res1 = tmp1 tmp2 = yellow_call_blue(x, y) if isinstance(tmp2, Generator): res2 = yield from tmp2 else: res2 = tmp2 print("yellow_call_yellow ends") return res1 + res2 async def a_wait_c(cwaitable: CWaitable[T]) -> T: print("a_wait_c start") if not isinstance(cwaitable, Generator): print("a_wait_c ends") return cwaitable x = next(cwaitable) cnt = 1 while True: try: print(f"a_wait_c submit async func {cnt} times") result = await x x = cwaitable.send(result) cnt += 1 except StopIteration as e: print("a_wait_c ends") return e.value if __name__ == "__main__": print(asyncio.run(a_wait_c(yellow_call_yellow(1, 2))))