Ho scritto un'interfaccia Python in un sistema di distribuzione del lavoro incentrato sui processi che stiamo sviluppando/utilizzando internamente sul mio posto di lavoro. Mentre i programmatori ragionevolmente qualificati, le persone primarie che usano questa interfaccia sono scienziati di ricerca, non sviluppatori di software, quindi la facilità d'uso e mantenere l'interfaccia al massimo grado possibile è fondamentale.Estrazione di una funzione in un contesto diverso in Python
La mia libreria srotola una sequenza di input in una sequenza di file pickle su un file server condiviso, quindi genera processi che caricano tali input, eseguono il calcolo, mettono in sottaceti i risultati e escono; lo script client quindi recupera e produce un generatore che carica e produce i risultati (o ripensa qualsiasi eccezione la funzione di calcolo ha fatto).
Ciò è utile solo perché la funzione di calcolo è di per sé uno degli ingressi serializzati. cPickle è abbastanza contenuto per decollare i riferimenti alle funzioni, ma richiede che la funzione decapata sia reimportabile nello stesso contesto. Questo è problematico. Ho già risolto il problema di trovare il modulo per reimportarlo, ma nella stragrande maggioranza del tempo, si tratta di una funzione di livello superiore che viene decapata e, quindi, non ha un percorso del modulo. L'unica strategia che ho trovato per essere in grado di deserializzare tale funzione sui nodi di calcolo è questo approccio poco nauseante verso simulando l'ambiente originale in cui la funzione è stata decapato prima deserializzazione esso:
...
# At this point, we've identified the source of the target function.
# A string by its name lives in "modname".
# In the real code, there is significant try/except work here.
targetModule = __import__(modname)
globalRef = globals()
for thingie in dir(targetModule):
if thingie not in globalRef:
globalRef[thingie] = targetModule.__dict__[thingie]
# sys.argv[2]: the path to the pickle file common to all jobs, which contains
# any data in common to all invocations of the target function, then the
# target function itself
commonFile = open(sys.argv[2], "rb")
commonUnpickle = cPickle.Unpickler(commonFile)
commonData = commonUnpickle.load()
# the actual function unpack I'm having trouble with:
doIt = commonUnpickle.load()
la linea finale è il più importante qui - è dove il mio modulo sta rilevando la funzione che dovrebbe effettivamente essere in esecuzione. Questo codice, come scritto, funziona come desiderato, ma manipolare direttamente le tabelle dei simboli come questo è sconvolgente.
Come posso fare questo, o qualcosa di molto simile a questo, che non costringe gli scienziati della ricerca a separare i loro script di calcolo in una struttura di classe appropriata (usano Python come il più eccellente calcolatore grafico di sempre e mi piacerebbe continuare lasciare che lo facciano) il modo in cui Pickle desidera disperatamente, senza la spiacevole, insicura e semplicemente spaventosa manipolazione __dict__
e globals()
che sto usando sopra? Credo fermamente che ci sia un modo migliore, ma exec "from {0} import *".format("modname")
non lo ha fatto, diversi tentativi di iniettare il carico di pickle nel riferimento targetModule
non lo hanno fatto, e eval("commonUnpickle.load()", targetModule.__dict__, locals())
non lo ha fatto. Tutti questi errori non riescono con AttributeError
di Unpickle a non riuscire a trovare la funzione in <module>
.
Che cos'è un modo migliore?
Avete considerato [ 'marshal'] (http://docs.python.org/library/marshal.html), invece? È il solito modo per mantenere gli "oggetti codice" (anche se per quanto ne so tu potresti avere tutti gli stessi problemi che hai con 'cPickle'). – Cameron
'marshal' non può comprimere tipi di dati definiti dall'utente, come l'oggetto attorno alla funzione se la mia funzione di destinazione è in realtà una funzione di istanza, uno scenario che funziona correttamente qui. I suggerimenti su come impacchettare il '__code__' sono utili (e mi aiutano a risolvere un altro problema che ho da qualche altra parte), ma non risolvono il problema" e il tuo piccolo contesto! _evil cackle_ "lo spazio problematico a cui sto mirando. –