From c6f2cff555229f48e25a594732ef8221d536eb36 Mon Sep 17 00:00:00 2001 From: Thomas Rausch Date: Fri, 19 Jul 2024 23:17:38 +0200 Subject: [PATCH] add sphinx docs (#15) --- .github/workflows/build.yml | 1 + Makefile | 9 +++ docs/Makefile | 20 +++++ docs/_static/.gitkeep | 0 docs/_static/rolo.png | Bin 0 -> 85947 bytes docs/_templates/.gitkeep | 0 docs/conf.py | 50 ++++++++++++ docs/gateway.md | 32 ++++++++ docs/getting_started.md | 109 +++++++++++++++++++++++++ docs/handler_chain.md | 101 +++++++++++++++++++++++ docs/index.md | 42 ++++++++++ docs/make.bat | 35 ++++++++ docs/requirements.txt | 3 + docs/router.md | 157 ++++++++++++++++++++++++++++++++++++ docs/serving.md | 95 ++++++++++++++++++++++ docs/websockets.md | 8 ++ pyproject.toml | 5 ++ 17 files changed, 667 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/_static/.gitkeep create mode 100644 docs/_static/rolo.png create mode 100644 docs/_templates/.gitkeep create mode 100644 docs/conf.py create mode 100644 docs/gateway.md create mode 100644 docs/getting_started.md create mode 100644 docs/handler_chain.md create mode 100644 docs/index.md create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt create mode 100644 docs/router.md create mode 100644 docs/serving.md create mode 100644 docs/websockets.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 52e514b..b868a33 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: paths-ignore: - 'README.md' + - 'docs/**' branches: - main pull_request: diff --git a/Makefile b/Makefile index f161186..f52beb3 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,15 @@ test-coverage: venv coveralls: venv $(VENV_RUN); coveralls +$(VENV_DIR)/.docs-install: pyproject.toml $(VENV_ACTIVATE) + $(VENV_RUN); pip install -e .[docs] + touch $(VENV_DIR)/.docs-install + +install-docs: $(VENV_DIR)/.docs-install + +docs: install-docs + $(VENV_RUN); cd docs && make html + dist: venv $(VENV_RUN); pip install --upgrade build; python -m build diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/_static/rolo.png b/docs/_static/rolo.png new file mode 100644 index 0000000000000000000000000000000000000000..24524e4829ea88dcd523814b21e39c7e07c346b3 GIT binary patch literal 85947 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 literal 0 HcmV?d00001 diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..d56725a --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,50 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Rolo' +copyright = '2024, LocalStack' +author = 'Thomas Rausch' +release = '0.6.x' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'myst_parser' +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "furo" +html_static_path = ["_static"] +html_title = "Rolo documentation" + +html_logo = "_static/rolo.png" +html_theme_options = { + "top_of_page_buttons": ["view", "edit"], + "source_repository": "https://github.com/localstack/rolo/", + "source_branch": "main", + "source_directory": "docs/", + "footer_icons": [ + { + "name": "GitHub", + "url": "https://github.com/localstack/rolo", + "html": """ + + + + """, + "class": "", + }, + ], +} diff --git a/docs/gateway.md b/docs/gateway.md new file mode 100644 index 0000000..22784a8 --- /dev/null +++ b/docs/gateway.md @@ -0,0 +1,32 @@ +Gateway +======= + +The `Gateway` serves as the factory for `HandlerChain` instances. +It also serves as the interface to HTTP servers. + +## Creating a Gateway + +```python +from rolo.gateway import Gateway + +gateway = Gateway( + request_handlers=[ + ... + ], + response_handlers=[ + ... + ], + exception_handlers=[ + ... + ], + finalizers=[ + ... + ] +) +``` + +## Protocol adapters + +You can use `rolo.gateway.wsgi` or `rolo.gateway.asgi` to expose a `Gateway` as either a WSGI or ASGI app. + +Read more in the [serving](serving.md) section. diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 0000000..1d313ca --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,109 @@ +Getting started +=============== + +## Installation + +Rolo is hosted on [pypi](https://pypi.org/project/rolo/) and can be installed via pip. + +```sh +pip install rolo +``` + +## Hello World + +Rolo provides different ways of building a web application. +It provides familiar concepts such as Router and `@route`, but also more flexible concepts like a Handler Chain. + +### Router + +Here is a simple [`Router`](router.md) that can be served as WSGI application using the Werkzeug dev server. +If you are familiar with Flask, `@route` works in a similar way. + +```python +from werkzeug import Request +from werkzeug.serving import run_simple + +from rolo import Router, route +from rolo.dispatcher import handler_dispatcher + +@route("/") +def hello(request: Request): + return {"message": "Hello World"} + +router = Router(dispatcher=handler_dispatcher()) +router.add(hello) + +run_simple("localhost", 8000, router.wsgi()) +``` + +And to test: +```console +curl localhost:8000/ +``` +Should yield +```json +{"message": "Hello World"} +``` + +`rolo.Request` and `rolo.Response` objects work in the same way as Werkzeug's [Request / Response](https://werkzeug.palletsprojects.com/en/latest/wrappers/) wrappers. + +### Gateway + +A Gateway holds a set of handlers that are combined into a handler chain. +Here is a simple example with a single request handler that dynamically creates a response object similar to httpbin. + +```python +from werkzeug.serving import run_simple + +from rolo import Response +from rolo.gateway import Gateway, RequestContext, HandlerChain +from rolo.gateway.wsgi import WsgiGateway + + +def echo_handler(chain: HandlerChain, context: RequestContext, response: Response): + response.status_code = 200 + response.set_json( + { + "method": context.request.method, + "path": context.request.path, + "query": context.request.args, + "headers": dict(context.request.headers), + } + ) + chain.stop() + + +gateway = Gateway( + request_handlers=[echo_handler], +) + +run_simple("localhost", 8000, WsgiGateway(gateway)) +``` + +And to test: +```console +curl -s -X POST "localhost:8000/foo/bar?a=1&b=2" | jq . +``` +Should give you: +```json +{ + "method": "POST", + "path": "/foo/bar", + "query": { + "a": "1", + "b": "2" + }, + "headers": { + "Host": "localhost:8000", + "User-Agent": "curl/7.81.0", + "Accept": "*/*" + } +} +``` + +## Next Steps + +Learn how to +* Use the [Router](router.md) +* Use the [Handler Chain](handler_chain.md) +* [Serve](serving.md) rolo through your favorite web server diff --git a/docs/handler_chain.md b/docs/handler_chain.md new file mode 100644 index 0000000..a52461a --- /dev/null +++ b/docs/handler_chain.md @@ -0,0 +1,101 @@ +Handler Chain +============= + +The rolo handler chain implements a variant of the chain-of-responsibility pattern to process an incoming HTTP request. +It is meant to be used together with a [`Gateway`](gateway.md), which is responsible for creating `HandlerChain` instances. + +Handler chains are a powerful abstraction to create complex HTTP server behavior, while keeping code cleanly encapsulated and the high-level logic easy to understand. +You can find a simple example how to create a handler chain in the [Getting Started](getting_started.md) guide. + +## Behavior + +A handler chain consists of: +* request handlers: process the request and attempt to create an initial response +* response handlers: process the response +* finalizers: handlers that are always executed at the end of running a handler chain +* exception handlers: run when an exception occurs during the execution of a handler + +Each HTTP request coming into the server has its own `HandlerChain` instance, since the handler chain holds state for the handling of a request. +A handler chain can be in three states that can be controlled by the handlers. + +* Running - the implicit state in which _all_ handlers are executed sequentially +* Stopped - a handler has called `chain.stop()`. This stops the execution of all request handlers, and + proceeds immediately to executing the response handlers. Response handlers and finalizers will be run, + even if the chain has been stopped. +* Terminated - a handler has called `chain.terminate()`. This stops the execution of all request + handlers, and all response handlers, but runs the finalizers at the end. + +If an exception occurs during the execution of request handlers, the chain by default stops the chain, +then runs each exception handler, and finally runs the response handlers. +Exceptions that happen during the execution of response or exception handlers are logged but do not modify the control flow of the chain. + +## Handlers + +Request handlers, response handlers, and finalizers need to satisfy the `Handler` protocol: + +```python +from rolo import Response +from rolo.gateway import HandlerChain, RequestContext + +def handle(chain: HandlerChain, context: RequestContext, response: Response): + ... +``` + +* `chain`: the HandlerChain instance currently being executed. The handler implementation can call for example `chain.stop()` to indicate that it should skip all other request handlers. +* `context`: the RequestContext contains the rolo `Request` object, as well as a universal property store. You can simply call `context.myattr = ...` to pass a value down to the next handler +* `response`: Handlers of a handler chain don't return a response, instead the response being populated is handed down from handler to handler, and can thus be enriched + +### Exception Handlers + +Exception handlers are similar, only they are also passed the `Exception` that was raised in the handler chain. + +```python +from rolo import Response +from rolo.gateway import HandlerChain, RequestContext + +def handle(chain: HandlerChain, exception: Exception, context: RequestContext, response: Response): + ... +``` + +## Builtin Handlers + +### Router handler + +Sometimes you have a `Gateway` but also want to use the [`Router`](router.md). +You can use the `RouterHandler` adapter to make a `Router` look like a handler chain `Handler`, and then pass it as handler to a Gateway. + +```python +from rolo import Router +from rolo.gateway import Gateway +from rolo.gateway.handlers import RouterHandler + +router: Router = ... +gateway: Gateway = ... + +gateway.request_handlers.append(RouterHandler(router)) +``` + +### Empty response handler + +With the `EmptyResponseHandler` response handler automatically creates a default response if the response in the chain is empty. +By default, it creates an empty 404 response, but it can be customized: + +```python +from rolo.gateway.handlers import EmptyResponseHandler + +gateway.response_handlers.append(EmptyResponseHandler(status_code=404, body=b'404 Not Found')) +``` + +### Werkzeug exception handler + +Werkzeug has a very useful [HTTP exception hierarchy](https://werkzeug.palletsprojects.com/en/latest/exceptions/) that can be used to programmatically trigger HTTP errors. +For instance, a request handler may raise a `NotFound` error. +To get the Gateway to automatically handle those exceptions and render them into JSON objects or HTML, you can use the `WerkzeugExceptionHandler`. + +```python +from rolo.gateway.handlers import WerkzeugExceptionHandler + +gateway.exception_handlers.append(WerkzeugExceptionHandler(output_format="json")) +``` + +In your request handler you can now raise any exception from `werkzeug.exceptions` and it will be rendered accordingly. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a568f6f --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +Rolo documentation +================== + +

+ Rolo HTTP +

+

+ Rolo HTTP: A Python framework for building HTTP-based server applications. +

+ +## Introduction + +Rolo is a flexible framework and library to build HTTP-based server applications beyond microservices and REST APIs. +You can build HTTP-based RPC servers, websocket proxies, or other server types that typical web frameworks are not designed for. +Rolo was originally designed to build the AWS RPC protocol server in [LocalStack](https://github.com/localstack/localstack). + +Rolo extends [Werkzeug](https://github.com/pallets/werkzeug/), a flexible Python HTTP server library, for you to use concepts you are familiar with like ``@route``, ``Request``, or ``Response``. +It introduces the concept of a ``Gateway`` and ``HandlerChain``, an implementation variant of the [chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern). + +Rolo is designed for environments that do not use asyncio, but still require asynchronous HTTP features like HTTP2 SSE or Websockets. +To allow asynchronous communication, Rolo introduces an ASGI/WSGI bridge, that allows you to serve Rolo applications through ASGI servers like Hypercorn. + +## Table of Content + +```{toctree} +:caption: Quickstart +:maxdepth: 2 + +getting_started +``` + +```{toctree} +:caption: User Guide +:maxdepth: 2 + +router +handler_chain +gateway +websockets +serving +``` + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..f3181e4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx +furo +myst_parser diff --git a/docs/router.md b/docs/router.md new file mode 100644 index 0000000..704dd09 --- /dev/null +++ b/docs/router.md @@ -0,0 +1,157 @@ +Router +====== + +Routers are based on Werkzeug's [URL Map](https://werkzeug.palletsprojects.com/en/2.3.x/routing/), but dispatch to handler functions directly. +All features from Werkzeug's URL routing are inherited, including the [rule string format](https://werkzeug.palletsprojects.com/en/latest/routing/#rule-format) and [type converters](https://werkzeug.palletsprojects.com/en/latest/routing/#built-in-converters). + +`@route` +-------- + +The `@route` decorator works similar to Flask or FastAPI, but they are not tied to an Application object. +Instead, you can define routes on functions or methods, and then add them directly to the router. + +```python +from rolo import Router, route, Response +from werkzeug import Request +from werkzeug.serving import run_simple + +@route("/users") +def list_users(_request: Request, args): + assert not args + return Response("user") + +@route("/users/") +def get_user_by_id(_request: Request, args): + assert args + return Response(f"{args['user_id']}") + +router = Router() +router.add(list_users) +router.add(get_user_by_id) + +# convert Router to a WSGI app and serve it through werkzeug +run_simple('localhost', 8080, router.wsgi(), use_reloader=True) +``` + +Depending on the _dispatcher_ your Router uses, the signature of your endpoints will look differently. + +Handler dispatcher +------------------ + +Routers use dispatchers to dispatch the request to functions. +In the previous example, the default dispatcher calls the function with the `Request` object and the request arguments as dictionary. +The "handler dispatcher" can transform functions into more Flask or FastAPI-like functions, that also allow you to return values that are automatically transformed. + +```python +from rolo import Router, route +from rolo.dispatcher import handler_dispatcher + +from werkzeug import Request +from werkzeug.serving import run_simple + +@route("/users") +def list_users(request: Request): + # query from db using the ?q= query string + query = request.args["q"] + # ... + return [{"user_id": ...}, ...] + +@route("/users/") +def get_user_by_id(_request: Request, user_id: int): + return {"user_id": user_id, "name": ...} + +router = Router(dispatcher=handler_dispatcher()) +router.add(list_users) +router.add(get_user_by_id) + +# convert Router to a WSGI app and serve it through werkzeug +run_simple('localhost', 8080, router.wsgi(), use_reloader=True) +``` + +Using classes +------------- + +Unlike Flask or FastAPI, Rolo allows you to use classes to organize your routes. +The above example can also be written as follows + +```python +from rolo import Router, route, Request +from rolo.dispatcher import handler_dispatcher + +class UserResource: + + @route("/users/") + def list_users(self, _request: Request): + return "user" + + @route("/users/") + def get_user_by_id(self, _request: Request, user_id: int): + return f"{user_id}" + +router = Router(dispatcher=handler_dispatcher()) +router.add(UserResource()) +``` +The router will scan the instantiated `UserResource` for `@route` decorators, and add them automatically. + +Resource classes +---------------- + +If you prefer the RESTful style that `Falcon `_ implements, you can use the `@resource` decorator on a class. +This will automatically create routes for all `on_` methods. +Here is an example + + +```python +from rolo import Router, resource, Request + +@resource("/users/") +class UserResource: + + def on_get(self, request: Request, user_id: int): + return {"user_id": user_id, "user": ...} + + def on_post(self, request: Request, user_id: int): + data = request.json + # ... do something + +router = Router() +router.add(UserResource()) +``` + +Pydantic integration +-------------------- + +Here's how the default example from the FastAPI documentation would look like with rolo: + +```python +import pydantic + +from rolo import Request, Router, route + + +class Item(pydantic.BaseModel): + name: str + price: float + is_offer: bool | None = None + + +@route("/", methods=["GET"]) +def read_root(request: Request): + return {"Hello": "World"} + + +@route("/items/", methods=["GET"]) +def read_item(request: Request, item_id: int): + return {"item_id": item_id, "q": request.query_string} + + +@route("/items/", methods=["PUT"]) +def update_item(request: Request, item_id: int, item: Item): + return {"item_name": item.name, "item_id": item_id} + + +router = Router() +router.add(read_root) +router.add(read_item) +router.add(update_item) +``` diff --git a/docs/serving.md b/docs/serving.md new file mode 100644 index 0000000..8d201f1 --- /dev/null +++ b/docs/serving.md @@ -0,0 +1,95 @@ +Serving +======= + +This guide shows you how to serve Rolo components through different Python web server technologies. + +WSGI +---- + +### Serving a Router as WSGI app + +If you only need a `Router` instance to serve your application, you can convert to a WSGI app using the `Router.wsgi()` method. + +```python +from rolo import Router, route +from rolo.dispatcher import handler_dispatcher + +@route("/") +def index(request): + return "hello world" + +router = Router(dispatcher=handler_dispatcher()) +router.add(index) + +app = router.wsgi() +``` + +Now you can use any old WSGI compliant server to serve the application. +For example, if this file is stored in `myapp.py`, using gunicorn, you can: + +```sh +pip install gunicorn +gunicorn -w 4 myapp:app +``` + +### Serving a Gateway as WSGI app + +Unless you need Websockets, the Rolo Request object is fully WSGI compliant, so you can also use any WSGI server to serve a `Gateway`. +Simply use the `WSGIGateway` adapter. + +```python +from rolo.gateway import Gateway +from rolo.gateway.wsgi import WsgiGateway + +gateway: Gateway = ... + +app = WsgiGateway(gateway) +``` + +Similar to the previous example, you can serve the `app` object through any WSGI compliant server. + +ASGI +---- + +ASGI servers like Hypercorn allow asynchronous server communication, which is needed for HTTP/2 streaming or Websockets. +Gateways can be served through the `AsgiGateway` adapter, which exposes a `Gateway` as an ASGI3 application. +Under the hood, it uses our own ASGI/WSGI bridge (`AsgiAdapter`), and converts ASGI calls to WSGI calls for regular HTTP requests, and uses ASGI websockets for serving rolo websockets. +File `myapp.py`: + +```python +from rolo.gateway import Gateway +from rolo.gateway.asgi import AsgiGateway + +gateway: Gateway = ... + +app = AsgiGateway(gateway) +``` + +Now you can use Hypercorn or other ASGI servers to serve the `app` object. + +```sh +pip install hypercorn +hypercorn myapp:app +``` + +Twisted +------- + +Rolo can be served through [Twisted](https://twisted.org/), which supports both WSGI and Websockets. +You will need twisted, and wsproto installed `pip install twisted wsproto`. + +```python +from rolo.gateway import Gateway +from rolo.serving.twisted import TwistedGateway +from twisted.internet import endpoints, reactor + +gateway: Gateway = ... + +# Rolo/Twisted adapter, that exposes a Rolo Gateway as a twisted.web.server.Site object +site = TwistedGateway(gateway) + +endpoint = endpoints.TCP4ServerEndpoint(reactor, 8000) +endpoint.listen(site) + +reactor.run() +``` diff --git a/docs/websockets.md b/docs/websockets.md new file mode 100644 index 0000000..5187eb5 --- /dev/null +++ b/docs/websockets.md @@ -0,0 +1,8 @@ +Websockets +========== + +Rolo supports Websockets through ASGI and Twisted (see Serving TODO: link) + +## Hello Yes + +why? diff --git a/pyproject.toml b/pyproject.toml index ef81492..4b39e91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,11 @@ dev = [ "twisted>=24", "ruff==0.1.0" ] +docs = [ + "sphinx", + "furo", + "myst_parser", +] [tool.setuptools] include-package-data = false