Ist es möglich, einen Python-Stack-Frame programmatisch zu konstruieren und die Ausführung an einem beliebigen Punkt in dem Code beginnen?

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

Frage

Ist es möglich, programmatisch einen Stapel aufzubauen (ein oder mehr Stack-Frames) in CPython und Ausführung zu einem beliebigen Code Punkt beginnen? Stellen Sie sich folgendes Szenario vor:

  1. Sie haben einen Workflow-Engine, wo Workflows können mit einigen Konstrukten (z Verzweigung, Makeln / Verbinden), die Anrufe an die Workflow-Engine.

  2. in Python Skript werden
  3. Ein blockierenden Aufruf, wie eine Wartezeit oder beitreten Sets mit einem anhaltenden Sicherungsspeicher von einer Art eines Zuhörer Zustand in einem Event-Dispatching Motor.

  4. Sie haben einen Workflow-Skript, das den Wartezustand in dem Motor fordert, für eine Bedingung warten, die später signalisiert werden. Damit wird der Hörer bei Dispatching Motor.

  5. Die Workflow-Skripts Zustand relevanten Stapelrahmen einschließlich der Programmzähler (oder eines gleichwertigen Zustand) werden beibehalten -. Als die Wartebedingung später Tagen oder Monaten auftreten könnte

  6. In der Zwischenzeit wird die Workflow-Engine gestoppt könnte und neu gestartet, was bedeutet, dass es möglich sein muss programmatisch speichern und den Kontext des Workflows Skript zu rekonstruieren.

  7. Die Veranstaltung Dispatching-Engine löst das Ereignis, dass der Wartezustand aufhebt.

  8. Die Workflow-Engine liest den serialisierte Zustand und stapelt und rekonstruiert einen Thread mit dem Stapel. Weiter geht es dann die Ausführung an dem Punkt, wo die Warte Dienst aufgerufen wurde.

Die Frage

Kann dies mit einem unmodifizierten Python-Interpreter getan werden? Noch besser ist, kann mir jemand zu einem gewissen Dokumentation verweisen, die diese Art der Sache kommen könnte oder ein Beispiel-Code, der programmatisch einen Stapelrahmen konstruiert und beginnt die Ausführung irgendwo in der Mitte eines Blocks von Code?

Edit: ‚unmodifizierten Python-Interpreter‘ Um zu klären, ich habe nichts dagegen die C-API (gibt es genügend Informationen in einer PyThreadState dies zu tun?), Aber ich will nicht gehen um die Interna des Python Interpreter Stossen und ein modifiziertes einen bauen zu müssen.

Update: Aus anfänglicher Untersuchung kann man den Ausführungskontext mit PyThreadState_Get() bekommen. Dies gibt den Thread-Zustand in einem PyThreadState (in pystate.h definiert), die einen Verweis auf den Stapelrahmen in frame hat. Ein Stapelrahmen in einem struct typedef'd gehalten PyFrameObject, die in frameobject.h definiert ist. PyFrameObject hat ein Feld f_lasti (Requisiten auf bobince ), die ausgedrückt einen Programmzähler aufweist, wie eine vom Anfang des Codeblocks versetzt ist.

Das letzte ist eine Art eine gute Nachricht, weil es bedeutet, dass, solange Sie den eigentlichen kompilierten Code-Block erhalten, sollten Sie in der Lage sein, Einheimische wie nötig für so viele Stack-Frames zu rekonstruieren und den Code neu starten. Ich würde sagen, dass dies bedeutet, dass es theoretisch möglich ist, ohne eine modifizierte Python interpereter machen zu müssen, auch wenn es bedeutet, dass der Code immer noch wahrscheinlich sein wird, knifflig und eng gekoppelt an bestimmten Versionen des Interpreters.

Die drei verbleibenden Probleme sind:

  • Transaktionszustand und ‚Saga‘ Rollback, die wahrscheinlich von der Art metaclass erreicht werden kann Hacker würde man verwenden, um eine O / R-Mapper zu bauen. Ich habe einmal einen Prototyp bauen, so habe ich eine Vorstellung davon, wie dies erreicht werden könnte.

  • Kräftig serializing Transaktionsstatus und willkürlich Einheimischen. Dies könnte durch das Lesen __locals__ erreicht werden (die aus dem Stapelrahmen verfügbar ist) und programmatisch einen Anruf Konstruktion beizen. Allerdings weiß ich nicht, was, wenn überhaupt, könnte hier gotchas da sein.

  • Versionierung und Upgrade von Workflows. Dies ist etwas schwieriger, da das System keine symbolische ancho Bereitstellungrs für die Workflow-Knoten. Alles, was wir haben, ist der Anker Um dies zu tun, würde man die Offsets aller den Einspeisepunkten und ordnet sie auf die neue Version identifizieren müssen. Wahrscheinlich machbar manuell zu tun, aber ich vermute, es wäre schwer zu automatisieren. Dies ist wahrscheinlich das größte Hindernis, wenn Sie diese Fähigkeit unterstützen wollen.

Update 2: PyCodeObject (code.h) hat eine Liste von Adr (f_lasti) -> Zeilennummer Zuordnungen in PyCodeObject.co_lnotab (korrigieren Sie mich, wenn hier falsch). Dies könnte verwendet werden, um eine Migration zu erleichtern Workflows auf eine neue Version zu aktualisieren, als gefrorene Befehlszeiger könnte an die entsprechende Stelle in dem neuen Skript abgebildet werden, in Bezug auf die Zeilennummern erfolgen. Immer noch ziemlich chaotisch, aber ein wenig vielversprechend.

