Est-il possible de construire un programme cadre de pile Python et commencer l'exécution à un point arbitraire dans le code?

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

Question

Est-il possible de construire une pile (programme un ou plusieurs cadres de pile) dans CPython et lancer l'exécution à un point de code arbitraire? Imaginez le scénario suivant:

  1. Vous avez un moteur de workflow où les flux de travail peuvent être scénarisées en Python avec quelques constructions (par exemple de branchement, en attente / rejoindre) qui sont des appels au moteur de workflow.

  2. Un appel de blocage, comme une attente ou d'y adhérer établit une condition d'auditeur dans un moteur à distribution des événements avec un magasin de soutien persistant de quelque sorte.

  3. Vous avez un script de workflow, qui appelle la condition d'attente dans le moteur, en attendant une condition qui sera signalé plus tard. Ceci met en place l'auditeur dans le moteur de répartition de l'événement.

  4. L'état de script de flux de travail, les cadres de pile pertinents, y compris le compteur de programme (ou un état équivalent) sont conservés -. Comme condition d'attente pourrait se produire des jours ou des mois plus tard

  5. Dans l'intervalle, le moteur de workflow peut être arrêté et démarré à nouveau, ce qui signifie qu'il doit être possible de stocker et de reconstruire le programme contexte du script de flux de travail.

  6. Le moteur de répartition de l'événement déclenche l'événement que l'état d'attente reprend.

  7. Le moteur de workflow lit l'état sérialisé et pile et reconstitue un fil avec la pile. Il poursuit ensuite l'exécution au point où le service d'attente a été appelé.

La question

Peut-on faire avec un interpréteur Python non modifié? Mieux encore, quelqu'un peut me pointer vers une documentation qui pourrait couvrir ce genre de chose ou un exemple de code qui construit un programme cadre de pile et commence l'exécution quelque part au milieu d'un bloc de code?

Modifier Pour clarifier « interpréteur python non modifié », je ne me dérange pas en utilisant l'API C (est-il suffisamment d'informations dans un PyThreadState pour le faire?), Mais je ne veux pas aller piquer autour des composants internes de l'interpréteur python et d'avoir à construire un modifié.

Mise à jour: une enquête initiale, on peut obtenir le contexte d'exécution avec PyThreadState_Get(). Ceci renvoie l'état de thread dans un PyThreadState (défini dans pystate.h), qui comporte une référence à la trame de pile dans frame. Un cadre de pile est maintenu dans un typedef struct à PyFrameObject, qui est défini dans frameobject.h. PyFrameObject a un f_lasti de terrain (accessoires bobince ) qui a un compteur de programme exprimé en décalage par rapport au début du bloc de code.

Ce dernier est une sorte de bonnes nouvelles, car cela signifie que tant que vous conservez le bloc réel de code compilé, vous devriez être en mesure de reconstruire les locaux pour autant de cadres de la pile au besoin et redémarrer le code. Je dirais que cela signifie qu'il est théoriquement possible sans avoir à faire un interpereter python modifié, même si cela signifie que le code est encore va probablement être fiddly et étroitement couplé à des versions spécifiques de l'interprète.

Les trois problèmes restants sont les suivants:

  • Etat des transactions et rollback « saga », qui peut sans doute être accompli par le genre de piratage métaclasse on utiliserait pour construire un O / R Mapper. J'ai fait construire un prototype une fois, donc j'avoir une idée juste de la façon dont cela pourrait se faire.

  • état de la transaction et la population locale sérialisation robustement arbitraires. Cela pourrait se faire en lisant __locals__ (qui est disponible à partir du cadre de pile) et la construction d'un appel à programatically décaper. Cependant, je ne sais pas quoi, le cas échéant, Gotchas il pourrait y avoir ici.

  • Versioning et mise à niveau des flux de travail. Ceci est un peu plus délicat, car le système ne fournit aucune ancho symboliquers pour les noeuds de flux de travail. Tout ce que nous avons est l'ancre Pour ce faire, il faudrait identifier les décalages de tous les points d'entrée et de les associer à la nouvelle version. Probablement possible de le faire manuellement, mais je pense qu'il serait difficile d'automatiser. Ceci est probablement le plus grand obstacle si vous voulez soutenir cette capacité.

Mise à jour 2: PyCodeObject (code.h) a une liste de adr (f_lasti) -> mappings de numéro de ligne dans PyCodeObject.co_lnotab (me corriger si mal ici). Cela pourrait être utilisé pour faciliter un processus de migration de mettre à jour les flux de travail vers une nouvelle version, en tant que pointeurs d'instruction congelés pourraient être mis en correspondance à l'endroit approprié dans le nouveau script, fait en fonction des numéros de ligne. Encore assez compliqué, mais un peu plus prometteur.

