Skip to content

Commit 4ce81a7

Browse files
committed
Stopped sharing history between cmd2 and python console
1 parent c2594ff commit 4ce81a7

File tree

2 files changed

+71
-12
lines changed

2 files changed

+71
-12
lines changed

cmd2/cmd2.py

100755100644
Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, persistent_histor
486486

487487
############################################################################################################
488488
# The following variables are used by tab-completion functions. They are reset each time complete() is run
489-
# using set_completion_defaults() and it is up to completer functions to set them before returning results.
489+
# in reset_completion_defaults() and it is up to completer functions to set them before returning results.
490490
############################################################################################################
491491

492492
# If true and a single match is returned to complete(), then a space will be appended
@@ -651,7 +651,7 @@ def colorize(self, val, color):
651651

652652
# ----- Methods related to tab completion -----
653653

654-
def set_completion_defaults(self):
654+
def reset_completion_defaults(self):
655655
"""
656656
Resets tab completion settings
657657
Needs to be called each time readline runs tab completion
@@ -1291,7 +1291,7 @@ def complete(self, text, state):
12911291
"""
12921292
if state == 0 and rl_type != RlType.NONE:
12931293
unclosed_quote = ''
1294-
self.set_completion_defaults()
1294+
self.reset_completion_defaults()
12951295

12961296
# lstrip the original line
12971297
orig_line = readline.get_line_buffer()
@@ -2025,12 +2025,11 @@ def _cmdloop(self):
20252025
# Set GNU readline's rl_basic_quote_characters to NULL so it won't automatically add a closing quote
20262026
# We don't need to worry about setting rl_completion_suppress_quote since we never declared
20272027
# rl_completer_quote_characters.
2028-
basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
2029-
old_basic_quote_characters = ctypes.cast(basic_quote_characters, ctypes.c_void_p).value
2030-
basic_quote_characters.value = None
2028+
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
2029+
old_basic_quotes = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
2030+
rl_basic_quote_characters.value = None
20312031

20322032
old_completer = readline.get_completer()
2033-
old_delims = readline.get_completer_delims()
20342033
readline.set_completer(self.complete)
20352034

20362035
# Break words on whitespace and quotes when tab completing
@@ -2040,6 +2039,7 @@ def _cmdloop(self):
20402039
# If redirection is allowed, then break words on those characters too
20412040
completer_delims += ''.join(constants.REDIRECTION_CHARS)
20422041

2042+
old_delims = readline.get_completer_delims()
20432043
readline.set_completer_delims(completer_delims)
20442044

20452045
# Enable tab completion
@@ -2076,7 +2076,7 @@ def _cmdloop(self):
20762076

20772077
if rl_type == RlType.GNU:
20782078
readline.set_completion_display_matches_hook(None)
2079-
basic_quote_characters.value = old_basic_quote_characters
2079+
rl_basic_quote_characters.value = old_basic_quotes
20802080
elif rl_type == RlType.PYREADLINE:
20812081
readline.rl.mode._display_completions = orig_pyreadline_display
20822082

@@ -2535,6 +2535,7 @@ def run(filename):
25352535
except IOError as e:
25362536
self.perror(e)
25372537

2538+
# noinspection PyUnusedLocal
25382539
def onecmd_plus_hooks(cmd_plus_args):
25392540
"""Run a cmd2.Cmd command from a Python script or the interactive Python console.
25402541
@@ -2556,6 +2557,8 @@ def onecmd_plus_hooks(cmd_plus_args):
25562557

25572558
if arg:
25582559
interp.runcode(arg)
2560+
2561+
# If there are no args, then we will open an interactive Python console
25592562
else:
25602563
# noinspection PyShadowingBuiltins
25612564
def quit():
@@ -2567,6 +2570,25 @@ def quit():
25672570

25682571
keepstate = None
25692572
try:
2573+
if rl_type != RlType.NONE:
2574+
# Save cmd2 history
2575+
saved_cmd2_history = []
2576+
for i in range(1, readline.get_current_history_length() + 1):
2577+
saved_cmd2_history.append(readline.get_history_item(i))
2578+
2579+
# Keep a list of commands run in the Python console
2580+
# noinspection PyAttributeOutsideInit
2581+
self.py_history = getattr(self, 'py_history', [])
2582+
2583+
# Restore py's history
2584+
readline.clear_history()
2585+
for item in self.py_history:
2586+
readline.add_history(item)
2587+
2588+
if self.use_rawinput and self.completekey:
2589+
# Disable tab completion while in interactive Python shell
2590+
readline.parse_and_bind(self.completekey + ": ")
2591+
25702592
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
25712593
keepstate = Statekeeper(sys, ('stdin', 'stdout'))
25722594
sys.stdout = self.stdout
@@ -2577,8 +2599,24 @@ def quit():
25772599
docstr))
25782600
except EmbeddedConsoleExit:
25792601
pass
2580-
if keepstate is not None:
2581-
keepstate.restore()
2602+
2603+
finally:
2604+
if keepstate is not None:
2605+
keepstate.restore()
2606+
2607+
if rl_type != RlType.NONE:
2608+
# Save py's history
2609+
for i in range(1, readline.get_current_history_length() + 1):
2610+
self.py_history.append(readline.get_history_item(i))
2611+
2612+
# Restore cmd2 history
2613+
readline.clear_history()
2614+
for item in saved_cmd2_history:
2615+
readline.add_history(item)
2616+
2617+
if self.use_rawinput and self.completekey:
2618+
# Enable tab completion since we are returning to cmd2
2619+
readline.parse_and_bind(self.completekey + ": complete")
25822620
except Exception:
25832621
pass
25842622
finally:
@@ -3430,5 +3468,3 @@ class CmdResult(namedtuple_with_two_defaults('CmdResult', ['out', 'err', 'war'])
34303468
def __bool__(self):
34313469
"""If err is an empty string, treat the result as a success; otherwise treat it as a failure."""
34323470
return not self.err
3433-
3434-

cmd2/rl_utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ class RlType(Enum):
3333
if 'pyreadline' in sys.modules:
3434
rl_type = RlType.PYREADLINE
3535

36+
# pyreadline is incomplete in terms of the Python readline API
37+
# Add the missing functions we need
38+
try:
39+
getattr(readline, 'remove_history_item')
40+
except AttributeError:
41+
# noinspection PyProtectedMember
42+
def pyreadline_remove_history_item(pos: int) -> None:
43+
"""
44+
An implementation of remove_history_item() for pyreadline
45+
:param pos: The 0-based position in history to remove
46+
"""
47+
# Save of the current location of the history cursor
48+
saved_cursor = readline.rl.mode._history.history_cursor
49+
50+
# Delete the history item
51+
del(readline.rl.mode._history.history[pos])
52+
53+
# Update the cursor if needed
54+
if saved_cursor > pos:
55+
readline.rl.mode._history.history_cursor -= 1
56+
57+
readline.remove_history_item = pyreadline_remove_history_item
58+
3659
elif 'gnureadline' in sys.modules or 'readline' in sys.modules:
3760
# We don't support libedit
3861
if 'libedit' not in readline.__doc__:

0 commit comments

Comments
 (0)