Skip to main content

HTTPX transport leveraging qs-codec for advanced query string encoding and decoding.

Project description

Smart, policy-driven query string merging & encoding for httpx powered by qs-codec.

PyPI version PyPI - Status Supported Python versions PyPI - Format Tests CodeQL License Codecov Codacy Quality GitHub Sponsors GitHub Repo stars Contributor Covenant mypy pylint isort Security Status

Overview

httpx-qs provides:

  • A transport wrapper SmartQueryStrings that merges existing URL query parameters with additional ones supplied via request.extensions.

  • A flexible merge_query utility with selectable conflict resolution policies.

  • Consistent, standards-aware encoding via qs-codec (RFC3986 percent-encoding, structured arrays, nested objects, etc.).

Why?

HTTPX already lets you pass params= when making requests, but sometimes you need to:

  • Inject additional query parameters from middleware/transport layers (e.g., auth tags, tracing IDs, feature flags) without losing the caller’s original intent.

  • Combine repeated keys or treat them deterministically (replace / keep / error) rather than always flattening.

  • Support nested data or list semantics consistent across clients and services.

qs-codec supplies the primitives (decoding & encoding with configurable ListFormat). httpx-qs stitches that into HTTPX’s transport pipeline so you can declaratively extend queries at request dispatch time.

Installation

pip install httpx-qs

Minimal Example

import httpx
from httpx_qs.transporters.smart_query_strings import SmartQueryStrings

client = httpx.Client(transport=SmartQueryStrings(httpx.HTTPTransport()))

response = client.get(
        "https://www.google.com",
        params={"a": "b", "c": "d"},
        extensions={"extra_query_params": {"c": "D", "tags": ["x", "y"]}},
)

print(str(response.request.url))
# Example (order may vary): https://www.google.com/?a=b&c=d&c=D&tags=x&tags=y

Using Merge Policies

Conflict resolution when a key already exists is controlled by MergePolicy.

Available policies:

  • combine (default): concatenate values → existing first, new afterward (a=1&a=2)

  • replace: last-wins, existing value is overwritten (a=2)

  • keep: first-wins, ignore the new value (a=1)

  • error: raise ValueError on duplicate key

Specify per request:

from httpx_qs import MergePolicy

r = client.get(
        "https://api.example.com/resources",
        params={"dup": "original"},
        extensions={
                "extra_query_params": {"dup": "override"},
                "extra_query_params_policy": MergePolicy.REPLACE,
        },
)
# Query contains only dup=override

Async Usage

SmartQueryStrings works equally for AsyncClient:

import httpx
from httpx_qs.transporters.smart_query_strings import SmartQueryStrings

async def main() -> None:
        async with httpx.AsyncClient(transport=SmartQueryStrings(httpx.AsyncHTTPTransport())) as client:
                r = await client.get(
                        "https://example.com/items",
                        params={"filters": "active"},
                        extensions={"extra_query_params": {"page": 2}},
                )
                print(r.request.url)

# Run with: asyncio.run(main())

merge_query Utility

You can use the underlying function directly:

from httpx_qs import merge_query, MergePolicy
from qs_codec import EncodeOptions, ListFormat

new_url = merge_query(
        "https://example.com?a=1",
        {"a": 2, "tags": ["x", "y"]},
        options=EncodeOptions(list_format=ListFormat.REPEAT),
        policy=MergePolicy.COMBINE,
)
# → https://example.com/?a=1&a=2&tags=x&tags=y

Why ListFormat.REPEAT by Default?

qs-codec exposes several list formatting strategies (e.g. repeat, brackets, indices). httpx-qs defaults to ListFormat.REPEAT because:

  • It matches common server expectations (key=value&key=value) without requiring bracket parsing logic.

  • It preserves original ordering while remaining unambiguous and simple for log inspection.

  • Many API gateways / proxies / caches reliably forward repeated keys whereas bracket syntaxes can be normalized away.

If your API prefers another convention (e.g. tags[]=x&tags[]=y or tags[0]=x) just pass a custom EncodeOptions via extensions['extra_query_params_options'] or parameter options when calling merge_query directly.

Advanced Per-Request Customization

from qs_codec import EncodeOptions, ListFormat

r = client.get(
        "https://service.local/search",
        params={"q": "test"},
        extensions={
                "extra_query_params": {"debug": True, "tags": ["alpha", "beta"]},
                "extra_query_params_policy": "combine",  # also accepts string values
                "extra_query_params_options": EncodeOptions(list_format=ListFormat.BRACKETS),
        },
)
# Example: ?q=test&debug=true&tags[]=alpha&tags[]=beta

Error Policy Example

try:
        client.get(
                "https://example.com",
                params={"token": "abc"},
                extensions={
                        "extra_query_params": {"token": "xyz"},
                        "extra_query_params_policy": "error",
                },
        )
except ValueError as exc:
        print("Duplicate detected:", exc)

Testing Strategy

The project includes unit tests covering policy behaviors, error handling, and transport-level integration. Run them with:

pytest

Further Reading

License

BSD-3-Clause. See LICENSE for details.

Contributing

Issues & PRs welcome. Please add tests for new behavior and keep doc examples in sync.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

httpx_qs-0.1.0.tar.gz (11.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

httpx_qs-0.1.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file httpx_qs-0.1.0.tar.gz.

File metadata

  • Download URL: httpx_qs-0.1.0.tar.gz
  • Upload date:
  • Size: 11.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for httpx_qs-0.1.0.tar.gz
Algorithm Hash digest
SHA256 aeef530ea93e4a2d7c97ec25eaa21202d10d027ea39ae8d4877a43fcd4dea424
MD5 60589df505fd6b38c206cdd43898e5a1
BLAKE2b-256 e5d512f0ab325e0efbbdf729d2932d43007ddf99f49355b67e3b75f89eef21ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for httpx_qs-0.1.0.tar.gz:

Publisher: publish.yml on techouse/httpx_qs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file httpx_qs-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: httpx_qs-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for httpx_qs-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d6398943921fc2a12afbf2bc549086a9edc580590de9ad0f75b181bb6db4d6c6
MD5 6a6cb023d9c79111ccb8c9f7dfe81a8f
BLAKE2b-256 2635d8d71bfa821c91325962e65128e3e6de7530dda24d5eae9cce7b8fec8c24

See more details on using hashes here.

Provenance

The following attestation bundles were made for httpx_qs-0.1.0-py3-none-any.whl:

Publisher: publish.yml on techouse/httpx_qs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page