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 PyPy support status 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.

Requirements

  • CPython 3.8-3.14 or PyPy 3.8-3.11

  • httpx>=0.28.1,<1.0.0

  • qs-codec>=1.3.1

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.2.1.tar.gz (12.2 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.2.1-py3-none-any.whl (8.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: httpx_qs-0.2.1.tar.gz
  • Upload date:
  • Size: 12.2 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.2.1.tar.gz
Algorithm Hash digest
SHA256 4408adef09c690ea9d4cf2acfcc12c7ff9637162ce6fcaef327cfb3d40fac024
MD5 757c42f5e09155300268e5bee7da66db
BLAKE2b-256 e9edc6440bba0963a6df5466d5273ef203bcbc26ef0f66586cc7627b5b3baa57

See more details on using hashes here.

Provenance

The following attestation bundles were made for httpx_qs-0.2.1.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.2.1-py3-none-any.whl.

File metadata

  • Download URL: httpx_qs-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 8.7 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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d6c7a6735355e9b92cc839b897f8d5513f09417c5f42a7a731b7c08f2a01700e
MD5 10fb883cd81d676547f1d71c3b30b77d
BLAKE2b-256 0bd58d51f70ff2e912d7085002b9745f8ac9f979cb36aa17fb3c6d488490ba90

See more details on using hashes here.

Provenance

The following attestation bundles were made for httpx_qs-0.2.1-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