Skip to content

Commit 1276ba9

Browse files
Merge branch 'innocuous-changes-for-monkeypatching' into process-event-refactor
Conflicts: bpython/curtsiesfrontend/repl.py
2 parents 21992b6 + b58db29 commit 1276ba9

File tree

3 files changed

+84
-46
lines changed

3 files changed

+84
-46
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def smarter_request_reload(desc):
277277
self.interact = self.status_bar # overwriting what bpython.Repl put there
278278
# interact is called to interact with the status bar,
279279
# so we're just using the same object
280-
self._current_line = '' # line currently being edited, without '>>> '
280+
self._current_line = '' # line currently being edited, without ps1 (usually '>>> ')
281281
self.current_stdouterr_line = '' # current line of output - stdout and stdin go here
282282
self.display_lines = [] # lines separated whenever logical line
283283
# length goes over what the terminal width
@@ -586,8 +586,10 @@ def send_current_block_to_external_editor(self, filename=None):
586586

587587
def send_session_to_external_editor(self, filename=None):
588588
for_editor = '### current bpython session - file will be reevaluated, ### lines will not be run\n'.encode('utf8')
589-
for_editor += ('\n'.join(line[4:] if line[:4] in ('... ', '>>> ') else '### '+line
590-
for line in self.getstdout().split('\n')).encode('utf8'))
589+
for_editor += ('\n'.join(line[len(self.ps1):] if line.startswith(self.ps1) else
590+
(line[len(self.ps2):] if line.startswith(self.ps2) else
591+
'### '+line)
592+
for line in self.getstdout().split('\n')).encode('utf8'))
591593
text = self.send_to_external_editor(for_editor)
592594
lines = text.split('\n')
593595
self.history = [line for line in lines if line[:4] != '### ']
@@ -627,7 +629,7 @@ def add_normal_character(self, char):
627629
char +
628630
self.current_line[self.cursor_offset:])
629631
self.cursor_offset += 1
630-
if self.config.cli_trim_prompts and self.current_line.startswith(">>> "):
632+
if self.config.cli_trim_prompts and self.current_line.startswith(self.ps1):
631633
self.current_line = self.current_line[4:]
632634
self.cursor_offset = max(0, self.cursor_offset - 4)
633635

@@ -673,11 +675,8 @@ def push(self, line, insert_into_history=True):
673675
code_to_run = '\n'.join(self.buffer)
674676

675677
logger.debug('running %r in interpreter', self.buffer)
676-
try:
677-
c = bool(code.compile_command('\n'.join(self.buffer)))
678-
self.saved_predicted_parse_error = False
679-
except (ValueError, SyntaxError, OverflowError):
680-
c = self.saved_predicted_parse_error = True
678+
c, code_will_parse = self.buffer_finished_will_parse()
679+
self.saved_predicted_parse_error = not code_will_parse
681680
if c:
682681
logger.debug('finished - buffer cleared')
683682
self.display_lines.extend(self.display_buffer_lines)
@@ -688,6 +687,21 @@ def push(self, line, insert_into_history=True):
688687
self.coderunner.load_code(code_to_run)
689688
self.run_code_and_maybe_finish()
690689

690+
def buffer_finished_will_parse(self):
691+
"""Returns a tuple of whether the buffer could be complete and whether it will parse
692+
693+
True, True means code block is finished and no predicted parse error
694+
True, False means code block is finished because a parse error is predicted
695+
False, True means code block is unfinished
696+
False, False isn't possible - an predicted error makes code block done"""
697+
try:
698+
finished = bool(code.compile_command('\n'.join(self.buffer)))
699+
code_will_parse = True
700+
except (ValueError, SyntaxError, OverflowError):
701+
finished = True
702+
code_will_parse = False
703+
return finished, code_will_parse
704+
691705
def run_code_and_maybe_finish(self, for_code=None):
692706
r = self.coderunner.run_code(for_code=for_code)
693707
if r:
@@ -1072,7 +1086,7 @@ def reevaluate(self, insert_into_history=False):
10721086
self.display_lines = []
10731087

10741088
if not self.weak_rewind:
1075-
self.interp = code.InteractiveInterpreter()
1089+
self.interp = self.interp.__class__()
10761090
self.coderunner.interp = self.interp
10771091

10781092
self.buffer = []

bpython/importcompletion.py

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,41 @@ def catch_warnings():
5656
fully_loaded = False
5757

5858

