Python Forum
Thread Rating:
  • 2 Vote(s) - 4.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
printobject.py
#1
this is the print_object() function i have been revising, as mentioned in another thread:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function

"""
Module to print objects like dictionaries and lists, nicely.
"""

license = """
Copyright © 2017, by Phil D. Howard - all other rights reserved

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

The author may be contacted using the short first name, middle initial,
and last name, separated by periods, with gmail dot com as the host part
of an email address.
"""

import os,sys,time
debug  = not os.environ.get('nodebug',False)
ex     = BrokenPipeError if sys.version_info.major > 2 else IOError
stderr = sys.stderr

#-----------------------------------------------------------------------
# file          printobject.py
# module        printobject
# import        from printobject import print_object
#-----------------------------------------------------------------------
from sys import argv, stderr, stdout, version_info

ver = version_info.major

if ver < 3:
    bytes = bytearray

if ver > 2:
    unicode = str
    long = int

flush       = stdout.flush
empty_text  = 'EMPTY'
end_of_text = 'end of'
NoneType    = type(None)

#----------------------------------------------------------------------
# function     __sortkey__
# purpose      key map so types are separate
# note         avoid importing decimal by the following cheat.
# cheat        decimal.Decimal is tested by attribute 'exp' existing.
# note         recode this to change mixed type ordering.
#----------------------------------------------------------------------
def __sortkey__(k):
    if isinstance(k,NoneType):  return (16,k)
    if isinstance(k,bool):      return (17,k)
    if isinstance(k,int):       return (32,k)
    if isinstance(k,long):      return (33,k)
    if isinstance(k,float):     return (34,k)
    if isinstance(k,complex):   return (35,k)
    if isinstance(k,str):       return (48,k)
    if isinstance(k,unicode):   return (49,k)
    if isinstance(k,bytearray): return (50,k)
    if isinstance(k,bytes):     return (51,k)
    if 'exp' in dir(k):         return (64,k)

    try:
        return (96,hash(k))

    except TypeError:
        return (99,0)

#----------------------------------------------------------------------
# function      __repr__
# purpose       Handle every exception repr() throws to return a string
#               that represents an issue with repr() gracefully.  Also
#               handle an emtpy or non-string return value similarly.
# usage         just like repr()
#----------------------------------------------------------------------
def __repr__(o):
    try:
        return repr(o)

    except:
        return '<unknown>'

#----------------------------------------------------------------------
# function      print_object
# purpose       Print a Python data object in an easily readable
#               consistent code format.
# note          strings are appended with their length in a splice
#               format (so it is still valid code).
# import        from printobject import print_object
# usage         callable function, in place of repr() and print()
# arguments     1 (...) object to be printed
# options       file= file to output to
#               indent= (int, default=0) how many spaces to indent
#               format to the right
#               name= the name of the object to be printed (a dict
#               key unless ending with '=')
# note          if name option ends with an append '=' then the
#               output code is a variable assignment.
# returns       None
# features      1: output can be used as Python code
#               2: dictionary members are output in key-sorted order
#               3: length of strings and lists are shown
#               4: data element types are shown
#----------------------------------------------------------------------
def print_object( *args, **opts ):
    
    # the object to be printed
    obj = args[0]

    comment = '#'
    popt          = {}

    # set up options 'filename' and 'file'
    close = False
    def_file = stdout
    filename = opts.pop('filename',False)
    if isinstance(filename,str):
        if '.' not in filename:
            filename = filename + '.po'
        try:
            def_file = open( filename, 'w' )
            close = True
        except IOError:
            return None
    file = opts.pop('file',def_file)
    opts['file'] = file
    popt['file'] = file

    flush = opts.pop('flush',False)
    if isinstance(flush,str):
        if flush.lower() in ('no','false'):
            flush = flush.lower() not in ('no','false')
    if isinstance(flush,(int,float)):
        flush = False if flush == 0 else True
    comma = opts.pop('comma',',')
    user_cmnt = opts.pop('comment','')
    depth = opts.pop('depth',0)
    asdict = opts.pop('dict',False)
    indent = opts.pop('indent',0)
    index = opts.pop('index',None)
    name = opts.pop('name','object')
    namewidth = opts.pop('namewidth',False)
    maxdepth = opts.pop('maxdepth',0x7fffffffffffffff)
    if maxdepth < 1:
        print( 'deeper objects not printed', **popt )
        return None
    if isinstance(index,int):
        comment = '#' + str(index)
    if asdict:
        stdout.flush()
        if isinstance(name,(str,unicode,bytearray,bytes,list,tuple)):
            prefix = repr(name) + '[:' + str(len(name)) + ']:'
        else:
            prefix = repr(name) + ':'
    else:
        if depth < 1:
            if isinstance(name,str):
                prefix = name + ' = '
            else:
                prefix = __repr__(name) + ' = '
        else:
            prefix = '  '
    if namewidth:
        prelen = len(prefix)
        if prelen < namewidth:
            prefix += (namewidth-prelen)*' '

    prefix = indent * ' ' + prefix
    prelen = len(prefix)
    spaces = prelen * ' '

    # leftover options might be intended for print()
    for k,v in opts.items():
        popt[k] = v

    # nothing (no args) passed to be printed: print an empty line
    if len(args) < 1:
        print( **popt )
        stdout.flush()
        return None

    opts['depth'] = depth + 1


