From c0d80a827ed40bb92516759380346dc0805e63f5 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 1 Jul 2016 16:30:49 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E9=95=BF=E6=96=87=E6=9C=AC=E7=9A=84=E6=8D=A2=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beeprint/block_helper.py | 21 +++ beeprint/constants.py | 7 +- beeprint/helper.py | 186 +++++++++++++++++++++++ beeprint/models.py | 150 +++++++++++++++++++ beeprint/printer.py | 266 +++++++++++---------------------- beeprint/settings.py | 3 +- beeprint/utils.py | 40 ++++- makefile | 5 + setup.py | 3 + tests/definition.py | 59 +++++--- tests/t.py | 3 +- tests/test_default_beeprint.py | 12 ++ 12 files changed, 545 insertions(+), 210 deletions(-) create mode 100644 beeprint/block_helper.py create mode 100644 beeprint/helper.py create mode 100644 beeprint/models.py diff --git a/beeprint/block_helper.py b/beeprint/block_helper.py new file mode 100644 index 0000000..7758e24 --- /dev/null +++ b/beeprint/block_helper.py @@ -0,0 +1,21 @@ +# -*- coding:utf-8 -*- +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals +from __future__ import division +import inspect +import sys +import types + +from . import constants as C +from .helper import pstr, typeval + + +def pair_block_key(position, key): + if position & C._AS_CLASS_ELEMENT_: + # class method name or attribute name no need to add u or b prefix + key = pstr(key) + else: + key = typeval(None, key) + + return key diff --git a/beeprint/constants.py b/beeprint/constants.py index ef28ab9..0aed047 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -39,6 +39,7 @@ _DL_STATEMENT = 3 # long string -_LS_WRAP_BY_NONE = 0 -_LS_WRAP_BY_TERMINAL = 1 -_LS_WRAP_BY_80_COLUMN = 2 +_TEXT_WRAP_BY_NONE = 0 +_TEXT_WRAP_BY_TERMINAL = 1 +# accompany with S.text_wrap_width argument +_TEXT_WRAP_BY_WIDTH = 2 diff --git a/beeprint/helper.py b/beeprint/helper.py new file mode 100644 index 0000000..3264fbc --- /dev/null +++ b/beeprint/helper.py @@ -0,0 +1,186 @@ +# -*- coding:utf-8 -*- +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals +from __future__ import division +import inspect +import sys +import types +import urwid + +from . import constants as C +from . import settings as S +from .utils import pyv +from .models import StringEncloser +from .terminal_size import get_terminal_size +from kitchen.text import display + + +def typeval(context, v): + try: + if S.united_str_coding_representation: + + st = string_type(v) + ret = u'' + if st == C._ST_UNDEFINED_: + ret = pstr(v) + else: + ret = string_handle(context, v, st) + + else: + ret = u'' + + except Exception as e: + raise + if S.priority_strategy == C._PS_CORRECTNESS_FIRST: + print_exc_plus() + raise e + # S.priority_strategy == C._PS_CONTENT_FIRST: + hx = v.decode("latin-1").encode("hex") + ret = pstr("") + + return ret + +def pstr(s): + '''convert all string to unicode + for unicode is python's built-in coding + ''' + res = u'' + + if isinstance(s, unicode): + res += s + elif isinstance(s, str): + # in python 2/3, it's utf8 + # so decode to unicode + res += s.decode(S.encoding) + else: + res += str(s) # .decode(S.encoding) + + return res + +def long_string_wrapper(ls, how): + if how == C._LS_WRAP_BY_80_COLUMN: + pass + pass + +def tail_symbol(position): + if (position & C._AS_LIST_ELEMENT_ or + position & C._AS_DICT_ELEMENT_ or + position & C._AS_CLASS_ELEMENT_ or + position & C._AS_TUPLE_ELEMENT_): + tail = u',' + else: + tail = u'' + return tail + +def string_type(s): + if pyv == 2: + # in py2, string literal is both instance of str and bytes + # a literal string is str (i.e: coding encoded, eg: utf8) + # a u-prefixed string is unicode + if isinstance(s, unicode): + return C._ST_UNICODE_ + elif isinstance(s, str): # same as isinstance(v, bytes) + return C._ST_LITERAL_ | C._ST_BYTES_ + else: + # in py3, + # a literal string is str (i.e: unicode encoded) + # a u-prefixed string is str + # a utf8 string is bytes + if isinstance(s, bytes): + return C._ST_BYTES_ + elif isinstance(s, str): + return C._ST_LITERAL_ | C._ST_UNICODE_ + + return C._ST_UNDEFINED_ + + +def string_handle(context, s, st): + if st & C._ST_BYTES_: + s = s.decode(S.encoding) + + s = s.replace(u'\n', u'\\n') + s = s.replace(u'\r', u'\\r') + + left_margin = 0 + if context is not None: + left_filling = u''.join([ + context.leading, + context.key_expr, + context.sep_expr, + ]) + left_margin = calc_width(left_filling) + + left_margin += 1 + + str_encloser = enclose_string(s, st) + left_margin += calc_width(str_encloser.string_type_mark) + + # t = urwid.Text(str_encloser.body) + width = 0 + if S.text_wrap_method == C._TEXT_WRAP_BY_TERMINAL: + width = get_terminal_size()[0] - left_margin + elif S.text_wrap_method == C._TEXT_WRAP_BY_WIDTH: + width = S.text_wrap_width - left_margin + else: + width = 0 + + if width > 0: + # seg_list = t.render((width,)).text + seg_list = wrap_string(str_encloser.body, width) + # print(seg_list) + indent_char_width = calc_width(S.leading) + for i in xrange(1, len(seg_list)): + seg_list[i] = int(left_margin/indent_char_width)*S.leading + seg_list[i] + s = '\n'.join(seg_list) + + str_encloser.body = s + + return str(str_encloser) + +def enclose_string(s, st): + str_encloser = StringEncloser(s) + + if st & C._ST_UNICODE_: + if S.str_display_not_prefix_u: + pass + else: + str_encloser.set_string_type_mark(u'u') + elif st & C._ST_BYTES_: + # in py3, printed string will enclose with b'' + if S.str_display_not_prefix_b: + pass + else: + str_encloser.set_string_type_mark(u'b') + + return str_encloser + +def calc_width(s): + return urwid.str_util.calc_width(s, 0, len(s)) + # return display.textual_width(s) + +def wrap_string(s, width): + # return display.wrap(s, width=width) + t = urwid.Text(s) + seg_list = t.render((width,)).text + seg_list = [seg.rstrip() for seg in seg_list] + return seg_list + +def is_extendable(obj): + '判断obj是否可以展开' + return isinstance(obj, dict) or hasattr(obj, '__dict__') or isinstance(obj, (tuple, list, types.FrameType)) + +def too_long(leadCnt, position, obj): + indent_str = leadCnt*S.leading + + choas = u'' + # assumpts its the value of list + if position & C._AS_VALUE_: + choas += '[' + choas += '\'' + + terminal_x = get_terminal_size()[0] + body = typeval(None, obj) + whole_line_x = len(indent_str) + len(body) + + return terminal_x - whole_line_x <= 0 diff --git a/beeprint/models.py b/beeprint/models.py new file mode 100644 index 0000000..643ad02 --- /dev/null +++ b/beeprint/models.py @@ -0,0 +1,150 @@ +# -*- coding:utf-8 -*- +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals +from __future__ import division + +from . import utils +from . import settings as S + +import urwid + + +class Linizer(object): + """ make a line of string to print """ + + left_quoter = "'" + right_quoter = "'" + + seperator = ":" + after_seperator = " " + + def __init__(self, position=None, lead_cnt=None, key=None, value=None): + + self.position = position + self.lead_cnt = lead_cnt + self.key = key + self.value = value + + self.value_left_brace = None + self.value_right_brace = None + + def __str__(self): + joint = [ + S.leading * self.lead_cnt, + ] + if self.key is not None: + joint += [ + # self.left_quoter, + self.key, + # self.right_quoter, + + self.seperator, + ] + + if self.value is not None: + joint += [ + self.after_seperator, + ] + + # value left brace + if self.value_left_brace is not None: + joint += [self.value_left_brace] + + # value + if self.value is not None: + joint += [self.value] + + # value right brace + if self.value_right_brace is not None: + joint += [self.value_right_brace] + + # tail + if self.value is not None: + joint += [self.tail] + + # new line + joint += [ + '\n', + ] + + return ''.join(joint) + + @property + def tail(self): + return utils.tail_symbol(self.position) + + +class Context(object): + + left_quoter = "'" + right_quoter = "'" + + seperator = ":" + after_seperator = " " + + def __init__(self, indent_char=None, position=None, lead_cnt=None, key=None, value=None): + + self.indent_char = indent_char + self.position = position + self.lead_cnt = lead_cnt + self.key = key + self.value = value + + self.value_left_brace = None + self.value_right_brace = None + + @property + def leading(self): + return self.indent_char*self.lead_cnt + + @property + def key_expr(self): + if self.key is None: + return '' + from .block_helper import pair_block_key + return pair_block_key(self.position, self.key) + + @property + def sep_expr(self): + if self.key is None: + return '' + return self.seperator + u' ' + + @property + def val_expr(self): + raise Exception("not yet implement") + + def calc_wrapped_line_indent(self): + leading = u''.join([ + self.leading, + self.key_expr, + self.sep_expr, + ]) + indent = urwid.str_util.calc_width(leading, 0, len(leading)) + return indent + +class StringEncloser(object): + + def __init__(self, body, lqm=u'\'', rqm=u'\''): + self.string_type_mark = u'' + self.body = body + self.left_quotation_mark = lqm + self.right_quotation_mark = rqm + + def set_string_type_mark(self, mark): + self.string_type_mark = mark + + def __str__(self): + return ''.join([ + self.string_type_mark, + self.left_quotation_mark, + self.body, + self.right_quotation_mark, + ]) + +class Block(object): + parent = None + position = None + indent_cnt = None + subject = None diff --git a/beeprint/printer.py b/beeprint/printer.py index e48f9e2..e4e51b4 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -9,11 +9,13 @@ import inspect from .utils import pyv +import urwid if pyv == 2: # avoid throw [UnicodeEncodeError: 'ascii' codec can't encode characters] # exceptions, without these lines, the sys.getdefaultencoding() returns ascii from imp import reload + reload(sys) sys.setdefaultencoding('utf-8') else: @@ -21,8 +23,63 @@ from . import settings as S from . import constants as C +from . import utils +from .utils import print_exc_plus +from .helper import typeval, pstr, tail_symbol, is_extendable, too_long from .debug_kit import debug +from . import models +from .block_helper import pair_block_key + + + +def beeprint(o, output=True): + """print data beautifully + + >>> beeprint(1) + 1 + + >>> beeprint(1.1) + 1.1 + + >>> beeprint(-1) + -1 + + >>> beeprint(-1.1) + -1.1 + + >>> beeprint("plain string") + 'plain string' + + >>> beeprint(u'unicode string') + 'unicode string' + >>> beeprint(u'utf8 string'.encode('utf-8')) + 'utf8 string' + + >>> beeprint(u'gb2312 string'.encode('gb2312')) + 'gb2312 string' + + >>> beeprint(u'\\\\') + '\\' + + >>> beeprint(u'\\\\'.encode("utf8")) + '\\' + """ + res = build_single_block(o, 0) + if output and not S.write_to_buffer_when_execute: + try: + print(res, end='') + except Exception as e: + print_exc_plus() + if type(e) is UnicodeEncodeError: + # UnicodeEncodeError: 'ascii' codec can't encode characters in + # position 35-36: ordinal not in range(128) + print(sys.getdefaultencoding()) + print('res value type:', type(res)) + else: + print('exception type :', type(e)) + else: + return res def object_attr_default_filter(obj, name, val): '过滤不需要的对象属性' @@ -59,91 +116,11 @@ def _b(s): S.buffer_handler.flush() return s -def pstr(s): - '''convert all string to unicode - for unicode is python's built-in coding - ''' - res = u'' - - if isinstance(s, unicode): - res += s - elif isinstance(s, str): - # in python 2/3, it's utf8 - # so decode to unicode - res += s.decode(S.encoding) - else: - res += str(s)#.decode(S.encoding) - - return res - -def beeprint(o, output=True): - - res = build_single_block(o, 0) - if output and not S.write_to_buffer_when_execute: - try: - print(res, end='') - except Exception as e: - print_exc_plus() - if type(e) is UnicodeEncodeError: - # UnicodeEncodeError: 'ascii' codec can't encode characters in - # position 35-36: ordinal not in range(128) - print(sys.getdefaultencoding()) - print('res value type:', type(res)) - else: - print('exception type :', type(e)) - else: - return res - - -def print_exc_plus(): - """ - Print the usual traceback information, followed by a listing of all the - local variables in each frame. - """ - tb = sys.exc_info()[2] - while 1: - if not tb.tb_next: - break - tb = tb.tb_next - stack = [] - f = tb.tb_frame - while f: - stack.append(f) - f = f.f_back - stack.reverse() - traceback.print_exc() - print("Locals by frame, innermost last") - for frame in stack: - print() - print("Frame %s in %s at line %s" % (frame.f_code.co_name, - frame.f_code.co_filename, - frame.f_lineno)) - for key, value in frame.f_locals.items(): - print("\t%20s = " % key, end='') - #We have to be careful not to cause a new error in our error - #printer! Calling str() on an unknown object could cause an - #error we don't want. - try: - print(value) - except: - print("") - - -def tail_symbol(position): - if (position & C._AS_LIST_ELEMENT_ or - position & C._AS_DICT_ELEMENT_ or - position & C._AS_CLASS_ELEMENT_ or - position & C._AS_TUPLE_ELEMENT_): - tail = u',' - else: - tail = u'' - return tail - def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): '遍历对象,判断对象内成员的类型,然后调用对应的 build_*_block() 处理' - debug(C._DL_FUNC_, leadCnt, + debug(C._DL_FUNC_, leadCnt, ('obj:%s leadCnt:%s position:%d' \ % (obj, leadCnt, position))) @@ -175,37 +152,29 @@ def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): ret += build_class_block(obj, leadCnt, position) else: debug(C._DL_STATEMENT, leadCnt, 'is simple type') - ret += _b(leadCnt * S.leading + typeval(obj) + pstr(tail + '\n')) + context = models.Context(indent_char=S.leading, position=position, lead_cnt=leadCnt) + ret += _b(leadCnt * S.leading + typeval(context, obj) + pstr(tail + '\n')) return ret - -def is_extendable(obj): - '判断obj是否可以展开' - return isinstance(obj, dict) or hasattr(obj, '__dict__') or isinstance(obj, (tuple, list, types.FrameType)) - - def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): - debug(C._DL_FUNC_, leadCnt, + debug(C._DL_FUNC_, leadCnt, ('key:%s, leadCnt:%s, position:%s' \ % (name, leadCnt, position))) ret = pstr('') tail = tail_symbol(position) - if position & C._AS_CLASS_ELEMENT_: - # class method name or attribute name no need to add u or b prefix - name = pstr(name) - else: - name = typeval(name) + name = pair_block_key(position, name) + ret += _b(S.leading * leadCnt + name + ':') if is_extendable(val) and S.max_depth > leadCnt: # value need to be dispalyed on new line # including: # class type & class instance # function type - if S.newline or (is_newline_obj(val) & - position & C._AS_ELEMENT_): + if S.newline or (is_newline_obj(val) & + position & C._AS_ELEMENT_): ret += _b(pstr('\n')) leadCnt = leadCnt + 1 position |= C._AS_ELEMENT_ @@ -220,15 +189,15 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): if S.max_depth <= leadCnt: ret += _b(pstr(" %s\n" % tail)) else: - ret += _b(pstr(" ") + typeval(val) + pstr(tail + '\n')) + context = models.Context(indent_char=S.leading, + position=position, + lead_cnt=leadCnt, + key=name) + ret += _b(pstr(" ") + typeval(context, val) + pstr(tail + '\n')) return ret -def build_string_block(s, leadCnt=0): - return _b(leadCnt * S.leading + typeval(s) + pstr('\n')) - - def build_list_block(o, leadCnt=0, position=C._AS_VALUE_): ret = pstr('') @@ -236,9 +205,9 @@ def build_list_block(o, leadCnt=0, position=C._AS_VALUE_): '所有元素显示在同一行' if S.list_in_line: - _f = map(lambda e: not is_extendable(e), o) + _f = map(lambda e: not (is_extendable(e) or too_long(leadCnt, position, e)), o) if all(_f): - _o = map(lambda e: typeval(e), o) + _o = map(lambda e: typeval(None, e), o) if S.newline or position & C._AS_ELEMENT_: ret += pstr(S.leading * leadCnt) ret += pstr("[") + ', '.join(_o) + pstr("]%s\n" % tail) @@ -269,7 +238,7 @@ def build_tuple_block(o, leadCnt=0, position=C._AS_VALUE_): if S.tuple_in_line: _f = map(lambda e: not is_extendable(e), o) if all(_f): - _o = map(lambda e: typeval(e), o) + _o = map(lambda e: typeval(None, e), o) if S.newline or position & C._AS_ELEMENT_: ret += pstr(S.leading * leadCnt) ret += _b(pstr("(") + ', '.join(_o) + ')%s\n' % tail) @@ -307,8 +276,8 @@ def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): # v = o[k] if dict_key_filter(o, k, v): continue - #ret += S.leading*(leadCnt + 1) + typeval(k) + pstr(": ") - #ret += build_single_block(v, leadCnt+1) + # ret += S.leading*(leadCnt + 1) + typeval(k) + pstr(": ") + # ret += build_single_block(v, leadCnt+1) ret += build_pair_block(k, v, leadCnt + 1, C._AS_DICT_ELEMENT_) # } @@ -318,8 +287,7 @@ def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): - - debug(C._DL_FUNC_, leadCnt, + debug(C._DL_FUNC_, leadCnt, ('obj:%s leadCnt:%s position:%d' \ % (o, leadCnt, position))) ret = pstr('') @@ -346,7 +314,12 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): ret += _b(_leading + pstr('method(%s):' % o.__name__) + pstr('\n')) else: '本身就是类,不是对象' - ret += _b(_leading + pstr('class(%s):' % o.__name__) + pstr('\n')) + try: + ret += _b(_leading + pstr('class(%s):' % o.__name__) + pstr('\n')) + except: + print(inspect.isclass(o)) + print(o, dir(o)) + raise # body props = dir(o) @@ -371,10 +344,10 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): else: position = C._AS_CLASS_ELEMENT_ - #'忽略掉 以__开头的成员、自引用成员、函数成员' - ret += build_pair_block(attr, - val, - leadCnt + 1, + # '忽略掉 以__开头的成员、自引用成员、函数成员' + ret += build_pair_block(attr, + val, + leadCnt + 1, position | C._AS_CLASS_ELEMENT_) # } @@ -389,64 +362,6 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): return ret -def typeval(v): - try: - if S.united_str_coding_representation: - st = string_type(v) - ret = u'' - if st & C._ST_UNICODE_ != 0: - if S.str_display_not_prefix_u: - ret = u"'" + v + u"'" - else: - ret = u"u'" + v + u"'" - elif st & C._ST_BYTES_ != 0: - # in py3, printed string will enclose with b'' - # ret = pstr(v) - if S.str_display_not_prefix_b: - ret = u"'" + v.decode(S.encoding) + u"'" - else: - ret = u"b'" + v.decode(S.encoding) + u"'" - else: - ret = pstr(v) - - ret = ret.replace(u'\n', u'\\n') - ret = ret.replace(u'\r', u'\\r') - else: - ret = u'' - - except Exception as e: - if S.priority_strategy == C._PS_CORRECTNESS_FIRST: - print_exc_plus() - raise e - # S.priority_strategy == C._PS_CONTENT_FIRST: - hx = v.decode("latin-1").encode("hex") - ret = pstr("") - - return ret - - -def string_type(s): - - if pyv == 2: - # in py2, string literal is both instance of str and bytes - # a literal string is str (i.e: coding encoded, eg: utf8) - # a u-prefixed string is unicode - if isinstance(s, unicode): - return C._ST_UNICODE_ - elif isinstance(s, str): # same as isinstance(v, bytes) - return C._ST_LITERAL_ | C._ST_BYTES_ - else: - # in py3, - # a literal string is str (i.e: unicode encoded) - # a u-prefixed string is str - # a utf8 string is bytes - if isinstance(s, bytes): - return C._ST_BYTES_ - elif isinstance(s, str): - return C._ST_LITERAL_ | C._ST_UNICODE_ - - return C._ST_UNDEFINED_ - def is_newline_obj(o): if hasattr(o, '__module__'): @@ -474,11 +389,12 @@ def is_class_instance(o): # instance of old-style class and of new-style class # method of instance of both class # function - o.__module__ - if (inspect.isclass(o) - or inspect.isfunction(o) - or inspect.ismethod(o)): + # o.__module__ in python 3.5 some instance has no this attribute + + if (inspect.isclass(o) + or inspect.isfunction(o) + or inspect.ismethod(o)): return False return True except: diff --git a/beeprint/settings.py b/beeprint/settings.py index b1abece..63a0ea2 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -52,4 +52,5 @@ element_display_last_with_comma = True # >> long string control -long_str_wrap_method = C._LS_WRAP_BY_TERMINAL +text_wrap_method = C._TEXT_WRAP_BY_TERMINAL +text_wrap_width = 80 diff --git a/beeprint/utils.py b/beeprint/utils.py index 5604e2a..fbb91f5 100644 --- a/beeprint/utils.py +++ b/beeprint/utils.py @@ -7,8 +7,6 @@ import sys import types -from . import constants as C - if sys.version_info < (3, 0): pyv = 2 @@ -35,7 +33,37 @@ def is_pan_function(name, val): """ return inspect.isfunction(val) or inspect.ismethod(val) -def long_string_wrapper(ls, how): - if how == C._LS_WRAP_BY_80_COLUMN: - pass - pass +def print_exc_plus(): + """ + Print the usual traceback information, followed by a listing of all the + local variables in each frame. + """ + tb = sys.exc_info()[2] + while 1: + if not tb.tb_next: + break + tb = tb.tb_next + stack = [] + f = tb.tb_frame + while f: + stack.append(f) + f = f.f_back + stack.reverse() + traceback.print_exc() + print("Locals by frame, innermost last") + for frame in stack: + print() + print("Frame %s in %s at line %s" % (frame.f_code.co_name, + frame.f_code.co_filename, + frame.f_lineno)) + for key, value in frame.f_locals.items(): + print("\t%20s = " % key, end='') + # We have to be careful not to cause a new error in our error + # printer! Calling str() on an unknown object could cause an + # error we don't want. + try: + print(value) + except: + print("") + + diff --git a/makefile b/makefile index 00370c5..b69c306 100644 --- a/makefile +++ b/makefile @@ -19,3 +19,8 @@ test27: test35: python3.5 -m unittest discover tests || true + +vtest: + python3.5 tests/test_default_beeprint.py || true + python2.7 tests/test_default_beeprint.py || true + diff --git a/setup.py b/setup.py index 1301a60..edf71ff 100644 --- a/setup.py +++ b/setup.py @@ -22,4 +22,7 @@ "Topic :: Software Development", "Topic :: Utilities", ], + install_requires=[ + 'urwid', + ], ) diff --git a/tests/definition.py b/tests/definition.py index 0ba554b..86a8018 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -18,30 +18,6 @@ def mth():pass inst_of_normal_class_old_style = NormalClassOldStyle() inst_of_normal_class_new_style = NormalClassNewStyle() -values = [ - 1, - 1.1, - "s", - u"us", - "a中文", - u"a中文", - [1], - (1,2), - EmptyFunc, - EmptyClassOldStyle, - EmptyClassNewStyle, - NormalClassOldStyle, - NormalClassNewStyle, - inst_of_normal_class_old_style, - inst_of_normal_class_new_style, - inst_of_normal_class_old_style.mth, - inst_of_normal_class_new_style.mth, - { - 'key': [], - u'key2': {}, - }, -] - long_text_en = """ The sky and the earth were at first one blurred1 entity2 like an egg. Pangu was born into it. @@ -67,3 +43,38 @@ def mth():pass {"english version": long_text_en}, {"simplify chinese versino": long_text_cn}, ] + +out_of_range = [ + [1, [2.1, ['2.2.1', ['2.2.2.1']]]], + { + 1: { + 1.1: { + '1.1.1': "value", + }, + }, + }, +] + +values = [ + 1, + 1.1, + "s", + u"us", + "a中文", + u"a中文", + [1], + (1,2), + EmptyFunc, + EmptyClassOldStyle, + EmptyClassNewStyle, + NormalClassOldStyle, + NormalClassNewStyle, + inst_of_normal_class_old_style, + inst_of_normal_class_new_style, + inst_of_normal_class_old_style.mth, + inst_of_normal_class_new_style.mth, + { + 'key': [], + u'key2': {}, + }, +] diff --git a/tests/t.py b/tests/t.py index 860ab51..8387490 100644 --- a/tests/t.py +++ b/tests/t.py @@ -97,9 +97,10 @@ def main(): # S.str_display_not_prefix_u = False # S.str_display_not_prefix_b = False + # S.max_depth = 3 + pp(df.values) pp(df.long_text_in_dict) pp(df.long_text_in_list) - # pp([inst_of_normal_class_old_style.mth, inst_of_normal_class_new_style.mth]) return for i in range(1, len(sys.argv)): diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index 8d7826a..082c62a 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -82,6 +82,18 @@ def test_complicate_data(self): self.assertEqual(res == ans or res == ans2, True) # self.assertEqual(res, ans) + ''' + def test_out_of_range(self): + ans = u"" + data_path = os.path.join(CUR_SCRIPT_PATH, + 'data/out_of_range.txt') + with codecs.open(data_path, encoding="utf8") as fp: + ans = fp.read() + + self.assertEqual(beeprint(df.out_of_range, output=False), ans) + ''' + + def test_long_text(self): pass # res = beeprint(long_text_en, output=False) From 06c2a859dd5847a81f4705d3b53acdd37a0b70ef Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 1 Jul 2016 17:01:03 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E4=BD=BF=E6=8D=A2=E8=A1=8C=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E5=B7=A6=E8=BE=B9=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beeprint/helper.py | 9 ++++++--- beeprint/printer.py | 4 ++-- tests/definition.py | 6 +++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/beeprint/helper.py b/beeprint/helper.py index 3264fbc..2f930a2 100644 --- a/beeprint/helper.py +++ b/beeprint/helper.py @@ -13,7 +13,7 @@ from .utils import pyv from .models import StringEncloser from .terminal_size import get_terminal_size -from kitchen.text import display +# from kitchen.text import display def typeval(context, v): @@ -128,10 +128,13 @@ def string_handle(context, s, st): if width > 0: # seg_list = t.render((width,)).text seg_list = wrap_string(str_encloser.body, width) - # print(seg_list) indent_char_width = calc_width(S.leading) for i in xrange(1, len(seg_list)): - seg_list[i] = int(left_margin/indent_char_width)*S.leading + seg_list[i] + seg_list[i] = ''.join([ + left_margin//indent_char_width*S.leading, + left_margin%2*u' ', + seg_list[i], + ]) s = '\n'.join(seg_list) str_encloser.body = s diff --git a/beeprint/printer.py b/beeprint/printer.py index e4e51b4..005e1f4 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -165,9 +165,9 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): tail = tail_symbol(position) - name = pair_block_key(position, name) + key = pair_block_key(position, name) - ret += _b(S.leading * leadCnt + name + ':') + ret += _b(S.leading * leadCnt + key + ':') if is_extendable(val) and S.max_depth > leadCnt: # value need to be dispalyed on new line # including: diff --git a/tests/definition.py b/tests/definition.py index 86a8018..ebaa634 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -19,11 +19,11 @@ def mth():pass inst_of_normal_class_new_style = NormalClassNewStyle() long_text_en = """ -The sky and the earth were at first one blurred1 entity2 like an egg. Pangu was born into it. +The sky and the earth were at first one blurred entity like an egg. Pangu was born into it. -The separation of the sky and the earth took eighteen thousand years-the yang which was light and pure rose to become the sky, and the yin which was heavy and murky3(朦胧的) sank to form the earth. Between them was Pangu, who went through nine changes every day, his wisdom greater than that of the sky and his ability greater than that of the earth. Every day the sky rose ten feet higher, the earth became ten feet thicker, and Pangu grew ten feet taller. +The separation of the sky and the earth took eighteen thousand years-the yang which was light and pure rose to become the sky, and the yin which was heavy and murky(朦胧的) sank to form the earth. Between them was Pangu, who went through nine changes every day, his wisdom greater than that of the sky and his ability greater than that of the earth. Every day the sky rose ten feet higher, the earth became ten feet thicker, and Pangu grew ten feet taller. -Another eighteen thousand years passed, and there was an extremely high sky, an extremely thick earth, and an extremely tall Pangu. After Pangu died, his head turned into the Five Sacred Mountains (Mount4 Tai, Mount Heng, Mount Hua, Mount Heng, Mount Song), his eyes turned into the moon and the sun, his blood changed into water in river and sea, his hair into grass. +Another eighteen thousand years passed, and there was an extremely high sky, an extremely thick earth, and an extremely tall Pangu. After Pangu died, his head turned into the Five Sacred Mountains (Mount Tai, Mount Heng, Mount Hua, Mount Heng, Mount Song), his eyes turned into the moon and the sun, his blood changed into water in river and sea, his hair into grass. In all, the universe and Pangu combine in one. """ From f7d4be9505868b442a411bf41e1fbd8e34af8bb7 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Sat, 9 Jul 2016 14:52:55 +0800 Subject: [PATCH 3/8] restructure: convert function to class --- beeprint/constants.py | 8 +- beeprint/debug_kit.py | 4 +- beeprint/helper.py | 40 +++- beeprint/models.py | 435 +++++++++++++++++++++++++++++++++++++++++- beeprint/printer.py | 325 +------------------------------ beeprint/utils.py | 22 +++ tests/definition.py | 5 +- tests/t.py | 2 +- 8 files changed, 505 insertions(+), 336 deletions(-) diff --git a/beeprint/constants.py b/beeprint/constants.py index 0aed047..15356ae 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -17,15 +17,15 @@ # compares to _AS_ELEMENT_, a value is a component of an element # it has not leading spaces except a span between a key # it belongs to a key -_AS_VALUE_ = 2 +_AS_VALUE_ = 1 << 1 # when display, these elements need comma between each others # it must has a parent block # eg: [1, 2], {'key1': 'val1', 'key2': 'val2'}, (1, 2) _AS_LIST_ELEMENT_ = \ - _AS_TUPLE_ELEMENT_ = 4 + _AS_TUPLE_ELEMENT_ = 1 << 2 -_AS_DICT_ELEMENT_ = 8 -_AS_CLASS_ELEMENT_ = 16 +_AS_DICT_ELEMENT_ = 1 << 3 +_AS_CLASS_ELEMENT_ = 1 << 4 # string type _ST_LITERAL_ = 1 # string literal depends on script's coding diff --git a/beeprint/debug_kit.py b/beeprint/debug_kit.py index 0d517ff..9786a55 100644 --- a/beeprint/debug_kit.py +++ b/beeprint/debug_kit.py @@ -19,10 +19,12 @@ def add_leading(depth, text): def debug(level, depth, text): if S.debug_level >= level: frame_list = inspect.stack() + frame_obj = frame_list[1][0] + class_name = frame_obj.f_locals['self'].__class__.__name__ caller_name = frame_list[1][3] depth = len(frame_list) - 4 if level == C._DL_FUNC_: depth -= 1 - text = caller_name + ': ' + text + text = class_name + '.' + caller_name + ': ' + text text = add_leading(depth, text) print(text) diff --git a/beeprint/helper.py b/beeprint/helper.py index 2f930a2..3749f5e 100644 --- a/beeprint/helper.py +++ b/beeprint/helper.py @@ -11,7 +11,6 @@ from . import constants as C from . import settings as S from .utils import pyv -from .models import StringEncloser from .terminal_size import get_terminal_size # from kitchen.text import display @@ -64,6 +63,11 @@ def long_string_wrapper(ls, how): pass def tail_symbol(position): + """calculate the tail of block + newline character does not include here because + when your calculate the length of whole line you only need to be + care about printable characters + """ if (position & C._AS_LIST_ELEMENT_ or position & C._AS_DICT_ELEMENT_ or position & C._AS_CLASS_ELEMENT_ or @@ -142,6 +146,7 @@ def string_handle(context, s, st): return str(str_encloser) def enclose_string(s, st): + from .models import StringEncloser str_encloser = StringEncloser(s) if st & C._ST_UNICODE_: @@ -187,3 +192,36 @@ def too_long(leadCnt, position, obj): whole_line_x = len(indent_str) + len(body) return terminal_x - whole_line_x <= 0 + +def object_attr_default_filter(obj, name, val): + '过滤不需要的对象属性' + + for propLeading in S.prop_leading_filters: + if name.startswith(propLeading): + return True + + for prop in S.prop_filters: + # filter is a string + if isinstance(prop, str) or isinstance(prop, unicode): + if name == prop: + return True + # filter is a type + # if type(prop) == types.TypeType: + if isinstance(prop, type): + if type(val) == prop: + return True + # filter is callable + elif hasattr(prop, '__call__'): + if prop(name, val): + return True + + return False + +def dict_key_filter(obj, name, val): + return False + +def _b(s): + if S.write_to_buffer_when_execute: + S.buffer_handler.write(s) + S.buffer_handler.flush() + return s diff --git a/beeprint/models.py b/beeprint/models.py index 643ad02..a635f0b 100644 --- a/beeprint/models.py +++ b/beeprint/models.py @@ -4,10 +4,19 @@ from __future__ import unicode_literals from __future__ import division -from . import utils -from . import settings as S +from collections import OrderedDict import urwid +import inspect + +from . import utils +from . import settings as S +from . import constants as C +from .debug_kit import debug +from .utils import is_newline_obj, is_class_instance +from .helper import object_attr_default_filter, dict_key_filter, tail_symbol, _b +from .helper import typeval, pstr, tail_symbol, is_extendable, too_long +from .block_helper import pair_block_key class Linizer(object): @@ -102,7 +111,6 @@ def leading(self): def key_expr(self): if self.key is None: return '' - from .block_helper import pair_block_key return pair_block_key(self.position, self.key) @property @@ -145,6 +153,423 @@ def __str__(self): class Block(object): parent = None - position = None - indent_cnt = None + position = 0 + indent_cnt = 0 subject = None + + def __init__(self, subj, parent=None, position=C._AS_ELEMENT_, indent=0): + self.subject = subj + self.parent = parent + self.position = position + self.indent_cnt = indent + + def __str__(self): + return self.build_block() + + def get_block_ending(self): + ending = u'\n' + if self.position & C._AS_VALUE_: + ending = u'' + return ending + + def get_line_ending(self): + """calculate the ending of line + newline character does not include here because + when your calculate the length of whole line you only need to be + care about printable characters + """ + position = self.position + tail = u'' + if position & C._AS_VALUE_: + tail = u'' + elif (position & C._AS_LIST_ELEMENT_ or + position & C._AS_DICT_ELEMENT_ or + position & C._AS_CLASS_ELEMENT_ or + position & C._AS_TUPLE_ELEMENT_): + tail = u',' + + if position & C._AS_CLASS_ELEMENT_: + elements = self.parent.get_elements() + if elements.index(self.subject) == len(elements)-1: + tail = u'' + + return tail + + def get_elements(self): + raise Exception("%s does not implement this method" % + self.__class__) + + def build_block(self): + """遍历对象,判断对象内成员的类型,然后构造对应的 *Block""" + + leadCnt = self.indent_cnt + obj = self.subject + position = self.position + + debug(C._DL_FUNC_, + leadCnt, + ('obj:{} leadCnt:{} position:{:b}'.format( + obj, leadCnt, position))) + + ret = pstr('') + + tail = self.get_line_ending() + + if S.max_depth < leadCnt: + if S.newline or position & C._AS_ELEMENT_: + ret = pstr(leadCnt * S.leading) + pstr("\n") + else: + ret = pstr(" ") + pstr("\n") + if position & C._AS_LIST_ELEMENT_: + ret = ret[:-1] + pstr(tail + "\n") + return _b(ret) + + if isinstance(obj, dict): + debug(C._DL_STATEMENT, leadCnt, 'is dict') + ret += str(DictBlock(obj, self, position=position, indent=leadCnt)) + elif isinstance(obj, list): + debug(C._DL_STATEMENT, leadCnt, 'is list') + ret += str(ListBlock(obj, self, position=position, indent=leadCnt)) + elif isinstance(obj, tuple): + debug(C._DL_STATEMENT, leadCnt, 'is tuple') + ret += str(TupleBlock(obj, self, position=position, indent=leadCnt)) + elif is_extendable(obj): + debug(C._DL_STATEMENT, leadCnt, 'is extendable') + ret += str(ClassBlock(obj, self, position=position, indent=leadCnt)) + else: + debug(C._DL_STATEMENT, leadCnt, 'is simple type') + context = Context(indent_char=S.leading, position=position, lead_cnt=leadCnt) + ret += _b(leadCnt * S.leading + typeval(context, obj) + pstr(tail + '\n')) + + return ret + +class ClassBlock(Block): + + props = None + + def get_elements(self): + o = self.subject + props = [] + for attr_name in dir(o): + if attr_name == '__abstractmethods__': + continue + + try: + attr = getattr(o, attr_name) + except Exception as e: + continue + + if object_attr_default_filter(o, attr_name, attr): + continue + + props.append((attr_name, attr)) + + return PositionDict(props) + + def get_line_ending(self): + """A class block would have many nested elements, + it would not know how its children respresent. + for example: + + Example One: + >>> class(A): + ... class(B), + + Example Two: + >>> class(A): + ... class(B): + ... prop: val + + As above, class(A) does not know and **should not know** whether class(B) has children + + So, the block of class(A) only need to do is just to add a newline to finish itself. + """ + return u'' + + def get_block_ending(self): + return u'' + + def build_block(self): + leadCnt = self.indent_cnt + o = self.subject + position = self.position + + debug(C._DL_FUNC_, leadCnt, + ('obj:{} leadCnt:{} position:{:b}'.format( + o, leadCnt, position))) + + ret = pstr('') + + tail = self.get_line_ending() + ending = self.get_block_ending() + + # { + _leading = pstr('') + if position & C._AS_ELEMENT_: + _leading += S.leading * leadCnt + # elif position & C._AS_DICT_ELEMENT_: + # _leading += pstr(' ') + elif position & C._AS_VALUE_: + _leading += pstr('') + + if is_class_instance(o): + ret += _b(_leading + pstr('instance(%s):' % + o.__class__.__name__) + pstr('\n')) + elif inspect.isfunction(o): + ret += _b(_leading + pstr('function(%s):' % o.__name__) + pstr('\n')) + elif inspect.isbuiltin(o): + ret += _b(_leading + pstr('builtin(%s):' % o.__name__) + pstr('\n')) + elif inspect.ismethod(o): + ret += _b(_leading + pstr('method(%s):' % o.__name__) + pstr('\n')) + else: + '本身就是类,不是对象' + try: + ret += _b(_leading + pstr('class(%s):' % o.__name__) + pstr('\n')) + except: + print(inspect.isclass(o)) + print(o, dir(o)) + raise + + # body + ele_ctnr = self.get_elements() + + props_cnt = len(ele_ctnr) + for idx, key, val in ele_ctnr: + ''' + '最后一个元素不需要再加(,)逗号' + if idx == props_cnt - 1: + position = C._AS_VALUE_ + else: + position = C._AS_CLASS_ELEMENT_ + ''' + + # '忽略掉 以__开头的成员、自引用成员、函数成员' + ret += str(PairBlock((key, val), + self, + position=C._AS_CLASS_ELEMENT_, + indent=leadCnt + 1)) + + # } + if props_cnt == 0: + # right strip ':\n' + ret = pstr(ret[:-2] + u',\n') + else: + ret += pstr(tail + ending) + return ret + +class DictBlock(Block): + + def __init__(self, *args, **kwargs): + kwargs.setdefault('position', C._AS_VALUE_) + super(DictBlock, self).__init__(*args, **kwargs) + + def build_block(self): + + leadCnt = self.indent_cnt + o = self.subject + position = self.position + + debug(C._DL_FUNC_, leadCnt, + ('obj:{} leadCnt:{} position:{:b}'.format( + o, leadCnt, position))) + + ret = pstr('') + tail = self.get_line_ending() + block_ending = self.get_block_ending() + # { + if S.newline or position & C._AS_ELEMENT_: + ret += _b(S.leading * leadCnt + pstr('{') + pstr('\n')) + else: + ret += _b(pstr('{') + pstr('\n')) + + # body + for k, v in o.items(): + # v = o[k] + if dict_key_filter(o, k, v): + continue + # ret += S.leading*(leadCnt + 1) + typeval(k) + pstr(": ") + # ret += build_single_block(v, leadCnt+1) + ret += str(PairBlock((k, v), + self, + position=C._AS_DICT_ELEMENT_, + indent=leadCnt + 1)) + + # } + ret += _b(S.leading * leadCnt + '}' + pstr(tail + block_ending)) + + return ret + +class ListBlock(Block): + + def __init__(self, *args, **kwargs): + kwargs.setdefault('position', C._AS_VALUE_) + super(ListBlock, self).__init__(*args, **kwargs) + + def build_block(self): + leadCnt = self.indent_cnt + o = self.subject + position = self.position + ret = pstr('') + + tail = self.get_line_ending() + block_ending = self.get_block_ending() + + '所有元素显示在同一行' + if S.list_in_line: + _f = map(lambda e: not (is_extendable(e) or too_long(leadCnt, position, e)), o) + if all(_f): + _o = map(lambda e: typeval(None, e), o) + if S.newline or position & C._AS_ELEMENT_: + ret += pstr(S.leading * leadCnt) + ret += pstr("[") + ', '.join(_o) + pstr("]" + tail + block_ending) + return _b(ret) + + # [ + if S.newline or position & C._AS_ELEMENT_: + ret += _b(S.leading * leadCnt + pstr('[') + block_ending) + else: + ret += _b(pstr('[') + block_ending) + + # body + for e in o: + ret += str(Block(e, self, + position=C._AS_ELEMENT_ | C._AS_LIST_ELEMENT_, + indent=leadCnt + 1, + )) + + # ] + ret += _b(S.leading * leadCnt + pstr(']' + tail + block_ending)) + + return ret + + + +class TupleBlock(Block): + + def __init__(self, *args, **kwargs): + kwargs.setdefault('position', C._AS_VALUE_) + super(TupleBlock, self).__init__(*args, **kwargs) + + def get_elements(self): + return self.subject + + def build_block(self): + leadCnt = self.indent_cnt + o = self.subject + position = self.position + + ret = pstr('') + + tail = self.get_line_ending() + block_ending = self.get_block_ending() + + if S.tuple_in_line: + _f = map(lambda e: not is_extendable(e), o) + if all(_f): + _o = map(lambda e: typeval(None, e), o) + if S.newline or position & C._AS_ELEMENT_: + ret += pstr(S.leading * leadCnt) + ret += _b(pstr("(") + ', '.join(_o) + ')' + tail + block_ending) + return ret + + # ( + if S.newline or position & C._AS_ELEMENT_: + ret += _b(S.leading * leadCnt + pstr('(\n')) + else: + ret += _b(pstr('(\n')) + + # body + for e in self.get_elements(): + ret += str(Block(e, + self, + position=C._AS_ELEMENT_ | C._AS_TUPLE_ELEMENT_, + indent=leadCnt + 1, + )) + + # ) + ret += _b(S.leading * leadCnt + pstr(')' + tail + block_ending)) + + return ret + +class PairBlock(Block): + + def build_block(self): + leadCnt = self.indent_cnt + name, val = self.subject + position = self.position + + debug(C._DL_FUNC_, leadCnt, + ('key:{}, leadCnt:{}, position:{:b}'.format( + name, leadCnt, position))) + ret = pstr('') + + tail = self.get_line_ending() + ending = self.get_block_ending() + + key = pair_block_key(position, name) + + ret += _b(S.leading * leadCnt + key + ':') + if is_extendable(val) and S.max_depth > leadCnt: + # value need to be dispalyed on new line + # including: + # class type & class instance + # function type + if S.newline or (is_newline_obj(val) and + position & C._AS_ELEMENT_): + ret += _b(pstr('\n')) + leadCnt = leadCnt + 1 + position &= ~C._AS_VALUE_ + position |= C._AS_ELEMENT_ + debug(C._DL_STATEMENT, leadCnt, 'make newline') + # value will be dispalyed immediately after one space + else: + ret += _b(pstr(" ")) + position &= ~C._AS_ELEMENT_ + position |= C._AS_VALUE_ + + ret += str(Block(val, self, position=position, indent=leadCnt)) + pstr(tail + ending) + else: + if S.max_depth <= leadCnt: + ret += _b(pstr(" " + tail + ending)) + else: + context = Context(indent_char=S.leading, + position=position, + lead_cnt=leadCnt, + key=name) + ret += _b(pstr(" ") + typeval(context, val) + pstr(tail + ending)) + + return ret + + +class PositionDict(OrderedDict): + + def __init__(self, tlist): + idx_by_key = {} + key_by_idx = {} + for idx, tup in enumerate(tlist): + key = tup[0] + idx_by_key[key] = idx + key_by_idx[idx] = key + + self.idx_by_key = idx_by_key + self.key_by_idx = key_by_idx + + super(PositionDict, self).__init__(tlist) + + def idx(self, key): + return self.idx_by_key[key] + + def indexed_items(self): + for key, val in super(PositionDict, self).items(): + idx = self.idx(key) + yield idx, key, val + + def index(self, pair): + return self.idx(pair[0]) + + def __iter__(self): + for key in super(PositionDict, self).__iter__(): + val = self[key] + idx = self.idx(key) + yield idx, key, val + #return self.indexed_items() diff --git a/beeprint/printer.py b/beeprint/printer.py index 005e1f4..d847fe8 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -26,12 +26,11 @@ from . import utils from .utils import print_exc_plus from .helper import typeval, pstr, tail_symbol, is_extendable, too_long -from .debug_kit import debug from . import models +from .models import Block from .block_helper import pair_block_key - def beeprint(o, output=True): """print data beautifully @@ -65,7 +64,7 @@ def beeprint(o, output=True): >>> beeprint(u'\\\\'.encode("utf8")) '\\' """ - res = build_single_block(o, 0) + res = str(Block(o)) if output and not S.write_to_buffer_when_execute: try: print(res, end='') @@ -80,323 +79,3 @@ def beeprint(o, output=True): print('exception type :', type(e)) else: return res - -def object_attr_default_filter(obj, name, val): - '过滤不需要的对象属性' - - for propLeading in S.prop_leading_filters: - if name.startswith(propLeading): - return True - - for prop in S.prop_filters: - # filter is a string - if isinstance(prop, str) or isinstance(prop, unicode): - if name == prop: - return True - # filter is a type - # if type(prop) == types.TypeType: - if isinstance(prop, type): - if type(val) == prop: - return True - # filter is callable - elif hasattr(prop, '__call__'): - if prop(name, val): - return True - - return False - - -def dict_key_filter(obj, name, val): - return False - - -def _b(s): - if S.write_to_buffer_when_execute: - S.buffer_handler.write(s) - S.buffer_handler.flush() - return s - - -def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): - '遍历对象,判断对象内成员的类型,然后调用对应的 build_*_block() 处理' - - debug(C._DL_FUNC_, leadCnt, - ('obj:%s leadCnt:%s position:%d' \ - % (obj, leadCnt, position))) - - ret = pstr('') - - tail = tail_symbol(position) - - if S.max_depth < leadCnt: - if S.newline or position & C._AS_ELEMENT_: - ret = pstr(leadCnt * S.leading) + pstr("\n") - else: - ret = pstr(" ") + pstr("\n") - if position & C._AS_LIST_ELEMENT_: - ret = ret[:-1] + pstr(tail + "\n") - return _b(ret) - - if isinstance(obj, dict): - debug(C._DL_STATEMENT, leadCnt, 'is dict') - ret += build_dict_block(obj, leadCnt, position) - elif isinstance(obj, list): - debug(C._DL_STATEMENT, leadCnt, 'is list') - ret += build_list_block(obj, leadCnt, position) - elif isinstance(obj, tuple): - debug(C._DL_STATEMENT, leadCnt, 'is tuple') - ret += build_tuple_block(obj, leadCnt, position) - # hasattr(obj, '__dict__') or isinstance(obj, object): - elif is_extendable(obj): - debug(C._DL_STATEMENT, leadCnt, 'is extendable') - ret += build_class_block(obj, leadCnt, position) - else: - debug(C._DL_STATEMENT, leadCnt, 'is simple type') - context = models.Context(indent_char=S.leading, position=position, lead_cnt=leadCnt) - ret += _b(leadCnt * S.leading + typeval(context, obj) + pstr(tail + '\n')) - - return ret - -def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): - debug(C._DL_FUNC_, leadCnt, - ('key:%s, leadCnt:%s, position:%s' \ - % (name, leadCnt, position))) - ret = pstr('') - - tail = tail_symbol(position) - - key = pair_block_key(position, name) - - ret += _b(S.leading * leadCnt + key + ':') - if is_extendable(val) and S.max_depth > leadCnt: - # value need to be dispalyed on new line - # including: - # class type & class instance - # function type - if S.newline or (is_newline_obj(val) & - position & C._AS_ELEMENT_): - ret += _b(pstr('\n')) - leadCnt = leadCnt + 1 - position |= C._AS_ELEMENT_ - debug(C._DL_STATEMENT, leadCnt, 'make newline') - # value will be dispalyed immediately after one space - else: - ret += _b(pstr(" ")) - position |= C._AS_VALUE_ - - ret += build_single_block(val, leadCnt, position) - else: - if S.max_depth <= leadCnt: - ret += _b(pstr(" %s\n" % tail)) - else: - context = models.Context(indent_char=S.leading, - position=position, - lead_cnt=leadCnt, - key=name) - ret += _b(pstr(" ") + typeval(context, val) + pstr(tail + '\n')) - - return ret - - -def build_list_block(o, leadCnt=0, position=C._AS_VALUE_): - ret = pstr('') - - tail = tail_symbol(position) - - '所有元素显示在同一行' - if S.list_in_line: - _f = map(lambda e: not (is_extendable(e) or too_long(leadCnt, position, e)), o) - if all(_f): - _o = map(lambda e: typeval(None, e), o) - if S.newline or position & C._AS_ELEMENT_: - ret += pstr(S.leading * leadCnt) - ret += pstr("[") + ', '.join(_o) + pstr("]%s\n" % tail) - return _b(ret) - - # [ - if S.newline or position & C._AS_ELEMENT_: - ret += _b(S.leading * leadCnt + pstr('[\n')) - else: - ret += _b(pstr('[\n')) - - # body - for e in o: - ret += build_single_block(e, leadCnt + 1, - C._AS_ELEMENT_ | C._AS_LIST_ELEMENT_) - - # ] - ret += _b(S.leading * leadCnt + pstr(']%s\n' % tail)) - - return ret - - -def build_tuple_block(o, leadCnt=0, position=C._AS_VALUE_): - ret = pstr('') - - tail = tail_symbol(position) - - if S.tuple_in_line: - _f = map(lambda e: not is_extendable(e), o) - if all(_f): - _o = map(lambda e: typeval(None, e), o) - if S.newline or position & C._AS_ELEMENT_: - ret += pstr(S.leading * leadCnt) - ret += _b(pstr("(") + ', '.join(_o) + ')%s\n' % tail) - return ret - - # ( - if S.newline or position & C._AS_ELEMENT_: - ret += _b(S.leading * leadCnt + pstr('(\n')) - else: - ret += _b(pstr('(\n')) - - # body - for e in o: - ret += build_single_block(e, leadCnt + 1, - C._AS_ELEMENT_ | C._AS_TUPLE_ELEMENT_) - - # ) - ret += _b(S.leading * leadCnt + pstr(')%s\n' % tail)) - - return ret - - -def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): - ret = pstr('') - - tail = tail_symbol(position) - # { - if S.newline or position & C._AS_ELEMENT_: - ret += _b(S.leading * leadCnt + pstr('{') + pstr('\n')) - else: - ret += _b(pstr('{') + pstr('\n')) - - # body - for k, v in o.items(): - # v = o[k] - if dict_key_filter(o, k, v): - continue - # ret += S.leading*(leadCnt + 1) + typeval(k) + pstr(": ") - # ret += build_single_block(v, leadCnt+1) - ret += build_pair_block(k, v, leadCnt + 1, C._AS_DICT_ELEMENT_) - - # } - ret += _b(S.leading * leadCnt + '}' + pstr(tail + u'\n')) - - return ret - - -def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): - debug(C._DL_FUNC_, leadCnt, - ('obj:%s leadCnt:%s position:%d' \ - % (o, leadCnt, position))) - ret = pstr('') - - tail = tail_symbol(position) - - # { - _leading = pstr('') - if position & C._AS_ELEMENT_: - _leading += S.leading * leadCnt - # elif position & C._AS_DICT_ELEMENT_: - # _leading += pstr(' ') - elif position & C._AS_VALUE_: - _leading += pstr('') - - if is_class_instance(o): - ret += _b(_leading + pstr('instance(%s):' % - o.__class__.__name__) + pstr('\n')) - elif inspect.isfunction(o): - ret += _b(_leading + pstr('function(%s):' % o.__name__) + pstr('\n')) - elif inspect.isbuiltin(o): - ret += _b(_leading + pstr('builtin(%s):' % o.__name__) + pstr('\n')) - elif inspect.ismethod(o): - ret += _b(_leading + pstr('method(%s):' % o.__name__) + pstr('\n')) - else: - '本身就是类,不是对象' - try: - ret += _b(_leading + pstr('class(%s):' % o.__name__) + pstr('\n')) - except: - print(inspect.isclass(o)) - print(o, dir(o)) - raise - - # body - props = dir(o) - props_cnt = len(props) - filter_count = 0 - for idx, attr in enumerate(props): - if attr == '__abstractmethods__': - continue - - try: - val = getattr(o, attr) - except Exception as e: - val = "" % e - '过滤不需要的属性' - if object_attr_default_filter(o, attr, val): - filter_count += 1 - continue - - '最后一个元素不需要再加(,)逗号' - if idx == props_cnt - 1: - position = C._AS_VALUE_ - else: - position = C._AS_CLASS_ELEMENT_ - - # '忽略掉 以__开头的成员、自引用成员、函数成员' - ret += build_pair_block(attr, - val, - leadCnt + 1, - position | C._AS_CLASS_ELEMENT_) - - # } - if filter_count == props_cnt: - # right strip ':\n' - ret = ret[:-2] - else: - # right strip ',\n' which belongs to last element of class - ret = ret[:-2] - - ret += pstr(tail + u'\n') - return ret - - - -def is_newline_obj(o): - if hasattr(o, '__module__'): - return True - return False - - -def is_class(o): - try: - # detect class - # for py3, to detect both old-style and new-style class - # for py2, to detect new-style class - o.__flags__ - return True - except: - if inspect.isclass(o): - return True - return False - - -def is_class_instance(o): - try: - # to detect: - # old-style class & new-style class - # instance of old-style class and of new-style class - # method of instance of both class - # function - - # o.__module__ in python 3.5 some instance has no this attribute - - if (inspect.isclass(o) - or inspect.isfunction(o) - or inspect.ismethod(o)): - return False - return True - except: - pass - return False diff --git a/beeprint/utils.py b/beeprint/utils.py index fbb91f5..6919a43 100644 --- a/beeprint/utils.py +++ b/beeprint/utils.py @@ -66,4 +66,26 @@ def print_exc_plus(): except: print("") +def is_newline_obj(o): + if hasattr(o, '__module__'): + return True + return False +def is_class_instance(o): + try: + # to detect: + # old-style class & new-style class + # instance of old-style class and of new-style class + # method of instance of both class + # function + + # o.__module__ in python 3.5 some instance has no this attribute + + if (inspect.isclass(o) + or inspect.isfunction(o) + or inspect.ismethod(o)): + return False + return True + except: + pass + return False diff --git a/tests/definition.py b/tests/definition.py index ebaa634..a765e9e 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -14,6 +14,7 @@ def mth():pass static_props = 1 lists = [] dicts = {} + tupl = (1,2) inst_of_normal_class_old_style = NormalClassOldStyle() inst_of_normal_class_new_style = NormalClassNewStyle() @@ -75,6 +76,8 @@ def mth():pass inst_of_normal_class_new_style.mth, { 'key': [], - u'key2': {}, }, + { + 'key': inst_of_normal_class_new_style, + } ] diff --git a/tests/t.py b/tests/t.py index 8387490..5075989 100644 --- a/tests/t.py +++ b/tests/t.py @@ -93,7 +93,7 @@ def builtin_test(): def main(): if len(sys.argv) == 1: - # S.debug_level = 9 + S.debug_level = 9 # S.str_display_not_prefix_u = False # S.str_display_not_prefix_b = False From f72a8d708000398981a258d56c5fa9c67f0d9133 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Thu, 25 Aug 2016 22:32:45 +0800 Subject: [PATCH 4/8] optimize style of last element of class in list Old: ... { 'key': instance(NormalClassNewStyle): dicts: { }, lists: [], static_props: 1, tupl: (1, 2) , }, ... Now: ... { 'key': instance(NormalClassNewStyle): dicts: { }, lists: [], static_props: 1, tupl: (1, 2) }, ... --- beeprint/models.py | 78 +++++++++++------ tests/data/all_in_one.txt | 97 +++++++++++++++++++++ tests/{test_default_beeprint.py => main.py} | 21 +++-- tests/t.py | 4 +- 4 files changed, 159 insertions(+), 41 deletions(-) create mode 100644 tests/data/all_in_one.txt rename tests/{test_default_beeprint.py => main.py} (83%) diff --git a/beeprint/models.py b/beeprint/models.py index a635f0b..7cf77ef 100644 --- a/beeprint/models.py +++ b/beeprint/models.py @@ -166,18 +166,18 @@ def __init__(self, subj, parent=None, position=C._AS_ELEMENT_, indent=0): def __str__(self): return self.build_block() - def get_block_ending(self): + def get_block_ending(self, value=None): ending = u'\n' if self.position & C._AS_VALUE_: ending = u'' + if self.position & C._AS_CLASS_ELEMENT_: + 'last element of class has no ending' + elements = self.parent.get_elements() + if elements.index(self.subject) == len(elements)-1: + ending = u'' return ending - def get_line_ending(self): - """calculate the ending of line - newline character does not include here because - when your calculate the length of whole line you only need to be - care about printable characters - """ + def get_element_ending(self, value=None): position = self.position tail = u'' if position & C._AS_VALUE_: @@ -188,10 +188,15 @@ def get_line_ending(self): position & C._AS_TUPLE_ELEMENT_): tail = u',' - if position & C._AS_CLASS_ELEMENT_: + if False: + pass + elif position & C._AS_CLASS_ELEMENT_: + 'last element of class has no ending' elements = self.parent.get_elements() if elements.index(self.subject) == len(elements)-1: tail = u'' + elif is_extendable(value): + tail = u'' return tail @@ -213,7 +218,9 @@ def build_block(self): ret = pstr('') - tail = self.get_line_ending() + tail = self.get_element_ending() + block_ending = u'' + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) if S.max_depth < leadCnt: if S.newline or position & C._AS_ELEMENT_: @@ -226,16 +233,16 @@ def build_block(self): if isinstance(obj, dict): debug(C._DL_STATEMENT, leadCnt, 'is dict') - ret += str(DictBlock(obj, self, position=position, indent=leadCnt)) + ret += str(DictBlock(obj, self.parent, position=position, indent=leadCnt)) elif isinstance(obj, list): debug(C._DL_STATEMENT, leadCnt, 'is list') - ret += str(ListBlock(obj, self, position=position, indent=leadCnt)) + ret += str(ListBlock(obj, self.parent, position=position, indent=leadCnt)) elif isinstance(obj, tuple): debug(C._DL_STATEMENT, leadCnt, 'is tuple') - ret += str(TupleBlock(obj, self, position=position, indent=leadCnt)) + ret += str(TupleBlock(obj, self.parent, position=position, indent=leadCnt)) elif is_extendable(obj): debug(C._DL_STATEMENT, leadCnt, 'is extendable') - ret += str(ClassBlock(obj, self, position=position, indent=leadCnt)) + ret += str(ClassBlock(obj, self.parent, position=position, indent=leadCnt)) else: debug(C._DL_STATEMENT, leadCnt, 'is simple type') context = Context(indent_char=S.leading, position=position, lead_cnt=leadCnt) @@ -266,7 +273,7 @@ def get_elements(self): return PositionDict(props) - def get_line_ending(self): + def get_element_ending(self): """A class block would have many nested elements, it would not know how its children respresent. for example: @@ -286,8 +293,10 @@ def get_line_ending(self): """ return u'' + ''' def get_block_ending(self): return u'' + ''' def build_block(self): leadCnt = self.indent_cnt @@ -300,8 +309,9 @@ def build_block(self): ret = pstr('') - tail = self.get_line_ending() - ending = self.get_block_ending() + tail = self.get_element_ending() + block_ending = self.get_block_ending() + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) # { _leading = pstr('') @@ -354,7 +364,7 @@ def build_block(self): # right strip ':\n' ret = pstr(ret[:-2] + u',\n') else: - ret += pstr(tail + ending) + ret += pstr(tail + block_ending) return ret class DictBlock(Block): @@ -374,8 +384,9 @@ def build_block(self): o, leadCnt, position))) ret = pstr('') - tail = self.get_line_ending() + tail = self.get_element_ending() block_ending = self.get_block_ending() + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) # { if S.newline or position & C._AS_ELEMENT_: ret += _b(S.leading * leadCnt + pstr('{') + pstr('\n')) @@ -411,8 +422,9 @@ def build_block(self): position = self.position ret = pstr('') - tail = self.get_line_ending() + tail = self.get_element_ending() block_ending = self.get_block_ending() + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) '所有元素显示在同一行' if S.list_in_line: @@ -458,10 +470,15 @@ def build_block(self): o = self.subject position = self.position + debug(C._DL_FUNC_, + leadCnt, + ('obj:{} leadCnt:{} position:{:b}'.format( + o, leadCnt, position))) ret = pstr('') - tail = self.get_line_ending() + tail = self.get_element_ending() block_ending = self.get_block_ending() + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) if S.tuple_in_line: _f = map(lambda e: not is_extendable(e), o) @@ -503,8 +520,9 @@ def build_block(self): name, leadCnt, position))) ret = pstr('') - tail = self.get_line_ending() - ending = self.get_block_ending() + tail = self.get_element_ending(val) + block_ending = self.get_block_ending(val) + debug(C._DL_STATEMENT, leadCnt, 'tail, block_ending: ' + str([tail, block_ending])) key = pair_block_key(position, name) @@ -518,25 +536,27 @@ def build_block(self): position & C._AS_ELEMENT_): ret += _b(pstr('\n')) leadCnt = leadCnt + 1 - position &= ~C._AS_VALUE_ - position |= C._AS_ELEMENT_ + # position &= ~C._AS_VALUE_ + # position |= C._AS_ELEMENT_ + position = C._AS_ELEMENT_ debug(C._DL_STATEMENT, leadCnt, 'make newline') # value will be dispalyed immediately after one space else: ret += _b(pstr(" ")) - position &= ~C._AS_ELEMENT_ - position |= C._AS_VALUE_ + # position &= ~C._AS_ELEMENT_ + # position |= C._AS_VALUE_ + position = C._AS_VALUE_ - ret += str(Block(val, self, position=position, indent=leadCnt)) + pstr(tail + ending) + ret += str(Block(val, self, position=position, indent=leadCnt)) + pstr(tail + block_ending) else: if S.max_depth <= leadCnt: - ret += _b(pstr(" " + tail + ending)) + ret += _b(pstr(" " + tail + block_ending)) else: context = Context(indent_char=S.leading, position=position, lead_cnt=leadCnt, key=name) - ret += _b(pstr(" ") + typeval(context, val) + pstr(tail + ending)) + ret += _b(pstr(" ") + typeval(context, val) + pstr(tail + block_ending)) return ret diff --git a/tests/data/all_in_one.txt b/tests/data/all_in_one.txt new file mode 100644 index 0000000..d5820a5 --- /dev/null +++ b/tests/data/all_in_one.txt @@ -0,0 +1,97 @@ +[ + 1, + 1.1, + 's', + 'us', + 'a中文', + 'a中文', + [1], + (1, 2), + function(EmptyFunc), + class(EmptyClassOldStyle), + class(EmptyClassNewStyle), + class(NormalClassOldStyle): + static_props: 1 + class(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + instance(NormalClassOldStyle): + static_props: 1 + instance(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + method(mth), + method(mth), + { + 'key': [] + }, + { + 'key': instance(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + }, +] +[ + { + 'english version': '\nThe sky and the earth were at first one blurred entity + like an egg. Pangu was born into it.\n \nThe separation + of the sky and the earth took eighteen thousand + years-the yang which was light and pure rose to become + the sky, and the yin which was heavy and murky(朦胧的) + sank to form the earth. Between them was Pangu, who went + through nine changes every day, his wisdom greater than + that of the sky and his ability greater than that of the + earth. Every day the sky rose ten feet higher, the earth + became ten feet thicker, and Pangu grew ten feet + taller.\n \nAnother eighteen thousand years passed, and + there was an extremely high sky, an extremely thick + earth, and an extremely tall Pangu. After Pangu died, + his head turned into the Five Sacred Mountains (Mount + Tai, Mount Heng, Mount Hua, Mount Heng, Mount Song), his + eyes turned into the moon and the sun, his blood changed + into water in river and sea, his hair into grass.\n \nIn + all, the universe and Pangu combine in one.\n', + }, + { + 'simplify chinese versino': '\n据民间神话传说古时盘古生在黑暗团中,他不能忍 + 受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽 + 阔。他为不使天地会重新合并,继续施展法术。每当 + 盘古的身体长高一尺,天空就随之增高一尺,经过1.8 + 万多年的努力,盘古变成一位顶天立地的巨人,而天 + 空也升得高不可及,大地也变得厚实无比。盘古生前 + 完成开天辟地的伟大业绩,死后永远留给后人无穷无 + 尽的宝藏,成为中华民族崇拜的英雄。\n', + }, +] +[ + [ + '\nThe sky and the earth were at first one blurred entity like an egg. Pangu + was born into it.\n \nThe separation of the sky and the earth took eighteen + thousand years-the yang which was light and pure rose to become the sky, + and the yin which was heavy and murky(朦胧的) sank to form the earth. + Between them was Pangu, who went through nine changes every day, his wisdom + greater than that of the sky and his ability greater than that of the + earth. Every day the sky rose ten feet higher, the earth became ten feet + thicker, and Pangu grew ten feet taller.\n \nAnother eighteen thousand + years passed, and there was an extremely high sky, an extremely thick + earth, and an extremely tall Pangu. After Pangu died, his head turned into + the Five Sacred Mountains (Mount Tai, Mount Heng, Mount Hua, Mount Heng, + Mount Song), his eyes turned into the moon and the sun, his blood changed + into water in river and sea, his hair into grass.\n \nIn all, the universe + and Pangu combine in one.\n', + '\n据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐 + 使天空高远,大地辽阔。他为不使天地会重新合并,继续施展法术。每当盘古的身体 + 长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶天立地的巨 + 人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业 + 绩,死后永远留给后人无穷无尽的宝藏,成为中华民族崇拜的英雄。\n', + ], +] diff --git a/tests/test_default_beeprint.py b/tests/main.py similarity index 83% rename from tests/test_default_beeprint.py rename to tests/main.py index 082c62a..8ff1273 100644 --- a/tests/test_default_beeprint.py +++ b/tests/main.py @@ -11,11 +11,14 @@ from beeprint.printer import beeprint, pyv from beeprint import settings as S +from beeprint import constants as C try: - from .definition import values, long_text_en + from .definition import values + import definition as df except: - from definition import values, long_text_en + from definition import values + import definition as df class TestSimpleTypes(unittest.TestCase): @@ -64,22 +67,18 @@ def test_string(self): def test_complicate_data(self): # S.str_display_not_prefix_u = False # S.str_display_not_prefix_b = False + S.text_wrap_method = C._TEXT_WRAP_BY_WIDTH ans = u"" data_path = os.path.join(CUR_SCRIPT_PATH, - 'data/tests_complicate_data.txt') + 'data/all_in_one.txt') with codecs.open(data_path, encoding="utf8") as fp: ans = fp.read() - # to prevent comparing fail in unordered keys to dict - ans2 = u"" - data_path = os.path.join(CUR_SCRIPT_PATH, - 'data/tests_complicate_data2.txt') - with codecs.open(data_path, encoding="utf8") as fp: - ans2 = fp.read() - res = beeprint(values, output=False) - self.assertEqual(res == ans or res == ans2, True) + res += beeprint(df.long_text_in_dict, output=False) + res += beeprint(df.long_text_in_list, output=False) + self.assertEqual(res, ans) # self.assertEqual(res, ans) ''' diff --git a/tests/t.py b/tests/t.py index 5075989..d2e128f 100644 --- a/tests/t.py +++ b/tests/t.py @@ -28,6 +28,7 @@ from beeprint.printer import beeprint as pp, pyv from beeprint import settings as S +from beeprint import constants as C try: from .definition import values @@ -93,11 +94,12 @@ def builtin_test(): def main(): if len(sys.argv) == 1: - S.debug_level = 9 + # S.debug_level = 9 # S.str_display_not_prefix_u = False # S.str_display_not_prefix_b = False # S.max_depth = 3 + S.text_wrap_method = C._TEXT_WRAP_BY_WIDTH pp(df.values) pp(df.long_text_in_dict) pp(df.long_text_in_list) From 01302a6040bebde28816b4845c5fc13bafd34aef Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 26 Aug 2016 20:25:44 +0800 Subject: [PATCH 5/8] docs --- README.md | 184 ++++++++++++++++++++- README.txt | 54 +----- docs/.gitignore | 2 + docs/Makefile | 225 +++++++++++++++++++++++++ docs/conf.py | 338 ++++++++++++++++++++++++++++++++++++++ docs/index.rst | 39 +++++ docs/make.bat | 281 +++++++++++++++++++++++++++++++ tests/data/all_in_one.txt | 17 +- tests/definition.py | 23 ++- 9 files changed, 1092 insertions(+), 71 deletions(-) mode change 120000 => 100644 README.md mode change 100644 => 120000 README.txt create mode 100644 docs/.gitignore create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat diff --git a/README.md b/README.md deleted file mode 120000 index c3ca074..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -README.txt \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d3e280 --- /dev/null +++ b/README.md @@ -0,0 +1,183 @@ +beeprint: Beautifully Print +=== +pprint is good, but not clean. So beeprint do it. + +Features +=== +- print dict elegantly +- outstanding mark to class and instance +- compatible with py2 py3 +- auto wrap text, including English and Chinese + +Contents +=== +[Examples](#Examples) +[Installation](#Installation) +[Settings](#Settings) + +Examples +=== + +Short list +--- +``` +[1, 2, 3, 4, 5, 6] +``` + +Compliated list +--- +``` +[ + 1, + [2], + { + 'key': 'val', + }, +] +``` + +Class Instance Formatted Display +--- +``` +instance(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) +``` + +Long Text Auto Wrapping +--- + +> auto wrapped by terminal width + +``` +[ + '\nThe sky and the earth were at first one blurred entity like an egg. Pangu + was born into it.\n \nThe separation of the sky and the earth took eighteen + thousand years-the yang which was light and pure rose to become the sky, + and the yin which was heavy and murky(朦胧的) sank to form the earth. + Between them was Pangu, who went through nine changes every day, his wisdom + greater than that of the sky and his ability greater than that of the + earth. Every day the sky rose ten feet higher, the earth became ten feet + thicker, and Pangu grew ten feet taller.\n \nAnother eighteen thousand + years passed, and there was an extremely high sky, an extremely thick + earth, and an extremely tall Pangu. After Pangu died, his head turned into + the Five Sacred Mountains (Mount Tai, Mount Heng, Mount Hua, Mount Heng, + Mount Song), his eyes turned into the moon and the sun, his blood changed + into water in river and sea, his hair into grass.\n \nIn all, the universe + and Pangu combine in one.\n', + '\n据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐 + 使天空高远,大地辽阔。他为不使天地会重新合并,继续施展法术。每当盘古的身体 + 长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶天立地的巨 + 人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业 + 绩,死后永远留给后人无穷无尽的宝藏,成为中华民族崇拜的英雄。\n', +] +``` + +Long Text in Dict Auto Wrapping +--- + +> auto wrapped by terminal width + +``` +[ + { + 'english version': '\nThe sky and the earth were at first one blurred entity + like an egg. Pangu was born into it.\n \nThe separation + of the sky and the earth took eighteen thousand + years-the yang which was light and pure rose to become + the sky, and the yin which was heavy and murky(朦胧的) + sank to form the earth. Between them was Pangu, who went + through nine changes every day, his wisdom greater than + that of the sky and his ability greater than that of the + earth. Every day the sky rose ten feet higher, the earth + became ten feet thicker, and Pangu grew ten feet + taller.\n \nAnother eighteen thousand years passed, and + there was an extremely high sky, an extremely thick + earth, and an extremely tall Pangu. After Pangu died, + his head turned into the Five Sacred Mountains (Mount + Tai, Mount Heng, Mount Hua, Mount Heng, Mount Song), his + eyes turned into the moon and the sun, his blood changed + into water in river and sea, his hair into grass.\n \nIn + all, the universe and Pangu combine in one.\n', + }, + { + 'simplify chinese versino': '\n据民间神话传说古时盘古生在黑暗团中,他不能忍 + 受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽 + 阔。他为不使天地会重新合并,继续施展法术。每当 + 盘古的身体长高一尺,天空就随之增高一尺,经过1.8 + 万多年的努力,盘古变成一位顶天立地的巨人,而天 + 空也升得高不可及,大地也变得厚实无比。盘古生前 + 完成开天辟地的伟大业绩,死后永远留给后人无穷无 + 尽的宝藏,成为中华民族崇拜的英雄。\n', + }, +] +``` + +Complicated data +--- +``` +[ + 1, + 1.1, + 'literal', + 'unicode', + 'literal中文', + 'unicode中文', + [1, 2, 3, 4, 5, 6], + [ + 1, + [2], + { + 'key': 'val', + }, + ], + (1, 2), + function(EmptyFunc), + class(EmptyClassOldStyle), + class(EmptyClassNewStyle), + class(NormalClassOldStyle): + static_props: 1 + class(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + instance(NormalClassOldStyle): + static_props: 1 + instance(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + method(mth), + method(mth), + { + 'key': [] + }, + { + 'key': instance(NormalClassNewStyle): + dicts: { + }, + lists: [], + static_props: 1, + tupl: (1, 2) + }, +] +``` + +Installation +=== +```shell +pip install beeprint +``` + +Settings +=== + +> more on [settings.py](./beeprint/settings.py) + diff --git a/README.txt b/README.txt deleted file mode 100644 index 4b7c3fd..0000000 --- a/README.txt +++ /dev/null @@ -1,53 +0,0 @@ -beeprint: Beautifully Print -=== -pprint is good, but not clean. So beeprint do it. - -Features -=== -- print dict elegantly -- format of sequential type is controllable -- outstanding mark to class and instance -- compatible with py2 py3 in same output - -Examples -=== - -Complicated data ---- -``` -[ - 1, - 1.1, - 's', - 'us', - 'a中文', - 'a中文', - [1], - (1, 2), - function(EmptyFunc), - class(EmptyClassOldStyle), - class(EmptyClassNewStyle), - class(NormalClassOldStyle): - static_props: 1, - class(NormalClassNewStyle): - dicts: { - }, - lists: [], - static_props: 1, - instance(NormalClassOldStyle): - static_props: 1, - instance(NormalClassNewStyle): - dicts: { - }, - lists: [], - static_props: 1, - method(mth), - method(mth), - { - 'key': [], - 'key2': { - }, - }, -] -``` - diff --git a/README.txt b/README.txt new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/README.txt @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..3774c5f --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_build/* +_build_html/* diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..068612b --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,225 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Beeprint.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Beeprint.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Beeprint" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Beeprint" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..5170d4d --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,338 @@ +# -*- coding: utf-8 -*- +# +# Beeprint documentation build configuration file, created by +# sphinx-quickstart on Fri Aug 26 17:19:03 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +# +# source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Beeprint' +copyright = u'2016, PanYangYang' +author = u'PanYangYang' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.0' +# The full version, including alpha/beta/rc tags. +release = u'1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# +# today = '' +# +# Else, today_fmt is used as the format for a strftime call. +# +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +# keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. +# " v documentation" by default. +# +# html_title = u'Beeprint v1.0' + +# A shorter title for the navigation bar. Default is the same as html_title. +# +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# +# html_logo = None + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +# +# html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +# +# html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# +# html_additional_pages = {} + +# If false, no module index is generated. +# +# html_domain_indices = True + +# If false, no index is generated. +# +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +# +# html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +# +# html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +# +# html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Beeprintdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Beeprint.tex', u'Beeprint Documentation', + u'PanYangYang', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# +# latex_use_parts = False + +# If true, show page references after internal links. +# +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# +# latex_show_urls = False + +# Documents to append as an appendix to all manuals. +# +# latex_appendices = [] + +# It false, will not define \strong, \code, itleref, \crossref ... but only +# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added +# packages. +# +# latex_keep_old_macro_names = True + +# If false, no module index is generated. +# +# latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'beeprint', u'Beeprint Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +# +# man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Beeprint', u'Beeprint Documentation', + author, 'Beeprint', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +# +# texinfo_appendices = [] + +# If false, no module index is generated. +# +# texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +# +# texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +# +# texinfo_no_detailmenu = False diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..40f8f37 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,39 @@ +Beeprint +======== + +Beeprint will print variables in a human friendly way. + +Look how easy it is to use:: + + from beeprint.printer import beeprint as pp + pp(some_variables) + +Features +-------- + +- Be awesome +- Make things faster + +Installation +------------ + +Install beeprint by running:: + + pip install beeprint + + +Contribute +---------- + +- Issue Tracker: github.com/panyanyany/beeprint/issues +- Source Code: github.com/panyanyany/beeprint + +Support +------- + +None + +License +------- + +The project is licensed under the BSD license. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..fc6aa14 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,281 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. epub3 to make an epub3 + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + echo. dummy to check syntax errors of document sources + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 1>NUL 2>NUL +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Beeprint.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Beeprint.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "epub3" ( + %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +if "%1" == "dummy" ( + %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. Dummy builder generates no files. + goto end +) + +:end diff --git a/tests/data/all_in_one.txt b/tests/data/all_in_one.txt index d5820a5..a185c70 100644 --- a/tests/data/all_in_one.txt +++ b/tests/data/all_in_one.txt @@ -1,11 +1,18 @@ [ 1, 1.1, - 's', - 'us', - 'a中文', - 'a中文', - [1], + 'literal', + 'unicode', + 'literal中文', + 'unicode中文', + [1, 2, 3, 4, 5, 6], + [ + 1, + [2], + { + 'key': 'val', + }, + ], (1, 2), function(EmptyFunc), class(EmptyClassOldStyle), diff --git a/tests/definition.py b/tests/definition.py index a765e9e..d991644 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -45,25 +45,24 @@ def mth():pass {"simplify chinese versino": long_text_cn}, ] -out_of_range = [ - [1, [2.1, ['2.2.1', ['2.2.2.1']]]], +short_list = [1, 2, 3, 4, 5, 6] +complicated_list = [ + 1, + [2, ], { - 1: { - 1.1: { - '1.1.1': "value", - }, - }, + 'key': 'val', }, ] values = [ 1, 1.1, - "s", - u"us", - "a中文", - u"a中文", - [1], + "literal", + u"unicode", + "literal中文", + u"unicode中文", + short_list, + complicated_list, (1,2), EmptyFunc, EmptyClassOldStyle, From 6dbc37f2b56393af938664c73000a3ef057c8a12 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 26 Aug 2016 20:27:46 +0800 Subject: [PATCH 6/8] docs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d3e280..e8ceb03 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Features Contents === -[Examples](#Examples) -[Installation](#Installation) -[Settings](#Settings) +- [Examples](#examples) +- [Installation](#installation) +- [Settings](#settings) Examples === From 29fc86c49ec481dc88146f080648f99cf88852c7 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 26 Aug 2016 20:31:11 +0800 Subject: [PATCH 7/8] docs --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e8ceb03..5bc745c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -beeprint: Beautifully Print +beeprint === -pprint is good, but not clean. So beeprint do it. +make your debug printing more friendly Features === - print dict elegantly +- auto wrap text, including English and Chinese - outstanding mark to class and instance - compatible with py2 py3 -- auto wrap text, including English and Chinese Contents === From fd0ec7118218b17e2536807412e8de1d4bfa35e0 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Fri, 26 Aug 2016 20:43:44 +0800 Subject: [PATCH 8/8] readme --- README.md | 123 ++++++++++++++++++++++++++---------------------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 5bc745c..a8c7325 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,28 @@ Contents Examples === -Short list +Import beeprint as pp --- ``` +from beeprint.printer import beeprint as pp +``` + +Short List +--- + +``` +In [2]: alist = [1, 2, 3, 4, 5, 6] + +In [3]: pp(alist) [1, 2, 3, 4, 5, 6] ``` -Compliated list +Complicated List --- ``` +In [4]: clist = [1, [2], {'key': 'val'}] + +In [5]: pp(clist) [ 1, [2], @@ -36,9 +49,20 @@ Compliated list ] ``` -Class Instance Formatted Display +Class Instance --- ``` +In [6]: class NormalClassNewStyle(object): + ...: def mth():pass + ...: static_props = 1 + ...: lists = [] + ...: dicts = {} + ...: tupl = (1,2) + ...: + +In [7]: obj = NormalClassNewStyle() + +In [8]: pp(obj) instance(NormalClassNewStyle): dicts: { }, @@ -47,73 +71,44 @@ instance(NormalClassNewStyle): tupl: (1, 2) ``` -Long Text Auto Wrapping +Long Text --- - -> auto wrapped by terminal width - ``` -[ - '\nThe sky and the earth were at first one blurred entity like an egg. Pangu - was born into it.\n \nThe separation of the sky and the earth took eighteen - thousand years-the yang which was light and pure rose to become the sky, - and the yin which was heavy and murky(朦胧的) sank to form the earth. - Between them was Pangu, who went through nine changes every day, his wisdom - greater than that of the sky and his ability greater than that of the - earth. Every day the sky rose ten feet higher, the earth became ten feet - thicker, and Pangu grew ten feet taller.\n \nAnother eighteen thousand - years passed, and there was an extremely high sky, an extremely thick - earth, and an extremely tall Pangu. After Pangu died, his head turned into - the Five Sacred Mountains (Mount Tai, Mount Heng, Mount Hua, Mount Heng, - Mount Song), his eyes turned into the moon and the sun, his blood changed - into water in river and sea, his hair into grass.\n \nIn all, the universe - and Pangu combine in one.\n', - '\n据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐 - 使天空高远,大地辽阔。他为不使天地会重新合并,继续施展法术。每当盘古的身体 - 长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶天立地的巨 - 人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业 - 绩,死后永远留给后人无穷无尽的宝藏,成为中华民族崇拜的英雄。\n', -] +In [27]: long_text_en = "The separation of the sky and the earth took eighteen thousand years-the yang which was light and pure rose to become the sky, and the yin which was heavy and murky(朦胧的) sank to form the earth. Between them was Pangu, who went through nine changes every day, his wisdom greater than that of the sky and his ability greater than that of the earth. Every day the sky rose ten feet higher, the earth became ten feet thicker, and Pangu grew ten feet taller." + +In [28]: pp(long_text_en) +'The separation of the sky and the earth took eighteen thousand years-the yang which was light and pure rose to + become the sky, and the yin which was heavy and murky(朦胧的) sank to form the earth. Between them was Pangu, + who went through nine changes every day, his wisdom greater than that of the sky and his ability greater than that + of the earth. Every day the sky rose ten feet higher, the earth became ten feet thicker, and Pangu grew ten feet + taller.' + +In [30]: long_text_cn = "据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽阔。他为不使天地会重新合并,继续施展法术。每当盘古的身体长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成 一位顶天立地的巨人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业绩,死后永远留给后人无穷 无尽的宝藏,成为中华民族崇拜的英雄。"" + +In [31]: pp(long_text_cn) +'据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽阔。他为不使天地会重新合 + 并,继续施展法术。每当盘古的身体长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶天立地的巨人,而 + 天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业绩,死后永远留给后人无穷无尽的宝藏,成为中华民 + 族崇拜的英雄。' ``` -Long Text in Dict Auto Wrapping +Long Text in Dict --- - -> auto wrapped by terminal width - ``` -[ - { - 'english version': '\nThe sky and the earth were at first one blurred entity - like an egg. Pangu was born into it.\n \nThe separation - of the sky and the earth took eighteen thousand - years-the yang which was light and pure rose to become - the sky, and the yin which was heavy and murky(朦胧的) - sank to form the earth. Between them was Pangu, who went - through nine changes every day, his wisdom greater than - that of the sky and his ability greater than that of the - earth. Every day the sky rose ten feet higher, the earth - became ten feet thicker, and Pangu grew ten feet - taller.\n \nAnother eighteen thousand years passed, and - there was an extremely high sky, an extremely thick - earth, and an extremely tall Pangu. After Pangu died, - his head turned into the Five Sacred Mountains (Mount - Tai, Mount Heng, Mount Hua, Mount Heng, Mount Song), his - eyes turned into the moon and the sun, his blood changed - into water in river and sea, his hair into grass.\n \nIn - all, the universe and Pangu combine in one.\n', - }, - { - 'simplify chinese versino': '\n据民间神话传说古时盘古生在黑暗团中,他不能忍 - 受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽 - 阔。他为不使天地会重新合并,继续施展法术。每当 - 盘古的身体长高一尺,天空就随之增高一尺,经过1.8 - 万多年的努力,盘古变成一位顶天立地的巨人,而天 - 空也升得高不可及,大地也变得厚实无比。盘古生前 - 完成开天辟地的伟大业绩,死后永远留给后人无穷无 - 尽的宝藏,成为中华民族崇拜的英雄。\n', - }, -] +In [33]: d = {'en': long_text_en, 'cn': long_text_cn} + +In [34]: pp(d) +{ + 'en': 'The separation of the sky and the earth took eighteen thousand years-the yang which was light and pure + rose to become the sky, and the yin which was heavy and murky(朦胧的) sank to form the earth. Between + them was Pangu, who went through nine changes every day, his wisdom greater than that of the sky and his + ability greater than that of the earth. Every day the sky rose ten feet higher, the earth became ten feet + thicker, and Pangu grew ten feet taller.', + 'cn': '据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽阔。他为不使天地 + 会重新合并,继续施展法术。每当盘古的身体长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶 + 天立地的巨人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业绩,死后永远留给后人无 + 穷无尽的宝藏,成为中华民族崇拜的英雄。', +} ``` Complicated data