lothc 0.0.1__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.
- lothc-0.0.1/.github/workflows/ci.yml +23 -0
- lothc-0.0.1/.github/workflows/docs.yml +29 -0
- lothc-0.0.1/.github/workflows/release.yml +57 -0
- lothc-0.0.1/.gitignore +29 -0
- lothc-0.0.1/.pre-commit-config.yaml +52 -0
- lothc-0.0.1/AGENTS.md +77 -0
- lothc-0.0.1/CLAUDE.md +427 -0
- lothc-0.0.1/LICENSE +21 -0
- lothc-0.0.1/PKG-INFO +106 -0
- lothc-0.0.1/README.md +81 -0
- lothc-0.0.1/Taskfile.yml +225 -0
- lothc-0.0.1/assets/favicon-ring.svg +89 -0
- lothc-0.0.1/assets/logo.svg +97 -0
- lothc-0.0.1/assets/perf-race.svg +214 -0
- lothc-0.0.1/asv.conf.json +22 -0
- lothc-0.0.1/asv_bench/__init__.py +0 -0
- lothc-0.0.1/asv_bench/_quick_check_worker.py +59 -0
- lothc-0.0.1/asv_bench/bench_download.py +113 -0
- lothc-0.0.1/asv_bench/bench_verbs.py +117 -0
- lothc-0.0.1/asv_bench/quick_check.py +137 -0
- lothc-0.0.1/benchmarks/Dockerfile +45 -0
- lothc-0.0.1/benchmarks/docker-compose.yml +24 -0
- lothc-0.0.1/benchmarks/entrypoint.sh +15 -0
- lothc-0.0.1/benchmarks/json_server/Cargo.toml +9 -0
- lothc-0.0.1/benchmarks/json_server/src/main.rs +60 -0
- lothc-0.0.1/benchmarks/race_svg.py +212 -0
- lothc-0.0.1/docs/assets/logo-dark.svg +97 -0
- lothc-0.0.1/docs/assets/logo-light.svg +97 -0
- lothc-0.0.1/docs/assets/perf-race.svg +1 -0
- lothc-0.0.1/docs/auth.md +63 -0
- lothc-0.0.1/docs/benchmarks.md +55 -0
- lothc-0.0.1/docs/development.md +15 -0
- lothc-0.0.1/docs/errors.md +31 -0
- lothc-0.0.1/docs/favicon.ico +0 -0
- lothc-0.0.1/docs/images/lothc-favicon.png +0 -0
- lothc-0.0.1/docs/index.md +190 -0
- lothc-0.0.1/docs/networking.md +16 -0
- lothc-0.0.1/docs/retries.md +26 -0
- lothc-0.0.1/docs/sse.md +185 -0
- lothc-0.0.1/docs/streaming.md +75 -0
- lothc-0.0.1/docs/stylesheets/extra.css +15 -0
- lothc-0.0.1/docs/verbs.md +143 -0
- lothc-0.0.1/examples/server.py +26 -0
- lothc-0.0.1/lothc/__init__.py +41 -0
- lothc-0.0.1/lothc/_client.py +1967 -0
- lothc-0.0.1/lothc/_compat.py +35 -0
- lothc-0.0.1/lothc/py.typed +0 -0
- lothc-0.0.1/perf.py +648 -0
- lothc-0.0.1/pyproject.toml +210 -0
- lothc-0.0.1/style-guide.md +365 -0
- lothc-0.0.1/tests/_server.py +261 -0
- lothc-0.0.1/tests/conftest.py +29 -0
- lothc-0.0.1/tests/test_auth.py +33 -0
- lothc-0.0.1/tests/test_cookies.py +17 -0
- lothc-0.0.1/tests/test_delete_head.py +34 -0
- lothc-0.0.1/tests/test_download.py +57 -0
- lothc-0.0.1/tests/test_get.py +122 -0
- lothc-0.0.1/tests/test_get_result.py +33 -0
- lothc-0.0.1/tests/test_redirects.py +16 -0
- lothc-0.0.1/tests/test_retries.py +66 -0
- lothc-0.0.1/tests/test_sse.py +76 -0
- lothc-0.0.1/tests/test_streaming.py +58 -0
- lothc-0.0.1/tests/test_transport_errors.py +24 -0
- lothc-0.0.1/tests/test_write.py +102 -0
- lothc-0.0.1/uv.lock +1938 -0
- lothc-0.0.1/vulture_whitelist.py +3 -0
- lothc-0.0.1/zensical.toml +126 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
pull_request:
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.13", "3.14"]
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: astral-sh/setup-uv@v10.0.1
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: uv sync --all-extras --group dev
|
|
20
|
+
- run: uv run ruff check .
|
|
21
|
+
- run: uv run ruff format --check .
|
|
22
|
+
- run: uv run basedpyright .
|
|
23
|
+
- run: uv run pytest tests
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Documentation
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- master
|
|
6
|
+
- main
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
pages: write
|
|
10
|
+
id-token: write
|
|
11
|
+
jobs:
|
|
12
|
+
deploy:
|
|
13
|
+
environment:
|
|
14
|
+
name: github-pages
|
|
15
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/configure-pages@v6
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: 3.x
|
|
23
|
+
- run: pip install zensical
|
|
24
|
+
- run: zensical build --clean
|
|
25
|
+
- uses: actions/upload-pages-artifact@v5
|
|
26
|
+
with:
|
|
27
|
+
path: site
|
|
28
|
+
- uses: actions/deploy-pages@v5
|
|
29
|
+
id: deployment
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
# Trusted Publishing (OIDC) — no PyPI token/secret needed. The environment name here must
|
|
11
|
+
# match the one configured as the trusted publisher on the PyPI project settings ("pypi").
|
|
12
|
+
environment:
|
|
13
|
+
name: pypi
|
|
14
|
+
url: https://pypi.org/p/lothc
|
|
15
|
+
permissions:
|
|
16
|
+
id-token: write
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
with:
|
|
20
|
+
# hatch-vcs derives the version from git tags — needs full history to see them,
|
|
21
|
+
# not just the single commit a shallow checkout would give it.
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
|
|
24
|
+
- uses: astral-sh/setup-uv@v10.0.1
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.13"
|
|
27
|
+
|
|
28
|
+
- run: uv build
|
|
29
|
+
|
|
30
|
+
- run: uv publish
|
|
31
|
+
|
|
32
|
+
deploy-docs:
|
|
33
|
+
needs: publish
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
pages: write
|
|
38
|
+
id-token: write
|
|
39
|
+
environment:
|
|
40
|
+
name: github-pages
|
|
41
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v7
|
|
44
|
+
|
|
45
|
+
- uses: astral-sh/setup-uv@v10.0.1
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.13"
|
|
48
|
+
|
|
49
|
+
- run: uv sync --group docs
|
|
50
|
+
- run: uv run zensical build
|
|
51
|
+
|
|
52
|
+
- uses: actions/upload-pages-artifact@v5
|
|
53
|
+
with:
|
|
54
|
+
path: site
|
|
55
|
+
|
|
56
|
+
- id: deployment
|
|
57
|
+
uses: actions/deploy-pages@v5
|
lothc-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
.ruff_cache/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.coverage
|
|
7
|
+
htmlcov/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.DS_Store
|
|
11
|
+
|
|
12
|
+
# Handy for temporary run scripts
|
|
13
|
+
tmp_*.py
|
|
14
|
+
run_*.py
|
|
15
|
+
|
|
16
|
+
# Local environment and scratch input files
|
|
17
|
+
.env
|
|
18
|
+
.in
|
|
19
|
+
|
|
20
|
+
# perf.py run output (JSON per run, named by ms-timestamp run id)
|
|
21
|
+
/results/
|
|
22
|
+
|
|
23
|
+
# zensical docs build output
|
|
24
|
+
/site/
|
|
25
|
+
|
|
26
|
+
# asv (airspeed velocity) benchmark environments/results/generated test data
|
|
27
|
+
/.asv/
|
|
28
|
+
/asv_bench/_data/
|
|
29
|
+
/asv_bench/.quick_check_history.json
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-added-large-files
|
|
10
|
+
- id: check-merge-conflict
|
|
11
|
+
|
|
12
|
+
- repo: local
|
|
13
|
+
hooks:
|
|
14
|
+
- id: ruff-check
|
|
15
|
+
name: ruff check
|
|
16
|
+
entry: uv run ruff check --fix
|
|
17
|
+
language: system
|
|
18
|
+
types: [python]
|
|
19
|
+
|
|
20
|
+
- id: ruff-format
|
|
21
|
+
name: ruff format
|
|
22
|
+
entry: uv run ruff format
|
|
23
|
+
language: system
|
|
24
|
+
types: [python]
|
|
25
|
+
|
|
26
|
+
- id: basedpyright
|
|
27
|
+
name: basedpyright
|
|
28
|
+
entry: uv run basedpyright .
|
|
29
|
+
language: system
|
|
30
|
+
files: \.py$
|
|
31
|
+
pass_filenames: false
|
|
32
|
+
|
|
33
|
+
- id: deptry
|
|
34
|
+
name: deptry
|
|
35
|
+
entry: uv run deptry .
|
|
36
|
+
language: system
|
|
37
|
+
files: \.py$
|
|
38
|
+
pass_filenames: false
|
|
39
|
+
|
|
40
|
+
- id: vulture
|
|
41
|
+
name: vulture
|
|
42
|
+
entry: uv run vulture --min-confidence 100 lothc tests examples benchmarks perf.py vulture_whitelist.py
|
|
43
|
+
language: system
|
|
44
|
+
files: \.py$
|
|
45
|
+
pass_filenames: false
|
|
46
|
+
|
|
47
|
+
- id: pylint
|
|
48
|
+
name: pylint
|
|
49
|
+
entry: uv run pylint lothc
|
|
50
|
+
language: system
|
|
51
|
+
files: \.py$
|
|
52
|
+
pass_filenames: false
|
lothc-0.0.1/AGENTS.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# lothc — agent reference
|
|
2
|
+
|
|
3
|
+
Typed HTTP client on pyreqwest. `HTTPClient` (async) / `SyncHTTPClient` (sync) — identical API,
|
|
4
|
+
mirror methods 1:1 (drop `await`, `async with` → `with`, async iterators → sync iterators).
|
|
5
|
+
|
|
6
|
+
## Build
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
async with HTTPClient.build(
|
|
10
|
+
base_url=None,
|
|
11
|
+
bearer_token=None,
|
|
12
|
+
bearer_auth=None,
|
|
13
|
+
default_headers=None,
|
|
14
|
+
timeout=30.0,
|
|
15
|
+
cookie_store=False,
|
|
16
|
+
follow_redirects=True,
|
|
17
|
+
max_redirects=None,
|
|
18
|
+
proxy=None,
|
|
19
|
+
max_retries=0,
|
|
20
|
+
retry_methods=None,
|
|
21
|
+
) as client:
|
|
22
|
+
...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`bearer_token: str` (static) xor `bearer_auth: Callable[[], Awaitable[str]]` (sync client:
|
|
26
|
+
`Callable[[], str]`) — resolved fresh per request, at most one of the two. `default_headers`
|
|
27
|
+
sent on every request. `cookie_store=True` = in-memory jar. `proxy: str | None`.
|
|
28
|
+
|
|
29
|
+
## Decode targets (`response_data_type`, default `bytes`)
|
|
30
|
+
|
|
31
|
+
- `bytes` — raw, default
|
|
32
|
+
- `lothc.JSON` — `dict[str, Any]` subclass, zero validation
|
|
33
|
+
- pydantic `BaseModel` subclass
|
|
34
|
+
- msgspec `Struct` subclass
|
|
35
|
+
- `TypedDict` subclass — validated via typeguard if installed, else warns once
|
|
36
|
+
(`LOTHC_SUPPRESS_TYPEGUARD_WARNING=1` to silence)
|
|
37
|
+
- bare `dict` / `dict[str, Any]` — NOT allowed, raises `TypeError`
|
|
38
|
+
|
|
39
|
+
## Verbs
|
|
40
|
+
|
|
41
|
+
- `get(path, *, params=None, headers=None, response_data_type=bytes, error_for_status=True) -> Data`
|
|
42
|
+
- `get_result(path, *, params=None, headers=None, response_data_type=bytes, headers_type=None, error_for_status=True) -> Result` —
|
|
43
|
+
`.data .status .headers .typed_headers`
|
|
44
|
+
- `post/put/patch(path, *, params=None, headers=None, json=None, form=None, content=None, response_data_type=bytes, error_for_status=True) -> Data` —
|
|
45
|
+
at most one of `json`/`form`/`content`, else `ValueError`
|
|
46
|
+
- `delete(path, *, params=None, headers=None, response_data_type=bytes, error_for_status=True) -> Data`
|
|
47
|
+
- `head(path, *, params=None, headers=None, headers_type=None, error_for_status=True) -> Result[None]`
|
|
48
|
+
- `sse(path, *, params=None, headers=None, response_data_type=None, id_type=str, error_for_status=True) -> Iterator[SSEEvent[TData, TId]]` —
|
|
49
|
+
always yields `SSEEvent(id=, event=, data=)` (kw-only, `SSEEvent[TData, TId=str]`).
|
|
50
|
+
`response_data_type` controls `.data`'s type only (default `str`); class | pydantic
|
|
51
|
+
`TypeAdapter` | msgspec `Decoder`. `.event` is always `str`, never `None` (spec defaults it to
|
|
52
|
+
`"message"` when absent from the wire). `.id` is genuinely `str | None` per spec — `id_type` is
|
|
53
|
+
the single knob for both requiredness and type: `str` (default, required, no coercion) | any
|
|
54
|
+
other bare type e.g. `int`/`uuid.UUID` (required, coerced via `id_type(raw)`) | that type
|
|
55
|
+
unioned with `None` e.g. `int | None` (optional, coerced when present, `None` when absent) |
|
|
56
|
+
bare `None` (optional, `.id: str | None`, never coerced) — pass a union the same way you'd
|
|
57
|
+
write `response_data_type=A | B` for a discriminated union, `TId` binds to whatever you pass
|
|
58
|
+
- `stream_get(path, *, params=None, headers=None, response_data_type=None, error_for_status=True) -> Iterator[bytes | TLine]` —
|
|
59
|
+
raw unbuffered bytes by default (safe for binary); `response_data_type` switches to
|
|
60
|
+
newline-buffered per-line decode
|
|
61
|
+
- `stream_post(path, *, params=None, headers=None, json=None, form=None, content=None, response_data_type=None, error_for_status=True) -> Iterator[bytes | TLine]`
|
|
62
|
+
|
|
63
|
+
`params`/`headers`: `dict`/`Mapping[str, str]`, or `BaseModel`/`Struct` (`None` fields omitted).
|
|
64
|
+
`json`: `dict` | `BaseModel` | `Struct`. `form`: `dict[str, int | bytes | str | File]`, `File =
|
|
65
|
+
tuple[str, bytes] | Path | BufferedIOBase`. `content`: raw `str | bytes` body.
|
|
66
|
+
|
|
67
|
+
## Errors
|
|
68
|
+
|
|
69
|
+
- `ResponseError(status, body_start)` — 4xx/5xx, raised when `error_for_status=True` (default)
|
|
70
|
+
- `TransportError` base; `TimeoutError`, `ConnectionError` subclasses — no response received
|
|
71
|
+
- pydantic/msgspec/typeguard validation errors propagate unwrapped (not translated)
|
|
72
|
+
|
|
73
|
+
## Retries
|
|
74
|
+
|
|
75
|
+
`max_retries` (default `0` = off), `retry_methods` (default `{GET,PUT,DELETE,HEAD}` — `POST`/
|
|
76
|
+
`PATCH` need explicit opt-in). Exponential backoff; retries on transport error or status in
|
|
77
|
+
`{429,500,502,503,504}`; honors `Retry-After`.
|