-
Notifications
You must be signed in to change notification settings - Fork 2
/
command_seq_reader.py
119 lines (110 loc) · 3.77 KB
/
command_seq_reader.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
r"""
last_assignment_or_evaluatable should return the last value assigned or otherwise
referred to in a stack of Python commands.
>>> last_assignment_or_evaluatable('a=1')
1
>>> last_assignment_or_evaluatable('a = 1\nb = 2')
2
>>> last_assignment_or_evaluatable('beast = "cow"\nx = 22\nbeast')
'cow'
>>> last_assignment_or_evaluatable('f=4.0\ni=7\ntarget=f')
4.0
>>> last_assignment_or_evaluatable('f=4.0\ni=7\ntarget=f', types_of_interest=(int,))
7
"""
import unittest, doctest, re
commandUnfinished = re.compile('unexpected EOF while parsing|EOL while scanning|unexpected character after line continuation character|invalid syntax')
def items_of_interest(dct, types_of_interest):
return [(k, v) for (k, v) in dct.items() if isinstance(v, types_of_interest)]
def recordingexec(arg, types_of_interest = (int, basestring)):
cmdbuf = []
lastcmd = ''
commandInProgress = []
lastlocals = []
for line in arg.strip().splitlines():
cmdbuf.append(line)
commandInProgress.append(line)
try:
oldlocals = items_of_interest(locals(), types_of_interest)
exec('\n'.join(cmdbuf))
newlocals = [itm for itm in
items_of_interest(locals(), types_of_interest)
if itm not in oldlocals]
if newlocals:
lastlocals = newlocals
lastcmd = '\n'.join(commandInProgress)
commandInProgress = []
except IndentationError:
pass
except SyntaxError, e:
if not commandUnfinished.search(str(e)):
raise
except Exception, e:
raise Exception, '%s:\n%s' % (e, line)
return (lastcmd, dict(lastlocals))
def last_assignment_or_evaluatable(s, types_of_interest=(basestring, int, float)):
s = s.strip()
if not s:
return None
exec(s)
(lastcmd, lastlocals) = recordingexec(s, types_of_interest)
try:
return eval(lastcmd)
except SyntaxError, e:
if 'invalid syntax' not in str(e):
raise
if len(lastlocals) > 1:
raise ValueError, "Multiple assignments - couldn't determine which one"
return eval(lastlocals.keys()[0])
class RecordingExecTestSuite(unittest.TestCase):
def testSimpleAssignments(self):
(lastcmd, lastlocals) = recordingexec('''
a = 1
b = "cow"
c = 3''')
self.assertEqual(lastcmd, 'c = 3')
self.assertEqual(lastlocals, {'c': 3})
def testIgnoreObj(self):
(lastcmd, lastlocals) = recordingexec('''
a = 1
b = 2
uts = unittest.TestSuite()
''', (unittest.TestSuite))
self.assertEqual(lastcmd, 'uts = unittest.TestSuite()')
self.assertEqual(lastlocals.keys(), ['uts'])
def testExecutableLastLine(self):
(lastcmd, lastlocals) = recordingexec('''
a = 1
print "hi mom"''')
self.assertEqual(lastcmd, 'print "hi mom"')
self.assertEqual(lastlocals, {'a': 1})
def testEvalLastLine(self):
(lastcmd, lastlocals) = recordingexec('''
a = 1
a''')
self.assertEqual(lastcmd, 'a')
self.assertEqual(lastlocals, {'a': 1})
class last_assignment_or_evaluatableTestSuite(unittest.TestCase):
def testSimpleAssignments(self):
lv = last_assignment_or_evaluatable('''
a = 1
b = "cow"
c = 3''')
self.assertEqual(lv, 3)
def testSimpleAssignments(self):
lv = last_assignment_or_evaluatable('''
a = 1
b = "cow"
c = 3
b''')
self.assertEqual(lv, 'cow')
def testMultiAssignFail(self):
self.assertRaises(ValueError, last_assignment_or_evaluatable, '''
a = 1
(b, c) = (2, 3)''')
def testEmpty(self):
lv = last_assignment_or_evaluatable('')
self.assertEqual(lv, None)
if __name__ == '__main__':
#unittest.main()
doctest.testmod()