Python Geocoding Toolbox providing comprehensive geocoding services and geodesic distance calculations
—
Geopy provides a comprehensive exception hierarchy covering authentication failures, quota exceeded, rate limiting, service unavailability, and parsing errors with detailed error information for robust error handling.
All geopy exceptions inherit from the base GeopyError class.
from geopy.exc import GeopyError
class GeopyError(Exception):
"""
Base exception for all geopy-specific errors.
All other geopy exceptions inherit from this class.
"""Errors related to geocoder initialization and configuration.
from geopy.exc import ConfigurationError
class ConfigurationError(GeopyError):
"""
Exception raised when geocoder is initialized with invalid parameters.
Common causes:
- Missing required API keys
- Invalid domain or scheme settings
- Conflicting authentication parameters
"""Errors returned by geocoding services during API requests.
from geopy.exc import (
GeocoderServiceError, GeocoderQueryError, GeocoderQuotaExceeded,
GeocoderRateLimited, GeocoderAuthenticationFailure,
GeocoderInsufficientPrivileges, GeocoderTimedOut,
GeocoderUnavailable, GeocoderParseError
)
class GeocoderServiceError(GeopyError):
"""
Base class for all geocoding service errors.
Represents generic service-side issues.
"""
class GeocoderQueryError(GeocoderServiceError):
"""
Exception raised when geocoding service cannot process the query.
Common causes:
- Malformed query string
- Invalid coordinates for reverse geocoding
- Query contains unsupported characters
- Query exceeds maximum length limits
"""
class GeocoderQuotaExceeded(GeocoderServiceError):
"""
Exception raised when API quota or usage limits are exceeded.
Common causes:
- Daily/monthly request limit reached
- Rate limits exceeded
- Billing account suspended
"""
class GeocoderRateLimited(GeocoderServiceError):
"""
Exception raised when requests are being rate limited.
Attributes:
- retry_after (int): Seconds to wait before retrying (if provided by service)
"""
def __init__(self, message, retry_after=None):
"""
Initialize rate limiting exception.
Parameters:
- message (str): Error message
- retry_after (int): Seconds to wait before retry
"""
super().__init__(message)
self.retry_after = retry_after
class GeocoderAuthenticationFailure(GeocoderServiceError):
"""
Exception raised when API authentication fails.
Common causes:
- Invalid API key
- Expired API key
- API key not authorized for service
- Missing authentication credentials
"""
class GeocoderInsufficientPrivileges(GeocoderServiceError):
"""
Exception raised when API key lacks required permissions.
Common causes:
- API key restricted to specific domains/IPs
- Service not enabled for API key
- Premium features require upgraded account
"""
class GeocoderTimedOut(GeocoderServiceError):
"""
Exception raised when geocoding request times out.
Common causes:
- Network connectivity issues
- Service experiencing high load
- Timeout value set too low
"""
class GeocoderUnavailable(GeocoderServiceError):
"""
Exception raised when geocoding service is unavailable.
Common causes:
- Service maintenance
- Server downtime
- Network infrastructure issues
"""
class GeocoderParseError(GeocoderServiceError):
"""
Exception raised when geocoder response cannot be parsed.
Common causes:
- Unexpected response format
- Malformed JSON/XML response
- Service API changes
"""Errors related to geopy utilities and helper functions.
from geopy.exc import GeocoderNotFound
class GeocoderNotFound(GeopyError):
"""
Exception raised when requesting unknown geocoder service.
Raised by get_geocoder_for_service() when service name
is not recognized.
"""from geopy.geocoders import Nominatim
from geopy.exc import (
GeopyError, GeocoderServiceError, GeocoderTimedOut,
GeocoderAuthenticationFailure, GeocoderQuotaExceeded
)
def safe_geocode(query, geolocator):
"""Safely geocode with comprehensive error handling"""
try:
location = geolocator.geocode(query, timeout=10)
return location
except GeocoderAuthenticationFailure as e:
print(f"Authentication failed: {e}")
print("Check your API key and permissions")
return None
except GeocoderQuotaExceeded as e:
print(f"Quota exceeded: {e}")
print("Consider upgrading your plan or waiting until quota resets")
return None
except GeocoderTimedOut as e:
print(f"Request timed out: {e}")
print("Try again with a longer timeout or check network connectivity")
return None
except GeocoderServiceError as e:
print(f"Service error: {e}")
print("The geocoding service encountered an error")
return None
except GeopyError as e:
print(f"Geopy error: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
# Usage
geolocator = Nominatim(user_agent="my_app")
result = safe_geocode("New York City", geolocator)
if result:
print(f"Found: {result.address}")
else:
print("Geocoding failed")from geopy.geocoders import GoogleV3
from geopy.exc import GeocoderRateLimited, GeocoderQuotaExceeded
import time
def geocode_with_retry(query, geolocator, max_retries=3):
"""Geocode with automatic retry on rate limiting"""
for attempt in range(max_retries + 1):
try:
return geolocator.geocode(query)
except GeocoderRateLimited as e:
if attempt == max_retries:
raise # Re-raise on final attempt
# Wait before retry
wait_time = e.retry_after if e.retry_after else 2 ** attempt
print(f"Rate limited. Waiting {wait_time} seconds...")
time.sleep(wait_time)
except GeocoderQuotaExceeded:
print("Quota exceeded. Cannot retry.")
raise
return None
# Usage
try:
geolocator = GoogleV3(api_key="your_api_key")
location = geocode_with_retry("San Francisco", geolocator)
print(f"Result: {location.address if location else 'Not found'}")
except GeocoderRateLimited:
print("Still rate limited after retries")
except GeocoderQuotaExceeded:
print("API quota exceeded")from geopy.geocoders import Nominatim, Photon, OpenCage
from geopy.exc import (
GeopyError, GeocoderServiceError, GeocoderTimedOut,
GeocoderUnavailable, GeocoderAuthenticationFailure
)
class FallbackGeocoder:
"""Geocoder with automatic fallback to alternative services"""
def __init__(self):
self.geocoders = [
("Primary", Nominatim(user_agent="fallback_app")),
("Fallback1", Photon(user_agent="fallback_app")),
# Add more geocoders as needed
]
def geocode(self, query, exactly_one=True):
"""Try geocoding with fallback services"""
last_error = None
for name, geocoder in self.geocoders:
try:
print(f"Trying {name}...")
result = geocoder.geocode(query, exactly_one=exactly_one, timeout=10)
if result:
print(f"Success with {name}")
return result
else:
print(f"No results from {name}")
except (GeocoderUnavailable, GeocoderTimedOut) as e:
print(f"{name} unavailable: {e}")
last_error = e
continue
except GeocoderAuthenticationFailure as e:
print(f"{name} authentication failed: {e}")
last_error = e
continue
except GeocoderServiceError as e:
print(f"{name} service error: {e}")
last_error = e
continue
except GeopyError as e:
print(f"{name} geopy error: {e}")
last_error = e
continue
# All services failed
if last_error:
raise last_error
else:
return None
# Usage
fallback_geocoder = FallbackGeocoder()
try:
location = fallback_geocoder.geocode("Paris, France")
if location:
print(f"Found: {location.address}")
else:
print("No results from any service")
except Exception as e:
print(f"All services failed: {e}")from geopy.geocoders import Bing
from geopy.exc import *
import logging
from datetime import datetime
from collections import defaultdict
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class MonitoredGeocoder:
"""Geocoder wrapper with error monitoring and logging"""
def __init__(self, geocoder):
self.geocoder = geocoder
self.error_counts = defaultdict(int)
self.total_requests = 0
self.successful_requests = 0
def geocode(self, query, **kwargs):
"""Geocode with monitoring"""
self.total_requests += 1
start_time = datetime.now()
try:
result = self.geocoder.geocode(query, **kwargs)
if result:
self.successful_requests += 1
duration = (datetime.now() - start_time).total_seconds()
logger.info(f"Geocoding success: '{query}' -> {result.address} ({duration:.2f}s)")
else:
logger.warning(f"Geocoding returned no results: '{query}'")
return result
except GeocoderAuthenticationFailure as e:
self.error_counts['auth_failure'] += 1
logger.error(f"Authentication failure for '{query}': {e}")
raise
except GeocoderQuotaExceeded as e:
self.error_counts['quota_exceeded'] += 1
logger.error(f"Quota exceeded for '{query}': {e}")
raise
except GeocoderRateLimited as e:
self.error_counts['rate_limited'] += 1
logger.warning(f"Rate limited for '{query}': {e} (retry_after={e.retry_after})")
raise
except GeocoderTimedOut as e:
self.error_counts['timeout'] += 1
logger.warning(f"Timeout for '{query}': {e}")
raise
except GeocoderUnavailable as e:
self.error_counts['unavailable'] += 1
logger.error(f"Service unavailable for '{query}': {e}")
raise
except GeocoderServiceError as e:
self.error_counts['service_error'] += 1
logger.error(f"Service error for '{query}': {e}")
raise
except GeopyError as e:
self.error_counts['geopy_error'] += 1
logger.error(f"Geopy error for '{query}': {e}")
raise
def get_stats(self):
"""Get monitoring statistics"""
success_rate = (self.successful_requests / self.total_requests * 100
if self.total_requests > 0 else 0)
return {
'total_requests': self.total_requests,
'successful_requests': self.successful_requests,
'success_rate': success_rate,
'error_counts': dict(self.error_counts)
}
def print_stats(self):
"""Print monitoring statistics"""
stats = self.get_stats()
print(f"\nGeocoding Statistics:")
print(f"Total requests: {stats['total_requests']}")
print(f"Successful requests: {stats['successful_requests']}")
print(f"Success rate: {stats['success_rate']:.2f}%")
print(f"Error breakdown:")
for error_type, count in stats['error_counts'].items():
print(f" {error_type}: {count}")
# Usage
base_geocoder = Bing(api_key="your_api_key")
monitored_geocoder = MonitoredGeocoder(base_geocoder)
# Test queries
test_queries = [
"New York City",
"Invalid address 123456",
"London, UK",
"Tokyo, Japan"
]
for query in test_queries:
try:
result = monitored_geocoder.geocode(query)
except Exception as e:
print(f"Error processing '{query}': {e}")
# Print statistics
monitored_geocoder.print_stats()from geopy.exc import *
from contextlib import contextmanager
import time
@contextmanager
def geocoding_context(max_retries=3, retry_delay=1):
"""Context manager for geocoding with automatic error handling"""
retry_count = 0
while retry_count <= max_retries:
try:
yield
break # Success, exit retry loop
except GeocoderRateLimited as e:
if retry_count == max_retries:
print(f"Rate limited after {max_retries} retries")
raise
wait_time = e.retry_after if e.retry_after else retry_delay * (2 ** retry_count)
print(f"Rate limited, waiting {wait_time} seconds...")
time.sleep(wait_time)
retry_count += 1
except (GeocoderTimedOut, GeocoderUnavailable) as e:
if retry_count == max_retries:
print(f"Service issue after {max_retries} retries: {e}")
raise
print(f"Service issue, retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
retry_count += 1
except (GeocoderAuthenticationFailure, GeocoderQuotaExceeded) as e:
print(f"Non-retryable error: {e}")
raise
except Exception as e:
print(f"Unexpected error: {e}")
raise
# Usage
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="context_app")
with geocoding_context(max_retries=3, retry_delay=2):
location = geolocator.geocode("San Francisco, CA")
print(f"Result: {location.address if location else 'Not found'}")Install with Tessl CLI
npx tessl i tessl/pypi-geopy