12.7. Callback dei canali¶
L’oggetto backend passato a protocol.register() è una classe Python. La libreria del protocollo non chiede alla classe quali metodi implementa; ispeziona l’istanza e collega quelli che trova. Questa introspezione è ciò che rende flessibile l’interfaccia del backend: il backend utile più piccolo ha due metodi, quello più elaborato ne ha dodici, e l’applicazione aderisce a ciascuna funzionalità un metodo alla volta.
12.7.1. Le regole di introspezione¶
Quando protocol.register() viene eseguito, la libreria scorre un elenco fisso di nomi richiamabili e collega ciascuno di quelli che trova nell’istanza del backend:
Aggiungere
readalla classe attivaCHANNEL_FLAG_READ. Una chiamata dell’host achannel_read()raggiunge il backend solo se questo flag è impostato.Aggiungere
writeattivaCHANNEL_FLAG_WRITE, abilitandochannel_write().Aggiungere
lockeunlockattivaCHANNEL_FLAG_LOCK, consentendo all’host di bloccare il canale per una lettura atomica multi-pacchetto.Aggiungere
pollconsente all’host di chiedere «c’è qualcosa di pronto?» a basso costo, senza forzare una lettura completa.
I metodi mancanti non sono errori: la libreria del protocollo lascia semplicemente disabilitata la funzionalità corrispondente. Un backend con solo size e read è perfettamente valido; è un canale dati di sola lettura.
12.7.2. Un canale sensore di sola lettura¶
Un canale sensore che pubblica una lettura aggiornata ogni volta che l’host la richiede, rifiutando le scritture dell’host, utilizza quattro dei callback:
import protocol
import struct
class TempChannel:
def __init__(self, read_sensor):
self._read_sensor = read_sensor
self._buf = b''
self._fresh = False
def poll(self):
# Tell the host whether a reading is waiting.
return self._fresh
def size(self):
# Sample fresh data on every host-side size query.
value = self._read_sensor()
self._buf = struct.pack('<f', value)
self._fresh = True
return len(self._buf)
def read(self, offset, size):
end = offset + size
if end >= len(self._buf):
self._fresh = False
return self._buf[offset:end]
protocol.register(name='temp', backend=TempChannel(read_temperature))
Esaminiamo cosa fa ciascun metodo:
pollrestituisce il flag di freschezza. L’host lo chiama prima di leggere e salta del tutto la lettura quando restituisceFalse. Questo evita il costo del round-trip per «nessun dato nuovo ancora».sizerigenera il buffer su richiesta e ne riporta la lunghezza. Eseguire qui il campionamento significa che il backend non ha bisogno di un task in background: ogni chiamata dell’host guida ogni misurazione.readrestituisce una porzione del buffer. La libreria del protocollo può chiamarlo più di una volta quando il buffer è più grande del payload massimo negoziato; l’argomentooffsetscorre i frammenti.L’assenza di
writesignifica che le scritture dell’host vengono rifiutate a livello di framing, prima che il backend sia coinvolto.
12.7.3. L’insieme completo dei callback¶
Per riferimento, ogni metodo che la libreria cerca su un backend:
Metodo |
Restituisce |
Scopo |
|---|---|---|
|
object |
Inizializzazione opzionale una tantum quando il canale si collega per la prima volta a un host. Restituisce qualsiasi valore non |
|
bool |
Restituisce |
|
bool |
Acquisisce il canale per un trasferimento atomico multi-pacchetto. |
|
bool |
Rilascia un |
|
int |
Numero di byte attualmente leggibili dal canale. |
|
tuple |
Fino a quattro interi che descrivono la struttura dei dati (ad es. altezza, larghezza, numero di byte dell’immagine). Usato dall’host per spacchettare buffer tipizzati. |
|
bytes |
Restituisce fino a size byte a partire da offset. Chiamato una volta per frammento quando il payload supera il massimo negoziato. |
|
bytes |
Variante zero-copy di |
|
int |
L’host ha scritto data a offset. |
|
int |
Opcode definito dall’applicazione al di fuori del modello read/write. Un valore di ritorno negativo è un errore. |
|
object |
Scarta qualsiasi dato bufferizzato. Chiamato quando l’host vuole reimpostare il canale. |
|
bool |
Significativo solo sui backend che rappresentano un trasporto fisico (i canali USB integrati). I canali applicativi non ne hanno bisogno. |
Questa è l’intera interfaccia del backend. Dodici nomi di metodi, tutti opzionali, e la libreria del protocollo decide cosa può fare ciascun canale in base a quali sono presenti.