Python and Django SDK for Cloudinary, a cloud-based image and video management service with comprehensive transformation, optimization, and delivery capabilities
Overall
score
94%
Comprehensive exception hierarchy for handling API errors, authentication issues, and operational failures.
Core exception classes that form the foundation of Cloudinary error handling.
class Error(Exception):
"""Base exception class for all Cloudinary errors.
All Cloudinary-specific exceptions inherit from this base class, allowing
for comprehensive error handling and logging.
Attributes:
message (str): Error message describing the issue
http_code (int): HTTP status code associated with the error (if applicable)
"""
def __init__(self, message=None, http_code=None):
"""Initialize base error.
Args:
message (str, optional): Error message
http_code (int, optional): HTTP status code
"""
super(Error, self).__init__(message)
self.message = message
self.http_code = http_code
def __str__(self):
"""Return string representation of the error."""
return self.message or super(Error, self).__str__()Exceptions related to HTTP communication and API responses.
class NotFound(Error):
"""Exception raised when a requested resource is not found.
Typically corresponds to HTTP 404 responses from the Cloudinary API.
Common scenarios:
- Requesting a non-existent public_id
- Accessing a deleted resource
- Referencing an invalid transformation
"""
class NotAllowed(Error):
"""Exception raised when an operation is not permitted.
Typically corresponds to HTTP 403 responses indicating insufficient permissions
or account limitations.
Common scenarios:
- Attempting operations without proper API credentials
- Exceeding account quotas or limits
- Accessing restricted functionality
"""
class AlreadyExists(Error):
"""Exception raised when attempting to create a resource that already exists.
Common scenarios:
- Creating a transformation with an existing name
- Uploading with overwrite=False to an existing public_id
- Creating upload presets with duplicate names
"""
class RateLimited(Error):
"""Exception raised when API rate limits are exceeded.
Indicates that too many requests have been made within the allowed time window.
The client should implement backoff and retry logic.
Attributes:
retry_after (int): Seconds to wait before retrying (if provided by API)
"""
def __init__(self, message=None, http_code=None, retry_after=None):
"""Initialize rate limit error.
Args:
message (str, optional): Error message
http_code (int, optional): HTTP status code (typically 429)
retry_after (int, optional): Seconds to wait before retry
"""
super(RateLimited, self).__init__(message, http_code)
self.retry_after = retry_after
class BadRequest(Error):
"""Exception raised for malformed requests or invalid parameters.
Typically corresponds to HTTP 400 responses indicating client-side errors.
Common scenarios:
- Invalid transformation parameters
- Malformed public_ids or resource identifiers
- Missing required parameters
- Invalid file formats or types
"""
class GeneralError(Error):
"""Exception raised for general API errors that don't fit other categories.
Used for unexpected server errors, temporary service issues, or other
unspecified problems with API operations.
"""
class AuthorizationRequired(Error):
"""Exception raised when authentication is required but missing or invalid.
Typically corresponds to HTTP 401 responses indicating authentication failures.
Common scenarios:
- Missing API credentials
- Invalid API key or secret
- Expired or malformed authentication tokens
- Attempting authenticated operations without proper credentials
"""from cloudinary import uploader, api
from cloudinary.exceptions import Error, NotFound, NotAllowed, BadRequest, AuthorizationRequired
try:
# Upload operation
result = uploader.upload("nonexistent_file.jpg")
except NotFound as e:
print(f"File not found: {e}")
except BadRequest as e:
print(f"Invalid request: {e}")
except AuthorizationRequired as e:
print(f"Authentication failed: {e}")
except Error as e:
print(f"Cloudinary error: {e}")
if hasattr(e, 'http_code'):
print(f"HTTP status: {e.http_code}")
except Exception as e:
print(f"Unexpected error: {e}")from cloudinary import api
from cloudinary.exceptions import NotFound, NotAllowed, RateLimited
def safe_get_resource(public_id):
"""Safely retrieve resource with comprehensive error handling."""
try:
return api.resource(public_id)
except NotFound:
print(f"Resource '{public_id}' not found")
return None
except NotAllowed as e:
print(f"Access denied for '{public_id}': {e}")
return None
except RateLimited as e:
print(f"Rate limited. Retry after: {getattr(e, 'retry_after', 60)} seconds")
return None
except Error as e:
print(f"Cloudinary API error: {e}")
return None
def safe_delete_resources(public_ids):
"""Safely delete multiple resources with error handling."""
try:
result = api.delete_resources(public_ids)
# Check for partial failures
if result.get('partial'):
print("Some resources could not be deleted:")
for public_id in result.get('deleted', {}):
if result['deleted'][public_id] != 'deleted':
print(f" {public_id}: {result['deleted'][public_id]}")
return result
except NotAllowed as e:
print(f"Deletion not allowed: {e}")
return None
except Error as e:
print(f"Error during deletion: {e}")
return Nonefrom cloudinary import uploader
from cloudinary.exceptions import BadRequest, NotAllowed, GeneralError
import time
def robust_upload(file_path, **options):
"""Upload with retry logic and comprehensive error handling."""
max_retries = 3
retry_delay = 1
for attempt in range(max_retries):
try:
result = uploader.upload(file_path, **options)
print(f"Upload successful: {result['public_id']}")
return result
except BadRequest as e:
print(f"Invalid upload parameters: {e}")
# Don't retry for bad requests
return None
except NotAllowed as e:
print(f"Upload not allowed: {e}")
return None
except RateLimited as e:
retry_after = getattr(e, 'retry_after', retry_delay * (2 ** attempt))
print(f"Rate limited. Waiting {retry_after} seconds...")
time.sleep(retry_after)
continue
except GeneralError as e:
if attempt < max_retries - 1:
print(f"Upload failed (attempt {attempt + 1}): {e}")
print(f"Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
retry_delay *= 2
continue
else:
print(f"Upload failed after {max_retries} attempts: {e}")
return None
except Error as e:
print(f"Unexpected Cloudinary error: {e}")
return None
except Exception as e:
print(f"System error during upload: {e}")
return None
return None
# Usage
result = robust_upload(
"image.jpg",
public_id="my_image",
folder="uploads",
overwrite=True
)
if result:
print(f"Successfully uploaded: {result['secure_url']}")
else:
print("Upload failed after all retry attempts")from cloudinary import api
from cloudinary.exceptions import Error
def process_resources_safely(public_ids, operation_func, **kwargs):
"""Process multiple resources with individual error handling."""
results = []
errors = []
for public_id in public_ids:
try:
result = operation_func(public_id, **kwargs)
results.append({'public_id': public_id, 'success': True, 'result': result})
except NotFound:
error_msg = f"Resource '{public_id}' not found"
errors.append({'public_id': public_id, 'error': error_msg})
results.append({'public_id': public_id, 'success': False, 'error': error_msg})
except NotAllowed as e:
error_msg = f"Operation not allowed for '{public_id}': {e}"
errors.append({'public_id': public_id, 'error': error_msg})
results.append({'public_id': public_id, 'success': False, 'error': error_msg})
except Error as e:
error_msg = f"Error processing '{public_id}': {e}"
errors.append({'public_id': public_id, 'error': error_msg})
results.append({'public_id': public_id, 'success': False, 'error': error_msg})
return {
'results': results,
'errors': errors,
'success_count': len([r for r in results if r['success']]),
'error_count': len(errors)
}
# Usage examples
def get_resource_details(public_id):
return api.resource(public_id)
def update_resource_tags(public_id, tags):
return api.update(public_id, tags=tags)
# Process multiple resources
public_ids = ["image1", "image2", "nonexistent", "image3"]
# Get details for all resources
details_result = process_resources_safely(public_ids, get_resource_details)
print(f"Successfully retrieved {details_result['success_count']} resources")
print(f"Failed to retrieve {details_result['error_count']} resources")
# Update tags for all resources
tag_result = process_resources_safely(
public_ids,
update_resource_tags,
tags=["batch_processed", "updated"]
)import logging
from cloudinary.exceptions import *
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class CloudinaryErrorHandler:
"""Advanced error handler with logging and recovery strategies."""
def __init__(self):
self.error_counts = {}
def handle_error(self, operation, error, context=None):
"""Handle errors with logging and recovery suggestions."""
error_type = type(error).__name__
self.error_counts[error_type] = self.error_counts.get(error_type, 0) + 1
logger.error(f"{operation} failed: {error_type} - {error}")
if context:
logger.error(f"Context: {context}")
# Provide recovery suggestions
if isinstance(error, NotFound):
logger.info("Recovery: Check if the resource exists or verify the public_id")
elif isinstance(error, AuthorizationRequired):
logger.info("Recovery: Verify API credentials and permissions")
elif isinstance(error, RateLimited):
retry_after = getattr(error, 'retry_after', 60)
logger.info(f"Recovery: Wait {retry_after} seconds before retrying")
elif isinstance(error, BadRequest):
logger.info("Recovery: Check request parameters and format")
elif isinstance(error, NotAllowed):
logger.info("Recovery: Check account limits and permissions")
def get_error_summary(self):
"""Get summary of encountered errors."""
return dict(self.error_counts)
# Usage
error_handler = CloudinaryErrorHandler()
try:
result = api.resource("nonexistent_image")
except Error as e:
error_handler.handle_error(
"Get resource",
e,
context={"public_id": "nonexistent_image"}
)
# Get error statistics
print("Error summary:", error_handler.get_error_summary())Install with Tessl CLI
npx tessl i tessl/pypi-cloudinarydocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10