Skip to content

Instantly share code, notes, and snippets.

@sharow
Last active December 15, 2015 17:09
Show Gist options
  • Save sharow/5293945 to your computer and use it in GitHub Desktop.
Save sharow/5293945 to your computer and use it in GitHub Desktop.
another repr() implementation. readable JSON(or some object) formatter. multibyte string, cut long string.
#!/usr/bin/env python
# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; coding: utf-8; -*-
""" prettyrepr.py - another repr() implementation
"""
import sys
version_major = sys.version_info[0]
if version_major == 3:
# python3.x
from functools import reduce
xrange = list
unicode = str
__all__ = ['BaseRepr', 'BasicRepr', 'NestedPrettyRepr',
'basic_repr', 'pretty_repr']
def basic_repr(obj, **kwargs):
return repr(BasicRepr(obj, **kwargs))
def pretty_repr(obj, **kwargs):
return repr(NestedPrettyRepr(obj, **kwargs))
class BaseRepr(object):
def __init__(self, obj, iterate_all=False, **kwargs):
self.level = 0
self._obj = obj
self._iterate_all = iterate_all
self._derived_path = ''
self._cur_path = ''
if 'derived_path' in kwargs:
self._derived_path = kwargs['derived_path']
if 'level' in kwargs:
self.level = kwargs['level']
def __iter__(self):
obj = self._obj
if isinstance(obj, (str, unicode, int, float, bool)):
yield self.handle_atom_before()
if isinstance(obj, str):
yield self.handle_str(obj)
elif isinstance(obj, unicode):
yield self.handle_unicode(obj)
elif isinstance(obj, int):
yield self.handle_int(obj)
elif isinstance(obj, float):
yield self.handle_float(obj)
elif isinstance(obj, bool):
yield self.handle_bool(obj)
else:
assert False
yield self.handle_atom_after()
elif self._is_iterable(obj, self._iterate_all):
block_handler = self._determine_block_handler(obj)
yield block_handler(self, open_block=True)
self.level += 1
writeback = '' # writeback for remove tail object's ', '
if isinstance(obj, dict):
for k, v in obj.items():
self._cur_path = "['%s']" % (k)
yield writeback
writeback = self.handle_atom_before()
yield writeback
writeback = self.handle_dict_key(k)
yield writeback
writeback = self.handle_atom_after()
yield writeback
writeback = self.handle_dict_separator()
for dict_item in self.handle_dict_item_it(v):
yield writeback
writeback = dict_item
yield writeback
writeback = self.handle_collection_separator()
else:
counter = 0
for i in iter(obj):
self._cur_path = "[%d]" % (counter)
for item in self._sub_iterator_context(i):
yield writeback
writeback = item
yield writeback
writeback = self.handle_collection_separator()
counter += 1
yield block_handler(self, open_block=False)
self.level -= 1
else:
yield self.handle_atom_before()
yield self.handle_unknown_atom(obj)
yield self.handle_atom_after()
def __repr__(self):
return ''.join(self)
__str__ = __repr__
__unicode__ = __repr__
def _sub_iterator_context(self, obj):
kwargs = self.generate_kwargs()
return self.__class__(obj, iterate_all=self._iterate_all, **kwargs)
@staticmethod
def _is_iterable(obj, iterate_all):
if iterate_all:
if hasattr(obj, '__iter__'):
return True
else:
return False
else:
if isinstance(obj, (list, dict, tuple, set, xrange)): #xrange
return True
else:
return False
@classmethod
def _determine_block_handler(cls, obj):
if isinstance(obj, list): return cls.handle_list
if isinstance(obj, xrange): return cls.handle_xrange
elif isinstance(obj, tuple):return cls.handle_tuple
elif isinstance(obj, set): return cls.handle_set
elif isinstance(obj, dict): return cls.handle_dict
else: return cls.handle_unknown_iterable
# for sub-instance args (derive current context(arg))
def generate_kwargs(self):
return {'derived_path': self.get_cur_path(),
'level': self.level}
@staticmethod
def super_generate_kwargs(baseclass, obj):
return super(baseclass, obj).__thisclass__.generate_kwargs(obj)
def get_cur_path(self):
return self._derived_path + self._cur_path
# need override
def handle_str(self, obj): pass
def handle_unicode(self, obj): pass
def handle_int(self, obj): pass
def handle_float(self, obj): pass
def handle_bool(self, obj): pass
def handle_unknown_atom(self, obj): pass
def handle_atom_before(self): pass
def handle_atom_after(self): pass
def handle_dict_key(self, obj): pass
def handle_dict_separator(self): pass
def handle_dict_item_it(self, obj): pass
def handle_collection_separator(self): pass
def handle_list(self, open_block): pass
def handle_xrange(self, open_block): pass
def handle_tuple(self, open_block): pass
def handle_set(self, open_block): pass
def handle_dict(self, open_block): pass
def handle_unknown_iterable(self, open_block): pass
# looks like default repr()
class BasicRepr(BaseRepr):
def __init__(self, obj, **kwargs):
"""
ex: obj = {'key': StringIO('abc')}
iterate_all=False => "{'key': <StringIO.StringIO instance at ??>}"
iterate_all=True => "{'key': <'abc'>}"
"""
BaseRepr.__init__(self, obj, **kwargs)
def handle_atom_before(self):
return ''
def handle_atom_after(self):
return ''
# atom handlers
def handle_str(self, obj):
return "'" + obj + "'"
def handle_unicode(self, obj):
return "u'" + obj.encode('utf-8') + "'"
def handle_int(self, obj):
return str(obj)
def handle_float(self, obj):
return str(obj)
def handle_bool(self, obj):
return str(obj)
def handle_unknown_atom(self, obj):
return repr(obj)
# other handlers
def handle_dict_key(self, obj):
return "'" + obj + "'"
def handle_dict_separator(self):
return ': '
def handle_dict_item_it(self, obj):
return self._sub_iterator_context(obj)
def handle_collection_separator(self):
return ', '
# block handlers
def handle_list(self, open_block):
return '[' if open_block else ']'
def handle_xrange(self, open_block):
return '[' if open_block else ']'
def handle_tuple(self, open_block):
return '(' if open_block else ')'
def handle_set(self, open_block):
if version_major == 2:
return 'set([' if open_block else '])'
elif version_major == 3:
return 'set(' if open_block else ')'
else:
assert False
def handle_dict(self, open_block):
return '{' if open_block else '}'
def handle_unknown_iterable(self, open_block):
return "'<" if open_block else ">'"
class NestedPrettyRepr(BaseRepr):
def __init__(self, obj, iterate_all=False,
indent=2, print_path=True, object_name='',
str_limit_len=48, str_replace_crlf=True,
dict_key_fmt="'%s'", comment_fmt='# %s', **kwargs):
self.indent = indent
self.cur_nest = 0
self.dict_key_fmt = dict_key_fmt
self.str_limit_len = str_limit_len
self.str_replace_crlf = str_replace_crlf
self.comment_fmt = comment_fmt
self.print_path = print_path
self.object_name = object_name
self._one_time_nest_stop = False
if 'cur_nested' in kwargs:
self.cur_nest = kwargs['cur_nested']
if 'one_time_nest_stop' in kwargs:
self._one_time_nest_stop = kwargs['one_time_nest_stop']
BaseRepr.__init__(self, obj, iterate_all, **kwargs)
def nest(self):
if self._one_time_nest_stop:
self._one_time_nest_stop = False
return ''
return ' ' * self.cur_nest
def generate_kwargs(self):
kwargs = self.super_generate_kwargs(BaseRepr, self)
kwargs.update({
'indent': self.indent,
'cur_nested': self.cur_nest,
'object_name': self.object_name,
'print_path': self.print_path,
'str_limit_len': self.str_limit_len,
'str_replace_crlf': self.str_replace_crlf,
'dict_key_fmt': self.dict_key_fmt,
'comment_fmt': self.comment_fmt,
'one_time_nest_stop': self._one_time_nest_stop
})
return kwargs
# atom handlers
def handle_atom_before(self):
return self.nest()
def handle_atom_after(self):
return ''
def handle_str(self, obj):
s = obj
limi = 0
cut = False
if self.str_limit_len > 0:
limit = self.str_limit_len - 3 # for '...'
if limit <= 0:
limit = self.str_limit_len
if limit > 0:
if len(s) > limit:
s = s[:limit]
cut = True
if self.str_replace_crlf:
s = s.replace('\r', '\\r')
s = s.replace('\n', '\\n')
if limit > 0:
if len(s) > limit:
s = s[:limit]
cut = True
if cut:
return "'%s...'" % (s)
else:
return "'%s'" % (s)
handle_unicode = handle_str
def handle_int(self, obj):
return str(obj)
def handle_float(self, obj):
return str(obj)
def handle_bool(self, obj):
return "'" + str(obj) + "'"
def handle_unknown_atom(self, obj):
return repr(obj)
# other handlers
def handle_dict_key(self, obj):
return self.dict_key_fmt % (obj)
def handle_dict_separator(self):
self._one_time_nest_stop = True
return ': '
def handle_dict_item_it(self, obj):
return filter(lambda x: self.nest() + x,
self._sub_iterator_context(obj))
def handle_collection_separator(self):
return ',\n'
# block handlers
def _handle_collection(self, open_block, outputs):
if open_block:
v = self.nest() + outputs[0]
if self.print_path:
path = self.get_cur_path()
if path != '':
path_str = '%s%s' % (self.object_name, path)
v += ' ' + self.comment_fmt % (path_str)
self.cur_nest += self.indent
return v + '\n'
else:
self.cur_nest -= self.indent
return '\n' + self.nest() + outputs[1]
def handle_list(self, open_block):
return self._handle_collection(open_block, ('[', ']'))
def handle_xrange(self, open_block):
return self._handle_collection(open_block, ('[', ']'))
def handle_tuple(self, open_block):
return self._handle_collection(open_block, ('(', ')'))
def handle_set(self, open_block):
if version_major == 2:
return self._handle_collection(open_block, ('set([', '])'))
elif version_major == 3:
return self._handle_collection(open_block, ('(', ')'))
def handle_dict(self, open_block):
return self._handle_collection(open_block, ('{', '}'))
def handle_unknown_iterable(self, open_block):
return self._handle_collection(open_block, ("'<", ">'"))
if __name__ == '__main__':
import sys
import json
print pretty_repr(json.load(sys.stdin))
@sharow
Copy link
Author

