Skip to content

Commit 88ce0b5

Browse files
committed
Added checks for readline implementation type
1 parent 0ef0e9b commit 88ce0b5

1 file changed

Lines changed: 41 additions & 26 deletions

File tree

cmd2.py

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import traceback
4545
import unittest
4646
from code import InteractiveConsole
47+
from enum import Enum
4748

4849
import pyparsing
4950
import pyperclip
@@ -128,25 +129,43 @@ def __subclasshook__(cls, C):
128129
except ImportError:
129130
pass
130131

131-
# Load the GNU readline lib so we can make changes to it
132-
readline_lib = None
133-
if not sys.platform.startswith('win'):
134-
import ctypes
135-
from ctypes.util import find_library
136132

137-
readline_lib_name = find_library("readline")
138-
if readline_lib_name is not None and readline_lib_name:
139-
readline_lib = ctypes.CDLL(readline_lib_name)
133+
# Tells what implementation of readline we are using
134+
class RlType(Enum):
135+
GNU = 1
136+
PYREADLINE = 2
137+
NONE = 3
140138

141-
# Save address that rl_basic_quote_characters is pointing to since we need to override and restore it
142-
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
143-
orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
144139

145-
else:
140+
# Check what implementation of readline we are using
141+
rl_type = RlType.NONE
142+
143+
if 'pyreadline' in sys.modules:
144+
rl_type = RlType.PYREADLINE
145+
146146
# Save the original pyreadline display completion function since we need to override it and restore it
147147
# noinspection PyProtectedMember
148148
orig_pyreadline_display = readline.rl.mode._display_completions
149149

150+
else:
151+
readline_lib_path = ''
152+
153+
if 'gnureadline' in sys.modules:
154+
readline_lib_path = sys.modules['gnureadline'].__dict__['__file__']
155+
elif 'readline' in sys.modules:
156+
readline_lib_path = sys.modules['readline'].__dict__['__file__']
157+
158+
# Load the readline lib so we can make changes to it
159+
if readline_lib_path:
160+
rl_type = RlType.GNU
161+
162+
import ctypes
163+
readline_lib = ctypes.CDLL(readline_lib_path)
164+
165+
# Save address that rl_basic_quote_characters is pointing to since we need to override and restore it
166+
rl_basic_quote_characters = ctypes.c_char_p.in_dll(readline_lib, "rl_basic_quote_characters")
167+
orig_rl_basic_quote_characters_addr = ctypes.cast(rl_basic_quote_characters, ctypes.c_void_p).value
168+
150169

151170
# BrokenPipeError and FileNotFoundError exist only in Python 3. Use IOError for Python 2.
152171
if six.PY3:
@@ -1227,7 +1246,7 @@ def display_match_list_gnu_readline(substitution, matches, longest_match_length)
12271246
:param matches: list[str] - the tab completion matches to display
12281247
:param longest_match_length: int - longest printed length of the matches
12291248
"""
1230-
if readline_lib is not None:
1249+
if rl_type == RlType.GNU:
12311250
# We will use readline's display function (rl_display_match_list()), so we
12321251
# need to encode our string as bytes to place in a C array.
12331252
if six.PY3:
@@ -1265,7 +1284,7 @@ def display_match_list_pyreadline(matches):
12651284
Prints a match list using pyreadline's _display_completions()
12661285
:param matches: list[str] - the tab completion matches to display
12671286
"""
1268-
if orig_pyreadline_display is not None:
1287+
if rl_type == RlType.PYREADLINE:
12691288
orig_pyreadline_display(matches)
12701289

12711290
def tokens_for_completion(self, line, begidx, endidx):
@@ -1903,8 +1922,7 @@ def _set_readline_line(new_line):
19031922
Sets the readline line buffer
19041923
:param new_line: str - the new line contents
19051924
"""
1906-
# GNU readline specific way to update line buffer
1907-
if readline_lib is not None:
1925+
if rl_type == RlType.GNU:
19081926
# Byte encode the new line
19091927
if six.PY3:
19101928
encoded_line = bytes(new_line, encoding='utf-8')
@@ -1914,8 +1932,7 @@ def _set_readline_line(new_line):
19141932
# Replace the line
19151933
readline_lib.rl_replace_line(encoded_line, 0)
19161934

1917-
# pyreadline specific way to update line buffer
1918-
elif sys.platform.startswith('win'):
1935+
elif rl_type == RlType.PYREADLINE:
19191936
readline.rl.mode.l_buffer.set_line(new_line)
19201937

19211938
@staticmethod
@@ -1924,13 +1941,11 @@ def _set_readline_point(new_point):
19241941
Sets the cursor offset in the readline line buffer
19251942
:param new_point: int - the new cursor offset
19261943
"""
1927-
# GNU readline specific way to update line point
1928-
if readline_lib is not None:
1944+
if rl_type == RlType.GNU:
19291945
rl_point = ctypes.c_int.in_dll(readline_lib, "rl_point")
19301946
rl_point.value = new_point
19311947

1932-
# pyreadline specific way to update line point
1933-
elif sys.platform.startswith('win'):
1948+
elif rl_type == RlType.PYREADLINE:
19341949
readline.rl.mode.l_buffer.point = new_point
19351950

19361951
# ----- Methods which override stuff in cmd -----
@@ -2656,15 +2671,15 @@ def _cmdloop(self):
26562671
if self.use_rawinput and self.completekey:
26572672

26582673
# Set up readline for our tab completion needs
2659-
if readline_lib is not None:
2674+
if rl_type == RlType.GNU:
26602675
readline.set_completion_display_matches_hook(self._display_matches_gnu_readline)
26612676

26622677
# Set GNU readline's rl_basic_quote_characters to NULL so it won't automatically add a closing quote
26632678
# We don't need to worry about setting rl_completion_suppress_quote since we never declared
26642679
# rl_completer_quote_characters.
26652680
rl_basic_quote_characters.value = None
26662681

2667-
elif sys.platform.startswith('win'):
2682+
elif rl_type == RlType.PYREADLINE:
26682683
readline.rl.mode._display_completions = self._display_matches_pyreadline
26692684

26702685
try:
@@ -2717,11 +2732,11 @@ def _cmdloop(self):
27172732
except NameError:
27182733
pass
27192734

2720-
if readline_lib is not None:
2735+
if rl_type == RlType.GNU:
27212736
readline.set_completion_display_matches_hook(None)
27222737
rl_basic_quote_characters.value = orig_rl_basic_quote_characters_addr
27232738

2724-
elif sys.platform.startswith('win'):
2739+
elif rl_type == RlType.PYREADLINE:
27252740
readline.rl.mode._display_completions = orig_pyreadline_display
27262741

27272742
# Need to set empty list this way because Python 2 doesn't support the clear() method on lists

0 commit comments

Comments
 (0)