Update 3: Ich denke, die Antwort auf diese Frage könnte Stackless Python . Sie können Aufgaben suspendieren und sie serialise. Ich habe nicht funktioniert, ob dies auch als auch mit dem Stapel arbeiten.

War es hilfreich?

Lösung 5

Mit Standard CPython dies wird durch die Mischung aus C und Python-Daten in dem Stapel kompliziert. den Call-Stack Wiederaufbau würde den C-Stack erfordert zugleich rekonstruiert werden. Damit ist es wirklich in dem zu hart Korb, wie es möglicherweise fest Paar konnte die Implementierung auf bestimmte Versionen von CPython.

Stackless Python ermöglicht Tasklets gebeizt werden, die aus der Box benötigt die meisten der Fähigkeit gibt.

Andere Tipps

Die Expat-Python-Bindungen in der normalen Python-Distribution bauen Stapelrahmen programtically. Aber gewarnt werden, stützt sie sich auf undokumentierte und privaten APIs.

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

Was Sie in der Regel wollen, sind Fortsetzungen, die ich sehe, ist bereits ein Tag auf diese Frage.

Wenn Sie die Möglichkeit haben, mit den gesamten Code in dem System zu arbeiten, sollten Sie versuchen, tun es auf diese Weise eher mit den Dolmetscher Stapel Interna als beschäftigen. Ich bin nicht sicher, wie leicht wird diese beibehalten werden.

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

In der Praxis würde ich Ihr Workflow-Engine so strukturiert, dass Ihre Skript Aktion einreicht Objekte an einen Manager. Der Manager könnte die Reihe von Aktionen an jedem Punkt Pickles und erlaubt sie geladen werden und beginnen, die Ausführung wieder (durch die Vorlage von Aktionen wieder aufnehmen).

Mit anderen Worten:. Machen Sie Ihren eigenen, auf Anwendungsebene, Stapel

Stackless Python ist wahrscheinlich die beste ... wenn Sie nicht total nichts dagegen zu einer anderen Python-Distribution zu überschreiten. stackless kann serialisiert alles in Python, sowie deren Tasklets. Wenn Sie in der Standard-Python-Distribution bleiben wollen, dann würde ich verwenden Dill , die serialisiert werden kann fast etwas 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 registriert es Typen in den pickle Registry ist, also, wenn Sie etwas Blackbox Code haben, der pickle verwendet, und Sie können es nicht wirklich bearbeiten, dann nur den Import Dill kann es auf magische Weise ohne monkeypatching die 3rd-Party-Code arbeiten.

Hier ist dill die ganze Interpreter-Sitzung Beizen ...

>>> # 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 hat auch einige gute Werkzeuge für das Helfen Sie verstehen was Ihr Beizen verursacht fehlschlagen, wenn Ihr Code fehlschlägt.

Sie auch gefragt, wo es verwendet wird Interpreter Zustand zu speichern?

IPython können dill verwenden, um die Interpreter-Sitzung in einer Datei zu speichern. https://nbtest.herokuapp.com /github/ipython/ipython/blob/master/examples/parallel/Using%20Dill.ipynb

klepto verwendet dill im Speicher zu unterstützen, auf Festplatte oder zu-Datenbank-Caching, das vermeidet Neuberechnung. https://github.com/uqfoundation/klepto/blob/master/tests /test_cache_info.py

mystic verwendet dill die Checkpoints für große Optimierungs Arbeitsplätze zu sichern, indem der Zustand des Optimierers sparend wie es ist in Bearbeitung. https://github.com/uqfoundation/mystic/blob/master/tests /test_solver_state.py

Es gibt ein paar anderen Pakete, die dill verwenden Zustand von Objekten oder Sitzungen zu speichern.

Sie können den vorhandenen Stapelrahmen greifen durch eine Ausnahme zu werfen und einen Frame zurück entlang der Zurückverfolgungs treten. Das Problem ist, gibt es keine Möglichkeit zur Wiederaufnahme der Ausführung in der Mitte (frame.f_lasti) des Codeblocks vorgesehen ist.

„Wiederaufnehmbare Ausnahmen“ sind eine sehr interessante Sprache Idee, obwohl es schwierig ist, eine angemessene Art und Weise zu denken, dass sie mit Python bestehenden ‚try / finally‘ und ‚mit‘ Blöcke interagieren können.

Für den Moment der normale Weg, dies zu tun, ist einfach Threads zu verwenden, um Ihren Workflow in einem eigenen Kontext zu seiner Steuerung auszuführen. (Oder Koroutinen / greenlets wenn Sie nichts dagegen haben sie bei der Zusammenstellung).

Ich habe die gleiche Art von Problem zu lösen. Ich frage mich, was das ursprüngliche Plakat zu tun beschlossen.

stackless behauptet, es Tasklets solange es keine dazugehörigen ‚belastet‘ C-Stack (belastet ist meine Wahl der Formulierung) Beize kann.

Ich werde wahrscheinlich eventlet verwenden und einen Weg Beizen ‚Staat‘ herauszufinden, ich möchte wirklich nicht, wenn eine explizite Zustandsmaschine schreiben ..

Wie wäre es mit JOBLIB ?

Ich bin mir nicht ganz sicher, das ist, was Sie wollen, aber es scheint die Idee, einen Workflow, von denen Stufen beibehalten werden können passen. JOBLIB die Nutzung Fall scheint zu sein, eine erneute Berechnung zu vermeiden, ich bin nicht sicher, ob dies ist, was Sie versuchen, hier zu tun oder etwas komplizierter?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top