Gestione degli errori
Gerarchia delle eccezioni, attributi d'errore, errori nei payload HTTP 200, validazione client e guida ai nuovi tentativi: Python SDK di Listings API.
Tutte le eccezioni possono essere importate dal modulo di primo livello listingsapi.
Gerarchia delle eccezioni
ListingsAPIError├── APIError API-reported failure: non-2xx, or an error payload in HTTP 200│ ├── AuthenticationError 401, or payload codes SY90005 / SY90001│ ├── PermissionDeniedError 403, or payload code SY90003│ ├── NotFoundError 404│ ├── ValidationError 400 / 422, mutation failures, client-side validation│ ├── RateLimitError 429, or payload code RATE_LIMITED (carries .retry_after)│ └── InternalServerError 5xx (safe to retry)└── APIConnectionError network failure: DNS, connection refused, timeoutIntercetta ListingsAPIError per gestire tutte le eccezioni che l'SDK può sollevare, APIError per tutti gli errori segnalati dall'API oppure una sottoclasse specifica per una gestione mirata.
Attributi delle eccezioni
APIError e le sue sottoclassi hanno i seguenti attributi:
APIConnectionError eredita direttamente da ListingsAPIError e non ha nessuno di questi attributi; il messaggio dell'eccezione descrive l'errore di rete.
Intercettare le eccezioni
import listingsapi client = listingsapi.ListingsAPI() try: location = client.locations.retrieve(16808)except listingsapi.AuthenticationError as e: print("Invalid API key:", e.code)except listingsapi.PermissionDeniedError as e: print("Not allowed:", e.status_code)except listingsapi.NotFoundError as e: print("No such location:", e.status_code)except listingsapi.ValidationError as e: for err in e.errors: print(err["code"], err["message"], err["context"])except listingsapi.RateLimitError as e: print("Rate limited, retry in", e.retry_after, "seconds")except listingsapi.InternalServerError as e: print("Server error (safe to retry):", e.status_code)except listingsapi.APIConnectionError as e: print("Network failure:", e)except listingsapi.APIError as e: print("Unexpected API error:", e.status_code, e.code, e.response_body)Errori nelle risposte HTTP 200
La piattaforma a volte segnala errori nel corpo di una risposta 200. L'SDK esamina ogni risposta e solleva le eccezioni, quindi non devi mai controllare i flag success o esaminare gli array errors manualmente:
- I corpi con
errors[]al primo livello vengono associati alle eccezioni in base al codice:SY90005eSY90001sollevanoAuthenticationError,SY90003sollevaPermissionDeniedError,RATE_LIMITEDsollevaRateLimitErrore qualsiasi altro codice sollevaAPIError. - Le risposte alle operazioni di scrittura (creazione, aggiornamento, archiviazione, pubblicazione di post) che contengono
success: falseo un elencoerrors[]sollevanoValidationError.
In entrambi i casi, status_code è lo stato HTTP effettivo (spesso 200), code è il primo codice della piattaforma e errors contiene tutte le voci normalizzate:
import listingsapi client = listingsapi.ListingsAPI() try: result = client.locations.update({"id": 16808, "phone": "not-a-phone"})except listingsapi.ValidationError as e: print(e.status_code) # 200: the API accepted the request but rejected the mutation print(e.code) # first platform code, e.g. "SY10005" for err in e.errors: print(err["code"], err["message"], err["context"])Validazione lato client
locations.add() e le funzioni ausiliarie per i post (posts.create_announcement(), posts.create_event(), posts.create_offer(), posts.bulk_publish()) validano il proprio payload localmente e sollevano ValidationError prima di qualsiasi chiamata di rete, quindi un payload non valido genera subito un errore con un messaggio chiaro, senza richiedere uno scambio di dati con il server.
Per locations.add():
namedeve contenere da 2 a 150 caratteridescriptiondeve contenere almeno 200 caratterisub_category_idè obbligatoriocountry_isoè obbligatoriocityè obbligatorio, salvo quandohide_address=True(attività con area di servizio)
Per le funzioni ausiliarie per i post:
nameè obbligatorio elocation_idsnon deve essere vuotositesdeve essere un sottoinsieme non vuoto diGOOGLE,FACEBOOKmessageè obbligatorio; quando è un dict con valori per sito, ogni sito di destinazione deve avere un testocta_typeecta_urldevono essere forniti insieme ecta_typedeve essere uno dei seguenti valori:BOOK,ORDER,SHOP,LEARN_MORE,SIGN_UP,GET_OFFERtitleè obbligatorio per eventi e offerte
Per questi errori sollevati localmente, status_code, code e response_body sono None e errors è vuoto; str(e) elenca tutti i problemi rilevati:
import listingsapi client = listingsapi.ListingsAPI() try: client.locations.add( name="Acme Dental", description="Too short.", sub_category_id=1432, country_iso="US", city="New York", )except listingsapi.ValidationError as e: print(e) # Invalid location: description must be at least 200 characters (got 10) print(e.status_code) # None: raised before any network callTentativi automatici
L'SDK riprova automaticamente in caso di 429, 500, 502, 503 e 504 per GET, HEAD e OPTIONS (per impostazione predefinita, 2 tentativi aggiuntivi, fattore di attesa esponenziale di 0,5 secondi, rispetto di Retry-After). Le operazioni di scrittura non vengono ritentate: una richiesta POST o DELETE viene inviata una sola volta e solleva un'eccezione al primo errore. Configura questo comportamento tramite l'argomento max_retries del costruttore; vedi Configurazione.
L'API v4 non supporta Idempotency-Key e non rileva le operazioni di scrittura ripetute, quindi un'operazione di scrittura che supera il tempo limite o restituisce 5xx potrebbe essere già stata completata. Ripeterla senza verifiche creerebbe una seconda sede, un secondo post o una seconda risposta a una recensione. Rileggi il record usando una chiave sotto il tuo controllo prima di ripetere un'operazione di scrittura.
Attendere prima di riprovare dopo un RateLimitError
Quando un'eccezione 429 persiste dopo i tentativi automatici integrati, RateLimitError.retry_after contiene il valore Retry-After del server in secondi (oppure None se l'header era assente). Un semplice esempio per le elaborazioni in batch:
import randomimport time import listingsapi client = listingsapi.ListingsAPI(max_retries=0) # manual retry control def with_backoff(call, max_attempts=5): for attempt in range(max_attempts): try: return call() except listingsapi.RateLimitError as e: if attempt == max_attempts - 1: raise if e.retry_after is not None: delay = e.retry_after else: delay = min(2 ** attempt, 30) + random.random() time.sleep(delay) page = with_backoff(lambda: client.locations.list(first=50))for location in page: print(location.name)Importare le eccezioni
# All exceptions from the top-level moduleimport listingsapi listingsapi.ListingsAPIErrorlistingsapi.APIErrorlistingsapi.AuthenticationErrorlistingsapi.PermissionDeniedErrorlistingsapi.NotFoundErrorlistingsapi.ValidationErrorlistingsapi.RateLimitErrorlistingsapi.InternalServerErrorlistingsapi.APIConnectionError # Or import individuallyfrom listingsapi import RateLimitError, ValidationErrorVedi anche
- Configurazione:
max_retriesetimeout - Guida rapida: il tuo primo script con gestione degli errori