코드의 임의의 지점에서 Python 스택 프레임을 프로그래밍 방식으로 구성하고 실행을 시작할 수 있습니까?

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

문제

cpython에서 스택 (하나 이상의 스택 프레임)을 프로그래밍 방식으로 구성하고 임의의 코드 지점에서 실행을 시작할 수 있습니까? 다음 시나리오를 상상해보십시오.

  1. You have a workflow engine where workflows can be scripted in Python with some constructs (eg branching, waiting/joining) that are calls to the workflow engine.

  2. 대기 또는 조인과 같은 차단 통화는 어떤 종류의 지속적인 후원 저장소가있는 이벤트 디스패치 엔진에서 리스너 조건을 설정합니다.

  3. 엔진의 대기 조건을 호출하는 워크 플로 스크립트가있어 나중에 신호를 보내는 일부 조건을 기다립니다. 이것은 이벤트 디스패치 엔진에서 리스너를 설정합니다.

  4. 대기 조건이 며칠 또는 몇 달 후에 발생할 수 있으므로 워크 플로 스크립트의 상태, 프로그램 카운터 (또는 동등한 상태)를 포함한 관련 스택 프레임이 지속됩니다.

  5. 중간에 워크 플로 엔진이 중지되고 다시 시작될 수 있으므로 워크 플로 스크립트의 컨텍스트를 프로그래밍 방식으로 저장하고 재구성 할 수 있어야합니다.

  6. 이벤트 파견 엔진은 대기 조건이 픽업되는 이벤트를 시작합니다.

  7. 워크 플로 엔진은 직렬화 된 상태와 스택을 읽고 스택으로 스레드를 재구성합니다. 그런 다음 대기 서비스가 호출되는 시점에서 계속 실행됩니다.

질문

수정되지 않은 Python 통역사로이를 수행 할 수 있습니까? 더 좋은 점은 누구든지 이런 종류의 것들이나 스택 프레임을 프로그래밍 방식으로 구성하고 코드 블록 중간에 실행을 시작하는 코드의 예를 다룰 수있는 문서를 지적 할 수 있습니까?

편집하다: '수정되지 않은 Python 통역사'를 명확히하기 위해 C API를 사용하지 않습니다 (PythreadState에 충분한 정보가 있습니까?) 수정 된 것.

업데이트: 일부 초기 조사에서 PyThreadState_Get(). 이것은 a에서 스레드 상태를 반환합니다 PyThreadState (정의 된 pystate.h), 스택 프레임에 대한 참조가 있습니다. frame. 스택 프레임은 PyFrameObject, 정의되어 있습니다 frameobject.h. PyFrameObject 필드가 있습니다 f_lasti (소품 Bobince) 코드 블록의 시작부터 오프셋으로 표현 된 프로그램 카운터가 있습니다.

마지막으로 좋은 소식입니다. 실제 컴파일 된 코드 블록을 보존하는 한 필요한만큼 스택 프레임에 대해 현지인을 재구성하고 코드를 다시 시작할 수 있기 때문입니다. 나는 이것이 수정 된 Python 인터페러를 만들 필요없이 이론적으로 가능하다는 것을 의미하지만, 코드가 여전히 특정 버전의 통역사와는 엄청나게 단단하게 결합 될 수 있음을 의미합니다.

남은 세 가지 문제는 다음과 같습니다.

  • 트랜잭션 상태와 '사가'롤백은 아마도 O/R 맵퍼를 구축하는 데 사용할 수있는 일종의 메타 클래스 해킹으로 달성 할 수 있습니다. 한 번 프로토 타입을 만들었으므로 이것이 어떻게 달성 될 수 있는지에 대한 공정한 아이디어가 있습니다.

  • 강력하게 일련의 거래 상태 및 임의의 현지인. 이것은 독서를 통해 달성 될 수 있습니다 __locals__ (스택 프레임에서 구할 수 있음) 프로그램적으로 Pickle에 대한 호출을 구성합니다. 그러나 나는 여기에 무엇이 있을지 모르겠습니다.

  • 워크 플로의 버전 작성 및 업그레이드. 시스템이 워크 플로 노드에 대한 상징적 앵커를 제공하지 않기 때문에 이것은 다소 까다로워집니다. 우리가 가진 모든 것은 이것을하기 위해 앵커입니다. 모든 진입 지점의 오프셋을 식별하고 새 버전에 매핑해야합니다. 아마도 수동으로 할 수 있지만 자동화하기가 어려울 것 같아요. 이 기능을 지원하려면 이것이 가장 큰 장애물 일 것입니다.

Update 2: PyCodeObject (code.h) ADDR 목록이 있습니다 (f_lasti)-> 줄 번호 매핑 PyCodeObject.co_lnotab (여기서 틀렸을 때 나를 수정하십시오). 이는 냉동 명령 포인터가 줄 번호로 수행 된 새 스크립트의 적절한 위치에 매핑 될 수 있으므로 마이그레이션 프로세스를 새 버전으로 업데이트하는 데 사용될 수 있습니다. 여전히 지저분하지만 조금 더 유망합니다.