Mise à jour 3: Je pense que la réponse à cette question pourrait être Stackless Python . vous pouvez suspendre les tâches et les serialise. Je n'ai pas travaillé savoir si cela va aussi travailler avec la pile ainsi.

Était-ce utile?

La solution 5

CPython ce type est compliquée par le mélange de données C et python dans la pile. La reconstruction de la pile d'appel nécessiterait la pile C à reconstruire en même temps. Ce qu'il met vraiment dans le panier trop dur car il pourrait potentiellement coupler étroitement la mise en œuvre à des versions spécifiques de CPython.

Stackless Python permet tasklets à décaper, ce qui donne plus de la capacité requise de la boîte.

Autres conseils

Les liaisons Python expat inclus dans la distribution de Python normale est construisaient des cadres de pile programtically. Attention cependant, il repose sur des API non documentées et privées.

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

Ce que vous voulez en général sont continuations, que je vois déjà une étiquette sur cette question.

Si vous avez la possibilité de travailler avec tout le code dans le système, vous pouvez essayer ce faisant de cette façon plutôt que de traiter les entrailles de la pile d'interprète. Je ne sais pas combien il est facile ce sera persistait.

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

Dans la pratique, je structurer votre moteur de workflow afin que votre script soumet des objets d'action à un gestionnaire. Le gestionnaire peut décaper l'ensemble des actions à tout moment et permettre les à charger et à commencer l'exécution à nouveau (en reprenant la présentation des actions).

En d'autres termes:. Faire vos propres, niveau d'application, pile

python Stackless est probablement le meilleur ... si vous ne me dérange pas tout à fait d'aller vers une autre distribution de python. stackless peut sérialiser tout en python, plus leurs tasklets. Si vous voulez rester dans la distribution standard de python, puis j'utiliser aneth , qui peut sérialiser presque quoi que ce soit en 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 enregistre il est types dans le registre de pickle, donc si vous avez un code de boîte noire qui utilise pickle et vous ne pouvez pas vraiment le modifier, puis l'aneth tout importateur peut faire comme par magie fonctionne sans monkeypatching le code 3ème partie.

Voici dill décaper toute la session de l'interprète ...

>>> # 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 a aussi quelques bons outils pour vous aider à comprendre ce qui est à l'origine de votre décapage à l'échec en cas d'échec de votre code.

Vous avez également demandé où il est utilisé pour enregistrer l'état interprète?

IPython peut utiliser dill pour sauver la session d'interprète dans un fichier. https://nbtest.herokuapp.com /github/ipython/ipython/blob/master/examples/parallel/Using%20Dill.ipynb

klepto utilise dill pour soutenir en mémoire, à disque, ou à la base de données mise en mémoire cache qui évite recalcul. https://github.com/uqfoundation/klepto/blob/master/tests /test_cache_info.py

mystique utilise dill pour enregistrer les points de contrôle pour des emplois de grande optimisation en sauvegardant l'état de l'optimiseur comme il est en cours. https://github.com/uqfoundation/mystic/blob/master/tests /test_solver_state.py

Il y a un couple d'autres paquets qui utilisent dill pour enregistrer l'état d'objets ou de sessions.

Vous pouvez saisir le cadre de la pile existante en lançant une exception et pas en arrière un cadre le long du retraçage. Le problème est qu'il n'y a aucun moyen prévu pour reprendre l'exécution dans le milieu (frame.f_lasti) du bloc de code.

« exceptions réactivables » sont une idée de langage très intéressant, mais il est difficile de penser à une façon raisonnable, ils pourraient interagir avec Python existant « try / finally » et « avec » des blocs.

Pour le moment, la façon normale de faire est tout simplement d'utiliser les threads pour gérer votre flux de travail dans un contexte distinct de son contrôleur. (Ou coroutines / greenlets si vous ne me dérange pas de les compiler dans).

J'ai le même type de problème à résoudre. Je me demande ce que l'affiche originale a décidé de le faire.

stackless prétend qu'il peut décaper tasklets tant qu'il n'y a pas de pile C « grevés » associé (grevés mon choix du phrasé).

Je vais probablement utiliser eventlet et trouver un moyen de décapage « état », je ne veux pas écrire une machine d'état explicite mais ..

Comment l'utilisation de JOBLIB ?

Je ne suis pas tout à fait sûr que ce soit ce que vous voulez, mais il semble correspondre à l'idée d'avoir un flux de travail dont les étapes peuvent être conservées. Le cas d'utilisation de JOBLIB semble être d'éviter recalcul, je ne sais pas si c'est ce que vous essayez de faire ici quelque chose de plus compliqué?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top