jero 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. jero-0.0.2/.github/actions/setup-python-env/action.yml +30 -0
  2. jero-0.0.2/.github/workflows/main.yml +70 -0
  3. jero-0.0.2/.github/workflows/on-release-main.yml +69 -0
  4. jero-0.0.2/.github/workflows/validate-codecov-config.yml +15 -0
  5. jero-0.0.2/.gitignore +211 -0
  6. jero-0.0.2/.pre-commit-config.yaml +41 -0
  7. jero-0.0.2/AGENTS.md +128 -0
  8. jero-0.0.2/CONTRIBUTING.md +126 -0
  9. jero-0.0.2/LICENSE +21 -0
  10. jero-0.0.2/PKG-INFO +128 -0
  11. jero-0.0.2/README.md +103 -0
  12. jero-0.0.2/Taskfile.yml +90 -0
  13. jero-0.0.2/bugs/README.md +19 -0
  14. jero-0.0.2/bugs/server-side-decode-returns-422.md +106 -0
  15. jero-0.0.2/codecov.yaml +9 -0
  16. jero-0.0.2/docs/index.md +8 -0
  17. jero-0.0.2/docs/modules.md +3 -0
  18. jero-0.0.2/jero/__init__.py +39 -0
  19. jero-0.0.2/jero/core.py +1518 -0
  20. jero-0.0.2/jero/forms.py +21 -0
  21. jero-0.0.2/jero/streaming.py +52 -0
  22. jero-0.0.2/jero/testing.py +722 -0
  23. jero-0.0.2/plans/form-typed-headers.md +146 -0
  24. jero-0.0.2/plans/forms.md +272 -0
  25. jero-0.0.2/plans/raw-headers-source.md +148 -0
  26. jero-0.0.2/plans/str-text-responses.md +93 -0
  27. jero-0.0.2/plans/streaming.md +207 -0
  28. jero-0.0.2/pyproject.toml +159 -0
  29. jero-0.0.2/style-guide.md +487 -0
  30. jero-0.0.2/tests/__init__.py +0 -0
  31. jero-0.0.2/tests/conftest.py +33 -0
  32. jero-0.0.2/tests/demo_app.py +224 -0
  33. jero-0.0.2/tests/test_auth.py +20 -0
  34. jero-0.0.2/tests/test_binding.py +125 -0
  35. jero-0.0.2/tests/test_endpoint.py +50 -0
  36. jero-0.0.2/tests/test_factory_harness.py +88 -0
  37. jero-0.0.2/tests/test_forms.py +420 -0
  38. jero-0.0.2/tests/test_resource.py +79 -0
  39. jero-0.0.2/tests/test_responses.py +75 -0
  40. jero-0.0.2/tests/test_routing.py +47 -0
  41. jero-0.0.2/tests/test_streaming.py +257 -0
  42. jero-0.0.2/tests/test_wiring.py +108 -0
  43. jero-0.0.2/uv.lock +838 -0
  44. jero-0.0.2/zensical.toml +82 -0