3 : 업데이트 3. 나는 이것에 대한 대답이 될 것이라고 생각합니다 스택이없는 파이썬. 작업을 중단하고 직렬화 할 수 있습니다. 나는 이것이 스택과도 작동하는지 여부를 해결하지 못했습니다.

도움이 되었습니까?

해결책 5

표준 cpython을 사용하면 스택에서 C와 Python 데이터의 혼합으로 인해 복잡합니다. 통화 스택을 재건하려면 C 스택을 동시에 재구성해야합니다. 이것은 구현을 특정 버전의 Cpython에 단단히 결합 할 수 있기 때문에 너무 단단한 바구니에 넣습니다.

스택리스 파이썬을 사용하면 작업 단을 절인 할 수 있으므로 상자 밖에서 필요한 대부분의 기능을 제공합니다.

다른 팁

정상적인 파이썬 분포에 포함 된 국외 파이썬 바인딩은 스택 프레임을 프로그래밍 방식으로 구성합니다. 그러나 문서화되지 않은 개인 API에 의존합니다.

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

당신이 일반적으로 원하는 것은 계속 해서이 질문에 대한 태그입니다.

시스템의 모든 코드를 사용하는 능력이 있다면 통역사 스택 내부를 다루지 않고 이런 식으로 시도 할 수 있습니다. 이것이 얼마나 쉽게 지속될 것인지 잘 모르겠습니다.

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

실제로, 스크립트가 액션 개체를 관리자에게 제출하도록 워크 플로 엔진을 구조화합니다. 관리자는 언제라도 일련의 작업을 피울 수 있고로드하고 다시 실행을 시작할 수 있습니다 (작업 제출을 재개하여).

다시 말해, 자신만의 응용 프로그램 수준, 스택을 만드십시오.

Stackless Python이 아마도 최고 일 것입니다 ... 당신이 완전히 다른 Python 분포로 넘어가는 것을 신경 쓰지 않는다면. stackless 직렬화 할 수 있습니다 모든 것 파이썬에서, 그들의 작업 단과 함께. 표준 파이썬 분포를 유지하고 싶다면 , 직렬화 할 수 있습니다 거의 파이썬에있는 모든 것.

>>> 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}

딜은 유형으로 등록합니다 pickle 레지스트리, 따라서 사용하는 블랙 박스 코드가있는 경우 pickle 그리고 당신은 실제로 그것을 편집 할 수는 없습니다. 그러면 딜을 가져 오는 것만으로 딜을 마술처럼 만들 수 있습니다.

여기에 있습니다 dill 전체 통역사 세션을 산세 ...

>>> # 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 또한 몇 가지 좋은 도구 코드가 실패했을 때 절도가 실패하게하는 원인을 이해하도록 도와줍니다.

또한 통역사 상태를 저장하는 데 사용되는 곳을 요청 했습니까?

IPYTHON 사용할 수 있습니다 dill 통역사 세션을 파일에 저장합니다. https://nbtest.herokuapp.com/github/ipython/ipython/blob/master/examples/parallel/using%20dill.ipynb

클레 토 용도 dill 재 계산을 피하는 메모리, 할아서 또는 대사 캐싱을 지원합니다. https://github.com/uqfoundation/klepto/blob/master/tests/test_cache_info.py

신비한 용도 dill 진행중인대로 최적화 상태를 저장하여 대규모 최적화 작업에 대한 체크 포인트를 저장합니다. https://github.com/uqfoundation/mystic/blob/master/tests/test_solver_state.py

사용하는 다른 패키지가 몇 개 있습니다 dill 물체 또는 세션의 상태를 저장합니다.

예외를 던지고 트레이스 백에서 하나의 프레임을 뒤로 물러서서 기존 스택 프레임을 잡을 수 있습니다. 문제는 코드 블록의 중간 (frame.f_lasti)에서 실행을 재개 할 방법이 없다는 것입니다.

“재개 가능한 예외”는 정말 흥미로운 언어 아이디어이지만, Python의 기존 'Try/Minde'및 '블록'과 상호 작용할 수있는 합리적인 방법을 생각하는 것은 까다 롭습니다.

현재이 작업을 수행하는 일반적인 방법은 스레드를 사용하여 컨트롤러와 별도의 컨텍스트에서 워크 플로를 실행하는 것입니다. (또는 코 루틴/그린 레트를 컴파일하는 것이 마음에 들지 않으면).

해결해야 할 것도 같은 유형의 문제가 있습니다. 원래 포스터가 무엇을하기로 결정했는지 궁금합니다.

'Embumbered'C 스택이 관련이없는 한 스택이없는 주장은 작업용을 피할 수 있다고 주장합니다 (Enbumbered는 내 선택의 여지가 있습니다).

아마 Eventlet을 사용하고 '상태'를 절대하는 방법을 알아낼 것입니다. 그래도 명시적인 상태 머신을 쓰고 싶지 않습니다.

사용하는 것은 어떻습니까 joblib?

나는 이것이 당신이 원하는 것이라고 확신하지 못하지만 어떤 단계를 지속 할 수있는 워크 플로우를 갖는 아이디어에 맞는 것 같습니다. Joblib의 유스 케이스는 재 계산을 피하는 것 같습니다. 이것이 당신이 여기서하려고하는 일인지 더 복잡한 일인지 확실하지 않습니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top