-
Notifications
You must be signed in to change notification settings - Fork 1
/
pyculator.py
executable file
·154 lines (113 loc) · 4.09 KB
/
pyculator.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
#!/usr/bin/env python3
import sys
import os
import re
import json
import tempfile
import signal
import time
from subprocess import Popen, PIPE, call
EDITOR = os.environ.get('EDITOR', 'vim')
# editors that will block the program that starts it (mostly commandline)
blocking_editors = ["vim", "nvim", "nano", "emacs"]
# editors that open the file to another process. (Mostly graphical)
nonblocking_editors = ["subl", "atom"]
def signal_handler(signal, frame):
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
class Pyculate:
def execute(filename):
print("Executing", filename)
processedFile = """
import atexit
import json
traces = dict()
def exit_handler():
print(json.dumps(traces))
atexit.register(exit_handler)
def trace(linenumber, result):
if not linenumber in traces:
traces[linenumber] = []
traces[linenumber].append(str(result))
"""
lines = list()
with open(filename, 'r') as myfile:
lines = myfile.readlines()
i = -1
for line in lines:
i += 1
m = re.match("([\t ]*)([^\#]*)\#()(?!\#)(.*)", line)
if m:
tabs = m.group(1)
code = m.group(2)
processedFile += "%s_ = %s;trace(%d, _)#>\n" % (tabs, code, i)
else:
processedFile += line
print("Running...")
p = Popen(['python3'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
(stdout_data, stderr) = p.communicate(input=processedFile.encode())
try:
traces = dict()
output_lines = stdout_data.split(b"\n")
if len(output_lines) > 1:
traces = json.loads(output_lines[-2].decode())
except ValueError as e:
print("Tracing failed")
traces = dict()
j = -1
for line in lines:
j += 1
i = str(j)
m = re.match("([^\#]*\#(?!\#)).*", line)
if m:
r = ""
if i in traces:
def sanitize(s):
s = str(s)
s = s.replace("\n", "\\n")
s = s.replace("\r", "\\n")
return s
if len(traces[i]) > 6:
boundary = traces[i][:3]+["..."] + traces[i][-3:]
r = ", ".join(sanitize(a) for a in boundary)
else:
r = ", ".join(sanitize(a) for a in traces[i][:3])
lines[j] = "%s %s\n" % (m.group(1), r)
return ("".join(lines), stderr.decode())
def execution_loop(filename):
while True:
editor_started = time.time()
call([EDITOR, filename])
# if the process is nonblocking
external = False
if EDITOR in blocking_editors:
external = False
elif EDITOR in nonblocking_editors:
external = True
elif time.time()-editor_started < 0.5:
external = True
print("Your editor seems to be running in a different process.")
if external:
input("Press enter after your changes to run the file. Ctrl+c to quit.")
(newContent, stderr) = Pyculate.execute(filename)
if len(stderr) > 0:
print(stderr)
if not external:
input("Press enter to edit again. Ctrl+c to quit.")
if newContent == "":
exit()
with open(filename, "w") as file:
file.write(newContent)
# Run with temporary file
if len(sys.argv) == 1:
initial_message = "import numpy as np\nimport sympy as sp\nimport scipy as sci\nfrom math import *\n\n\n\n\n\n"
with tempfile.NamedTemporaryFile(suffix=".pyculator.py") as tf:
tf.write(initial_message.encode())
tf.flush()
Pyculate.execution_loop(tf.name)
# Run with the file given as a parameter
if len(sys.argv) == 2:
filename = sys.argv[1]
if not os.path.isfile(filename):
exit("Invalid File")
Pyculate.execution_loop(filename)