59+
def module_matches(cw, prefix=''):
60+
"""Modules names to replace cw with"""
61+
full = '%s.%s' % (prefix, cw) if prefix else cw
62+
matches = [name for name in modules
63+
if (name.startswith(full) and
64+
name.find('.', len(full)) == -1)]
65+
if prefix:
66+
return [match[len(prefix)+1:] for match in matches]
67+
else:
68+
return matches
69+
70+
def attr_matches(cw, prefix='', only_modules=False):
71+
"""Attributes to replace name with"""
72+
full = '%s.%s' % (prefix, cw) if prefix else cw
73+
module_name, _, name_after_dot = full.rpartition('.')
74+
if module_name not in sys.modules:
75+
return []
76+
module = sys.modules[module_name]
77+
if only_modules:
78+
matches = [name for name in dir(module)
79+
if name.startswith(name_after_dot) and
80+
'%s.%s' % (module_name, name) in sys.modules]
81+
else:
82+
matches = [name for name in dir(module) if name.startswith(name_after_dot)]
83+
module_part, _, _ = cw.rpartition('.')
84+
if module_part:
85+
return ['%s.%s' % (module_part, m) for m in matches]
86+
return matches
87+
88+
def module_attr_matches(name):
89+
"""Only attributes which are modules to replace name with"""
90+
return attr_matches(name, prefix='', only_modules=True)
91+
5992
def complete(cursor_offset, line):
6093
"""Construct a full list of possibly completions for imports."""
61-
# TODO if this is done in a thread (as it prob will be in Windows) we'll need this
62-
# if not fully_loaded:
63-
# return []
6494
tokens = line.split()
6595
if 'from' not in tokens and 'import' not in tokens:
6696
return None
@@ -69,39 +99,6 @@ def complete(cursor_offset, line):
6999
if result is None:
70100
return None
71101

72-
def module_matches(cw, prefix=''):
73-
"""Modules names to replace cw with"""
74-
full = '%s.%s' % (prefix, cw) if prefix else cw
75-
matches = [name for name in modules
76-
if (name.startswith(full) and
77-
name.find('.', len(full)) == -1)]
78-
if prefix:
79-
return [match[len(prefix)+1:] for match in matches]
80-
else:
81-
return matches
82-
83-
def attr_matches(cw, prefix='', only_modules=False):
84-
"""Attributes to replace name with"""
85-
full = '%s.%s' % (prefix, cw) if prefix else cw
86-
module_name, _, name_after_dot = full.rpartition('.')
87-
if module_name not in sys.modules:
88-
return []
89-
module = sys.modules[module_name]
90-
if only_modules:
91-
matches = [name for name in dir(module)
92-
if name.startswith(name_after_dot) and
93-
'%s.%s' % (module_name, name) in sys.modules]
94-
else:
95-
matches = [name for name in dir(module) if name.startswith(name_after_dot)]
96-
module_part, _, _ = cw.rpartition('.')
97-
if module_part:
98-
return ['%s.%s' % (module_part, m) for m in matches]
99-
return matches
100-
101-
def module_attr_matches(name):
102-
"""Only attributes which are modules to replace name with"""
103-
return attr_matches(name, prefix='', only_modules=True)
104-
105102
if lineparts.current_from_import_from(cursor_offset, line) is not None:
106103
if lineparts.current_from_import_import(cursor_offset, line) is not None:
107104
# `from a import <b|>` completion

bpython/test/test_curtsies_repl.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import unittest
2+
import sys
3+
py3 = (sys.version_info[0] == 3)
4+
5+
from bpython.curtsiesfrontend import repl
6+
7+
class TestCurtsiesRepl(unittest.TestCase):
8+
9+
def setUp(self):
10+
self.repl = repl.Repl()
11+
12+
def test_buffer_finished_will_parse(self):
13+
self.repl.buffer = ['1 + 1']
14+
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
15+
self.repl.buffer = ['def foo(x):']
16+
self.assertTrue(self.repl.buffer_finished_will_parse(), (False, True))
17+
self.repl.buffer = ['def foo(x)']
18+
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, False))
19+
self.repl.buffer = ['def foo(x):', 'return 1']
20+
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, False))
21+
self.repl.buffer = ['def foo(x):', ' return 1']
22+
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
23+
self.repl.buffer = ['def foo(x):', ' return 1', '']
24+
self.assertTrue(self.repl.buffer_finished_will_parse(), (True, True))
25+
26+
if __name__ == '__main__':
27+
unittest.main()

0 commit comments

Comments
 (0)