pythonでjson又はyamlを色付きでpretty printする


# 概要

コマンドラインから、json またはyamlを色付きでpretty printする。

また、pythonのオブジェクトの一部(basestring, int, long, float, dict, listと、\_\_dict\_\_を持ってるobject)を色付けで表示することができる。

## コマンドラインから

```
# json
$ echo '{ "name": "pp.py", "list": [ 1, 2, 3, "Da!" ] }' | pp.py

#yaml
$ echo '{ name: pp.py, list: [ 1, 2, 3, Da! ] }' | pp.py
```

![pp_cmdline.png](https://qiita-image-store.s3.amazonaws.com/0/38680/65fe3a1a-b91b-7b64-e4d1-bcd45d6b3d9b.png)

## モジュールとして

```
>>> import pp
>>> pp.pprint({ "name": "pp.py", "list": [ 1, 2, 3, "Da!" ] })
{
  list: [ 1, 2, 3, "Da!", ],
  name: "pp.py",
}
```

![pp_module.png](https://qiita-image-store.s3.amazonaws.com/0/38680/6dbe387f-2aa6-cc0b-8270-c8fcf8ba73d0.png)

## loggingに出す場合はpprintf

```
>>> import pp
>>> import logging
>>> logging.basicConfig()
>>> logging.warning( pp.pprintf( object ) )
```

![pp_logging.png](https://qiita-image-store.s3.amazonaws.com/0/38680/8dc1b3ae-cf5a-8e60-b707-06ffdd8f7733.png)

# ソース

```python:pp.py
#!/bin/env python
# -*- coding: utf-8 -*-

class color:
    BLUE = '\033[1;34m'
    BLUEL = '\033[0;34m'
    GREEN = '\033[1;32m'
    GREENL = '\033[0;32m'
    CYAN = '\033[1;36m'
    CYANL = '\033[0;36m'
    RED = '\033[1;31m'
    REDL = '\033[0;31m'
    PURPLE = '\033[1;35m'
    PURPLEL = '\033[0;35m'
    YELLOW = '\033[1;33m'
    BROWN = '\033[0;33m'
    WHITE = '\033[1;37m'
    GRAYL = '\033[0;37m'
    GRAYD = '\033[1;30m'
    BLACK = '\033[0;30m'
    ENDC = '\033[0m'

def pretty_convert(obj):
    if isinstance(obj, (float)):
        return u'{1}{2}{0}'.format(color.ENDC, color.PURPLEL, obj)
    elif isinstance(obj, ( bool )):
        return u'{1}{2}{0}'.format(color.ENDC, color.CYAN, obj)
    elif isinstance(obj, ( int, long, complex )):
        return u'{1}{2}{0}'.format(color.ENDC, color.BLUE, obj)
    elif isinstance(obj, ( unicode )):
        return u'{1}"{0}{2}{3}{0}{1}"{0}'.format(color.ENDC, color.REDL, color.RED, obj)
    elif isinstance(obj, ( str )):
        return u'{1}"{0}{2}{3}{0}{1}"{0}'.format(color.ENDC, color.REDL, color.RED, unicode(obj,'utf-8'))
    elif isinstance(obj, ( dict )):
        return dict((u'{1}{2}:{0}'.format(color.ENDC, color.YELLOW,k), pretty_convert(v)) for k, v in obj.items())
    elif isinstance(obj, (list, tuple)):
        return map(pretty_convert, obj)
    elif isinstance(obj, type(None)):
        return 'None'
    elif hasattr(obj, '__dict__') :
        return dict((u'{1}{2}:{0}'.format(color.ENDC, color.YELLOW,k), pretty_convert(v)) for k, v in obj.__dict__.items())
    else:
        return '<pp: cannot decode>'

def walk_obj(buf, obj, indent_num=2, depth=0, eol='', wrap_len=60, wrap_total=100, lf='\n'):
    indent = ' '*indent_num*depth
    buf.write(u'{0}'.format(eol if not eol else eol+indent) )
    if isinstance(obj, (basestring, int, float, complex)):
        buf.write(u'{0}'.format(obj).replace(lf, '{0}{1} '.format(lf, indent)) )
    elif isinstance(obj, ( dict )):
        if not obj:
            buf.write('{}')
            return
        eol, eol_org = lf, eol
        buf.write('{ ')
        for key in sorted(obj.keys()):
            buf.write(u'{0}{1}{2}{3} '.format(eol, indent, ' '*indent_num, key))
            walk_obj(buf=buf, obj=obj[key], indent_num=indent_num, depth=depth+1, eol='', wrap_len=wrap_len, lf=lf )
            buf.write(',')
        buf.write('{0}}}'.format(eol if not eol else eol+indent) )
        eol=eol_org
    elif isinstance(obj, (list, tuple)):
        if not obj:
            buf.write('[]')
            return
        eol_org, indent_org = eol, indent
        for item in obj:
            if isinstance(item, (list, dict)):
                eol = lf
                break
            else:
                eol = ''
                indent = ''
                continue
        if max(map(len,obj if obj else " " )) > wrap_len or sum(map(len,obj)) > wrap_total:
            eol = lf
        buf.write('[ ')
        for item in obj:
            walk_obj(buf=buf, obj=item, indent_num=indent_num, depth=depth+1, eol=eol, wrap_len=wrap_len, lf=lf )
            buf.write(', ')
        buf.write('{0}]'.format(eol if not eol else eol+indent_org) )
        eol, indent = eol_org, indent_org

def pprint(obj, indent=2, b=None, lf='\n'):
    if not b:
        import sys, codecs
        b = codecs.getwriter('utf_8')(sys.stdout)
    walk_obj(b, pretty_convert(obj), indent_num=indent)
    b.write(lf)

def pprintf(obj, indent=2):
    from StringIO import StringIO
    buf=StringIO()
    walk_obj(buf, pretty_convert(obj), indent_num=indent)
    return buf.getvalue()


if __name__ == '__main__':
  import sys
  import codecs
  import json

  if len(sys.argv) < 2:
     raw_data = codecs.getreader('utf_8')(sys.stdin).read()
  else:
     raw_data = codecs.open(sys.argv[1], 'r', 'utf_8').read()

  indent = 2 if len(sys.argv) <3 else int(sys.argv[2])

  parsed_data = None
  try:
      parsed_data = json.loads(raw_data)
  except ValueError as e1:
      #print e1
      try:
          import yaml
          parsed_data = yaml.load(raw_data)
      except yaml.scanner.ScannerError as e2:
          #print e2
          pass
  if not parsed_data:
      sys.exit(2)

  pprint(parsed_data, indent)
  b = codecs.getwriter('utf_8')(sys.stdout)
```


https://github.com/aminamid/pp