È possibile costruire uno stack frame programmazione Python e avviare l'esecuzione in un punto arbitrario nel codice?

StackOverflow https://stackoverflow.com/questions/541329

Domanda

E 'possibile costruire una pila di programmazione (una o più stack frame) in CPython e avviare l'esecuzione in un punto di codice arbitrario? Immaginate il seguente scenario:

  1. Si dispone di un motore di workflow in cui i flussi di lavoro possono essere script in Python con alcuni costrutti (per esempio ramificazione, in attesa / unione) che sono chiamate al motore di workflow.

  2. Una chiamata di blocco, come ad esempio un'attesa o partecipare istituisce un condizione ascoltatore in un motore-invio di evento con un archivio di backup permanente di qualche tipo.

  3. Hai uno script del flusso di lavoro, che prevede la condizione di attesa nel motore, in attesa di qualche condizione che verrà segnalata in seguito. Questo imposta l'ascoltatore nel motore dispacciamento evento.

  4. Lo stato dello script del flusso di lavoro, stack frame rilevanti tra cui il contatore di programma (o stato equivalente) sono persisteva -. Come la condizione di attesa potrebbe verificarsi giorni o mesi più tardi

  5. Nel frattempo, il motore di workflow potrebbe essere interrotto e riavviato, il che significa che deve essere possibile memorizzare a livello di codice e ricostruire il contesto dello script del flusso di lavoro.

  6. Il motore di dispacciamento evento genera l'evento che la condizione di attesa prende.

  7. Il motore di workflow legge lo stato serializzato e impilare e ricostruisce un filo con la pila. E poi continua l'esecuzione nel punto in cui il servizio di attesa è stato chiamato.

La questione

Può questo essere fatto con un interprete non modificato Python? Ancora meglio, qualcuno mi può puntare a qualche documentazione che potrebbe coprire questo genere di cose o di un esempio di codice che costruisce a livello di codice uno stack frame e inizia l'esecuzione da qualche parte nel mezzo di un blocco di codice?

Modifica Per chiarire 'non modificato interprete python', non mi dispiace utilizzando l'API C (c'è abbastanza informazioni in un PyThreadState per fare questo?), Ma io non voglio andare rovistando l'interno dell'interprete Python e dover costruire uno modificato.

Aggiornamento: Da qualche indagine iniziale, si può ottenere il contesto di esecuzione con PyThreadState_Get(). Questo restituisce lo stato del filo in un PyThreadState (definito in pystate.h), che ha un riferimento al telaio stack frame. Un telaio pila viene tenuto in una typedef struct a PyFrameObject, che è definita in frameobject.h. PyFrameObject ha un campo f_lasti (oggetti di scena a bobince ) che ha un contatore di programma espresso come offset dall'inizio del blocco di codice.

Questa ultima è una sorta di una buona notizia, perché significa che, fintanto che si conserva il blocco di codice compilato vero e proprio, si dovrebbe essere in grado di ricostruire i locali per tutti i fotogrammi di stack come necessario e riavviare il codice. Direi che questo significa che è teoricamente possibile, senza dover fare un interpereter pitone modificato, anche se significa che il codice è ancora destinata probabilmente ad essere poco pratici e strettamente accoppiati a specifiche versioni dell'interprete.

I tre problemi restanti sono:

  • Stato di transazione e rollback 'saga', che probabilmente può essere realizzata mediante una sorta di meta-classe hacker si potrebbe usare per costruire un / R mapper O. Ho fatto costruire un prototipo, una volta, in modo da avere una buona idea di come questo potrebbe essere realizzato.

  • Robusto serializzazione stato di transazione e la gente del posto arbitrarie. Ciò potrebbe essere ottenuto leggendo __locals__ (che è disponibile dal telaio stack) e programatically costruzione di una chiamata a decapare. Tuttavia, non so che cosa, se del caso, trucchi ci potrebbe essere qui.

  • versioni e l'aggiornamento dei flussi di lavoro. Questo è un po 'più complicato, in quanto il sistema non esegue alcuna ancho simbolicaRS per i nodi del flusso di lavoro. Tutto quello che abbiamo è l'ancora Per fare questo, si dovrebbe individuare gli offset di tutti i punti di ingresso e di mapparli alla nuova versione. Probabilmente fattibile per fare manualmente, ma ho il sospetto che sarebbe stato difficile da automatizzare. Questo è probabilmente il più grande ostacolo se si desidera supportare questa funzionalità.

Aggiornamento 2: PyCodeObject (code.h) ha una lista di addr (f_lasti) -> mappature numero di riga nel PyCodeObject.co_lnotab (correggetemi se sbagliato qui). Questo potrebbe essere utilizzato per facilitare un processo di migrazione per aggiornare i flussi di lavoro ad una nuova versione, come puntatori istruzione congelati possono essere mappati nel luogo appropriato nel nuovo script, fatto in termini di numeri di riga. Ancora abbastanza disordinato, ma un po 'più promettente.

Aggiornamento 3: Penso che la risposta a questo potrebbe essere Stackless Python . È possibile sospendere le attività e le puntate. Non ho lavorato fuori se questo funziona anche con lo stack pure.

È stato utile?

Soluzione 5

Con questa norma CPython è complicata dalla miscela di dati C e Python nella pila. Ricostruire lo stack di chiamate richiederebbe la pila C da ricostruire allo stesso tempo. Questo in realtà lo mette nel cestino troppo difficile come potrebbe potenzialmente strettamente paio l'implementazione di versioni specifiche di CPython.

