2014-10-15 8 views
5

Ho provato più approcci per decollare una funzione python con dipendenze, seguendo molti consigli su StackOverflow (come dill, cloudpickle, ecc.) Ma tutti sembrano incappare in un problema fondamentale che non posso risolvere.Serializzare una funzione python con dipendenze

Ho un modulo principale che tenta di decapitare una funzione da un modulo importato, lo invia su ssh per essere deselezionato ed eseguito su una macchina remota.

Così principale ha:

import dill (for example) 
    import modulea 

    serial=dill.dumps(modulea.func) 
    send (serial) 

Sulla macchina remota:

 import dill 
     receive serial 
     funcremote = dill.loads(serial) 
     funcremote() 

Se le funzioni di essere in salamoia e inviati sono funzioni di alto livello definite in sé principale, tutto funziona. Quando si trovano in un modulo importato, la funzione di caricamento non riesce con i messaggi del tipo "modulo modulea non trovato".

Sembra che il nome del modulo sia decapitato insieme al nome della funzione. Non vedo alcun modo per "aggiustare" il sottaceto per rimuovere la dipendenza, o in alternativa, per creare un modulo fittizio nel ricevitore per diventare il destinatario del disfacimento.

Qualsiasi suggerimento sarà molto apprezzato.

--prasanna

+0

È una cosa facile da fare per sostituire il nome del modulo di una funzione al momento dello scaricamento. Durante la serializzazione, è possibile sostituire qualsiasi '__name__' con' __main__', e voilà ... dovrebbe funzionare ... cioè, a meno che la funzione non abbia ** eventuali ** dipendenze nel modulo allegato. Allora fallirà. –

+0

Il problema è che il file dill.loads ha esito negativo, non viene mai annullato. Come indicato correttamente nel post sottostante, poiché nell'esempio precedente non è disponibile, i carichi muoiono. Quindi rinominarlo dopo il fatto non aiuta. – Prasanna

+0

Non sto parlando di ridenominarlo dopo il fatto, sto parlando di sostituire l'attributo al momento del caricamento con un pickler personalizzato. Quello funzionerebbe come sopra dettagliato. –

risposta

10

Sono il dill autore. Faccio esattamente questa cosa su ssh, ma con successo. Attualmente, dill e uno degli altri serializzatori moduli pickle per riferimento ... quindi per passare con successo una funzione definita in un file, è necessario assicurarsi che il modulo pertinente sia installato anche sull'altra macchina. Non credo ci sia alcun serializzatore di oggetti che serializza direttamente i moduli (cioè non per riferimento).

Detto questo, dill ha alcune opzioni per serializzare le dipendenze degli oggetti. Ad esempio, per le istanze di classe, il valore predefinito in dill consiste nel non serializzare le istanze di classe per riferimento ... in modo che la definizione di classe possa anche essere serializzata e inviare con l'istanza. In dill, è anche possibile (utilizzare una funzionalità molto nuova per) serializzare gli handle di file serializzando il file, invece di farlo per riferimento. Ma di nuovo, se si ha il caso di una funzione definita in un modulo, si è sfortunati, poiché i moduli sono serializzati per riferimento universalmente malissimo.

Potrebbe essere possibile utilizzare dill per farlo, tuttavia, non solo con il decapaggio dell'oggetto, ma con l'estrazione dell'origine e l'invio del codice sorgente. In pathos.pp, dill, per estrarre la fonte e le dipendenze di qualsiasi oggetto (comprese le funzioni) e passarle a un altro computer/processo/ecc. Tuttavia, poiché questa non è una cosa facile da fare, dill può anche utilizzare il failover del tentativo di estrarre un'importazione rilevante e inviarlo invece del codice sorgente.

Si può capire, si spera, questa è una cosa caotica e disordinata da fare (come indicato in una delle dipendenze della funzione che sto estrapolando di seguito). Tuttavia, ciò che si sta chiedendo viene eseguito con successo nel pacchetto pathos per passare il codice e le dipendenze a macchine diverse attraverso le porte con tunnel ssh.

>>> import dill 
>>> 
>>> print dill.source.importable(dill.source.importable) 
from dill.source import importable 
>>> print dill.source.importable(dill.source.importable, source=True) 
def _closuredsource(func, alias=''): 
    """get source code for closured objects; return a dict of 'name' 
    and 'code blocks'""" 
    #FIXME: this entire function is a messy messy HACK 
    #  - pollutes global namespace 
    #  - fails if name of freevars are reused 
    #  - can unnecessarily duplicate function code 
    from dill.detect import freevars 
    free_vars = freevars(func) 
    func_vars = {} 
    # split into 'funcs' and 'non-funcs' 
    for name,obj in list(free_vars.items()): 
     if not isfunction(obj): 
      # get source for 'non-funcs' 
      free_vars[name] = getsource(obj, force=True, alias=name) 
      continue 
     # get source for 'funcs' 

#…snip… …snip… …snip… …snip… …snip… 

      # get source code of objects referred to by obj in global scope 
      from dill.detect import globalvars 
      obj = globalvars(obj) #XXX: don't worry about alias? 
      obj = list(getsource(_obj,name,force=True) for (name,_obj) in obj.items()) 
      obj = '\n'.join(obj) if obj else '' 
      # combine all referred-to source (global then enclosing) 
      if not obj: return src 
      if not src: return obj 
      return obj + src 
     except: 
      if tried_import: raise 
      tried_source = True 
      source = not source 
    # should never get here 
    return 

immagino cosa potrebbe anche essere costruito attorno al metodo dill.detect.parents, che fornisce un elenco di puntatori a tutti oggetto padre per un dato oggetto ... e si potrebbe ricostruire tutte le dipendenze di ogni funzione come oggetti ... ma questo non è implementato.

BTW: per stabilire un tunnel ssh, basta fare questo:

>>> t = pathos.Tunnel.Tunnel() 
>>> t.connect('login.university.edu') 
39322 
>>> t 
Tunnel('-q -N -L39322:login.university.edu:45075 login.university.edu') 

Poi si può lavorare attraverso la porta locale con ZMQ, o ssh, o qualsiasi altra cosa. Se si desidera farlo con ssh, anche pathos ha incorporato quello.

+0

Sì, ci gioco da ieri. Cercando qualcosa di più semplice - il mio modulo è abbastanza pulito in quanto ogni funzione a cui sono interessato utilizza solo uno o due di una piccola serie di funzioni di utilità nel modulo. Quindi, posso dapprima mandare/sottaceti e inviare su ciascuna funzione di utilità, quindi inviare semplicemente la funzione che voglio venga eseguita in quel contesto. – Prasanna

+0

BTW Guardo nei tunnel del pathos. In questo momento sto usando execnet (http://codespeak.net/execnet/#) – Prasanna

+2

Se il tuo modulo è costituito da un solo file, potresti sottaceti il ​​modulo con 'dill.source.getsource', e poi decapare la funzione come un oggetto e inviarlo dopo. Oppure, come menzionato nel mio commento sulla tua domanda precedente, potresti estendere 'dill.Pickler' e' dill.Unpickler' controllando l'attributo '__module__' per qualsiasi funzione, e se il dato modulo non è disponibile, allora imposta' __module__ = '__main __' 'e dovrebbe funzionare fino a quando non ci sono dipendenze mancanti. –

Problemi correlati