Conquistare i tentativi in Python utilizzando Tenacity un tutorial completo

Complete tutorial on conquering retries in Python using Tenacity.

PYTHON TOOLBOX

Migliorare i tuoi progetti Python con meccanismi di ripetizione robusti e tecniche di gestione degli errori

Spesso, provare ancora una volta porta al successo. Credito foto: Creato dall'autore, Canva

Questo articolo discuterà dell’uso di base e delle capacità di personalizzazione di Tenacity.

Questa libreria Python strumentale fornisce un meccanismo di ripetizione. Esploreremo anche le capacità di ripetizione e gestione delle eccezioni di Tenacity attraverso un esempio pratico.

Introduzione

Immagina di gestire centinaia di servizi web, alcuni situati all’estero (con alta latenza) e altri piuttosto vecchi (e non molto stabili). Come ti sentiresti?

Il mio collega Wang si trova in una situazione del genere. Mi ha detto che era piuttosto frustrato:

Ogni giorno deve controllare lo stato di invocazione di questi servizi remoti e spesso si verifica problemi di timeout o altre anomalie. La risoluzione dei problemi è particolarmente difficile.

Inoltre, gran parte del codice lato client è stato scritto dai suoi predecessori, rendendo difficile effettuare una ristrutturazione sostanziale. Quindi, i servizi devono continuare a funzionare così come sono.

Sarebbe fantastico se ci fosse un modo per riconnettere automaticamente queste chiamate remote dopo che si è verificata un’eccezione. Con le lacrime agli occhi, Wang mi guardò.

Gli assicurai che non era un problema e gli presentai uno strumento nuovo nel mio toolbox: Tenacity. Con un solo decoratore, il codice esistente può acquisire capacità di ripetizione. Vediamo come usarlo.

Installazione e uso di base

Dato che il sito web ufficiale di Tenacity offre solo un semplice documento API, iniziamo con l’installazione della libreria e qualche uso di base.

Installazione

Se stai usando pip, esegui semplicemente il seguente comando:

python -m pip install tenacity

Se stai usando Anaconda, Tenacity non è nel canale predefinito, quindi devi installarlo da conda-forge:

conda install -c conda-forge tenacity

Uso di base

Dopo aver installato Tenacity, vediamo alcuni utilizzi di base della libreria.

Aggiungi semplicemente un decoratore @retry e il tuo codice avrà la capacità di ripetizione:

@retry()async def coro_func():    pass

Se vuoi che il codice smetta di riprovare dopo un certo numero di tentativi, puoi scriverlo in questo modo:

@retry(stop=stop_after_attempt(5))async def coro_func():    pass

Ovviamente, per evitare ripetizioni frequenti che possono esaurire i pool di connessione, consiglio di aggiungere un tempo di attesa prima di ogni ripetizione. Ad esempio, se vuoi attendere 2 secondi prima di ogni connessione:

@retry(wait=wait_fixed(2))async def coro_func():    pass

Anche se non è menzionato nella documentazione, preferisco attendere un secondo in più rispetto all’ultima volta prima di ogni ripetizione per ridurre al minimo lo spreco di risorse:

@retry(wait=wait_incrementing(start=1, increment=1, max=5))async def coro_func():    pass

Infine, se il ripetizione è causata da un’eccezione generata nel metodo, è meglio lanciare di nuovo l’eccezione. Questo consente una gestione delle eccezioni più flessibile durante la chiamata del metodo:

@retry(reraise=True, stop=stop_after_attempt(3))async def coro_func():    pass

Funzionalità avanzate: Callback personalizzati

Oltre a alcuni casi d’uso comuni, è possibile aggiungere una propria logica di determinazione della ripetizione, ad esempio decidendo in base al risultato dell’esecuzione del metodo o stampando i parametri di invocazione del metodo prima dell’esecuzione.

In questo caso, possiamo utilizzare i Callback personalizzati per la personalizzazione.

