Skip to main content

py-doh-core

Python bindings for doh-core, a Rust DNS resolver library that only speaks secure transports — DNS-over-HTTPS (RFC 8484), DNS-over-TLS (RFC 7858), and DNS-over-QUIC (RFC 9250) — with no fallback to classic plaintext DNS. If a query can't be answered securely, you get a clear exception instead of a silent, unencrypted lookup.

pip install py-doh-core

Quickstart

import py_doh_core as doh

transport = doh.DohTransport("https://dns.google/dns-query")
response = transport.resolve("example.com", "A")

for answer in response.answers:
    print(answer.name, answer.ttl, answer.rdata)

Every resolve() has an async def-compatible aresolve() twin:

import asyncio
import py_doh_core as doh


async def main() -> None:
    transport = doh.DotTransport("dns.google")
    response = await transport.aresolve("example.com", "AAAA")
    print(response.answers[0].rdata)


asyncio.run(main())

resolve() blocks the calling thread but releases the GIL for the network call, so other Python threads keep running; aresolve() returns a normal awaitable. Both forms exist on all three transports below.

Transports

Class Protocol Constructor
DohTransport DNS-over-HTTPS DohTransport(server_url, method=None) — server_url e.g. "https://dns.google/dns-query" (must be https://); method is "get" (default) or "post"
DotTransport DNS-over-TLS DotTransport(server_addr) — server_addr is host[:port], default port 853
DoqTransport DNS-over-QUIC DoqTransport(server_addr) — same host[:port] form; the QUIC connection is pooled and reused across every resolve()/aresolve() call on the instance

All three share the same API: resolve, aresolve, resolve_many, aresolve_many. Pick a transport, not a different way of calling it.

Responses are typed, not dicts

resolve()/aresolve() return a ParsedResponse with real attributes — response.answers[0].rdata, not response["answers"][0]["rdata"] — mirroring every field of the underlying doh_core::ParsedResponse (header flags, question, answer/authority/additional sections, wire size). op_code and response_code are OpCode/ResponseCode enums, not magic strings or ints:

if response.response_code == doh.ResponseCode.NXDOMAIN:
    ...  # the name doesn't exist -- this is a successful response, not an error

See py_doh_core.pyi for the full type signatures.

Handling errors

Anything that isn't a clean "no error" or "name does not exist" response — a bad server URL, a connection failure, SERVFAIL, REFUSED, a malformed reply — raises doh.DohError rather than returning a half-empty result:

try:
    response = transport.resolve("dnssec-failed.org", "A")
except doh.DohError as exc:
    print("lookup failed:", exc)

str(exc) carries the same message the underlying Rust library produces.

Multiple record types in one call

resolve_many()/aresolve_many() query several record types for one name against a single transport instance. Queries run in turn, reusing the connection where that matters (DoqTransport's pooled connection in particular), and one type's failure doesn't abort the rest — each entry in the returned list is a QueryResult with exactly one of response/error set:

for result in transport.resolve_many("example.com", ["A", "AAAA", "MX"]):
    if result.error is not None:
        print(result.record_type, "failed:", result.error)
    else:
        print(result.record_type, [a.rdata for a in result.response.answers])

An unparseable record type string is the one exception: it raises DohError immediately, before any query in the batch is sent.

Logging

Verbose/debug output uses Python's standard logging module — no separate init call needed:

import logging

logging.basicConfig(level=logging.DEBUG)

Logger names follow the Rust module path, e.g. doh_core.transport.doh, doh_core.transport.doq, py_doh_core.transport. DEBUG shows one line per query (server, method, connection reuse, response codes); a small amount of extra detail (e.g. response sizes) logs at level 5, below logging.DEBUG (10) — pass level=5 to see it. Scope to just this library with logging.getLogger("doh_core").setLevel(logging.DEBUG).

Only doh_core/py_doh_core targets are bridged to Python — dependency crates (reqwest, h2, rustls, quinn) are deliberately not, since their logging runs on long-lived background threads that can outlive a single resolve() call and, in rare cases, still be active as the Python interpreter shuts down.

Development

py-doh-core is a PyO3 cdylib extension module, built with maturin. It's intentionally excluded from the main Cargo workspace (see the root Cargo.toml) since it needs maturin's linker setup to resolve Python symbols at import time — plain cargo build/cargo test --workspace can't link it.

Build from source

cd py-doh-core
python3 -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop

Tests

pip install pytest pytest-asyncio
pytest

tests/test_resolve.py runs live against real public resolvers (no mocking layer, same approach the Rust side uses). DoT/DoQ cases skip automatically if port 853 is unreachable on the current network.

Metadata

Release files for py-doh-core 0.1.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 py-doh-core 0.1.1
File Size Uploaded
py_doh_core-0.1.1.tar.gz 82.8 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for py-doh-core 0.1.1
File
py_doh_core-0.1.1-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details
py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
py_doh_core-0.1.1-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
py_doh_core-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl CPython 3.9 abi3 macOS 10.12+ x86-64 Details

Total release size: 18.0 MB

Release files / py_doh_core-0.1.1.tar.gz

Download URL py_doh_core-0.1.1.tar.gz
Size 82.8 kB
Tags Source
SHA-256 checksum
How to use checksums
469166eac33639a08abf4b809ef1499c70185ea401e0c8340b224111d13a6684
BLAKE2b-256 checksum
How to use checksums
82c1ca80dec7fb696e8a9e2a2bfba7d9fe77b4a4220eacaf1e87ae84311911d0
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 Aug 18, 2026.

Transparency log

Release files / py_doh_core-0.1.1-cp39-abi3-win_amd64.whl

Download URL py_doh_core-0.1.1-cp39-abi3-win_amd64.whl
Size 3.1 MB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
297d83e182c306796c9ab889b35aaefaeef13c55bba6902529d80e5f550ba67e
BLAKE2b-256 checksum
How to use checksums
a605c723b966676b5891ec4b82b4cff1d9ff5fe0f3bd5878f0018985f5d2c055
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 Aug 18, 2026.

Transparency log

Release files / py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.8 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
ff49d082aeda195ef7d52b52de4a8b9a7476c339b95cbc29e8e09db0933473db
BLAKE2b-256 checksum
How to use checksums
262c6d2064f43a8bfe92bd2d944a062ee3117fb3217e356d0fa7bb905d65ca00
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 Aug 18, 2026.

Transparency log

Release files / py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL py_doh_core-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 3.9 MB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
f7c31b03ed2fa3db3b7325c2c8ac23bcd1ca54be07bc8dcf8cab322a4c0ab879
BLAKE2b-256 checksum
How to use checksums
44d3d9a38b29d9a302bd113dbf8a805850edfed601f5760bedbe0e30eb6248e1
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 Aug 18, 2026.

Transparency log

Release files / py_doh_core-0.1.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL py_doh_core-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
Size 3.5 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
fba80694184d11f6e906931ec466c8b0cc2cba98f8547ac7db2059cb316a6d2c
BLAKE2b-256 checksum
How to use checksums
60910bbaec28e2b04f4e5ea3f389ea6abde04874b55e05086acf86b44687e790
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 Aug 18, 2026.

Transparency log

Release files / py_doh_core-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl

Download URL py_doh_core-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl
Size 3.6 MB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
a05f47e3b1f6fe5c7f3ca30f5aa9b2eb99e6813535b1fbaec17f67505496e874
BLAKE2b-256 checksum
How to use checksums
0e918eeed73d9f3d897e63d1a3472d60802af31fc5a2862c26d5bd335cc4f982
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 Aug 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

6 release files

0.1.0

6 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