-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathoverride_readline.py
166 lines (142 loc) · 5.22 KB
/
override_readline.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#
# Install readline override code and explain what happened.
#
# Run this as `python -m override_readline`, picking the appropriate Python
# executable. This runs on both Python 2.7 and 3.x, hence the use of `io`,
# `os.path` and explicit Unicode.
#
# First target sitecustomize.py (better for venvs and system-wide overrides)
# and then fall back to usercustomize.py if we don't have permission.
#
# Based on the script in https://stackoverflow.com/a/38606990. Thanks, Alastair!
#
import importlib
import io
import os
import os.path
import site
import sys
_HEADER = """
This script will attempt to install an override in Python's site customization
modules that replaces the default readline module with gnureadline.
First check the existing readline module and its replacement
------------------------------------------------------------
"""
_REPORT = """
The following override was added to {filename}:
(full path: {full_path})
{padded_override}
Feel free to remove this{or_even_file} if not needed anymore
(It is also pretty harmless to leave it in there...)
"""
OVERRIDE = u"""
# Added by override_readline script in gnureadline package
import sys
def add_override_message_to_hook():
try:
old_hook = sys.__interactivehook__
except AttributeError:
return
def hook():
old_hook()
print("Using GNU readline instead of the default readline (see {filename})")
sys.__interactivehook__ = hook
try:
import gnureadline as readline
add_override_message_to_hook()
except ImportError:
import readline
sys.modules["readline"] = readline
# End of override_readline block
"""
def check_module(module_name):
"""Attempt to import `module_name` and report basic features."""
try:
module = importlib.import_module(module_name)
except ImportError:
print("Module {name}: not found".format(name=module_name))
return None
try:
# Python 3.13 introduced a backend attribute on readline module
is_libedit = module.backend == "editline"
except AttributeError:
is_libedit = "libedit" in module.__doc__
backend = "libedit" if is_libedit else "GNU readline"
path = getattr(module, "__file__", "(built-in)")
kwargs = dict(name=module_name, backend=backend, path=path)
print("Module {name}: based on {backend}, {path}".format(**kwargs))
return module
def install_override(customize_path):
"""Add override to specified customization module and report back."""
site_directory, customize_filename = os.path.split(customize_path)
banner = "\nAdd override to {filename}\n--------------------------------\n"
print(banner.format(filename=customize_filename))
if not os.path.exists(site_directory):
os.makedirs(site_directory)
print("Created site directory at {dir}".format(dir=site_directory))
file_mode = "r+t" if os.path.exists(customize_path) else "w+t"
with io.open(customize_path, file_mode) as customize_file:
if file_mode == "w+t":
print("Created customize module at {path}".format(path=customize_path))
existing_text = customize_file.read()
override = OVERRIDE.format(filename=customize_filename)
if override in existing_text:
print("Readline override already enabled, nothing to do")
else:
# File pointer should already be at the end of the file after read()
customize_file.write(override)
kwargs = dict(
filename=customize_filename,
full_path=customize_path,
padded_override="\n ".join(override.split("\n")),
or_even_file=" (or even the entire file)" if file_mode == "w+t" else "",
)
print(_REPORT.format(**kwargs))
def override_usercustomize():
if not site.ENABLE_USER_SITE:
print(
"Could not override usercustomize.py because user site "
"directory is not enabled (maybe you are in a virtualenv?)"
)
return False
try:
import usercustomize
except ImportError:
path = os.path.join(site.getusersitepackages(), "usercustomize.py")
else:
path = usercustomize.__file__
try:
install_override(path)
except OSError:
print("Could not override usercustomize.py because file open/write failed")
return False
else:
return True
def override_sitecustomize():
try:
import sitecustomize
except ImportError:
path = os.path.join(site.getsitepackages()[0], "sitecustomize.py")
else:
path = sitecustomize.__file__
try:
install_override(path)
except OSError:
print("Could not override sitecustomize.py because file open/write failed")
return False
else:
return True
def main():
print(_HEADER)
readline = check_module("readline")
gnureadline = check_module("gnureadline")
if not gnureadline:
raise RuntimeError("Please install gnureadline first")
if readline == gnureadline:
print("It looks like readline is already overridden, but let's make sure")
success = override_usercustomize()
if not success:
success = override_sitecustomize()
return 0 if success else 1
if __name__ == '__main__':
sys.exit(main())