Skip to content

Commit 252b104

Browse files
committed
bam
1 parent 7678099 commit 252b104

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

Python3-porting.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
See cgi-bin/p4_encoder.py for some proposed changes
2+
3+
14
Notes from Peter Wentworth about porting to Python 3
25
'''
36
Hi Philip - a bug in the code I sent you [NB, see: python3_viz.zip]! I

cgi-bin/p4_encoder.py

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#!/usr/bin/python3 -u
2+
3+
# Python 3 version of encoder by David Pritchard, built upon work by Peter Wentworth
4+
# (diff with pg_encoder.py, which is for Python 2)
5+
6+
7+
# given an arbitrary piece of Python data, encode it in such a manner
8+
# that it can be later encoded into JSON.
9+
# http://json.org/
10+
#
11+
# Format:
12+
# * None, int, float, str, bool - unchanged (long is removed in Python 3)
13+
# (json.dumps encodes these fine verbatim)
14+
# * list - ['LIST', unique_id, elt1, elt2, elt3, ..., eltN]
15+
# * tuple - ['TUPLE', unique_id, elt1, elt2, elt3, ..., eltN]
16+
# * set - ['SET', unique_id, elt1, elt2, elt3, ..., eltN]
17+
# * dict - ['DICT', unique_id, [key1, value1], [key2, value2], ..., [keyN, valueN]]
18+
# * instance - ['INSTANCE', class name, unique_id, [attr1, value1], [attr2, value2], ..., [attrN, valueN]]
19+
# * class - ['CLASS', class name, unique_id, [list of superclass names], [attr1, value1], [attr2, value2], ..., [attrN, valueN]]
20+
# * circular reference - ['CIRCULAR_REF', unique_id]
21+
# * other - [<type name>, unique_id, string representation of object]
22+
#
23+
#
24+
# the unique_id is derived from id(), which allows us to explicitly
25+
# capture aliasing of compound values
26+
27+
# Key: real ID from id()
28+
# Value: a small integer for greater readability, set by cur_small_id
29+
real_to_small_IDs = {}
30+
cur_small_id = 1
31+
32+
import re, types
33+
#typeRE = re.compile("<type '(.*)'>") # not used in Python 3
34+
classRE = re.compile("<class '(.*)'>")
35+
functionRE = re.compile("<function (\w*) (.*)>") # new case for Python 3
36+
37+
# When we find a <class x> and x is in this list, don't confuse the beginner by listing the inner details
38+
native_types = ['int', 'float', 'str', 'tuple', 'list', 'set', 'dict', 'bool', 'NoneType', 'bytes', 'type', 'object']
39+
40+
def encode(dat, ignore_id=False):
41+
42+
def append_attributes(ret, new_compound_obj_ids, dict):
43+
""" Put attributes onto ret. """
44+
# traverse the __dict__ to grab attributes
45+
# (filter out useless-seeming ones):
46+
47+
user_attrs = sorted([e for e in dict.keys()
48+
if e not in {'__doc__', '__module__', '__return__', '__locals__',
49+
'__weakref__', '__dict__'}
50+
])
51+
for attr in user_attrs:
52+
foo = [encode_helper(attr, new_compound_obj_ids),
53+
encode_helper(dict[attr], new_compound_obj_ids)]
54+
ret.append(foo)
55+
56+
def encode_helper(dat, compound_obj_ids):
57+
# primitive type
58+
if dat is None or type(dat) in (int, float, str, bool):
59+
return dat
60+
# compound type
61+
else:
62+
my_id = id(dat)
63+
64+
global cur_small_id
65+
if my_id not in real_to_small_IDs:
66+
if ignore_id:
67+
real_to_small_IDs[my_id] = 99999
68+
else:
69+
real_to_small_IDs[my_id] = cur_small_id
70+
cur_small_id += 1
71+
72+
if my_id in compound_obj_ids:
73+
return ['CIRCULAR_REF', real_to_small_IDs[my_id]]
74+
75+
new_compound_obj_ids = compound_obj_ids.union([my_id])
76+
77+
typ = type(dat)
78+
obj_as_string = object.__repr__(dat)
79+
80+
my_small_id = real_to_small_IDs[my_id]
81+
82+
if typ == list:
83+
ret = ['LIST', my_small_id]
84+
for e in dat: ret.append(encode_helper(e, new_compound_obj_ids))
85+
elif typ == tuple:
86+
ret = ['TUPLE', my_small_id]
87+
for e in dat: ret.append(encode_helper(e, new_compound_obj_ids))
88+
elif typ == set:
89+
ret = ['SET', my_small_id]
90+
for e in dat: ret.append(encode_helper(e, new_compound_obj_ids))
91+
elif typ == dict:
92+
ret = ['DICT', my_small_id]
93+
append_attributes(ret, new_compound_obj_ids, dat)
94+
95+
elif typ == type: # its a class. What a mess they made of it!
96+
superclass_names = [e.__name__ for e in dat.__bases__]
97+
ret = ['CLASS', dat.__name__, my_small_id, superclass_names]
98+
if dat.__name__ not in native_types:
99+
if hasattr(dat, '__dict__'):
100+
append_attributes(ret, new_compound_obj_ids, dat.__dict__)
101+
102+
elif repr(typ)[:6] == "<class" and obj_as_string.find('object') >= 0: # is it an instance?
103+
ret = ['INSTANCE', dat.__class__.__name__, my_small_id]
104+
if hasattr(dat, '__dict__'):
105+
append_attributes(ret, new_compound_obj_ids, dat.__dict__)
106+
107+
else:
108+
typeStr = repr(typ)
109+
m = classRE.match(typeStr)
110+
assert m, typ
111+
ret = [m.group(1), my_small_id , obj_as_string]
112+
113+
return ret
114+
115+
return encode_helper(dat, set())
116+
117+
118+
if __name__ == '__main__':
119+
120+
def test(actual, expected=0):
121+
""" Compare the actual to the expected value, and print a suitable message. """
122+
import sys
123+
linenum = sys._getframe(1).f_lineno # get the caller's line number.
124+
if (expected == actual):
125+
msg = "Test on line %s passed." % (linenum)
126+
else:
127+
msg = "Test on line %s failed. Expected '%s', but got '%s'." % (linenum, expected, actual)
128+
print(msg)
129+
130+
class P():
131+
p_attr1 = 123
132+
def p_method(self, x):
133+
return 2*x
134+
135+
class Q(P):
136+
pass
137+
138+
p1 = P()
139+
q1 = Q()
140+
141+
addr = 1
142+
143+
test(encode("hello"),"hello")
144+
test(encode(123),123)
145+
test(encode(123.45),123.45)
146+
test(encode(132432134423143132432134423143),132432134423143132432134423143)
147+
test(encode(False),False)
148+
test(encode(None),None)
149+
150+
151+
test(encode((1,2)), ['TUPLE', addr, 1, 2])
152+
153+
addr += 1
154+
test(encode([1,2]), ['LIST', addr, 1, 2])
155+
156+
addr += 1
157+
test(encode({1:'mon'}), ['DICT', addr, [1, 'mon']])
158+
159+
addr += 1
160+
test(encode(test), ['function', addr, 'test'])
161+
162+
addr += 1
163+
test(encode(P), ['CLASS', 'P', addr, ['object'], ['p_attr1', 123], ['p_method', ['function', addr+1, 'p_method']]])
164+
165+
addr += 2
166+
test(encode(Q), ['CLASS', 'Q', addr, ['P']])
167+
168+
addr += 1
169+
test(encode(p1), ['INSTANCE', 'P', addr])
170+
171+
addr += 1
172+
test(encode(q1), ['INSTANCE', 'Q', addr])
173+
174+
addr += 1
175+
test(encode(min), ['builtin_function_or_method', addr, '<built-in function min>'] )
176+
177+
addr += 1
178+
test(encode(range(1,3)), ['range', addr, 'range(1, 3)'])
179+
180+
addr += 1
181+
test(encode({1,2}), ['SET', addr, 1, 2])
182+
183+
addr += 1
184+
p = [1,2,3]
185+
p.append(p) # make a circular reference
186+
187+
test(encode(p), ['LIST', addr, 1, 2, 3, ['CIRCULAR_REF', addr]])
188+
189+
# Need some new tests for z = type(123)
190+
191+
192+
print(encode({"stdout": "", "func_name": "<module>", "globals": {"sum": 0, "friends": ["LIST", 1, "Joe", "Bill"], "length": 3, "f": "Joe"}, "stack_locals": [], "line": 7, "event": "step_line"}))

0 commit comments

Comments
 (0)