sharow commented Apr 2, 2013

$ curl "http://maps.google.com/maps/geo?q=東京&output=json&key=ABQIAAAAYtWO2s_klJQZgGk9oArIARTB9suGdOarCoAGySaRlJcFzsJRGxTWO1LlmU6_G0RYEf8P0-y3hppdfQ" | python prettyrepr.py

{
  'Status': {  # ['Status']
    'code': 200,
    'request': 'geocode'
  },
  'Placemark': [  # ['Placemark']
    {  # ['Placemark'][0]
      'Point': {  # ['Placemark'][0]['Point']
        'coordinates': [  # ['Placemark'][0]['Point']['coordinates']
          139.6917064,
          35.6894875,
          0
        ]
      },
      'ExtendedData': {  # ['Placemark'][0]['ExtendedData']
        'LatLonBox': {  # ['Placemark'][0]['ExtendedData']['LatLonBox']
          'west': 139.510574,
          'east': 139.910202,
          'north': 35.817813,
          'south': 35.528873
        }
      },
      'AddressDetails': {  # ['Placemark'][0]['AddressDetails']
        'Country': {  # ['Placemark'][0]['AddressDetails']['Country']
          'CountryName': 'Japan',
          'AdministrativeArea': {  # ['Placemark'][0]['AddressDetails']['Country']['AdministrativeArea']
            'AdministrativeAreaName': 'Tokyo'
          },
          'CountryNameCode': 'JP'
        },
        'Accuracy': 2
      },
      'id': 'p1',
      'address': 'Tokyo, Japan'
    },
    {  # ['Placemark'][1]
      'Point': {  # ['Placemark'][1]['Point']
        'coordinates': [  # ['Placemark'][1]['Point']['coordinates']
          139.766084,
          35.681382,
          0
        ]
      },
      'ExtendedData': {  # ['Placemark'][1]['ExtendedData']
        'LatLonBox': {  # ['Placemark'][1]['ExtendedData']['LatLonBox']
          'west': 139.753973,
          'east': 139.778324,
          'north': 35.69147,
          'south': 35.673559
        }
      },
      'AddressDetails': {  # ['Placemark'][1]['AddressDetails']
        'Country': {  # ['Placemark'][1]['AddressDetails']['Country']
          'CountryName': 'Japan',
          'AdministrativeArea': {  # ['Placemark'][1]['AddressDetails']['Country']['AdministrativeArea']
            'AdministrativeAreaName': 'Tokyo',
            'Locality': {  # ['Placemark'][1]['AddressDetails']['Country']['AdministrativeArea']['Locality']
              'AddressLine': [  # ['Placemark'][1]['AddressDetails']['Country']['AdministrativeArea']['Locality']['AddressLine']
                'Tokyo Station'
              ],
              'LocalityName': 'Chiyoda'
            }
          },
          'CountryNameCode': 'JP'
        },
        'Accuracy': 9
      },
      'id': 'p2',
      'address': 'Tokyo Station, 1丁目-9 Marunouchi, Chiyoda, Tok...'
    }
  ],
  'name': '東京'
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment