From d448489ad8b9fb6c108700740279dd0419898794 Mon Sep 17 00:00:00 2001 From: Achille Roussel Date: Tue, 18 Jun 2024 12:18:47 -0700 Subject: [PATCH] add documentation Signed-off-by: Achille Roussel --- src/dispatch/__init__.py | 17 +++++++++++++ src/dispatch/http.py | 1 + src/dispatch/test.py | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/dispatch/__init__.py b/src/dispatch/__init__.py index ea0090a..95f95ca 100644 --- a/src/dispatch/__init__.py +++ b/src/dispatch/__init__.py @@ -65,6 +65,23 @@ def function(func): async def main(coro: Coroutine[Any, Any, T], addr: Optional[str] = None) -> T: + """Entrypoint of dispatch applications. This function creates a new + Dispatch server and runs the provided coroutine in the server's event loop. + + Programs typically don't use this function directly, unless they manage + their own event loop. Most of the time, the `run` function is a more + convenient way to run a dispatch application. + + Args: + coro: The coroutine to run as the entrypoint, the function returns + when the coroutine returns. + + addr: The address to bind the server to. If not provided, the server + will bind to the address specified by the `DISPATCH_ENDPOINT_ADDR` + + Returns: + The value returned by the coroutine. + """ address = addr or str(os.environ.get("DISPATCH_ENDPOINT_ADDR")) or "localhost:8000" parsed_url = urlsplit("//" + address) diff --git a/src/dispatch/http.py b/src/dispatch/http.py index 55778bd..591642b 100644 --- a/src/dispatch/http.py +++ b/src/dispatch/http.py @@ -79,6 +79,7 @@ def function(self, func): return self.registry.function(func) def batch(self) -> Batch: + """Create a new batch.""" return self.registry.batch() async def run( diff --git a/src/dispatch/test.py b/src/dispatch/test.py index 2ad018d..b3768d1 100644 --- a/src/dispatch/test.py +++ b/src/dispatch/test.py @@ -273,6 +273,19 @@ def session(self) -> aiohttp.ClientSession: async def main(coro: Coroutine[Any, Any, None]) -> None: + """Entrypoint for dispatch function tests, which creates a local Dispatch + server and runs the provided coroutine in the event loop of the server. + + This is a low-level primitive that most test programs wouldn't use directly, + and instead would use one of the `function` or `method` decorators. + + Args: + coro: The coroutine to run as the entrypoint, the function returns + when the coroutine returns. + + Returns: + The value returned by the coroutine. + """ reg = default_registry() api = Service() app = Dispatch(reg) @@ -297,10 +310,48 @@ async def main(coro: Coroutine[Any, Any, None]) -> None: def run(coro: Coroutine[Any, Any, None]) -> None: + """Runs the provided coroutine in the test server's event loop. This + function is a convenience wrapper around the `main` function that runs the + coroutine in the event loop of the test server. + + Programs typically don't use this function directly, unless they manage + their own event loop. Most of the time, the `run` function is a more + convenient way to run a dispatch application. + + Args: + coro: The coroutine to run as the entrypoint, the function returns + when the coroutine returns. + + Returns: + The value returned by the coroutine. + """ return asyncio.run(main(coro)) def function(fn: Callable[[], Coroutine[Any, Any, None]]) -> Callable[[], None]: + """This decorator is used to write tests that execute in a local Dispatch + server. + + The decorated function would typically be a coroutine that implements the + test and returns when the test is done, for example: + + ```python + import dispatch + import dispatch.test + + @dispatch.function + def greet(name: str) -> str: + return f"Hello {name}!" + + @dispatch.test.function + async def test_greet(): + assert await greet("World") == "Hello World!" + ``` + + The test runs dispatch functions with the full dispatch capability, + including retrying temporary errors, etc... + """ + @wraps(fn) def wrapper(): return run(fn()) @@ -309,6 +360,10 @@ def wrapper(): def method(fn: Callable[[T], Coroutine[Any, Any, None]]) -> Callable[[T], None]: + """This decorator is similar to the `function` decorator but is intended to + apply to methods of a class (with a `self` value as first argument). + """ + @wraps(fn) def wrapper(self: T): return run(fn(self))