Error Handlers
Error handling utilities for ONVIF operations.
This module provides utilities to gracefully handle ONVIF SOAP errors,
particularly the common ActionNotSupported fault that occurs when devices
don't implement certain optional ONVIF operations.
ONVIF devices may not support all operations defined in the specification.
When an unsupported operation is called, the device returns a SOAP fault with
the ActionNotSupported subcode. These utilities help detect and handle such
cases.
Features
- Detect
ActionNotSupportedSOAP faults - Provide safe operation calls with default fallbacks
- Ignore unsupported operations using a decorator
- Support graceful degradation in multi-device environments
Common Use Cases
- Feature Detection: Check if a device supports an operation
- Graceful Degradation: Continue execution when an operation fails
- Multi-Device Support: Handle devices with varying capabilities
- Safe Exploration: Test operations without crashing
Notes
- Works with both
ONVIFOperationExceptionand rawzeep.Fault - Preserves stack traces for non-
ActionNotSupportederrors - Minimal performance overhead for supported operations
- Thread-safe because no shared state is maintained
See Also
ONVIFOperationException: Custom exception wrapperzeep.exceptions.Fault: Base SOAP fault exception
is_action_not_supported(exception: ONVIFOperationException | Fault) -> bool
Check whether an exception is caused by an ActionNotSupported SOAP fault.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exception
|
ONVIFOperationException | Fault
|
The exception to inspect. Can be an
|
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Example
from onvif import ONVIFClient, is_action_not_supported
try:
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
system_uris = device.GetSystemUris()
except ONVIFOperationException as e:
if is_action_not_supported(e):
# Fallback: Get basic device information
device_info = device.GetDeviceInformation()
Source code in onvif\utils\error_handlers.py
def is_action_not_supported(exception: ONVIFOperationException | Fault) -> bool:
"""
Check whether an exception is caused by an `ActionNotSupported` SOAP fault.
Args:
exception: The exception to inspect. Can be an
[`ONVIFOperationException`](onvif_exception.md) or a raw `zeep.exceptions.Fault`.
Returns:
`True` if the exception contains an `ActionNotSupported` SOAP fault, `False` otherwise.
Example:
```python linenums="1"
from onvif import ONVIFClient, is_action_not_supported
try:
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
system_uris = device.GetSystemUris()
except ONVIFOperationException as e:
if is_action_not_supported(e):
# Fallback: Get basic device information
device_info = device.GetDeviceInformation()
```
"""
try:
# Handle ONVIFOperationException
if isinstance(exception, ONVIFOperationException):
original = exception.original_exception
else:
original = exception
if not isinstance(original, Fault):
return False
subcodes = getattr(original, "subcodes", None)
if not subcodes:
return False
for subcode in subcodes:
localname = getattr(subcode, "localname", None)
if localname == "ActionNotSupported":
logger.debug("Detected ActionNotSupported fault")
return True
if "ActionNotSupported" in str(subcode):
logger.debug("Detected ActionNotSupported fault in subcode")
return True
except OSError as error:
logger.debug("Error checking ActionNotSupported: %s", error)
return False
safe_call(func, default: Any | None = None, handle_unsupported: bool = True, log_error: bool = True) -> Any | None
Safely call an ONVIF operation with graceful error handling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
The callable that performs the ONVIF operation. |
required | |
default
|
Any | None
|
The value to return when the operation is unsupported and
|
None
|
handle_unsupported
|
bool
|
Whether to catch |
True
|
log_error
|
bool
|
Whether to log errors encountered during the operation.
Defaults to |
True
|
Version History
- Changed in
>=v0.4.0:→ignore_unsupportedhandle_unsupported.
Returns:
| Type | Description |
|---|---|
Any | None
|
The result returned by |
Raises:
| Type | Description |
|---|---|
ONVIFOperationException
|
If the operation fails for a reason other
than |
Exception
|
If |
Example
from onvif import ONVIFClient, safe_call
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
# Returns None if operation is not supported
ip_filter = safe_call(device.GetIPAddressFilter)
if ip_filter:
print(f"IP Address Filter: {ip_filter}")
else:
print("GetIPAddressFilter not supported or returned None")
services = safe_call(
lambda: device.GetServices(IncludeCapability=False),
handle_unsupported=False # Raise exception if not supported
)
print(f"Found {len(services)} services")
Source code in onvif\utils\error_handlers.py
def safe_call(
func,
default: Any | None = None,
handle_unsupported: bool = True,
log_error: bool = True,
) -> Any | None:
"""
Safely call an ONVIF operation with graceful error handling.
Args:
func: The callable that performs the ONVIF operation.
default (Any | None): The value to return when the operation is unsupported and
`handle_unsupported` is enabled. Defaults to `None`.
handle_unsupported (bool): Whether to catch `ActionNotSupported` faults and
return `default` instead of raising the exception. Defaults to
`True`.
log_error (bool): Whether to log errors encountered during the operation.
Defaults to `True`.
!!! tip "Version History"
- Changed in [`>=v0.4.0`](/onvif-python/releases/#v0.4.0): ~~`ignore_unsupported`~~ → `handle_unsupported`.
Returns:
The result returned by `func`, or `default` when the operation is
unsupported and `handle_unsupported` is enabled.
Raises:
ONVIFOperationException: If the operation fails for a reason other
than `ActionNotSupported`, or if `handle_unsupported` is disabled.
Exception: If `func` raises an unexpected exception.
Example:
```python linenums="1"
from onvif import ONVIFClient, safe_call
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
# Returns None if operation is not supported
ip_filter = safe_call(device.GetIPAddressFilter)
if ip_filter:
print(f"IP Address Filter: {ip_filter}")
else:
print("GetIPAddressFilter not supported or returned None")
services = safe_call(
lambda: device.GetServices(IncludeCapability=False),
handle_unsupported=False # Raise exception if not supported
)
print(f"Found {len(services)} services")
```
"""
try:
result = func()
logger.debug("Safe call succeeded")
return result
except ONVIFOperationException as e:
# Check if it's ActionNotSupported error
if handle_unsupported and is_action_not_supported(e):
if log_error:
logger.warning("Operation not supported: %s", e.operation)
return default
# Re-raise other errors
if log_error:
logger.error("ONVIF operation failed in safe_call: %s", e.operation)
raise
except Exception as e: # pylint: disable=broad-except
# Wrap unexpected exceptions
if log_error:
logger.error("Unexpected error in safe_call: %s", e)
raise
ignore_unsupported(func) -> Any | None
Decorator to ignore ActionNotSupported SOAP faults.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
The function to decorate. The function may accept positional and keyword arguments. |
required |
Returns:
| Type | Description |
|---|---|
Any | None
|
A wrapped function that returns |
Raises:
| Type | Description |
|---|---|
ONVIFOperationException
|
If the decorated function fails for a reason
other than |
Exception
|
If the decorated function raises an unexpected exception. |
Example
from onvif import ONVIFClient, ignore_unsupported
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
@ignore_unsupported
def get_zero_configuration():
return device.GetZeroConfiguration()
@ignore_unsupported
def get_ntp():
return device.GetNTP()
zero_conf = get_zero_configuration()
if zero_conf:
print(f"Zero Configuration: {zero_conf}")
else:
print("GetZeroConfiguration not supported")
ntp = get_ntp()
if ntp:
print(f"NTP: {ntp}")
else:
print("GetNTP not supported")
Source code in onvif\utils\error_handlers.py
def ignore_unsupported(func) -> Any | None:
"""Decorator to ignore `ActionNotSupported` SOAP faults.
Args:
func: The function to decorate. The function may accept positional
and keyword arguments.
Returns:
A wrapped function that returns `None` when an `ActionNotSupported` fault occurs.
Raises:
ONVIFOperationException: If the decorated function fails for a reason
other than `ActionNotSupported`.
Exception: If the decorated function raises an unexpected exception.
Example:
```python linenums="1"
from onvif import ONVIFClient, ignore_unsupported
client = ONVIFClient("192.168.1.17", 80, "admin", "password")
device = client.devicemgmt()
@ignore_unsupported
def get_zero_configuration():
return device.GetZeroConfiguration()
@ignore_unsupported
def get_ntp():
return device.GetNTP()
zero_conf = get_zero_configuration()
if zero_conf:
print(f"Zero Configuration: {zero_conf}")
else:
print("GetZeroConfiguration not supported")
ntp = get_ntp()
if ntp:
print(f"NTP: {ntp}")
else:
print("GetNTP not supported")
```
"""
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
logger.debug("Decorated function %s succeeded", func.__name__)
return result
except ONVIFOperationException as e:
if is_action_not_supported(e):
logger.warning(
"Operation not supported in %s: %s", func.__name__, e.operation
)
return None
logger.error("ONVIF operation failed in %s: %s", func.__name__, e.operation)
raise
return wrapper