Skip to content

Commit 06a5538

Browse files
Improve CodeRunner documentation.
1 parent 2576656 commit 06a5538

File tree

3 files changed

+37
-30
lines changed

3 files changed

+37
-30
lines changed

bpython/curtsiesfrontend/coderunner.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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
55
on a queue to ask the main (UI) thread to refresh the display or get
66
information.
77
"""
@@ -54,26 +54,33 @@ def __init__(self, args):
5454

5555

5656
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.
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 returns Done.
83+
requested user input. This continues until run_code returns True.
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
99106
self.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
102109
self.orig_sigint_handler = None
103110

104111
@property
@@ -130,8 +137,8 @@ def run_code(self, for_code=None):
130137
if self.code_thread is None:
131138
assert self.source is not None
132139
self.code_thread = threading.Thread(
133-
target=self._blocking_run_code,
134-
name='codethread')
140+
target=self._blocking_run_code, name="codethread"
141+
)
135142
self.code_thread.daemon = True
136143
if is_main_thread():
137144
self.orig_sigint_handler = signal.getsignal(signal.SIGINT)
@@ -151,9 +158,7 @@ def run_code(self, for_code=None):
151158
request = self.requests_from_code_thread.get()
152159
logger.debug("request received from code was %r", request)
153160
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)
157162
if isinstance(request, (Wait, Refresh)):
158163
self.code_is_waiting = True
159164
if isinstance(request, Refresh):
@@ -188,9 +193,9 @@ def _blocking_run_code(self):
188193
except SystemExit as e:
189194
self.requests_from_code_thread.push(SystemExitRequest(*e.args))
190195
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+
)
194199

195200
def request_from_main_thread(self, force_refresh=False):
196201
"""Return the argument passed in to .run_code(for_code)

bpython/curtsiesfrontend/interaction.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ def wait_for_request_or_notify(self):
154154
try:
155155
r = self.request_or_notify_queue.get(True, 1)
156156
except queue.Empty:
157-
raise Exception('Main thread blocked because task thread not calling back')
157+
raise Exception(
158+
"Main thread blocked because task thread not calling back"
159+
)
158160
return r
159161

160162
# interaction interface - should be called from other threads

bpython/curtsiesfrontend/repl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ def switch(self, task):
797797
"""Runs task in another thread"""
798798
t = threading.Thread(target=task)
799799
t.daemon = True
800-
logging.debug('starting task thread')
800+
logging.debug("starting task thread")
801801
t.start()
802802
self.interact.wait_for_request_or_notify()
803803

0 commit comments

Comments
 (0)