Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further cleanups for packaging and README #23

Merged
merged 2 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ jobs:
- name: Set up uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install pypa/build
run: uv run build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
run: just build
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
124 changes: 86 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ This example relies on the following dependencies:
If we have these dependencies in our Python environment, we can run this simple script:

```sh
$ poetry run python examples/asgi_minimal.py
$ python examples/asgi_minimal.py
[2022-03-20 16:59:58 -0700] [91988] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
```

Expand All @@ -215,36 +215,58 @@ OK

**Note**: that our regex path _does not_ match capital letters, so that request 404s.

## Examples
## For Contributors

Runnable examples are provided in the [`examples` directory](https://github.com/erewok/tokamak/examples). For instance, you can run the experimental `tokamak` application with `trio` and `hypercorn` like so:
This project uses `uv` for managing dependencies and virtual environments.

In addition, to contribute to this project, we recommend using `just`: https://github.com/casey/just

You can run various common workflows using the above tools, try the following:

```sh
$ poetry install -E "full"
Installing dependencies from lock file

Package operations: 8 installs, 0 updates, 0 removals

• Installing h11 (0.13.0)
• Installing hpack (4.0.0)
• Installing hyperframe (6.0.1)
• Installing h2 (4.1.0)
• Installing priority (2.0.0)
• Installing toml (0.10.2)
• Installing wsproto (1.1.0)
• Installing hypercorn (0.13.2)

Installing the current project: tokamak (0.2.1)
❯ poetry run python examples/tokamak_app.py
❯ just
just --list
Available recipes:
benchmark # Run the benchmark
bootstrap default="3.12" # Install dependencies used by this project
build *args # Build the project as a package (uv build)
check # Run code quality checks
check-types # Run mypy checks
ci-test coverage_dir='./coverage' # Run the project tests for CI environment (e.g. with code coverage)
example name # Run an example
format # Run the code formatter
sync # Sync dependencies with environment
test *args # Run all tests locally

❯ just check
+ uv run ruff check tokamak tests
All checks passed!

❯ just test
+ uv run pytest
...

```

### Examples

Runnable examples are provided in the [`examples` directory](https://github.com/erewok/tokamak/examples). In addition, this project includes a [`justfile`](./justfile) (see [just](https://github.com/casey/just)) for easily running examples.

For instance, you can run the experimental `tokamak` application with `trio` and `hypercorn` like so:

```sh
$ just example tokamak_app
uv run --extra examples python examples/tokamak_app.py
Installed 13 packages in 5.55s
========·°·°~> Starting tokamak °°···°°🚀···°°
[2022-03-20 11:05:01 -0700] [63023] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
[2024-11-19 09:01:24 -0800] [32768] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
```

In a separate terminal, you can make various requests, such as the following:

```sh
❯ curl http://localhost:8000
{"received": {}}
ok

❯ curl http://localhost:8000/info/erik -d '{"some_data": "something"}'
{"received": {"some_data": "something"}}
Expand All @@ -253,22 +275,48 @@ In a separate terminal, you can make various requests, such as the following:
Back in the first terminal, where you launched the example `tokamak` application, you should see the following:

```sh
❯ poetry run python examples/tokamak_app.py
❯ just example tokamak_app
uv run --extra examples python examples/tokamak_app.py
Installed 13 packages in 5.55s
========·°·°~> Starting tokamak °°···°°🚀···°°
[2022-03-20 11:05:01 -0700] [63023] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
{} {'type': 'http', 'http_version': '1.1', 'asgi': {'spec_version': '2.1', 'version': '3.0'}, 'method': 'GET', 'scheme': 'http', 'path': '/', 'raw_path': b'/', 'query_string': b'', 'root_path': '', 'headers': <Headers([(b'host', b'localhost:8000'), (b'user-agent', b'curl/7.81.0'), (b'accept', b'*/*')])>, 'client': ('127.0.0.1', 55379), 'server': ('127.0.0.1', 8000), 'extensions': {}} <Headers([(b'host', b'localhost:8000'), (b'user-agent', b'curl/7.81.0'), (b'accept', b'*/*')])> b'' 1.1 GET
Sleeping 1s for total seconds: 0
Sleeping 1s for total seconds: 1
Sleeping 1s for total seconds: 2
Sleeping 1s for total seconds: 3
Sleeping 1s for total seconds: 4
{'user': 'erik'} {'type': 'http', 'http_version': '1.1', 'asgi': {'spec_version': '2.1', 'version': '3.0'}, 'method': 'POST', 'scheme': 'http', 'path': '/info/erik', 'raw_path': b'/info/erik', 'query_string': b'', 'root_path': '', 'headers': <Headers([(b'host', b'localhost:8000'), (b'user-agent', b'curl/7.81.0'), (b'accept', b'*/*'), (b'content-length', b'26'), (b'content-type', b'application/x-www-form-urlencoded')])>, 'client': ('127.0.0.1', 55386), 'server': ('127.0.0.1', 8000), 'extensions': {}} <Headers([(b'host', b'localhost:8000'), (b'user-agent', b'curl/7.81.0'), (b'accept', b'*/*'), (b'content-length', b'26'), (b'content-type', b'application/x-www-form-urlencoded')])> b'' 1.1 POST
Sleeping 1s for total seconds: 0
Sleeping 1s for total seconds: 5
Sleeping 1s for total seconds: 1
Sleeping 1s for total seconds: 6
Sleeping 1s for total seconds: 2
Sleeping 1s for total seconds: 7
Sleeping 1s for total seconds: 3
Sleeping 1s for total seconds: 8
[2024-11-19 09:01:24 -0800] [32768] [INFO] Running on http://127.0.0.1:8000 (CTRL + C to quit)
request.app.db={}, request.context={'user': 'erik'}, request.scope={'type': 'http', 'http_version': '1.1', 'asgi': {'spec_version': '2.1', 'version': '3.0'}, 'method': 'POST', 'scheme': 'http', 'path': '/info/erik', 'raw_path': b'/info/erik', 'query_string': b'', 'root_path': '', 'headers': [(b'host', b'localhost:8000'), (b'user-agent', b'curl/8.7.1'), (b'accept', b'*/*'), (b'content-length', b'26'), (b'content-type', b'application/x-www-form-urlencoded')], 'client': ('127.0.0.1', 63965), 'server': ('127.0.0.1', 8000), 'state': {}, 'extensions': {}, 'app': <tokamak.web.app.Tokamak object at 0x11865c4a0>}, headers=[(b'host', b'localhost:8000'), (b'user-agent', b'curl/8.7.1'), (b'accept', b'*/*'), (b'content-length', b'26'), (b'content-type', b'application/x-www-form-urlencoded')], qparams=b'', http_version='1.1', method='POST'
Sleeping 1s for total iterations: 0
Sleeping 1s for total iterations: 1
Sleeping 1s for total iterations: 2
```

## Benchmark

This project was iniatated around the time that the router for [`Werkzeug`](https://github.com/pallets/werkzeug.git) (which powers Flask) was rewritten as well. That router was redesigned to use a modified Radix Tree and so we created a benchmark to compare their implementation with this one.

To run the benchmark against Werkzeug `main`, run the following:

```sh
uv run --extra benchmarks python -m benchmark.compare_werkzeug
Path | Ratio (percent difference from baseline)
Tokamak Tree is quicker: /users/{username}/following | 0.64
Werkzeug Tree is quicker: /repos/{owner}/{repo}/downloads | 0.80
Werkzeug Tree is quicker: /repos/{owner}/{repo}/hooks/{id}/pings | 0.70
...

****** TIMING STATISTICS TOKAMAK FASTER THAN BASELINE ******
Better Total 5270
Best improvement (min vs baseline) 0.12283152787580384 for path /
Mean Improvement: 0.6338085629349435
Median Improvement: 0.618251951398763
Std Dev Improvements: 0.18516141441278625
Mean Path Length: 19.97020872865275
Mean Dynamic Segment Count: 0.6757115749525616
****** TIMING STATISTICS TOKAMAK END ******

****** TIMING STATISTICS WERKZEUG FASTER THAN BASELINE ******
Better Total 4730
Best improvement (min vs baseline) 0.23255522605196324 for path /repos/{owner}/{repo}/labels/{name}
Mean Improvement: 0.6003032685655771
Median Improvement: 0.5382439859668042
Std Dev Improvements: 0.20348200780749615
Mean Path Length: 36.14545454545455
Mean Dynamic Segment Count: 2.468076109936575
****** TIMING STATISTICS WERKZEUG END ******
```
1 change: 1 addition & 0 deletions benchmark/compare_werkzeug.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
"/repos/{owner}/{repo}/statuses/{sha}",
"/repos/{owner}/{repo}/commits/{ref}/statuses",
"/repos/{owner}/{repo}/commits/{ref}/status",
"/repos/{owner}/{repo}/commits/{ref}",
"/search/repositories",
"/search/code",
"/search/issues",
Expand Down
10 changes: 7 additions & 3 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ test *args:
ci-test coverage_dir='./coverage':
uv run pytest --cov=tokamak --cov-report xml --junitxml=./coverage/unittest.junit.xml

# Run the API server
examples name:
uv run --examples {{name}}
# Run an example
example name:
uv run --extra examples python examples/{{name}}.py

# Run the benchmark
benchmark:
uv run --extra benchmarks python -m benchmark.compare_werkzeug
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tokamak"
version = "0.6.0"
version = "0.6.1"
description = "HTTP Router based on radix trees"
readme = "README.md"
authors = [
Expand Down Expand Up @@ -54,12 +54,13 @@ docs = [
"mkdocs-material>=9.5.44",
"mkdocs-section-index>=0.3.9",
]
benchmark = [
benchmarks = [
"Werkzeug",
]
examples = [
"starlette",
"hypercorn",
"trio",
]

[tool.uv.sources]
Expand Down
5 changes: 0 additions & 5 deletions scripts/release.sh

This file was deleted.

9 changes: 0 additions & 9 deletions scripts/run_tests.sh

This file was deleted.

36 changes: 0 additions & 36 deletions setup.cfg

This file was deleted.

6 changes: 4 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading