Skip to content

Commit d8f7b0d

Browse files
author
Philip Guo
committed
integrated more of john's changes for python 3 compatibility
1 parent 5b0df5e commit d8f7b0d

File tree

3 files changed

+52
-20
lines changed

3 files changed

+52
-20
lines changed

PyTutorGAE/README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Thanks to John DeNero, this version of Online Python Tutor (v3) should work on both Python 2 and 3.
2+
3+
TODO: write more detailed instructions for running on Google App Engine (Python 2.7) and CGI (Python 2.X or 3.X)
4+

PyTutorGAE/pg_encoder.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2323
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2424

25+
# Thanks to John DeNero for making the encoder work on both Python 2 and 3
26+
2527

2628
# Given an arbitrary piece of Python data, encode it in such a manner
2729
# that it can be later encoded into JSON.
@@ -50,11 +52,17 @@
5052

5153

5254
import re, types
55+
import sys
5356
typeRE = re.compile("<type '(.*)'>")
5457
classRE = re.compile("<class '(.*)'>")
5558

5659
import inspect
5760

61+
is_python3 = (sys.version_info[0] == 3)
62+
if is_python3:
63+
long = None # Avoid NameError when evaluating "long"
64+
65+
5866
def get_name(obj):
5967
"""Return the name of an object."""
6068
return obj.__name__ if hasattr(obj, '__name__') else get_name(type(obj))
@@ -141,23 +149,6 @@ def encode(self, dat):
141149
# don't display some built-in locals ...
142150
if k not in ('__module__', '__return__', '__locals__'):
143151
new_obj.append([self.encode(k), self.encode(v)])
144-
145-
elif typ in (types.InstanceType, types.ClassType, types.TypeType) or \
146-
classRE.match(str(typ)):
147-
# ugh, classRE match is a bit of a hack :(
148-
if typ == types.InstanceType or classRE.match(str(typ)):
149-
new_obj.extend(['INSTANCE', dat.__class__.__name__])
150-
else:
151-
superclass_names = [e.__name__ for e in dat.__bases__]
152-
new_obj.extend(['CLASS', dat.__name__, superclass_names])
153-
154-
# traverse inside of its __dict__ to grab attributes
155-
# (filter out useless-seeming ones):
156-
user_attrs = sorted([e for e in dat.__dict__.keys()
157-
if e not in ('__doc__', '__module__', '__return__')])
158-
159-
for attr in user_attrs:
160-
new_obj.append([self.encode(attr), self.encode(dat.__dict__[attr])])
161152
elif typ in (types.FunctionType, types.MethodType):
162153
# NB: In Python 3.0, getargspec is deprecated in favor of getfullargspec
163154
argspec = inspect.getargspec(dat)
@@ -173,6 +164,8 @@ def encode(self, dat):
173164
func_name = u"\u03BB" # Unicode lambda :)
174165
pretty_name = func_name + '(' + ', '.join(printed_args) + ')'
175166
new_obj.extend(['FUNCTION', pretty_name, None]) # the final element will be filled in later
167+
elif self.is_class(dat) or self.is_instance(dat):
168+
self.encode_class_or_instance(dat, new_obj)
176169
else:
177170
typeStr = str(typ)
178171
m = typeRE.match(typeStr)
@@ -181,3 +174,41 @@ def encode(self, dat):
181174

182175
return ret
183176

177+
178+
def is_class(self, dat):
179+
"""Return whether dat is a class."""
180+
if is_python3:
181+
return isinstance(dat, type)
182+
else:
183+
return type(dat) in (types.ClassType, types.TypeType)
184+
185+
186+
def is_instance(self, dat):
187+
"""Return whether dat is an instance of a class."""
188+
if is_python3:
189+
return isinstance(type(dat), type) and not isinstance(dat, type)
190+
else:
191+
# ugh, classRE match is a bit of a hack :(
192+
return type(dat) == types.InstanceType or classRE.match(str(type(dat)))
193+
194+
195+
def encode_class_or_instance(self, dat, new_obj):
196+
"""Encode dat as a class or instance."""
197+
if self.is_instance(dat):
198+
new_obj.extend(['INSTANCE', get_name(dat.__class__)])
199+
else:
200+
superclass_names = [e.__name__ for e in dat.__bases__ if e is not object]
201+
new_obj.extend(['CLASS', get_name(dat), superclass_names])
202+
203+
# traverse inside of its __dict__ to grab attributes
204+
# (filter out useless-seeming ones):
205+
hidden = ('__doc__', '__module__', '__return__', '__dict__',
206+
'__locals__', '__weakref__')
207+
if hasattr(dat, '__dict__'):
208+
user_attrs = sorted([e for e in dat.__dict__ if e not in hidden])
209+
else:
210+
user_attrs = []
211+
212+
for attr in user_attrs:
213+
new_obj.append([self.encode(attr), self.encode(dat.__dict__[attr])])
214+

PyTutorGAE/pg_logger.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ def __init__(self, finalizer_func):
128128
# to make an educated guess based on the contents of local
129129
# variables inherited from possible parent frame candidates.
130130
def get_parent_frame(self, frame):
131-
# TODO(denero) Is this true in Python 3?!?
132131
for (func_obj, parent_frame) in self.closures.items():
133132
# ok, there's a possible match, but let's compare the
134133
# local variables in parent_frame to those of frame
@@ -280,7 +279,6 @@ def create_encoded_stack_entry(cur_frame):
280279
for pid in parent_frame_id_list:
281280
parent_frame = self.lookup_zombie_frame_by_id(pid)
282281
if k in parent_frame.f_locals:
283-
# TODO(denero) Check Python3
284282
# ignore __return__, which is never copied
285283
if k != '__return__':
286284
# these values SHOULD BE ALIASES
@@ -298,7 +296,6 @@ def create_encoded_stack_entry(cur_frame):
298296
encoded_val = self.encoder.encode(v)
299297

300298
# UGH, this is SUPER ugly but needed for nested function defs
301-
# TODO(denero) Is this true in Python 3?!?
302299
if type(v) in (types.FunctionType, types.MethodType):
303300
try:
304301
enclosing_frame = self.closures[v]

0 commit comments

Comments
 (0)