Ci sono due modi per estendere Custom Callbacks:

Uno è l’approccio raccomandato dalla documentazione: scrivere un metodo di estensione.

Questo metodo riceverà un’istanza di RetryCallState come parametro quando viene eseguito.

Attraverso questo parametro, possiamo ottenere il metodo incapsulato, i parametri della chiamata al metodo, il risultato restituito e le eccezioni generate.

Ad esempio, possiamo utilizzare questo approccio per verificare il valore restituito di un metodo e riprovare se il valore è pari:

from tenacity import *def check_is_even(retry_state: RetryCallState):    if retry_state.outcome.exception():        return True    return retry_state.outcome.result() % 2 == 0

Ovviamente, prima di effettuare questa valutazione, se viene generata un’eccezione, riprova direttamente.

Se è necessario passare parametri aggiuntivi nel metodo di estensione, è possibile aggiungere un wrapper esterno al metodo di estensione.

Ad esempio, questo wrapper passerà un parametro logger. Quando il numero di tentativi supera due, stamperà il tempo di riprova, il nome del metodo e i parametri del metodo nel log:

def my_before_log(logger: Logger):    def my_log(retry_state: RetryCallState):        fn = retry_state.fn        args = retry_state.args        attempt = retry_state.attempt_number        if attempt > 2:            logger.warning(f"Inizio riprova metodo {fn.__name__} con argomenti: {args}")    return my_log

Esempio di rete nel mondo reale

Infine, per darti una profonda impressione sull’uso di Tenacity nei tuoi progetti, utilizzerò un progetto client remoto come esempio per mostrare come integrare le potenti capacità di Tenacity.

Questo progetto simulerà l’accesso a un servizio HTTP e deciderà se riprovare o meno in base al codice di stato restituito.

Ovviamente, per evitare di sprecare risorse del server a causa di tempi di attesa di connessione lunghi, aggiungerò anche un timeout di 2 secondi per ogni richiesta. Se si verifica un timeout, la connessione verrà riprovata.

Diagramma di flusso del progetto. Immagine dell'autore

Prima di iniziare il codice, implementerò diversi metodi di estensione. Uno dei metodi consiste nel verificare quando il numero di tentativi di un metodo supera due e stampare un messaggio di avviso nel log:

Un altro metodo di estensione consiste nel verificare il codice di stato restituito. Se il codice di stato è superiore a 300, riprova. Ovviamente, i timeout attiveranno anche nuovi tentativi.

Successivamente, abbiamo l’implementazione del metodo di chiamata remota. Dopo aver scritto il metodo, ricordati di aggiungere il decoratore di riprova di Tenacity. La strategia che utilizzo qui è di riprovare fino a 20 volte, attendendo 1 secondo in più rispetto al tentativo precedente prima di ogni riprova.

Ovviamente, non dimenticare di aggiungere i due metodi di estensione che abbiamo appena implementato:

Dopo diversi tentativi, alla fine ho ottenuto il risultato corretto. Screenshot dell'autore

Missione compiuta! Non è stato super semplice?

Conclusione

Sono contento di aver aiutato Wang a risolvere un altro problema.

Utilizzando Tenacity, possiamo facilmente dotare il codice esistente di vari meccanismi di riprova, migliorando così la robustezza e le capacità di auto-ripristino del programma.

Sarei ancora più felice se questa libreria potesse aiutarti a risolvere i problemi. Non esitare a lasciare un commento e discutere.

Oltre a migliorare la velocità di esecuzione e le prestazioni del codice, l’utilizzo di vari strumenti per migliorare l’efficienza del lavoro è anche un miglioramento delle prestazioni:

Peng Qian

Python Toolbox

Visualizza elenco 4 storie

Come membro di Nisoo, una parte della tua quota di iscrizione va agli scrittori che leggi e hai accesso completo ad ogni storia…

Nisoo.com