Skip to main content

serp-fang (Fang)

Fang is Serpentine's synchronous HTTP(S) client — the httpx equivalent. It is layered like the Rust HTTP stacks: a native transport core (serp-socket: std::net + system TLS — schannel on Windows, OpenSSL on Linux/macOS) with a thin pure-Serpentine façade that mirrors httpx names and behavior. The same source dual-runs under CPython (ssl module).

from serp_fang import Client, Response, client, get, post

r: Response = get("https://example.com/")
print(r.status_code, r.header("content-type"))
print(r.text())
r.raise_for_status()

p: Response = post("https://api.example/items", b'{"name": "fang"}',
                   content_type="application/json")
j = p.json()

c: Client = client("https://api.example", timeout=10.0, follow_redirects=True)
c.set_basic_auth("user", "secret")
r2: Response = c.get("/items")

httpx-equivalent API

Top-level verbs (each takes timeout=5.0, follow_redirects=False, verify=True keywords, matching httpx defaults):

  • get / head / options / delete (url, ...) — get also takes auth_user / auth_pass
  • post / put / patch (url, content, content_type=...) — raw-body variants
  • post_form(url, form) — httpx data=...
  • post_json(url, payload) — httpx json=... (payload is a dynamic PyVal)
  • post_multipart(url, fields, files, boundary="") — httpx files=... with FilePart
  • request(method, url, ...) — bodyless generic request
  • stream(method, url, ...) -> ResponseStream — httpx stream: read() chunks, close()
  • download(url, path, ...) — streams a body straight to disk (constant memory)
  • upload_file(method, url, path, content_type=...) — file body, wire-chunked sends
  • helpers: with_params(url, params) (httpx params=...), basic_auth(user, pw), build_multipart, multipart_content_type, header_value, parse_response, decode_chunked

Client (httpx.Client): base_url, default headers, persistent cookies jar, timeout, follow_redirects, max_redirects, verify, basic auth; methods mirror the verbs plus stream/download/upload_file/set_header/ set_cookie/set_basic_auth/close. Create with client(...) (kwargs like httpx.Client).

Response (httpx.Response): status_code, reason_phrase, http_version, url (final URL after redirects), headers, content, history (pre-redirect URLs), elapsed; methods text(), json(), header(name), cookies(), is_success()/is_redirect()/is_client_error()/is_server_error()/ is_error()/is_informational(), has_redirect_location(), raise_for_status() (raises HTTPStatusError).

Behavior parity with httpx:

  • https with SNI + system trust roots; verify=False opt-out; cert failures raise OSError("certificate verify failed").
  • Redirects (when follow_redirects=True): 301/302/303 convert to GET and drop the body (307/308 preserve), Location is resolved with urljoin, authorization is stripped on cross-host hops, TooManyRedirects past max_redirects.
  • Cookies: set-cookie responses populate the client jar and are replayed.
  • accept-encoding: gzip is sent and gzip/deflate bodies decode transparently (streams request identity instead).
  • Content-Length and chunked framing, incremental in ResponseStream.

Divergences from httpx (language subset)

  • Properties are methods: r.text(), not r.text.
  • kwargs that take lists/dicts/bytes in httpx are explicit parameters or Client fields (defaults may only be scalar literals).
  • One connection per request (connection: close) — no pooling/keep-alive, no HTTP/2, no proxies.
  • text() decodes UTF-8 (no charset sniffing).
  • Transport errors are the normalized serp-socket OSErrors ("connection refused", "timed out", "tls handshake failed", ...).

Async (AsyncClient, D45)

async with AsyncClient(base_url="http://127.0.0.1:8000", timeout=5.0) as client:
    r: Response = await client.get("/items/1")

AsyncClient runs the same engine over sockets in async mode: every would-block suspends only the calling task, so asyncio.gather of several requests overlaps them (four 300 ms requests complete in ~0.3 s natively). Methods: request/get/head/options/ delete/post/put/patch/post_form/post_json/stream/aclose, set_header/set_basic_auth/ set_cookie, async with. Divergences: stream() returns a ResponseStream whose read() is a plain (cooperative) call — no aiter_bytes; connects/TLS handshakes are still blocking; under CPython the async mode is a no-op, so requests there run one at a time.

Metadata

Release files for serp-fang 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for serp-fang 0.3.1
File Size Uploaded
serp_fang-0.3.1.tar.gz 13.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for serp-fang 0.3.1
File Interpreter ABI Platform
serp_fang-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 24.7 kB

Release files / serp_fang-0.3.1.tar.gz

Download URL serp_fang-0.3.1.tar.gz
Size 13.2 kB
Tags Source
SHA-256 checksum
How to use checksums
0286d3ec6604c964bdbc4bbaac585ab88c40ebbf8a7e9feed0d037b851799959
BLAKE2b-256 checksum
How to use checksums
dacb6b27df13f7c4c51d35fc654cb5b750a783cc76a67972e05b3e67beff1bf1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / serp_fang-0.3.1-py3-none-any.whl

Download URL serp_fang-0.3.1-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
441d020f5a4996c09b7b49a35e8bc4a0378418971533dd662d9d124d7b637d6a
BLAKE2b-256 checksum
How to use checksums
0b5234bade2f2ecbf9dde8b731ac6f251267e2fd128a8f7321f49ce6f8b0841e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page