A tolerant, self-documenting Python SOAP client library
Project description
soapix
A tolerant, self-documenting Python SOAP client library.
soapix is designed to work with real-world SOAP services that don't perfectly follow the spec — handling namespace quirks, loose validation, and unclear error messages that break other libraries.
Built for the real world: where WSDL files are messy, error messages are cryptic, and you need async out of the box.
Features
- Tolerant validation — optional fields can be omitted; required
Nonefields sendxsi:nilinstead of crashing - Namespace tolerance — trailing slashes, case differences, and URI fragments are normalized automatically
- Auto-documentation — generates terminal, Markdown, and HTML docs directly from the WSDL
- Meaningful errors — exceptions include service name, method, endpoint, sent payload, and a human-readable hint
- Async support — native
AsyncSoapClientwithasync/await - WSDL caching — in-memory and file-based caching with TTL
- Retry & timeout — configurable per-client
- Type stubs — full
.pyistubs for IDE autocomplete
Requirements
- Python 3.10+
- Dependencies:
httpx,lxml,rich,pydantic
Installation
pip install soapix
Quick Start
from soapix import SoapClient
client = SoapClient("http://service.example.com/?wsdl")
result = client.service.GetUser(userId=123)
print(result["name"])
Configuration
All options are keyword-only and passed to the constructor.
client = SoapClient(
"http://service.example.com/?wsdl",
timeout=60.0, # HTTP timeout in seconds (default: 30.0)
retries=3, # Retry count on transient failures (default: 0)
strict=False, # Strict WSDL validation (default: False)
debug=True, # Print request/response XML to terminal (default: False)
cache=None, # Cache instance, or None to disable (default: MemoryCache)
)
| Option | Type | Default | Description |
|---|---|---|---|
timeout |
float |
30.0 |
HTTP request timeout in seconds |
retries |
int |
0 |
Number of retries on connection/timeout errors |
strict |
bool |
False |
If True, raises on missing required fields instead of sending xsi:nil |
debug |
bool |
False |
Prints colourised request and response XML to the terminal |
cache |
Cache | None |
MemoryCache |
WSDL parse cache; None disables caching |
verify |
bool | str |
True |
SSL verification: True (system certs), False (skip), or path to a CA bundle file |
auth |
tuple | None |
None |
HTTP Basic Auth credentials as (username, password) |
Calling Operations
Operations are accessed through client.service.<OperationName>(...) using keyword arguments that match the WSDL parameter names.
# Simple call
result = client.service.GetUser(userId=42)
# Multiple parameters
result = client.service.CreateUser(name="Alice", email="alice@example.com")
# Optional parameters can be omitted in tolerant mode (default)
result = client.service.GetUser(userId=42) # locale is optional — omitted
result = client.service.GetUser(userId=42, locale="en-US") # or explicitly passed
# Required field sent as None → xsi:nil in tolerant mode
result = client.service.GetUser(userId=None)
The return value is a plain Python dict (or scalar for leaf values). Nested elements become nested dicts; repeated elements become lists.
# Nested elements → nested dicts
result = client.service.GetOrder(orderId=1)
# {
# "orderId": 1,
# "customer": {"id": 42, "name": "Alice"}, # nested element
# "items": [ # repeated element → list
# {"sku": "A1", "qty": 2},
# {"sku": "B3", "qty": 1},
# ]
# }
result = client.service.GetUser(userId=1)
# {"userId": 1, "name": "Alice", "email": "alice@example.com", "active": True}
Async Client
import asyncio
from soapix import AsyncSoapClient
async def main():
async with AsyncSoapClient("http://service.example.com/?wsdl") as client:
result = await client.service.GetUser(userId=123)
print(result["name"])
asyncio.run(main())
AsyncSoapClient accepts the same options as SoapClient. Use it as an async context manager to ensure the underlying HTTP connection is properly closed.
Strict Mode
By default, soapix operates in tolerant mode: missing required fields send xsi:nil, and unknown namespaces are silently normalised. Enable strict mode to raise exceptions instead:
client = SoapClient("http://service.example.com/?wsdl", strict=True)
# Raises SerializationError if a required field is missing
client.service.GetUser() # userId is required → raises
client.service.GetUser(userId=None) # None on required field → raises
SSL & Authentication
SSL verification
# Default — uses system certificate store
client = SoapClient("https://service.example.com/?wsdl")
# Custom CA bundle (corporate / self-signed certificates)
client = SoapClient("https://service.example.com/?wsdl", verify="/path/to/ca-bundle.pem")
# Disable SSL verification — development only, not recommended for production
client = SoapClient("https://service.example.com/?wsdl", verify=False)
To obtain the server's CA certificate:
openssl s_client -connect service.example.com:443 -showcerts 2>/dev/null \
| sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > ca.pem
HTTP Basic Auth
Some services require credentials to access the WSDL itself and/or to call operations. Pass auth as a (username, password) tuple — it applies to both WSDL fetching and all SOAP calls:
client = SoapClient(
"https://service.example.com/?wsdl",
auth=("username", "password"),
)
Combined
client = SoapClient(
"https://service.example.com/?wsdl",
verify="/path/to/ca-bundle.pem",
auth=("username", "password"),
)
Debug Mode
Enable debug=True to print the full SOAP envelope sent and the raw XML response to the terminal, with syntax highlighting via rich.
client = SoapClient("http://service.example.com/?wsdl", debug=True)
client.service.GetUser(userId=1)
# ── REQUEST ──────────────────────────────
# POST http://service.example.com/
# SOAPAction: "..."
#
# <?xml version="1.0" ...>
# <soap:Envelope ...>
# ...
# </soap:Envelope>
#
# ── RESPONSE (200 OK, 42ms) ──────────────
# <?xml version="1.0" ...>
# ...
WSDL Caching
soapix caches parsed WSDL documents to avoid re-fetching and re-parsing on every instantiation.
MemoryCache (default)
from soapix.cache import MemoryCache
cache = MemoryCache(
ttl=300, # Seconds until entries expire (default: 300, None = no expiry)
maxsize=64, # Max entries before oldest is evicted (default: 64)
)
client = SoapClient("http://service.example.com/?wsdl", cache=cache)
A module-level default cache is shared across all SoapClient instances that don't specify one. Use get_default_cache() to access it:
from soapix.cache import get_default_cache
get_default_cache().clear() # flush all cached WSDLs
FileCache
FileCache persists parsed WSDL documents to disk using pickle. Useful across process restarts.
from soapix.cache import FileCache
cache = FileCache(
cache_dir=".soapix_cache", # Directory to store cache files (created if absent)
ttl=3600, # Seconds until entries expire (default: 3600)
)
client = SoapClient("http://service.example.com/?wsdl", cache=cache)
Note: FileCache uses
picklefor serialisation. Only use it with WSDL sources you trust — do not load cache files from untrusted or user-supplied paths.
Disable caching
client = SoapClient("http://service.example.com/?wsdl", cache=None)
# WSDL is fetched and parsed on every instantiation
Auto-Documentation
soapix can generate human-readable documentation from the WSDL — terminal output, Markdown, or HTML.
Terminal
client.docs()
┌─────────────────────────────────────────────────────────┐
│ UserService │
├──────────────┬───────────────┬──────────┬──────────────┤
│ Operation │ Parameter │ Type │ Required │
├──────────────┼───────────────┼──────────┼──────────────┤
│ GetUser │ userId │ int │ Yes │
│ │ locale │ string │ No │
├──────────────┼───────────────┼──────────┼──────────────┤
│ CreateUser │ name │ string │ Yes │
│ │ email │ string │ Yes │
└──────────────┴───────────────┴──────────┴──────────────┘
Markdown
# Returns a string
md = client.docs(output="markdown")
# Writes to a file
client.docs(output="markdown", path="api_docs.md")
HTML
# Returns a string
html = client.docs(output="html")
# Writes to a file (includes a search box)
client.docs(output="html", path="api_docs.html")
You can also use DocsGenerator directly if you have a parsed WSDL document:
from soapix.docs.generator import DocsGenerator
from soapix.wsdl.parser import WsdlParser
doc = WsdlParser().load("service.wsdl")
gen = DocsGenerator(doc)
gen.render(output="terminal")
gen.render(output="markdown", path="api_docs.md")
gen.render(output="html", path="api_docs.html")
Error Handling
soapix raises structured exceptions with actionable context.
from soapix.exceptions import (
SoapFaultError, # Server returned a soap:Fault
HttpError, # HTTP 4xx/5xx or connection failure
TimeoutError, # Request exceeded the timeout
SerializationError, # Python value could not be serialised to XML
WsdlParseError, # WSDL could not be read or parsed
WsdlNotFoundError, # WSDL URL or path not reachable
WsdlImportError, # xs:import could not be resolved
)
Exception hierarchy
SoapixError
├── WsdlParseError
│ ├── WsdlNotFoundError
│ └── WsdlImportError
├── SoapCallError
│ ├── SoapFaultError
│ ├── HttpError
│ └── TimeoutError
└── SerializationError
Catching errors
from soapix.exceptions import SoapFaultError, HttpError, TimeoutError
try:
result = client.service.GetUser(userId=999)
except SoapFaultError as e:
print(e.fault_code) # e.g. "Server"
print(e.fault_string) # e.g. "User not found"
print(e.detail) # raw XML detail block, if any
except TimeoutError:
print("Request timed out — increase timeout or check the endpoint")
except HttpError as e:
print(f"HTTP error: {e}")
Error messages include structured context:
'GetUser' call failed
Service : UserService
Method : GetUser
Endpoint : http://service.example.com/
Sent : {'userId': None}
Hint : userId is required (int) — None cannot be sent in strict mode
Retry & Timeout
client = SoapClient(
"http://service.example.com/?wsdl",
timeout=10.0, # fail fast
retries=3, # retry up to 3 times on connection/timeout errors
)
Retries apply to HttpError (connection failures) and TimeoutError. Server-side SOAP faults (SoapFaultError) are not retried.
Comparison
| Feature | Zeep | Suds | soapix |
|---|---|---|---|
| Tolerant validation | No | No | Yes |
| Namespace tolerance | Partial | Partial | Full |
| Meaningful errors | No | No | Yes |
| Auto documentation | No | No | Yes |
| Async support | Partial | No | Native |
| WSDL caching | No | No | Yes |
| Retry & timeout | Manual | Manual | Built-in |
| Type stubs | Partial | No | Yes |
| Python 3.10+ | Yes | No | Yes |
Notes:
- Zeep meaningful errors: Zeep raises structured
Faultexceptions but does not include sent payload, endpoint, or human-readable hints in the error output.- Zeep async (Partial): Zeep supports async via a separate
AsyncTransportconfiguration; soapix'sAsyncSoapClientworks natively withasync/awaitand no extra setup.- Zeep WSDL caching (No): Zeep re-parses the WSDL on every instantiation by default; caching requires a custom transport wrapper.
- Suds: The original
sudslibrary is unmaintained. Comparison is based onsuds-community, its last active fork.
License
MIT — see LICENSE.
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 soapix-0.2.2.tar.gz.
File metadata
- Download URL: soapix-0.2.2.tar.gz
- Upload date:
- Size: 40.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fae1d6c8732fc66e94ff4593fc044285ea17ee50a119f98a4d99b70035926044
|
|
| MD5 |
a9f8e60bc276b44da87395fa96646cfc
|
|
| BLAKE2b-256 |
de6b5a89e3740f2dbcf538db7e0521d2da8f1d0849eaf8d8642d9c158062cb59
|
File details
Details for the file soapix-0.2.2-py3-none-any.whl.
File metadata
- Download URL: soapix-0.2.2-py3-none-any.whl
- Upload date:
- Size: 35.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60a37b489f4c3e3f9996e6fe9783067a51308a84140918bf992216b808dc7088
|
|
| MD5 |
226294f8590a1542a0816ad92b73086e
|
|
| BLAKE2b-256 |
955648e260a361aacf56daaaff3a726464f3febb6a77faa4286dcc7890b0912a
|