@@ -0,0 +1,30 @@
1
+ name: "Setup Python Environment"
2
+ description: "Set up Python environment for the given Python version"
3
+
4
+ inputs:
5
+ python-version:
6
+ description: "Python version to use"
7
+ required: true
8
+ default: "3.14"
9
+ uv-version:
10
+ description: "uv version to use"
11
+ required: true
12
+ default: "0.10.12"
13
+
14
+ runs:
15
+ using: "composite"
16
+ steps:
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ inputs.python-version }}
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v6
23
+ with:
24
+ version: ${{ inputs.uv-version }}
25
+ enable-cache: 'true'
26
+ cache-suffix: ${{ matrix.python-version }}
27
+
28
+ - name: Install Python dependencies
29
+ run: uv sync --frozen
30
+ shell: bash
@@ -0,0 +1,70 @@
1
+ name: Main
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ types: [opened, synchronize, reopened, ready_for_review]
9
+
10
+ jobs:
11
+ quality:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Check out
15
+ uses: actions/checkout@v4
16
+
17
+ - uses: actions/cache@v4
18
+ with:
19
+ path: ~/.cache/pre-commit
20
+ key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
21
+
22
+ - name: Set up the environment
23
+ uses: ./.github/actions/setup-python-env
24
+
25
+ - name: Run checks
26
+ run: |
27
+ uv lock --locked
28
+ uv run prek run -a
29
+
30
+ tests-and-type-check:
31
+ runs-on: ubuntu-latest
32
+ strategy:
33
+ matrix:
34
+ python-version: ["3.14"]
35
+ fail-fast: false
36
+ defaults:
37
+ run:
38
+ shell: bash
39
+ steps:
40
+ - name: Check out
41
+ uses: actions/checkout@v4
42
+
43
+ - name: Set up the environment
44
+ uses: ./.github/actions/setup-python-env
45
+ with:
46
+ python-version: ${{ matrix.python-version }}
47
+
48
+ - name: Run tests
49
+ run: uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml
50
+
51
+ - name: Check typing
52
+ run: uv run pyright
53
+
54
+ - name: Upload coverage reports to Codecov with GitHub Action on Python 3.14
55
+ uses: codecov/codecov-action@v4
56
+ if: ${{ matrix.python-version == '3.14' }}
57
+ env:
58
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
59
+
60
+ check-docs:
61
+ runs-on: ubuntu-latest
62
+ steps:
63
+ - name: Check out
64
+ uses: actions/checkout@v4
65
+
66
+ - name: Set up the environment
67
+ uses: ./.github/actions/setup-python-env
68
+
69
+ - name: Check if documentation can be built
70
+ run: uv run zensical build -s
@@ -0,0 +1,69 @@
1
+ name: release-main
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ # Trusted Publishing (OIDC) — no PyPI token needed. The environment name
12
+ # must match the one configured on the PyPI publisher ("pypi").
13
+ environment:
14
+ name: pypi
15
+ url: https://pypi.org/p/jero
16
+ permissions:
17
+ id-token: write
18
+ steps:
19
+ - name: Check out
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up the environment
23
+ uses: ./.github/actions/setup-python-env
24
+
25
+ # pyproject.toml is the single source of truth for the version; the release
26
+ # tag must match it exactly, or the release fails here (no silent drift).
27
+ - name: Verify the release tag matches the pyproject version
28
+ run: |
29
+ version="$(python -c 'import tomllib, pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
30
+ tag="${{ github.event.release.tag_name }}"
31
+ if [ "$version" != "$tag" ]; then
32
+ echo "::error::release tag '$tag' does not match pyproject version '$version' — bump 'version' in pyproject.toml to match the tag"
33
+ exit 1
34
+ fi
35
+
36
+ - name: Build package
37
+ run: uv build
38
+
39
+ - name: Publish package
40
+ run: uv publish
41
+
42
+ deploy-docs:
43
+ needs: publish
44
+ permissions:
45
+ contents: read
46
+ pages: write
47
+ id-token: write
48
+ environment:
49
+ name: github-pages
50
+ url: ${{ steps.deployment.outputs.page_url }}
51
+ runs-on: ubuntu-latest
52
+ steps:
53
+ - name: Check out
54
+ uses: actions/checkout@v4
55
+
56
+ - name: Set up the environment
57
+ uses: ./.github/actions/setup-python-env
58
+
59
+ - name: Build documentation
60
+ run: uv run zensical build --clean
61
+
62
+ - name: Upload artifact
63
+ uses: actions/upload-pages-artifact@v4
64
+ with:
65
+ path: site
66
+
67
+ - name: Deploy to GitHub Pages
68
+ id: deployment
69
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,15 @@
1
+ name: validate-codecov-config
2
+
3
+ on:
4
+ pull_request:
5
+ paths: [codecov.yaml]
6
+ push:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ validate-codecov-config:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Validate codecov configuration
15
+ run: curl -sSL --fail-with-body --data-binary @codecov.yaml https://codecov.io/validate
jero-0.0.2/.gitignore ADDED
@@ -0,0 +1,211 @@
1
+ docs/source
2
+
3
+ # From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore
4
+
5
+ # Byte-compiled / optimized / DLL files
6
+ __pycache__/
7
+ *.py[codz]
8
+ *$py.class
9
+
10
+ # C extensions
11
+ *.so
12
+
13
+ # Distribution / packaging
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ share/python-wheels/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+ MANIFEST
32
+
33
+ # PyInstaller
34
+ # Usually these files are written by a python script from a template
35
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
36
+ *.manifest
37
+ *.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .nox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ nosetests.xml
51
+ coverage.xml
52
+ *.cover
53
+ *.py.cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+ cover/
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+
78
+ # PyBuilder
79
+ .pybuilder/
80
+ target/
81
+
82
+ # Jupyter Notebook
83
+ .ipynb_checkpoints
84
+
85
+ # IPython
86
+ profile_default/
87
+ ipython_config.py
88
+
89
+ # pyenv
90
+ # For a library or package, you might want to ignore these files since the code is
91
+ # intended to run in multiple environments; otherwise, check them in:
92
+ # .python-version
93
+
94
+ # pipenv
95
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
97
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
98
+ # install all needed dependencies.
99
+ #Pipfile.lock
100
+
101
+ # UV
102
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
103
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
104
+ # commonly ignored for libraries.
105
+ #uv.lock
106
+
107
+ # poetry
108
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
109
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
110
+ # commonly ignored for libraries.
111
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
112
+ #poetry.lock
113
+ #poetry.toml
114
+
115
+ # pdm
116
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
117
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
118
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
119
+ #pdm.lock
120
+ #pdm.toml
121
+ .pdm-python
122
+ .pdm-build/
123
+
124
+ # pixi
125
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
126
+ #pixi.lock
127
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
128
+ # in the .venv directory. It is recommended not to include this directory in version control.
129
+ .pixi
130
+
131
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
132
+ __pypackages__/
133
+
134
+ # Celery stuff
135
+ celerybeat-schedule
136
+ celerybeat.pid
137
+
138
+ # SageMath parsed files
139
+ *.sage.py
140
+
141
+ # Environments
142
+ .env
143
+ .envrc
144
+ .venv
145
+ env/
146
+ venv/
147
+ ENV/
148
+ env.bak/
149
+ venv.bak/
150
+
151
+ # Spyder project settings
152
+ .spyderproject
153
+ .spyproject
154
+
155
+ # Rope project settings
156
+ .ropeproject
157
+
158
+ # mkdocs/zensical documentation
159
+ /site
160
+
161
+ # mypy
162
+ .mypy_cache/
163
+ .dmypy.json
164
+ dmypy.json
165
+
166
+ # Pyre type checker
167
+ .pyre/
168
+
169
+ # pytype static type analyzer
170
+ .pytype/
171
+
172
+ # Cython debug symbols
173
+ cython_debug/
174
+
175
+ # PyCharm
176
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
177
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
178
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
179
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
180
+ #.idea/
181
+
182
+ # Abstra
183
+ # Abstra is an AI-powered process automation framework.
184
+ # Ignore directories containing user credentials, local state, and settings.
185
+ # Learn more at https://abstra.io/docs
186
+ .abstra/
187
+
188
+ # Visual Studio Code
189
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
190
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
191
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
192
+ # you could uncomment the following to ignore the entire vscode folder
193
+ # .vscode/
194
+
195
+ # Ruff stuff:
196
+ .ruff_cache/
197
+
198
+ # PyPI configuration file
199
+ .pypirc
200
+
201
+ # Cursor
202
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
203
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
204
+ # refer to https://docs.cursor.com/context/ignore-files
205
+ .cursorignore
206
+ .cursorindexingignore
207
+
208
+ # Marimo
209
+ marimo/_static/
210
+ marimo/_lsp/
211
+ __marimo__/
@@ -0,0 +1,41 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: "v6.0.0"
4
+ hooks:
5
+ - id: check-case-conflict
6
+ - id: check-merge-conflict
7
+ - id: check-toml
8
+ - id: check-yaml
9
+ - id: check-json
10
+ - id: pretty-format-json
11
+ args: [--autofix, --no-sort-keys]
12
+ - id: end-of-file-fixer
13
+ - id: trailing-whitespace
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: "v0.15.15"
17
+ hooks:
18
+ - id: ruff-check
19
+ args: [--exit-non-zero-on-fix]
20
+ - id: ruff-format
21
+
22
+ - repo: local
23
+ hooks:
24
+ - id: pyright
25
+ name: pyright
26
+ entry: uv run pyright
27
+ language: system
28
+ types: [python]
29
+ pass_filenames: false
30
+
31
+ - id: deptry
32
+ name: deptry
33
+ entry: uv run deptry .
34
+ language: system
35
+ pass_filenames: false
36
+
37
+ - id: pylint
38
+ name: pylint
39
+ entry: uv run pylint
40
+ language: system
41
+ types: [python]
jero-0.0.2/AGENTS.md ADDED
@@ -0,0 +1,128 @@
1
+ # AGENTS.md
2
+
3
+ ## jero
4
+
5
+ An opinionated, msgspec-first ASGI web framework (Python 3.14). The goal is a
6
+ framework that is **both** very fast **and** a joy to build on — achieved by
7
+ being aggressively opinionated rather than flexible.
8
+
9
+ ## Design philosophy (three pillars, all non-negotiable)
10
+
11
+ 1. **Speed.** All introspection happens once, at wiring time; the per-request
12
+ path is just dict lookup → msgspec decode → call → encode, nothing else.
13
+ Benchmarks co-lead blacksheep and well outpace FastAPI. Never add per-request
14
+ work — resolve it at startup.
15
+
16
+ 2. **Opinionated, scaffolded DX.** There is *one blessed way* to do each thing,
17
+ and the framework encodes the expertise so the developer can't get it wrong
18
+ (lifecycle, REST semantics, validation, dependency wiring). Contracts are
19
+ checked at startup and fail loud with a precise `WiringError` — never
20
+ silently at runtime. The aim: there's a framework answer to "how do I do X?"
21
+ so that question never reaches code review.
22
+
23
+ 3. **Strict, expressive typing — not optional.** Everything is fully, statically
24
+ typed under pyright-strict. If you don't like typing, this is not your
25
+ framework. Types are not decoration — they *are* the contract (the binding,
26
+ the `WiringError`s) and, soon, the source of the OpenAPI spec (see roadmap).
27
+ Every design decision must produce precise static types; prefer rich,
28
+ self-documenting generics (`-> NDJSONStreamingResponse[Movie]`,
29
+ `BaseApp[Factory]`) over loose annotations. A feature that can't be expressed
30
+ in exact static types isn't done. **Never** reach for `Any` or untyped
31
+ `dict`/`bytes` I/O to dodge a type — it punches a hole in both the contract
32
+ and the generated docs. Hold this standard in every future design session.
33
+
34
+ These pull against each other constantly; keep all three in mind on every change.
35
+
36
+ ## Working on jero
37
+
38
+ - **Read `style-guide.md` before writing code** — project conventions beyond what
39
+ ruff/pyright enforce (dataclasses, no globals, member ordering, no nested
40
+ funcs/classes, etc.).
41
+ - pyright **strict** and ruff must stay clean; tests must pass. `task pyright`,
42
+ `task ruff`, `task test` — or run everything at once with `task check`.
43
+ - **Never suppress a lint/type error to make it pass — always fix the code.**
44
+ Adding *any* ignore/disable — `# pylint: disable=…`, `# noqa`, `# type: ignore`,
45
+ `# pyright: ignore`, a `disable`/`ignore`/`per-file-ignores` entry in config, a
46
+ `deptry` ignore, etc. — is **forbidden without explicit human approval first**.
47
+ Ask, with the specific rule and why a code fix won't do; apply it only once the
48
+ human says yes. This applies to *every* tool, every time — no exceptions, no
49
+ "obviously fine" cases.
50
+ - For framework-level / design changes, **discuss the design first** — don't just
51
+ implement. Give options + a recommendation, then build once decided.
52
+ - **Testing stance:** tests run only through the public boundary — `TestClient`
53
+ against demo apps in `tests/`. **Do not unit-test `jero/` internals directly**;
54
+ they're covered transitively. This is deliberate (style-guide rule 7, and it
55
+ lets the internals be refactored freely — which they are, often). Don't "fix"
56
+ the absence of internal tests. Revisit only once the internals stabilize
57
+ (approaching a stable release), and even then only a thin layer for the
58
+ intricate *pure* helpers (`_parse_template`/`_route_segments`, the
59
+ `__orig_bases__` factory-type extraction, SSE/NDJSON formatting).
60
+
61
+ ## The contract (how apps are built)
62
+
63
+ - **`Resource`** — a class with CRUD methods: `create` / `read_one` / `read_many`
64
+ / `update` / `partial_update` / `delete` → POST / GET(item) / GET(collection)
65
+ / PUT / PATCH / DELETE.
66
+ - **`Endpoint`** — bare verbs (`get`/`post`/…) for non-resource routes (health,
67
+ webhooks, actions). One path per Endpoint.
68
+ - Handler args bind **by name**, each a msgspec Struct: `json`, `content` (raw
69
+ bytes, exclusive with `json`), `params` (query), `path` (URL template slots),
70
+ `headers`, `user` (auth result). Return a Struct, `list[Struct]`, `bytes`, or a
71
+ `BytesResponse`/`JSONResponse`/streaming response.
72
+ - **A JSON body is always a Struct — never a raw `dict`.** The
73
+ `@api.get(...) → return {"a": 1}` idiom is gone: a `dict`/blob return is a
74
+ `WiringError` at startup. JSON in and out is a typed Struct, every time — that's
75
+ what gives it validation *and* a schema for the OpenAPI spec. No exceptions.
76
+ - **Auth**: an object with `authenticate(headers: Struct) -> UserStruct`; the
77
+ user type is checked against handlers at startup.
78
+ - **Wiring / DI**: there is **no DI container** — and that's deliberate, not a
79
+ gap. You hand-wire classes in `_wire` (subclass `BaseApp[Factory]`, linear
80
+ async, no yield); a dependency is just a constructor argument. The one thing
81
+ the language doesn't give you free — lifecycle — is what the framework adds:
82
+ open resources with `self._aenter` / `self._enter` (the app owns two exit
83
+ stacks, closed in reverse at shutdown, even on partial failure), and a
84
+ `BaseFactory` (stacks injected) groups construction. Past that there's nothing
85
+ to "resolve." Per-request resources are an `async with` inside the handler.
86
+ Do **not** add an injection/resolver system.
87
+ - REST error semantics throughout (404/400/422/401/405, auto HEAD + OPTIONS);
88
+ camelCase on the wire via msgspec `rename`.
89
+ - **Naming convention**: foundations you extend once are `Base*` (`BaseApp`,
90
+ `BaseFactory`); the request vocabulary you implement many specific subclasses of
91
+ stays plain (`Resource`, `Endpoint`).
92
+
93
+ ## Layout
94
+
95
+ - `jero/core.py` — the framework. `jero/testing.py` — sync in-process `TestClient`
96
+ + `FactoryHarness`. `jero/forms.py` / `jero/streaming.py` — form parts and
97
+ streaming response types.
98
+ - Runtime deps are intentionally sparse: `msgspec` for typed validation/JSON and
99
+ `python-multipart` for buffered `multipart/form-data` parsing.
100
+ - `tests/` — pytest suite driven through `TestClient` against demo apps in
101
+ `tests/demo_app.py`.
102
+ - `plans/` — design plans for not-yet-built features (e.g. `streaming.md`,
103
+ `forms.md`), staged for review before implementation.
104
+ - `bugs/` — one markdown note per bug, tracked in `bugs/README.md` (the manifest).
105
+ **Never delete a fixed bug note** — flip its row to `Done` in the manifest and
106
+ update the Open/Done counts. A fix isn't done until it has a regression test.
107
+ - Demo apps and the competitor/benchmark harness live in a **separate repo**, not here.
108
+
109
+ ## Status & sharp edges
110
+
111
+ - **Built**: routing + path-param templates, Resource/Endpoint, all binding
112
+ sources, auth, REST semantics, response kinds, `BaseApp`/`BaseFactory`
113
+ lifecycle, `TestClient`, the test suite.
114
+ - **Performance (validated natively)**: on the authed write path
115
+ (`POST /movies` — bearer auth + JSON decode + encode + 201, C=200), jero ≈
116
+ blacksheep (~43k req/s, a tie), ~2× litestar, ~3× robyn, ~6× idiomatic FastAPI.
117
+ Tight unimodal latency — trustworthy. (The benchmark harness lives in a
118
+ separate repo; run natively rather than under emulation for real figures.)
119
+ - **Unbuilt**: custom status codes, `Location` header on 201. Minor polish: the
120
+ factory's `es`/`aes` stack injection matches by name with no startup check — a
121
+ `WiringError` on an unrecognized param would close that.
122
+ - **Roadmap**: auto-generated **OpenAPI spec + live, hosted docs**. This is the
123
+ reason every endpoint must be statically typed end to end — the schema is
124
+ *derived from the types* (Struct sources, typed returns including generics like
125
+ `NDJSONStreamingResponse[Movie]`), with no runtime guessing. Any feature that
126
+ escapes static typing won't appear in the spec, so don't add one.
127
+ - **Untested**: no non-trivial real app has been built on it yet — that's where
128
+ the opinions (pagination, streaming, cross-cutting concerns) get stress-tested.
@@ -0,0 +1,126 @@
1
+ # Contributing to `jero`
2
+
3
+ Contributions are welcome, and they are greatly appreciated!
4
+ Every little bit helps, and credit will always be given.
5
+
6
+ You can contribute in many ways:
7
+
8
+ # Types of Contributions
9
+
10
+ ## Report Bugs
11
+
12
+ Report bugs at https://github.com/RogerThomas/jero/issues
13
+
14
+ If you are reporting a bug, please include:
15
+
16
+ - Your operating system name and version.
17
+ - Any details about your local setup that might be helpful in troubleshooting.
18
+ - Detailed steps to reproduce the bug.
19
+
20
+ ## Fix Bugs
21
+
22
+ Look through the GitHub issues for bugs.
23
+ Anything tagged with "bug" and "help wanted" is open to whoever wants to implement a fix for it.
24
+
25
+ ## Implement Features
26
+
27
+ Look through the GitHub issues for features.
28
+ Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it.
29
+
30
+ ## Write Documentation
31
+
32
+ jero could always use more documentation, whether as part of the official docs, in docstrings, or even on the web in blog posts, articles, and such.
33
+
34
+ ## Submit Feedback
35
+
36
+ The best way to send feedback is to file an issue at https://github.com/RogerThomas/jero/issues.
37
+
38
+ If you are proposing a new feature:
39
+
40
+ - Explain in detail how it would work.
41
+ - Keep the scope as narrow as possible, to make it easier to implement.
42
+ - Remember that this is a volunteer-driven project, and that contributions
43
+ are welcome :)
44
+
45
+ # Get Started!
46
+
47
+ Ready to contribute? Here's how to set up `jero` for local development.
48
+ Please note this documentation assumes you already have `uv` and `Git` installed and ready to go.
49
+
50
+ 1. Fork the `jero` repo on GitHub.
51
+
52
+ 2. Clone your fork locally:
53
+
54
+ ```bash
55
+ cd <directory_in_which_repo_should_be_created>
56
+ git clone git@github.com:YOUR_NAME/jero.git
57
+ ```
58
+
59
+ 3. Now we need to install the environment. Navigate into the directory
60
+
61
+ ```bash
62
+ cd jero
63
+ ```
64
+
65
+ Then, install and activate the environment with:
66
+
67
+ ```bash
68
+ uv sync
69
+ ```
70
+
71
+ 4. Install pre-commit to run linters/formatters at commit time:
72
+
73
+ ```bash
74
+ uv run pre-commit install
75
+ ```
76
+
77
+ 5. Create a branch for local development:
78
+
79
+ ```bash
80
+ git checkout -b name-of-your-bugfix-or-feature
81
+ ```
82
+
83
+ Now you can make your changes locally.
84
+
85
+ 6. Don't forget to add test cases for your added functionality to the `tests` directory.
86
+
87
+ 7. When you're done making changes, check that your changes pass the formatting tests.
88
+
89
+ ```bash
90
+ make check
91
+ ```
92
+
93
+ Now, validate that all unit tests are passing:
94
+
95
+ ```bash
96
+ make test
97
+ ```
98
+
99
+ 9. Before raising a pull request you should also run tox.
100
+ This will run the tests across different versions of Python:
101
+
102
+ ```bash
103
+ tox
104
+ ```
105
+
106
+ This requires you to have multiple versions of python installed.
107
+ This step is also triggered in the CI/CD pipeline, so you could also choose to skip this step locally.
108
+
109
+ 10. Commit your changes and push your branch to GitHub:
110
+
111
+ ```bash
112
+ git add .
113
+ git commit -m "Your detailed description of your changes."
114
+ git push origin name-of-your-bugfix-or-feature
115
+ ```
116
+
117
+ 11. Submit a pull request through the GitHub website.
118
+
119
+ # Pull Request Guidelines
120
+
121
+ Before you submit a pull request, check that it meets these guidelines:
122
+
123
+ 1. The pull request should include tests.
124
+
125
+ 2. If the pull request adds functionality, the docs should be updated.
126
+ Put your new functionality into a function with a docstring, and add the feature to the list in `README.md`.