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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy