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.