-
Notifications
You must be signed in to change notification settings - Fork 0
/
unrepl.py
164 lines (131 loc) · 4.54 KB
/
unrepl.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
import sys
import unrepl
import argparse
__version__ = "1.0.5"
Pythonista = sys.platform == "ios"
if Pythonista:
import clipboard
import console
else:
try:
from tkinter import messagebox
from tkinter import Tk
from tkinter import _tkinter
except ModuleNotFoundError:
raise ModuleNotFoundError("tkinter required.")
def _is_repl(text):
lines = text.splitlines()
return len(lines) != 0 and lines[0].startswith(">>> ")
def _has_output_lines(text):
lines = text.splitlines()
for line in lines:
if not (line.startswith(">>> ") or line.startswith("... ") or line.strip() == ""):
return True
return False
def _get_clipboard():
if Pythonista:
contents = clipboard.get()
else:
r = Tk()
r.withdraw()
try:
contents = r.clipboard_get()
except _tkinter.TclError:
contents = ""
r.destroy()
return contents
def _set_clipboard(contents):
if Pythonista:
clipboard.set(contents)
else:
r = Tk()
r.withdraw()
r.clipboard_clear()
r.clipboard_append(contents)
r.after(500, r.destroy)
r.mainloop()
def _messagebox_showinfo(title, message):
if Pythonista:
console.alert(title, message)
else:
r = Tk()
r.withdraw()
messagebox.showinfo(title, message)
r.destroy()
def _messagebox_askyesnocancel(title, message):
if Pythonista:
response = console.alert(title, message, "yes", "no")
return response == 1
else:
r = Tk()
r.withdraw()
response = messagebox.askyesnocancel(title, message)
r.destroy()
return response
def unrepl(code, use_print_statements=True):
"""
Cleans up a code fragment from a REPL, with output lines
Parameters
----------
code : str
code to clean up
use_print_statements : bool
if True (default) use print statements for lines that generate output
if False, use lines untranslated for lines that generate output
Returns
-------
Converted code, if proper REPL output
Exceptions
----------
Raises a ValueError if code is not proper REPL output, i.e.
first line starts with `>>> `.
"""
if not _is_repl(code):
raise ValueError("not proper REPL code")
lines = code.splitlines()
result_code = []
last_line_code_index = None
for line in lines:
if line.startswith(">>>") or line.startswith("..."):
result_code.append(line[4:])
if line[4:].strip() != "" and not line[4:].strip().startswith("#"):
last_line_code_index = len(result_code) - 1
else:
if use_print_statements:
if line.strip() != "":
if last_line_code_index is not None:
last_line = result_code[last_line_code_index]
expression = last_line.strip()
indent_level = len(last_line) - len(last_line.lstrip())
result_code[last_line_code_index] = f"{indent_level * ' '}_ = {expression}; print(repr(_)) # {expression.strip()}"
last_line_code_index = None
result_code.append(f"# {line}")
return "".join(f"{line}\n" for line in result_code)
def _main():
parser = argparse.ArgumentParser(description="converts REPL output in clipboard.")
parser.add_argument("-u", metavar="y|n", type=str, default="", help="use print statements")
parsed = parser.parse_args()
u = parsed.u
if u == "":
use_print_statements = None # denotes that it should be queried
elif u.lower().startswith("y"):
use_print_statements = True
elif u.lower().startswith("n"):
use_print_statements = False
else:
raise AttributeError(f"-u should be followed by y or n, not {u}")
contents = _get_clipboard()
if _is_repl(contents):
if use_print_statements is None:
if _has_output_lines(contents):
use_print_statements = _messagebox_askyesnocancel("unrepl", "Use print statements ?")
else:
use_print_statements = False
if use_print_statements is not None: # didn't press Cancel
translated_contents = unrepl(contents, use_print_statements=use_print_statements)
_set_clipboard(translated_contents)
else:
_messagebox_showinfo("unrepl", "Clipboard does not contain proper REPL output")
unrepl.__version__ = __version__
if __name__ == "__main__":
_main()