#----------------------------------------------------------------------
# dictionary:
# print one line for each member, in sorted-key order.
# print both key and value with space padding for vertical line-up.
# call print_object() recursively to format values the same way.
#----------------------------------------------------------------------
    if isinstance(obj,dict):
        if len( obj ) == 0:
            print( prefix+
                   '{},',
                   comment,
                   empty_text,
                   'dict'+
                   user_cmnt,
                   **popt )
        else:
            print( prefix+
                   '{',
                   comment,
                   'dict'+
                   user_cmnt,
                   'len =',
                   len( obj ),
                   **popt )

            # determine the width to use
            w = 0
            for k in obj.keys():
                if 'split' in dir(k): # if k is a string
                    w = max( w, len(__repr__(k))+
                             len(__repr__(len(k)))+5 )
                else:
                    w = max( w, len(__repr__(k))+2 )

            for k,v in sorted(obj.items(),key=__sortkey__):
                print_object( v,
                              name=k,
                              dict=True,
                              indent=prelen+2,
                              namewidth=w,
                              **opts )

            print( spaces + '}' + comma,
                   comment,
                   end_of_text,
                   'dict',
                   'len =',
                   len( obj ),
                   **popt )

#----------------------------------------------------------------------
# set:
# print one line for each member, in sorted-key order
# with space padding for vertical line-up.
#----------------------------------------------------------------------
    elif isinstance(obj,set):
        if len( obj ) == 0:
            print( prefix+
                   'set([]),',
                   comment,
                   empty_text,
                   'set'+
                   user_cmnt,
                   **popt )
        else:
            print( prefix+
                   '{',
                   comment,
                   'set'+
                   user_cmnt,
                   'len =',
                   len( obj ),
                   **popt )

            # determine the width to use
            w = 0
            for k in obj:
                w = max( w ,len(__repr__(k))+5 )

            for m in sorted(obj,key=__sortkey__):
                print( spaces+
                       '  '+
                       __repr__(m)+
                       comma,
                       comment,
                       'set'+
                       user_cmnt,
                       **popt )

            print( spaces+
                   '}'+
                   comma,
                   comment,
                   end_of_text,
                   'set',
                   'len =',
                   len( obj ),
                   **popt )

#----------------------------------------------------------------------
# bytearray:
# print bytes with escape sequence coding for unprintable bytes.
# append slicing code that shows the length as a full slice.
#----------------------------------------------------------------------
    elif isinstance(obj,bytearray):
        if len(obj) < 1:
            print( prefix+
                   "bytearray(b'')[:0]"+
                   comma,
                   comment,
                   empty_text,
                   'bytearray'+
                   user_cmnt,
                   **popt )
        else:
            v = "bytearray(b'"
            for n in obj:
                v += (repr(chr(n)) if ver < 3 else ascii(chr(n)))[1:-1]
            v += "')[:" + str(len(obj)) + ']'
            print( prefix+
                   v+
                   comma,
                   comment,
                   'bytearray'+
                   user_cmnt,
                   **popt )

#----------------------------------------------------------------------
# bytes (in Python3 only):
# print bytes with escape sequence coding for unprintable bytes.
# append slicing code that shows the length as a full slice.
#----------------------------------------------------------------------
    elif ver > 2 and isinstance(obj,bytes):
        if len(obj) < 1:
            print( prefix+
                   "b''[:0]"+
                   comma,
                   comment,
                   empty_text,
                   'bytes'+
                   user_cmnt,
                   **popt )
        else:
            v = "'"
            for n in obj:
                v += ascii(chr(n))[1:-1]
            v += "'[:" + str(len(obj)) + ']'
            print( prefix+
                   v+
                   comma,
                   comment,
                   'bytes'+
                   user_cmnt,
                   **popt )

#----------------------------------------------------------------------
# unicode string:
# print characters with escape sequence coding for unprintable ones.
# append slicing code that shows the length as a full slice.
#----------------------------------------------------------------------
    elif ver < 3 and isinstance(obj,unicode):
        if len(obj) < 1:
            print( prefix+
                   "u''[:0]"+
                   comma,
                   comment,
                   empty_text,
                   'unicode'+
                   user_cmnt,
                   **popt )
        else:
            v = "'"
            for x in obj:
                n = ord(x)
                if n < 256:
                    v += (repr(x) if ver < 3 else ascii(x))[1:-1]
                elif n < 65536:
                    v += '\\u%04x' % (n,)
                else:
                    v += '\\U%08x' % (n,)
            v += "'[:" + str(len(obj)) + ']'
            print( prefix+
                   v+
                   comma,
                   comment,
                   'str'+
                   user_cmnt,
                   **popt )

