11"""For running Python code that could interrupt itself at any time in order to,
2- for example, ask for a read on stdin, or a write on stdout
2+ for example, ask for a read on stdin, or a write on stdout.
33
4- The CodeRunner spawns a thread to run code in, and that code can block
4+ The CodeRunner spawns a thread to run code in. That code can block
55on a queue to ask the main (UI) thread to refresh the display or get
66information.
77"""
@@ -54,26 +54,33 @@ def __init__(self, args):
5454
5555
5656class CodeRunner :
57- """Runs user code in an interpreter.
58-
59- Running code requests a refresh by calling
60- request_from_main_thread(force_refresh=True), which
61- suspends execution of the code by blocking on a queue
62- that the main thread was blocked on.
63-
64- After load_code() is called with the source code to be run,
65- the run_code() method should be called to start running the code.
66- The running code may request screen refreshes and user input
67- by calling request_from_main_thread.
68- When this are called, the running source code cedes
69- control, and the current run_code() method call returns.
70-
71- The return value of run_code() determines whether the method ought
72- to be called again to complete execution of the source code.
57+ """Runs user code in a pausable thread.
58+
59+ >>> cr = CodeRunner()
60+ >>> def get_input():
61+ ... print('waiting for a number plz')
62+ ... return cr.request_from_main_thread()
63+ ...
64+ >>> i = InteractiveInterpreter(locals={'get_input': get_input})
65+ >>> cr.interp = i
66+ >>> cr.load_code('x = get_input(); print(x * 2)')
67+ >>> finished = cr.run_code()
68+ waiting for a number plz
69+ >>> # do something else, user code thread is paused
70+ >>> finished = cr.run_code(for_code=21)
71+ 42
72+
73+ As user code executes it can make requests for values or simply
74+ request that the screen be refreshed with `request_from_main_thread()`.
75+ This pauses the user code execution thread and wakes up the main thread,
76+ where run_code() returns whether user code has finished executing.
77+ This is cooperative multitasking: even though there are two threads,
78+ the main thread and the user code thread, the two threads work cede
79+ control to one another like like green threads with no parallelism.
7380
7481 Once the screen refresh has occurred or the requested user input
7582 has been gathered, run_code() should be called again, passing in any
76- requested user input. This continues until run_code returnsDone .
83+ requested user input. This continues until run_code returnsTrue .
7784
7885 The code thread is responsible for telling the main thread
7986 what it wants returned in the next run_code call - CodeRunner
@@ -98,7 +105,7 @@ def __init__(self, interp=None, request_refresh=lambda: None):
98105# waiting for response from main thread
99106self .code_is_waiting = False
100107# sigint happened while in main thread
101- self .sigint_happened_in_main_thread = False # TODO rename context to thread
108+ self .sigint_happened_in_main_thread = False
102109self .orig_sigint_handler = None
103110
104111@property
@@ -130,8 +137,8 @@ def run_code(self, for_code=None):
130137if self .code_thread is None :
131138assert self .source is not None
132139self .code_thread = threading .Thread (
133- target = self ._blocking_run_code ,
134- name = 'codethread' )
140+ target = self ._blocking_run_code ,name = "codethread"
141+ )
135142self .code_thread .daemon = True
136143if is_main_thread ():
137144self .orig_sigint_handler = signal .getsignal (signal .SIGINT )
@@ -151,9 +158,7 @@ def run_code(self, for_code=None):
151158request = self .requests_from_code_thread .get ()
152159logger .debug ("request received from code was %r" ,request )
153160if not isinstance (request ,RequestFromCodeRunner ):
154- raise ValueError (
155- "Not a valid value from code thread: %r" % request
156- )
161+ raise ValueError ("Not a valid value from code thread: %r" % request )
157162if isinstance (request , (Wait ,Refresh )):
158163self .code_is_waiting = True
159164if isinstance (request ,Refresh ):
@@ -188,9 +193,9 @@ def _blocking_run_code(self):
188193except SystemExit as e :
189194self .requests_from_code_thread .push (SystemExitRequest (* e .args ))
190195return
191- self .requests_from_code_thread .put (Unfinished ()
192- if unfinished
193- else Done () )
196+ self .requests_from_code_thread .put (
197+ Unfinished () if unfinished else Done ()
198+ )
194199
195200def request_from_main_thread (self ,force_refresh = False ):
196201"""Return the argument passed in to .run_code(for_code)