[Python] Gestire eventi con callback

Alessandro Re ale a ale-re.net
Dom 8 Mar 2015 01:23:23 CET


Ciao lista,

ho un problema con delle callback, e sapendo che ci sono sistemi in
cui si usano molto (e.g. in ambito web), magari qualcuno di voi mi può
illuminare (scusate, il post è un po' lungo).

Ho degli oggetti python che fanno riferimento a degli oggetti C++,
quindi a volte mi trovo nella situazione in cui un oggetto C++ viene
distrutto e, se l'oggetto python esiste ancora e cerca di far
riferimento ad esso, provoca - giustamente - errore.

Ora, dal lato python ho delle callback per gestire degli eventi che
arrivano da C++: quando un certo evento accade, una funzione python
viene chiamata.

Quindi immaginate di avere un oggetto A che emette un evento E, ed un
oggetto B che ha una callback C associata ad E: quando E accade, C
viene chiamata.

Il problema è questo: può capitare che un metodo remove() di B venga
chiamato, provocando la rimozione dell'oggetto C++ sottostante; ma non
dell'oggetto python B, perché la sua callback è ancora registrata. Se
per caso in C venisse usato l'oggetto C++, si alzerebbe una bella
eccezione.

Ora, la mia domanda è: come posso costruire in python un sistema in
cui io posso attaccare due oggetti, ma quando un certo metodo (e.g.
remove) viene chiamato, allora tutte le callback registrate vengono
rimosse senza troppo sforzo e tutti gli oggetti garbage-collected come
ci si aspetterebbe?

Conoscete dei pattern, degli idiomi, o qualunque cosa per poter
implementare questo?

Ho poi una seconda domanda, che fortunatamente richiede meno parole :)

A volte, viene dato un certo comando, ad esempio "send_data(123)";
questa operazione viene fatta in modo asincrono, cioé il controllo
torna subito all'utente e una callback verrà chiamata quando
l'operazione è stata completata.

Come posso rendere sincrona (bloccante) l'invocazione di un comando?

Un modo tanto semplice quanto barbaro è il seguente:

free_to_go = False
def on_packet_sent():
    global free_to_go
    free_to_go = True

def send_data_blocking(data):
    global free_to_go
    free_to_go = False
    send_data(data, callback=on_packet_sent)
    while not free_to_go:
         pass

send_data_blocking(123)

Però ovviamente fa cagare :D Conoscete delle soluzioni migliori (e.g.
thread safe, che sospendano il processo, etc)?

Grazie mille!
~Ale


Maggiori informazioni sulla lista Python