Stackless Python permette tasklets da salamoia, che dà la maggior parte della capacità necessaria, fuori dalla scatola.

Altri suggerimenti

I binding python expat inclusi nella distribuzione normale di Python sta costruendo stack frame modo pianificato. Attenzione però, si basa sulle API non documentate e private.

http: //svn.python. org / view / python / trunk / Moduli / pyexpat.c? rev = 64048 & view = auto

Quello che in genere vogliono sei continuazioni, che vedo è già un tag su questa questione.

Se avete la possibilità di lavorare con tutto il codice nel sistema, si consiglia di provare facendo in questo modo piuttosto che fare con l'interno dello stack interprete. Non sono sicuro di quanto facilmente questo sarà essere mantenuta.

http://www.ps.uni-sb.de /~duchier/python/continuations.html

In pratica, vorrei strutturare il motore di workflow in modo che lo script invia oggetti azione a un gestore. Il gestore potrebbe sottaceto l'insieme di azioni in qualsiasi momento e consentire loro di essere caricati e ricominciare esecuzione (riprendendo la presentazione delle azioni).

In altre parole:. Fare il vostro proprio, a livello di applicazione, di stack

Stackless pitone è probabilmente il migliore ... se non ti dispiace totalmente andare oltre a una distribuzione diversa pitone. stackless può serializzare tutto in python, più i loro tasklets. Se si vuole rimanere nella distribuzione standard di Python, quindi mi piacerebbe utilizzare aneto , che può serializzare quasi nulla in python.

>>> import dill
>>> 
>>> def foo(a):
...   def bar(x):
...     return a*x
...   return bar
... 
>>> class baz(object):
...   def __call__(self, a,x):
...     return foo(a)(x)
... 
>>> b = baz()
>>> b(3,2)
6
>>> c = baz.__call__
>>> c(b,3,2)
6
>>> g = dill.loads(dill.dumps(globals()))
>>> g
{'dill': <module 'dill' from '/Library/Frameworks/Python.framework/Versions/7.2/lib/python2.7/site-packages/dill-0.2a.dev-py2.7.egg/dill/__init__.pyc'>, 'c': <unbound method baz.__call__>, 'b': <__main__.baz object at 0x4d61970>, 'g': {...}, '__builtins__': <module '__builtin__' (built-in)>, 'baz': <class '__main__.baz'>, '_version': '2', '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x4d39d30>, '__doc__': None}

Dill registra è tipi nel Registro di sistema pickle, quindi se avete un po 'di codice di scatola nera che utilizza pickle e non si può davvero modificarlo, poi basta importare aneto può magicamente farlo funzionare senza monkeypatching il codice 3rd party.

Ecco dill decapaggio l'intera sessione interprete ...

>>> # continuing from above
>>> dill.dump_session('foobar.pkl')
>>>
>>> ^D
dude@sakurai>$ python
Python 2.7.5 (default, Sep 30 2013, 20:15:49) 
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.load_session('foobar.pkl')
>>> c(b,3,2)
6

dill ha anche href="https://github.com/uqfoundation/dill/blob/master/dill/detect.py" rel="nofollow"> alcuni buoni strumenti per aiutare a capire cosa sta causando il decapaggio a fallire quando il codice non riesce.

È anche chiesto dove è utilizzato per salvare lo stato interprete?

IPython può usare dill per salvare la sessione interprete in un file. https://nbtest.herokuapp.com /github/ipython/ipython/blob/master/examples/parallel/Using%20Dill.ipynb

KLEPTO utilizza dill per supportare la memorizzazione nella cache in memoria, to-disk, o per database che evita ricalcolo. https://github.com/uqfoundation/klepto/blob/master/tests /test_cache_info.py

mistico utilizza dill per salvare i posti di blocco per i grandi lavori di ottimizzazione salvando lo stato del ottimizzatore come è in corso. https://github.com/uqfoundation/mystic/blob/master/tests /test_solver_state.py

Ci sono un paio di altri pacchetti che utilizzano dill per salvare lo stato di oggetti o sessioni.

Si può afferrare il telaio stack esistente gettando un'eccezione e tornare indietro di un fotogramma avanti nel traceback. Il problema è che non c'è alcun modo disponibile per riprendere l'esecuzione nel mezzo (frame.f_lasti) del blocco di codice.

“eccezioni ripristinabili” sono un'idea lingua davvero interessante, anche se è difficile pensare a un modo ragionevole che potessero interagire con Python esistente ‘try / finally’ e ‘con’ blocchi.

Per il momento, il modo normale di fare questo è semplicemente quello di utilizzare i thread per eseguire il flusso di lavoro in un contesto separato per il suo controllore. (O coroutine / greenlets se non ti dispiace li compilazione in).

Ho lo stesso tipo di problema da risolvere. Mi chiedo che cosa il manifesto originale ha deciso di fare.

stackless afferma che può salamoia tasklets finché non c'è associato 'gravato' C pila (gravati è la mia scelta di fraseggio).

Io probabilmente usare eventlet e capire il modo di decapaggio 'stato', davvero non voglio scrivere una macchina a stati esplicito però ..

Come sull'utilizzo joblib ?

Io non sono molto sicuro che questo è ciò che si vuole, ma sembra per adattarsi l'idea di avere un flusso di lavoro di cui fasi possono essere mantenuta. caso d'uso di Joblib sembra essere quello di evitare ricalcolo, non sono sicuro se questo è ciò che si sta cercando di fare qui o qualcosa di più complicato?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top