#----------------------------------------------------------------------
# string:
# print characters with escape sequence coding for unprintable ones.
# append slicing code that shows the length as a full slice.
#----------------------------------------------------------------------
    elif isinstance(obj,str):
        if len(obj) < 1:
            print( prefix+
                   "''[:0]"+
                   comma,
                   comment,
                   empty_text,
                   'str'+
                   user_cmnt,
                   **popt )
        else:
            v = "'"
            for x in obj:
                n = ord(x)
                if n < 256:
                    v += (repr(x) if ver < 3 else ascii(x))[1:-1]
                elif n < 65536:
                    v += '\\u%04x' % (n,)
                else:
                    v += '\\U%08x' % (n,)
            v += "'[:" + str(len(obj)) + ']'
            print( prefix+
                   v+
                   comma,
                   comment,
                   'str'+
                   user_cmnt,
                   **popt )

#----------------------------------------------------------------------
# list or tuple:
# print one line for each member, in the original order.
# append slicing code that shows the length as a full slice
#----------------------------------------------------------------------
    elif isinstance(obj,(list,tuple)):
        betype = '[]list' if isinstance(obj,list) else '()tuple'
        if len( obj ) == 0:
            print( prefix+
                   betype[:2]+
                   comma,
                   comment,
                   empty_text,
                   betype[2:],
                   user_cmnt,
                   **popt )
        else:
            print( prefix+
                   betype[0],
                   comment,
                   betype[2:],
                   'len =',
                   len( obj ),
                   user_cmnt,
                   **popt )
            n = 0
            for v in obj:
                print_object( v,
                              indent=prelen,
                              index=n,
                              **opts )
                n += 1
            print( spaces+
                   betype[1]+
                   '[:'+
                   str(len(obj))+
                   ']'+
                   comma,
                   comment,
                   end_of_text,
                   betype[2:],
                   **popt )

#----------------------------------------------------------------------
# bool or NoneType:
#----------------------------------------------------------------------
    elif isinstance(obj,(bool,NoneType)):
        t = repr(type(obj)).split(' ')[1][1:-2]
        print( prefix+
               repr(obj)+
               comma,
               comment,
               t+
               user_cmnt,
               **popt )

#----------------------------------------------------------------------
# unknown or simple object: print it vaguely on one line
# the output code cannot be used due to this
#----------------------------------------------------------------------
    else:
        t = repr(type(obj)).split(' ')[1][1:-2]
        print( prefix+
               __repr__(obj)+
               comma,
               comment,
               t+
               user_cmnt,
               **popt )

#----------------------------------------------------------------------
# done
#----------------------------------------------------------------------
    if flush:
        file.flush()
    if close:
        file.close()
    return None

#----------------------------------------------------------------------
# test the print_object() function
#----------------------------------------------------------------------
def test_printobject_module( args ):
    from copy import deepcopy
    from subprocess import call

    print( 'from printobject import print_object' )

    d = {
        'zero':0,
        'one':1,
        'two':2,
        'three':3,
        't':True,
        'f':False,
        4:'four',
        5:'five',
        1.0j:(0.0,1.0j),
        1.0+1.0j:(1.0,1.0j),
        }

    x = [
        'this',
        'is',
        'a',
        'nested',
        'list'
        ]

    o = [
        None,
        False,
        True,
        0,
        2**64,
        1.25,
        2.5j,
        'string one',
        'string two',
        d,
        x,
        {},
        set([]),
        [],
        (),
        set([0]),
        ]

    print_object( o, name='first_list' )
    
    return None

#----------------------------------------------------------------------
def main( args ):

    print( '# BEGIN test of print_object()' )
    test_printobject_module( args )
    print( '# END test of print_object()' )

    return 0

#----------------------------------------------------------------------
if __name__ == '__main__':
    if version_info.major < 3:
        BrokenPipeError = IOError
    try:
        result=main(argv)
        stdout.flush()
    except BrokenPipeError:
        result=99
    except KeyboardInterrupt:
        print('')
        result=98
    if result is 0 or result is None or result is True:
        exit(0)
    if result is 1 or result is False:
        exit(1)
    if isinstance(result,str):
        print(result,file=stderr)
        exit(2)
    try:
        exit(int(result))
    except ValueError:
        print(str(result),file=stderr)
        exit(3)
    except TypeError:
        exit(4)
# EOF
**ducks**
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020