1
1
"""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.
3
3
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
5
5
on a queue to ask the main (UI) thread to refresh the display or get
6
6
information.
7
7
"""
@@ -54,26 +54,33 @@ def __init__(self, args):
54
54
55
55
56
56
class 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.
73
80
74
81
Once the screen refresh has occurred or the requested user input
75
82
has been gathered, run_code() should be called again, passing in any
76
- requested user input. This continues until run_code returns Done .
83
+ requested user input. This continues until run_code returns True .
77
84
78
85
The code thread is responsible for telling the main thread
79
86
what it wants returned in the next run_code call - CodeRunner
@@ -98,7 +105,7 @@ def __init__(self, interp=None, request_refresh=lambda: None):
98
105
# waiting for response from main thread
99
106
self .code_is_waiting = False
100
107
# 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
102
109
self .orig_sigint_handler = None
103
110
104
111
@property
@@ -130,8 +137,8 @@ def run_code(self, for_code=None):
130
137
if self .code_thread is None :
131
138
assert self .source is not None
132
139
self .code_thread = threading .Thread (
133
- target = self ._blocking_run_code ,
134
- name = 'codethread' )
140
+ target = self ._blocking_run_code , name = "codethread"
141
+ )
135
142
self .code_thread .daemon = True
136
143
if is_main_thread ():
137
144
self .orig_sigint_handler = signal .getsignal (signal .SIGINT )
@@ -151,9 +158,7 @@ def run_code(self, for_code=None):
151
158
request = self .requests_from_code_thread .get ()
152
159
logger .debug ("request received from code was %r" , request )
153
160
if 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 )
157
162
if isinstance (request , (Wait , Refresh )):
158
163
self .code_is_waiting = True
159
164
if isinstance (request , Refresh ):
@@ -188,9 +193,9 @@ def _blocking_run_code(self):
188
193
except SystemExit as e :
189
194
self .requests_from_code_thread .push (SystemExitRequest (* e .args ))
190
195
return
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
+ )
194
199
195
200
def request_from_main_thread (self , force_refresh = False ):
196
201
"""Return the argument passed in to .run_code(for_code)
0 commit comments