CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-geopy

Python Geocoding Toolbox providing comprehensive geocoding services and geodesic distance calculations

Pending
Overview
Eval results
Files

error-handling.mddocs/

Error Handling

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.

Capabilities

Base Exception

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.
    """

Configuration Errors

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
    """

Service Errors

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
    """

Utility Errors

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.
    """

Usage Examples

Basic Error Handling

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")

Rate Limiting Error Handling

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")

Service Fallback Strategy

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}")

Error Logging and Monitoring

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()

Custom Error Handling Context Manager

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

docs

async-support.md

core-data-types.md

distance-calculations.md

error-handling.md

geocoding-services.md

index.md

rate-limiting.md

tile.json