Sync and async IPP/IPPS client: send print jobs and monitor network printers
Project description
ippx
Sync and async IPP/IPPS client for Python. Send print jobs and monitor network printers, including printers exposed over the internet behind TLS with source IP allowlisting.
Built on httpx with a pure sans-IO codec for the IPP binary protocol (RFC 8010) and the RFC 8011 required operation set.
ippx is a plain library: the only runtime dependency is httpx. No framework, no server, no Docker required. Use it from a script, a CLI, a cron job, a serverless function, or an async web app.
Why
pyipp is monitoring only. ippx can actually print, in both async (FastAPI-native) and sync code, with first-class support for the realities of network printers: self-signed certificates, legacy cipher suites, HTTP Basic auth, and flaky WAN links.
Install
pip install ippx
Python 3.11+. Single runtime dependency: httpx. Fully typed (PEP 561).
Quick start (async, e.g. inside FastAPI)
from ippx import AsyncIppClient, BasicAuth, TlsConfig
async def print_invoice(pdf_bytes: bytes) -> None:
async with AsyncIppClient(
"ipps://printer.example.com:631/ipp/print",
auth=BasicAuth("user", "password"),
tls=TlsConfig(fingerprint="sha256:AB:CD:..."),
) as printer:
job = await printer.print_job(
pdf_bytes,
document_format="application/pdf",
job_name="invoice-123",
job_attributes={"copies": 1, "sides": "two-sided-long-edge"},
)
result = await printer.wait_for_job(job.job_id, timeout=120)
print(result.state, result.state_reasons)
Quick start (sync)
from ippx import IppClient
with IppClient("ipps://203.0.113.10:631/ipp/print") as printer:
caps = printer.get_printer_attributes()
print(caps.make_and_model, caps.state, caps.document_formats)
job = printer.print_job(pdf_bytes, document_format="application/pdf")
Operations
The RFC 8011 required operation set, supported by every conformant printer:
| Method | IPP operation |
|---|---|
print_job(document, ...) |
Print-Job |
validate_job(...) |
Validate-Job (pre-flight without printing) |
cancel_job(job_id) |
Cancel-Job |
get_printer_attributes(...) |
Get-Printer-Attributes |
get_job_attributes(job_id, ...) |
Get-Job-Attributes |
get_jobs(...) |
Get-Jobs |
wait_for_job(job_id, timeout=...) |
polling helper over Get-Job-Attributes |
wait_for_job polls with exponential backoff (1s doubling to a 15s cap by
default) until the job reaches a terminal state (completed, canceled,
aborted) and raises JobTimeoutError otherwise.
A printer's full operations-supported list decodes to the named Operation
enum (the complete IANA IPP registry, not just the implemented set), so you
can introspect capabilities like Operation.IDENTIFY_PRINTER.
Note that for get_jobs most printers only return job-id and job-uri by
default, per RFC 8011; pass
requested_attributes=["job-id", "job-state", "job-name"] to get more.
Authentication
- HTTP Basic:
auth=ippx.BasicAuth("user", "pw")(or a plain("user", "pw")tuple) - HTTP Digest:
auth=ippx.DigestAuth("user", "pw") - TLS client certificate:
tls=TlsConfig(client_cert=("cert.pem", "key.pem")) - requesting-user-name: set via
requesting_user_name=, sent on every request (identification only, not authentication)
TLS
Printers almost universally ship self-signed certificates. TlsConfig gives
you four options, strongest first:
TlsConfig() # system CA validation (default)
TlsConfig(verify="/path/to/printer-ca.pem") # custom CA bundle
TlsConfig(fingerprint="sha256:AB:CD:...") # pin the exact certificate
TlsConfig(verify=False) # no validation (last resort)
Fingerprint pinning verifies the SHA-256 digest of the server certificate during the TLS handshake itself, so there is no window between checking and using the connection. Get a printer's fingerprint with:
openssl s_client -connect printer:631 < /dev/null 2>/dev/null \
| openssl x509 -fingerprint -sha256 -noout
For an internet-exposed printer (port forward locked to a source WAN IP), the recommended setup is fingerprint pinning plus HTTP Basic auth.
Two behaviours worth knowing:
- Printers very commonly offer only plain-RSA key exchange (no ECDHE), which
stock OpenSSL policy rejects with a bare handshake-failure alert. Every
context ippx builds therefore uses
DEFAULT:@SECLEVEL=1ciphers so real hardware is reachable and genuine certificate problems surface as truthful certificate errors; override withTlsConfig(ciphers=...)to tighten or loosen. - With a custom CA bundle, hostname verification is disabled (printers are usually addressed by IP), so any certificate issued by that CA is accepted.
Job attributes
Pass common Job Template attributes as a plain dict; tags are inferred for
copies, sides, media, print-quality, print-color-mode,
orientation-requested, output-bin, number-up, printer-resolution and
others. For anything else, pass ippx.Attribute objects with explicit tags.
Verified hardware
| Printer | Print-Job | Status | Notes |
|---|---|---|---|
| HP Color LaserJet Pro M283fdw | yes | verified | Full IPPS path with fingerprint pinning: Get-Printer-Attributes (128 attrs incl. nested media-col collections), Validate-Job, Print-Job to completion, job polling, Get-Jobs, sync and async. Offers RSA-key-exchange ciphers only, handled by the default TlsConfig cipher policy. |
Also verified end-to-end against a live CUPS 2.4 server (Get-Printer-Attributes, Validate-Job, Print-Job, job polling to completion, Get-Jobs, sync and async clients). Contributions to this table are welcome.
Not in scope (yet)
- Document conversion: bring your own PDF/PCL/PWG raster bytes
- Create-Job / Send-Document multi-document jobs
- IPP event subscriptions (RFC 3995): printer firmware support is too rare
to rely on; use
wait_for_jobpolling - Encoding IPP collections in requests (decoding is fully supported)
- mDNS/driverless discovery
Development
pip install -e .[dev]
ruff check src tests
pytest
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ippx-0.1.0.tar.gz.
File metadata
- Download URL: ippx-0.1.0.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a8eba0c6d12983695a7b20eb14a597b61c437b2794622190e5963ad4049d946
|
|
| MD5 |
1a1e9a64b07c13910508739d296dfb17
|
|
| BLAKE2b-256 |
3beb0284211a3bc8a37e6840c011fc654b22dafa7ad75799fc5a69fdbadd033a
|
Provenance
The following attestation bundles were made for ippx-0.1.0.tar.gz:
Publisher:
release.yml on smck83/ippx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ippx-0.1.0.tar.gz -
Subject digest:
7a8eba0c6d12983695a7b20eb14a597b61c437b2794622190e5963ad4049d946 - Sigstore transparency entry: 1808873637
- Sigstore integration time:
-
Permalink:
smck83/ippx@fef8a9a3b708ca8a351d73e4ab6fb934d13e0780 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/smck83
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fef8a9a3b708ca8a351d73e4ab6fb934d13e0780 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ippx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ippx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb1b4ede7bc4813c313e4a899414024d3ce9dc26639a0dc8c58fe430ac49150e
|
|
| MD5 |
5d93b408fe707355bd8292adbd111f09
|
|
| BLAKE2b-256 |
ec3bbf595ae13e73a301ab71d2a2d76334914f9fde30daa1f8e17b5ab97a3e36
|
Provenance
The following attestation bundles were made for ippx-0.1.0-py3-none-any.whl:
Publisher:
release.yml on smck83/ippx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ippx-0.1.0-py3-none-any.whl -
Subject digest:
eb1b4ede7bc4813c313e4a899414024d3ce9dc26639a0dc8c58fe430ac49150e - Sigstore transparency entry: 1808873640
- Sigstore integration time:
-
Permalink:
smck83/ippx@fef8a9a3b708ca8a351d73e4ab6fb934d13e0780 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/smck83
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fef8a9a3b708ca8a351d73e4ab6fb934d13e0780 -
Trigger Event:
release
-
Statement type: