Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2e54cbb

Browse filesBrowse files
threads -> greenlets
also fixed ctrl-c during raw_input bug --HG-- branch : scroll-frontend
1 parent 1934a83 commit 2e54cbb
Copy full SHA for 2e54cbb

4 files changed

+48-61Lines changed: 48 additions & 61 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎bpython/curtsiesfrontend/coderunner.py‎

Copy file name to clipboardExpand all lines: bpython/curtsiesfrontend/coderunner.py
+18-25Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import code
2-
import Queue
32
import signal
43
import sys
5-
import threading
4+
import greenlet
65
import logging
76

87
class SigintHappened(object):
@@ -16,54 +15,51 @@ class CodeRunner(object):
1615
def __init__(self, interp=None, stuff_a_refresh_request=lambda:None):
1716
self.interp = interp or code.InteractiveInterpreter()
1817
self.source = None
19-
self.code_thread = None
20-
self.requests_from_code_thread = Queue.Queue(maxsize=1)
21-
self.responses_for_code_thread = Queue.Queue(maxsize=1)
18+
self.main_greenlet = greenlet.getcurrent()
19+
self.code_greenlet = None
2220
self.stuff_a_refresh_request = stuff_a_refresh_request
2321
self.code_is_waiting = False
2422
self.sigint_happened = False
2523
self.orig_sigint_handler = None
2624

2725
@property
2826
def running(self):
29-
return self.source and self.code_thread
27+
return self.source and self.code_greenlet
3028

3129
def load_code(self, source):
3230
"""Prep code to be run"""
3331
self.source = source
34-
self.code_thread = None
32+
self.code_greenlet = None
3533

3634
def _unload_code(self):
3735
"""Called when done running code"""
3836
self.source = None
39-
self.code_thread = None
37+
self.code_greenlet = None
4038
self.code_is_waiting = False
4139

4240
def run_code(self, for_code=None):
4341
"""Returns Truthy values if code finishes, False otherwise
4442
45-
if for_code is provided, send that value to the code thread
43+
if for_code is provided, send that value to the code greenlet
4644
if source code is complete, returns "done"
4745
if source code is incomplete, returns "unfinished"
4846
"""
49-
if self.code_thread is None:
47+
if self.code_greenlet is None:
5048
assert self.source is not None
51-
self.code_thread = threading.Thread(target=self._blocking_run_code, name='codethread')
52-
self.code_thread.daemon = True
49+
self.code_greenlet = greenlet.greenlet(self._blocking_run_code)
5350
self.orig_sigint_handler = signal.getsignal(signal.SIGINT)
5451
signal.signal(signal.SIGINT, self.sigint_handler)
55-
self.code_thread.start()
52+
request = self.code_greenlet.switch()
5653
else:
5754
assert self.code_is_waiting
5855
self.code_is_waiting = False
5956
signal.signal(signal.SIGINT, self.sigint_handler)
6057
if self.sigint_happened:
6158
self.sigint_happened = False
62-
self.responses_for_code_thread.put(SigintHappened)
59+
request = self.code_greenlet.switch(SigintHappened)
6360
else:
64-
self.responses_for_code_thread.put(for_code)
61+
request = self.code_greenlet.switch(for_code)
6562

66-
request = self.requests_from_code_thread.get()
6763
if request in ['wait', 'refresh']:
6864
self.code_is_waiting = True
6965
if request == 'refresh':
@@ -78,10 +74,10 @@ def run_code(self, for_code=None):
7874
self._unload_code()
7975
raise SystemExitFromCodeThread()
8076
else:
81-
raise ValueError("Not a valid request_from_code_thread value: %r" % request)
77+
raise ValueError("Not a valid value from code greenlet: %r" % request)
8278

8379
def sigint_handler(self, *args):
84-
if threading.current_thread() is self.code_thread:
80+
if greenlet.getcurrent() is self.code_greenlet:
8581
logging.debug('sigint while running user code!')
8682
raise KeyboardInterrupt()
8783
else:
@@ -92,25 +88,22 @@ def _blocking_run_code(self):
9288
try:
9389
unfinished = self.interp.runsource(self.source)
9490
except SystemExit:
95-
self.requests_from_code_thread.put('SystemExit')
96-
return
97-
self.requests_from_code_thread.put('unfinished' if unfinished else 'done')
91+
return 'SystemExit'
92+
return 'unfinished' if unfinished else 'done'
9893

9994
def wait_and_get_value(self):
10095
"""Return the argument passed in to .run_code(for_code)
10196
10297
Nothing means calls to run_code must be...
10398
"""
104-
self.requests_from_code_thread.put('wait')
105-
value = self.responses_for_code_thread.get()
99+
value = self.main_greenlet.switch('wait')
106100
if value is SigintHappened:
107101
raise KeyboardInterrupt()
108102
return value
109103

110104
def refresh_and_get_value(self):
111105
"""Returns the argument passed in to .run_code(for_code) """
112-
self.requests_from_code_thread.put('refresh')
113-
value = self.responses_for_code_thread.get()
106+
value = self.main_greenlet.switch('refresh')
114107
if value is SigintHappened:
115108
raise KeyboardInterrupt()
116109
return value
Collapse file

‎bpython/curtsiesfrontend/interaction.py‎

Copy file name to clipboardExpand all lines: bpython/curtsiesfrontend/interaction.py
+17-24Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Queue
1+
import greenlet
22
import time
33

44
from bpython.repl import Interaction as BpythonInteraction
@@ -28,8 +28,8 @@ def __init__(self, initial_message='', permanent_text=""):
2828
self.message_start_time = time.time()
2929
self.message_time = 3
3030
self.permanent_text = permanent_text
31-
self.response_queue = Queue.Queue(maxsize=1)
32-
self.request_or_notify_queue = Queue.Queue()
31+
self.main_greenlet = greenlet.getcurrent()
32+
self.request_greenlet = None
3333

3434
@property
3535
def has_focus(self):
@@ -53,16 +53,17 @@ def process_event(self, e):
5353
elif e == "":
5454
raise SystemExit()
5555
elif self.in_prompt and e in ("\n", "\r"):
56-
self.response_queue.put(self._current_line)
56+
line = self._current_line
5757
self.escape()
58+
self.main_greenlet_switch(line)
5859
elif self.in_confirm:
5960
if e in ('y', 'Y'):
60-
self.response_queue.put(True)
61+
self.request_greenlet.switch(True)
6162
else:
62-
self.response_queue.put(False)
63+
self.request_greenlet.switch(False)
6364
self.escape()
6465
elif e in ['\x1b', '\t', '\x1b\t', '\x1b\x1b']:
65-
self.response_queue.put(False)
66+
self.request_greenlet.switch(False)
6667
self.escape()
6768
else: # add normal character
6869
self._current_line = (self._current_line[:self.cursor_offset_in_line] +
@@ -72,7 +73,6 @@ def process_event(self, e):
7273

7374
def escape(self):
7475
"""unfocus from statusbar, clear prompt state, wait for notify call"""
75-
self.wait_for_request_or_notify()
7676
self.in_prompt = False
7777
self.in_confirm = False
7878
self.prompt = ''
@@ -89,30 +89,23 @@ def current_line(self):
8989
return self._message
9090
return self.permanent_text
9191

92-
def wait_for_request_or_notify(self):
93-
try:
94-
r = self.request_or_notify_queue.get(True, 1)
95-
except Queue.Empty:
96-
raise Exception('Main thread blocked because task thread not calling back')
97-
return r
98-
99-
# interaction interface - should be called from other threads
92+
# interaction interface - should be called from other greenlets
10093
def notify(self, msg, n=3):
94+
self.request_greenlet = greenlet.getcurrent()
10195
self.message_time = n
10296
self.message(msg)
103-
self.request_or_notify_queue.put(msg)
104-
# below Really ought to be called from threads other than the mainloop because they block
97+
self.main_greenlet.switch(msg)
98+
99+
# below Really ought to be called from greenlets other than main because they block
105100
def confirm(self, q):
106101
"""Expected to return True or False, given question prompt q"""
102+
self.request_greenlet = greenlet.getcurrent()
107103
self.prompt = q
108104
self.in_confirm = True
109-
self.request_or_notify_queue.put(q)
110-
return self.response_queue.get()
105+
return self.main_greenlet.switch(q)
111106
def file_prompt(self, s):
112107
"""Expected to return a file name, given """
108+
self.request_greenlet = greenlet.getcurrent()
113109
self.prompt = s.replace('Esc', 'Tab')
114110
self.in_prompt = True
115-
self.request_or_notify_queue.put(s)
116-
r = self.response_queue.get()
117-
return r
118-
111+
return self.main_greenlet.switch(s)
Collapse file

‎bpython/curtsiesfrontend/repl.py‎

Copy file name to clipboardExpand all lines: bpython/curtsiesfrontend/repl.py
+12-11Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import code
66
import threading
7+
import greenlet
78
import subprocess
89
import tempfile
910

@@ -60,6 +61,9 @@ def process_event(self, e):
6061
#TODO EOF on ctrl-d
6162
elif isinstance(e, events.SigIntEvent):
6263
self.coderunner.sigint_happened = True
64+
self.has_focus = False
65+
self.current_line = ''
66+
self.cursor_offset_in_line = 0
6367
self.repl.run_code_and_maybe_finish()
6468
else: # add normal character
6569
logging.debug('adding normal char %r to current line', e)
@@ -203,7 +207,8 @@ def start_background_tasks(self):
203207
t.start()
204208

205209
def importcompletion_thread(self):
206-
"""quick tasks we want to do bits of during downtime"""
210+
"""task that should run on startup in the background"""
211+
#TODO use locks or something to avoid error on import completion right at startup
207212
while importcompletion.find_coroutine(): # returns None when fully initialized
208213
pass
209214

@@ -292,18 +297,12 @@ def process_event(self, e):
292297
self.undo()
293298
self.update_completion()
294299
elif e in key_dispatch[self.config.save_key]: # ctrl-s for save
295-
t = threading.Thread(target=self.write2file)
296-
t.daemon = True
297-
logging.debug('starting write2file thread')
298-
t.start()
299-
self.interact.wait_for_request_or_notify()
300+
g = greenlet.greenlet(self.write2file)
301+
g.switch()
300302
# F8 for pastebin
301303
elif e in key_dispatch[self.config.pastebin_key]:
302-
t = threading.Thread(target=self.pastebin)
303-
t.daemon = True
304-
logging.debug('starting pastebin thread')
305-
t.start()
306-
self.interact.wait_for_request_or_notify()
304+
g = greenlet.greenlet(self.pastebin)
305+
g.switch()
307306
elif e in key_dispatch[self.config.external_editor_key]:
308307
self.send_to_external_editor()
309308
#TODO add PAD keys hack as in bpython.cli
@@ -482,6 +481,8 @@ def run_code_and_maybe_finish(self, for_code=None):
482481
if err:
483482
indent = 0
484483

484+
#TODO This should be printed ABOVE the error that just happened instead
485+
# or maybe just thrown away and not shown
485486
if self.current_stdouterr_line:
486487
self.display_lines.extend(paint.display_linize(self.current_stdouterr_line, self.width))
487488
self.current_stdouterr_line = ''
Collapse file

‎setup.py‎

Copy file name to clipboardExpand all lines: setup.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def initialize_options(self):
153153
'pygments'
154154
],
155155
extras_require = {
156-
'curtsies': ['curtsies>=0.0.24'],
156+
'curtsies': ['curtsies>=0.0.24', 'greenlet'],
157157
'urwid' : ['urwid']
158158
},
159159
tests_require = ['mock'],

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.