Of course! Let's dive deep into using the suds library in Python.
What is suds?
suds is a lightweight SOAP-based web service client for Python. It was a very popular choice for interacting with SOAP APIs before the rise of more modern alternatives like zeep.
Key Characteristics:
- Lightweight: It has minimal dependencies.
- Dynamic: It doesn't require you to generate a client stub from a WSDL file beforehand. It inspects the WSDL at runtime.
- "Magic": It dynamically creates Python objects and methods to match the WSDL's definitions, which can feel like magic but sometimes makes debugging harder.
⚠️ Important: suds vs. suds-jurko
The original suds library is no longer actively maintained. The most common and recommended fork is suds-jurko. You should almost always use this version.
To install it:
pip install suds-jurko
A Practical Example: Consuming a Weather API
Let's walk through a complete example of using suds-jurko to call a public SOAP web service. We'll use the Global Weather service, which provides a simple way to get weather data for a city.
Step 1: Find the WSDL URL
A SOAP service is defined by a WSDL (Web Services Description Language) file. For this example, we'll use:
http://www.webservicex.net/globalweather.asmx?wsdl
Step 2: Write the Python Client
Here is a complete, commented script to connect to the service and get the weather for London.
# Import the necessary modules
from suds.client import Client
from suds.sax.element import Element
from suds.sudsobject import Factory
# The URL of the WSDL file for the Global Weather service
WSDL_URL = 'http://www.webservicex.net/globalweather.asmx?wsdl'
try:
# 1. Create the SOAP client
# The client object will automatically inspect the WSDL and create methods
# and data types that match the service definition.
client = Client(WSDL_URL)
# You can inspect the service's metadata
print("--- Service Metadata ---")
print(f"Service Name: {client.wsdl.services[0].name}")
print(f"Available Ports (Endpoints): {list(client.wsdl.services[0].ports.keys())}")
print(f"Available Service Functions: {client.wsdl.services[0].ports[list(client.wsdl.services[0].ports.keys())[0]].methods.keys()}")
print("-" * 25)
# 2. Prepare the input parameters
# The service requires a 'CityName' and a 'CountryName'.
# suds-jurko automatically creates Python objects for the complex types
# defined in the WSDL (like 'GetWeatherRequest').
# Option A: Using the Factory (Recommended for complex types)
# This is the most robust way to create the request object.
request = Factory('GetWeatherRequest')
request.CityName = 'London'
request.CountryName = 'United Kingdom'
# Option B: Using a dictionary (can work for simple cases)
# This is less reliable if the WSDL defines the type with specific ordering or required elements.
# request_dict = {'CityName': 'London', 'CountryName': 'United Kingdom'}
# 3. Call the SOAP service method
# The method name 'GetWeather' comes directly from the WSDL.
print("\n--- Calling SOAP Service ---")
print(f"Requesting weather for: {request.CityName}, {request.CountryName}")
response = client.service.GetWeather(request)
# 4. Process the response
print("\n--- Response Received ---")
print(f"Raw response type: {type(response)}")
# The response is a suds object, which behaves like a dictionary or an object.
# You can access its attributes using dot notation.
if response and hasattr(response, 'GetWeatherResult'):
# The actual weather data is XML, which is returned as a string
weather_xml = response.GetWeatherResult
print("\n--- Weather Data (XML) ---")
print(weather_xml)
else:
print("Error: Could not retrieve weather data.")
print(f"Details: {response}")
except Exception as e:
print(f"\nAn error occurred: {e}")
# For more detailed debugging, you can print the last sent/received messages
# print(client.last_sent())
# print(client.last_received())
Key Concepts Explained
-
Client(wsdl_url): This is the entry point. It downloads and parses the WSDL, building an in-memory representation of the service's available methods and data types. -
Inspecting the Service: Before calling anything, it's incredibly useful to inspect the
clientobject to understand what's available.client.wsdl.services: Lists the available services.client.service: This is the object that holds all the callable methods (e.g.,client.service.GetWeather).client.factory: This is aFactoryobject used to create instances of the complex types defined in the WSDL.
-
Creating Request Objects (
Factory): The WSDL defines the structure of your request. Instead of manually creating dictionaries, you should use theclient.factory. This ensures your request object has the correct type and structure expected by the server.Factory('TypeName'): Creates an object of the type specified in the WSDL.- You then set the attributes on this object (e.g.,
request.CityName = 'London').
-
Calling the Service: You call methods on the
client.serviceobject, passing your request object as an argument.sudshandles the process of converting your Python object into the correct SOAP XML message and sending it over the network. -
Handling the Response: The response is also a
sudsobject. It behaves much like a Python dictionary or a simple object. You can access its data using dot notation (e.g.,response.GetWeatherResult). For simple services, this might be a string or a number. For more complex ones, it will be anothersudsobject.
Common Tasks and Troubleshooting
Viewing the SOAP Messages
Debugging SOAP can be tricky because you can't see the raw request and response. suds makes this easy.
# After a call has been made...
print("--- Last Sent SOAP Message ---")
print(client.last_sent())
print("\n--- Last Received SOAP Message ---")
print(client.last_received())
This is invaluable for seeing exactly what XML suds generated and what the server sent back.
Handling SOAP Faults
When the server returns a SOAP fault (an error), suds raises a WebFault exception.
from suds.sax.document import Document
from suds.sax.element import Element
try:
# Call a method that will likely fail
client.service.GetWeather(Factory('GetWeatherRequest')(CityName='InvalidCity', CountryName='InvalidCountry'))
except Exception as e:
print(f"Caught an exception: {e}")
# Check if it's a SOAP fault
if hasattr(e, 'document'):
print("This is a SOAP Fault!")
fault = e.documentRootElement
print(f"Fault Code: {fault.find('faultcode').text}")
print(f"Fault String: {fault.find('faultstring').text}")
Disabling Caching
By default, suds caches the WSDL file to speed up subsequent runs. If the WSDL changes, you might need to disable this.
# Create a client with caching disabled from suds.cache import NoCache client = Client(WSDL_URL, cache=NoCache())
suds vs. zeep: Which one to use?
While suds-jurko is a great library, zeep is the modern standard for SOAP in Python.
| Feature | suds-jurko |
zeep |
|---|---|---|
| Status | Maintenance fork of a legacy library. | Actively developed and recommended. |
| Approach | More "magical," dynamic object creation. | More explicit, uses type hints and dataclasses. |
| WSDL Handling | Parses WSDL into Python objects at runtime. | Converts WSDL types into native Python types (e.g., xs:dateTime -> datetime.datetime). |
| Ease of Use | Can be simpler for very quick, simple tasks. | Steeper initial learning curve but much more powerful and predictable. |
| Performance | Generally slower. | Significantly faster. |
| Error Handling | Raises generic Exception for SOAP faults. |
Raises specific zeep.exceptions.Fault for SOAP faults. |
| Recommendation | Only for maintaining old projects or for very simple, one-off scripts. | Use for all new projects. It's more robust, faster, and better supported. |
Conclusion: If you are starting a new project, you should strongly consider using zeep. However, if you are working on legacy code or need a quick and simple solution for a non-complex SOAP service, suds-jurko is still a perfectly viable tool.
