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.
5052
5153
5254import re , types
55+ import sys
5356typeRE = re .compile ("<type '(.*)'>" )
5457classRE = re .compile ("<class '(.*)'>" )
5558
5659import inspect
5760
61+ is_python3 = (sys .version_info [0 ] == 3 )
62+ if is_python3 :
63+ long = None # Avoid NameError when evaluating "long"
64+
65+
5866def 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+
0 commit comments