From 499d867c7e0af85d56356241c3946b19cbe95a32 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Wed, 8 Jun 2016 23:49:57 +0800 Subject: [PATCH 01/10] add gb2312 test --- tests/test_default_beeprint.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index bbe42f5..73e602f 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -40,9 +40,16 @@ def test_string(self): # utf8 string s = u'utf8 string'.encode('utf-8') if pyv == 2: - self.assertEqual(beeprint(s, output=False), "\"utf8 string\"\n") + self.assertEqual(beeprint(s, output=False), u"\"utf8 string\"\n") elif pyv == 3: - self.assertEqual(beeprint(s, output=False), "b'utf8 string'\n") + self.assertEqual(beeprint(s, output=False), u"b'utf8 string'\n") + + # gb2312 string + s = u'gb2312 string'.encode('gb2312') + if pyv == 2: + self.assertEqual(beeprint(s, output=False), u"\"gb2312 string\"\n") + elif pyv == 3: + self.assertEqual(beeprint(s, output=False), u"b'gb2312 string'\n") if __name__ == '__main__': From e913272cf873180615cc18f46e9aa4a1291c5565 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Sun, 12 Jun 2016 18:21:49 +0800 Subject: [PATCH 02/10] united string coding representation --- beeprint/constants.py | 1 + beeprint/printer.py | 39 ++++++++++++++++++---------------- beeprint/settings.py | 6 ++++++ makefile | 6 ++++++ tests/test_default_beeprint.py | 28 ++++++++++++------------ 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/beeprint/constants.py b/beeprint/constants.py index ee6563c..a6b1772 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -22,3 +22,4 @@ ST_LITERAL = 1 # string literal depends on script's coding ST_UNICODE = 2 ST_BYTES = 4 +ST_UNDEFINED = 0 diff --git a/beeprint/printer.py b/beeprint/printer.py index 7caafd6..58d80a2 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -342,20 +342,22 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): def typeval(v): try: - st = string_type(v) - ret = u'' - if st == C.ST_LITERAL: - ret = u'"' + pstr(v) + u'"' - elif st == C.ST_UNICODE: - ret = u"u'" + v + u"'" - elif st == C.ST_BYTES: - # in py3, printed string will enclose with b'' - ret = pstr(v) - else: - ret = pstr(v) + if S.united_str_coding_representation: + st = string_type(v) + ret = u'' + if st & C.ST_UNICODE != 0: + ret = u"u'" + v + u"'" + elif st & C.ST_BYTES != 0: + # in py3, printed string will enclose with b'' + # ret = pstr(v) + 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') + 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: @@ -372,19 +374,20 @@ def string_type(s): if pyv == 2: # in py2, string literal is both instance of str and bytes - # a literal string is str - # a utf8 string is str + # 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 + return C.ST_LITERAL | C.ST_BYTES else: # in py3, - # a literal string is str + # 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 + return C.ST_LITERAL | C.ST_UNICODE + + return C.ST_UNDEFINED diff --git a/beeprint/settings.py b/beeprint/settings.py index a5a801c..7f1dde9 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -27,3 +27,9 @@ priority_strategy = C._PS_CONTENT_FIRST debug = False + +# united_str_coding_representation +# In spite of python version +# unicode string will be displayed as u'' +# non-unicode string will be displayed as b'' +united_str_coding_representation = True diff --git a/makefile b/makefile index 7e9764e..00370c5 100644 --- a/makefile +++ b/makefile @@ -13,3 +13,9 @@ register: full: python setup.py sdist bdist_egg upload + +test27: + python2.7 -m unittest discover tests || true + +test35: + python3.5 -m unittest discover tests || true diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index 73e602f..6efeb3c 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -28,28 +28,30 @@ def test_negative(self): def test_string(self): # string literal # S.debug = True - self.assertEqual(beeprint("plain string", output=False), '"plain string"\n') + if pyv == 2: + self.assertEqual(beeprint("plain string", output=False), "b'plain string'\n") + elif pyv == 3: + self.assertEqual(beeprint("plain string", output=False), "u'plain string'\n") # unicode string s = u'unicode string' - if pyv == 2: - self.assertEqual(beeprint(s, output=False), u"u'unicode string'\n") - elif pyv == 3: - self.assertEqual(beeprint(s, output=False), u'"unicode string"\n') + self.assertEqual(beeprint(s, output=False), u"u'unicode string'\n") # utf8 string s = u'utf8 string'.encode('utf-8') - if pyv == 2: - self.assertEqual(beeprint(s, output=False), u"\"utf8 string\"\n") - elif pyv == 3: - self.assertEqual(beeprint(s, output=False), u"b'utf8 string'\n") + self.assertEqual(beeprint(s, output=False), u"b'utf8 string'\n") # gb2312 string s = u'gb2312 string'.encode('gb2312') - if pyv == 2: - self.assertEqual(beeprint(s, output=False), u"\"gb2312 string\"\n") - elif pyv == 3: - self.assertEqual(beeprint(s, output=False), u"b'gb2312 string'\n") + self.assertEqual(beeprint(s, output=False), u"b'gb2312 string'\n") + + # unicode special characters string + s = u'\\' + self.assertEqual(beeprint(s, output=False), u"u'\\'\n") + + # utf8 special characters string + s = u'\\'.encode("utf8") + self.assertEqual(beeprint(s, output=False), u"b'\\'\n") if __name__ == '__main__': From 9fdf5e27e8b601fd11720138a915cf344b44e5ae Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Mon, 13 Jun 2016 17:53:04 +0800 Subject: [PATCH 03/10] solved: _AS_VALUE_ shows wrong indent --- beeprint/constants.py | 13 +++++ beeprint/debug_kit.py | 14 +++-- beeprint/printer.py | 117 ++++++++++++++++++++++++++++++------- beeprint/settings.py | 5 +- tests/t.py | 131 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 253 insertions(+), 27 deletions(-) create mode 100644 tests/t.py diff --git a/beeprint/constants.py b/beeprint/constants.py index a6b1772..7b2521c 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -11,8 +11,16 @@ # 比如: _PS_CONTENT_FIRST = 2 +# an element will occupy a single block +# it has its own leading spaces _AS_ELEMENT_ = 1 +# 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 +# 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_DICT_ELEMENT_ = \ _AS_TUPLE_ELEMENT_ = \ @@ -23,3 +31,8 @@ ST_UNICODE = 2 ST_BYTES = 4 ST_UNDEFINED = 0 + +# debug level +_DL_MODULE_ = 1 +_DL_FUNC_ = 2 +_DL_STATEMENT = 3 diff --git a/beeprint/debug_kit.py b/beeprint/debug_kit.py index 0964d24..0d517ff 100644 --- a/beeprint/debug_kit.py +++ b/beeprint/debug_kit.py @@ -5,6 +5,7 @@ from __future__ import division from . import settings as S from . import constants as C +import inspect ### global variables @@ -16,7 +17,12 @@ def add_leading(depth, text): return text def debug(level, depth, text): - if S.debug is False: - return - text = add_leading(depth, text) - print(text) + if S.debug_level >= level: + frame_list = inspect.stack() + caller_name = frame_list[1][3] + depth = len(frame_list) - 4 + if level == C._DL_FUNC_: + depth -= 1 + text = caller_name + ': ' + text + text = add_leading(depth, text) + print(text) diff --git a/beeprint/printer.py b/beeprint/printer.py index 58d80a2..2867c47 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -6,6 +6,7 @@ import sys import traceback import types +import inspect if sys.version_info < (3, 0): # avoid throw [UnicodeEncodeError: 'ascii' codec can't encode characters] @@ -32,14 +33,15 @@ def object_attr_default_filter(obj, name, val): return True for prop in S.prop_filters: - # filter is a type - if type(prop) == types.TypeType: - if type(val) == prop: - return True # filter is a string - elif isinstance(prop, basestring) or isinstance(prop, str): + if isinstance(prop, str) or isinstance(prop, unicode): if name == prop: return True + # filter is a type + # if type(prop) == types.TypeType: + elif isinstance(prop, type): + if type(val) == prop: + return True # filter is callable elif hasattr(prop, '__call__'): return prop(name, val) @@ -141,7 +143,9 @@ def tail_symbol(position): def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): '遍历对象,判断对象内成员的类型,然后调用对应的 build_*_block() 处理' - debug(0, leadCnt, 'ready to build %s:%s' % (type(obj), obj)) + debug(C._DL_FUNC_, leadCnt, + ('obj:%s leadCnt:%s position:%d' \ + % (obj, leadCnt, position))) ret = pstr('') @@ -157,20 +161,20 @@ def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): return _b(ret) if isinstance(obj, dict): - debug(0, leadCnt, 'is dict') + debug(C._DL_STATEMENT, leadCnt, 'is dict') ret += build_dict_block(obj, leadCnt, position) elif isinstance(obj, list): - debug(0, leadCnt, 'is list') + debug(C._DL_STATEMENT, leadCnt, 'is list') ret += build_list_block(obj, leadCnt, position) elif isinstance(obj, tuple): - debug(0, leadCnt, 'is 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(0, leadCnt, 'is extendable') + debug(C._DL_STATEMENT, leadCnt, 'is extendable') ret += build_class_block(obj, leadCnt, position) else: - debug(0, leadCnt, 'is simple type') + debug(C._DL_STATEMENT, leadCnt, 'is simple type') ret += _b(leadCnt * S.leading + typeval(obj) + pstr(tail + '\n')) return ret @@ -182,19 +186,31 @@ def is_extendable(obj): 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) ret += _b(S.leading * leadCnt + typeval(name) + ':') if is_extendable(val) and S.maxDeep > leadCnt: - if S.newline or isinstance(val, (types.InstanceType, types.FunctionType)): + # 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, C._AS_VALUE_) + ret += build_single_block(val, leadCnt, position) else: if S.maxDeep <= leadCnt: ret += _b(pstr(" %s\n" % tail)) @@ -248,6 +264,8 @@ def build_tuple_block(o, leadCnt=0, position=C._AS_VALUE_): _f = map(lambda e: not is_extendable(e), o) if all(_f): _o = map(lambda e: typeval(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 @@ -279,8 +297,8 @@ def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): ret += _b(pstr('{') + pstr('\n')) # body - for k in o: - v = o[k] + 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(": ") @@ -294,14 +312,30 @@ 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, + ('obj:%s leadCnt:%s position:%d' \ + % (o, leadCnt, position))) ret = pstr('') # { - _leading = S.leading * leadCnt - - if hasattr(o, '__class__'): - ret += _b(_leading + pstr('object(%s):' % + _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: '本身就是类,不是对象' ret += _b(_leading + pstr('class(%s):' % o.__name__) + pstr('\n')) @@ -330,10 +364,13 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): position = C._AS_CLASS_ELEMENT_ #'忽略掉 以__开头的成员、自引用成员、函数成员' - ret += build_pair_block(attr, val, leadCnt + 1, position) + ret += build_pair_block(attr, + val, + leadCnt + 1, + position & C._AS_CLASS_ELEMENT_) ret += pstr((leadCnt + 1) * S.leading) + \ - pstr("\n" % filter_count) + pstr("<... %d props>\n" % filter_count) # } #ret += S.leading*leadCnt + '}' + pstr('\n') @@ -391,3 +428,41 @@ def string_type(s): return C.ST_LITERAL | C.ST_UNICODE return C.ST_UNDEFINED + + +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__ + + if (inspect.isclass(o) + or inspect.isfunction(o) + or inspect.ismethod(o)): + return False + return True + except: + pass + return False diff --git a/beeprint/settings.py b/beeprint/settings.py index 7f1dde9..5c9b7fd 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -21,12 +21,13 @@ # 过滤以 x 开头的属性 prop_leading_filters = ["__", "func_"] # 根据类型过滤对象的属性 -prop_filters = [types.MethodType] +prop_filters = [types.BuiltinFunctionType, types.BuiltinMethodType, 'im_func', 'im_self', 'im_class'] # >> 优先策略 priority_strategy = C._PS_CONTENT_FIRST -debug = False +# debug = False +debug_level = 0 # united_str_coding_representation # In spite of python version diff --git a/tests/t.py b/tests/t.py new file mode 100644 index 0000000..576d535 --- /dev/null +++ b/tests/t.py @@ -0,0 +1,131 @@ +# -*- coding:utf-8 -*- +from __future__ import print_function + +import unittest +import os +import sys +import types +import inspect + +CUR_SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) +BEEPRINT_PATH = os.path.abspath(os.path.join(CUR_SCRIPT_PATH, '..')) +sys.path.append(BEEPRINT_PATH) + +if sys.version_info < (3, 0): + # 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') + + pyv = 2 +else: + unicode = str + pyv = 3 + +from beeprint.printer import beeprint as pp, pyv +from beeprint import settings as S + +d = { + "key": "val", + "list": [1, 2, 3, "string", 1.2], + "dict": { + "key": "val", + }, +} + +def f(): pass + +class c: + def mth():pass + static_props = 1 + +class c2(object): + def mth():pass + static_props = 1 + +ic = c() +ic2 = c2() + +values = [ + 1, + 1.1, + "s", + u"us", + "a中文", + u"a中文", + [1], + (1,2), + f, + c, + c2, + ic, + ic2, + ic.mth, + ic2.mth, + { + 'key': 'val', + u'key2': u'val', + }, +] + +# >> utilities +def detect_same_attrs(*args): + d_attrs_by_val_list = {} + for e in args: + for attr in dir(e): + d_attrs_by_val_list.setdefault(attr, []) + d_attrs_by_val_list[attr].append(e) + + same_attrs = [] + for attr, values in d_attrs_by_val_list.items(): + if len(values) == len(args): + same_attrs.append((attr, values)) + + same_attrs.sort(key=lambda e: e[0]) + + return same_attrs +# << utilities + +def class_test(): + pass + +def inst_test(): + for v in values: + try: + print('%40s: %s' % (v, v.__module__)) + except: + pass + continue + if pyv == 2: + print('%40s: %s' % (v, isinstance(v, (types.InstanceType, object)))) + else: + print('%40s: %s' % (v, isinstance(v, object))) + + same_attrs = detect_same_attrs(ic, ic2) + for attr, v in same_attrs: + print('%40s: %s' % (attr, v)) + +def builtin_test(): + for v in [ic.mth.im_func, ic2.mth.im_func]: + print('%40s: %s' % (v, inspect.ismethod(v))) + +args = { + "class_test": class_test, + "inst_test": inst_test, + "builtin_test": builtin_test, +} + +def main(): + if len(sys.argv) == 1: + # S.debug_level = 9 + pp(values) + # pp([ic.mth, ic2.mth]) + return + + for i in range(1, len(sys.argv)): + fn = sys.argv[i] + args[fn]() + +if __name__ == '__main__': + main() From 654042918b09e9cd188e5fe081ea85c989bbb174 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Mon, 13 Jun 2016 18:10:48 +0800 Subject: [PATCH 04/10] improve: make output cleaner --- beeprint/printer.py | 15 +++++++++++---- beeprint/settings.py | 2 +- tests/t.py | 5 +++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/beeprint/printer.py b/beeprint/printer.py index 2867c47..9beaecc 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -193,7 +193,12 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): tail = tail_symbol(position) - ret += _b(S.leading * leadCnt + typeval(name) + ':') + 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) + ret += _b(S.leading * leadCnt + name + ':') if is_extendable(val) and S.maxDeep > leadCnt: # value need to be dispalyed on new line # including: @@ -367,10 +372,12 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): ret += build_pair_block(attr, val, leadCnt + 1, - position & C._AS_CLASS_ELEMENT_) + position | C._AS_CLASS_ELEMENT_) - ret += pstr((leadCnt + 1) * S.leading) + \ - pstr("<... %d props>\n" % filter_count) + if filter_count == props_cnt: + ret = ret[:-2] + pstr(',\n') + # ret += pstr((leadCnt + 1) * S.leading) + \ + # pstr("<... %d props>\n" % filter_count) # } #ret += S.leading*leadCnt + '}' + pstr('\n') diff --git a/beeprint/settings.py b/beeprint/settings.py index 5c9b7fd..6949080 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -21,7 +21,7 @@ # 过滤以 x 开头的属性 prop_leading_filters = ["__", "func_"] # 根据类型过滤对象的属性 -prop_filters = [types.BuiltinFunctionType, types.BuiltinMethodType, 'im_func', 'im_self', 'im_class'] +prop_filters = [types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType, 'im_func', 'im_self', 'im_class'] # >> 优先策略 priority_strategy = C._PS_CONTENT_FIRST diff --git a/tests/t.py b/tests/t.py index 576d535..d7862e1 100644 --- a/tests/t.py +++ b/tests/t.py @@ -36,6 +36,9 @@ def f(): pass +class CE: pass +class CE2(object): pass + class c: def mth():pass static_props = 1 @@ -57,6 +60,8 @@ def mth():pass [1], (1,2), f, + CE, + CE2, c, c2, ic, From 0929d97af2466c3f959f813cad02d8ad967f83b6 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Tue, 14 Jun 2016 14:26:39 +0800 Subject: [PATCH 05/10] avoid importing root directory --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 From 4843c5d0af98765c74d35e087e8ec4fc0e283481 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Tue, 14 Jun 2016 14:36:38 +0800 Subject: [PATCH 06/10] improve: string display not prefix u and b --- beeprint/printer.py | 10 ++++++++-- beeprint/settings.py | 2 ++ tests/test_default_beeprint.py | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/beeprint/printer.py b/beeprint/printer.py index 9beaecc..e9081b0 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -390,11 +390,17 @@ def typeval(v): st = string_type(v) ret = u'' if st & C.ST_UNICODE != 0: - ret = u"u'" + v + u"'" + 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) - ret = u"b'" + v.decode(S.encoding) + u"'" + 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) diff --git a/beeprint/settings.py b/beeprint/settings.py index 6949080..193308e 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -34,3 +34,5 @@ # unicode string will be displayed as u'' # non-unicode string will be displayed as b'' united_str_coding_representation = True +str_display_not_prefix_u = True +str_display_not_prefix_b = True diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index 6efeb3c..cf735c9 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -28,6 +28,8 @@ def test_negative(self): def test_string(self): # string literal # S.debug = True + S.str_display_not_prefix_u = False + S.str_display_not_prefix_b = False if pyv == 2: self.assertEqual(beeprint("plain string", output=False), "b'plain string'\n") elif pyv == 3: From e077a4c22a28bc5cbdaf4071ad61e8a0293a5798 Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Tue, 14 Jun 2016 15:16:51 +0800 Subject: [PATCH 07/10] solved: dict key no quotes; dict element no comma --- beeprint/constants.py | 7 ++-- beeprint/printer.py | 6 +-- tests/data/tests_complicate_data.txt | 29 ++++++++++++++ tests/data/tests_complicate_data2.txt | 29 ++++++++++++++ tests/t.py | 3 ++ tests/test_default_beeprint.py | 58 +++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 tests/data/tests_complicate_data.txt create mode 100644 tests/data/tests_complicate_data2.txt diff --git a/beeprint/constants.py b/beeprint/constants.py index 7b2521c..a22c181 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -22,9 +22,10 @@ # it must has a parent block # eg: [1, 2], {'key1': 'val1', 'key2': 'val2'}, (1, 2) _AS_LIST_ELEMENT_ = \ - _AS_DICT_ELEMENT_ = \ - _AS_TUPLE_ELEMENT_ = \ - _AS_CLASS_ELEMENT_ = 4 + _AS_TUPLE_ELEMENT_ = 4 + +_AS_DICT_ELEMENT_ = 8 +_AS_CLASS_ELEMENT_ = 16 # string type ST_LITERAL = 1 # string literal depends on script's coding diff --git a/beeprint/printer.py b/beeprint/printer.py index e9081b0..3624335 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -134,9 +134,9 @@ def tail_symbol(position): position & C._AS_DICT_ELEMENT_ or position & C._AS_CLASS_ELEMENT_ or position & C._AS_TUPLE_ELEMENT_): - tail = ',' + tail = u',' else: - tail = '' + tail = u'' return tail @@ -308,7 +308,7 @@ def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): continue #ret += S.leading*(leadCnt + 1) + typeval(k) + pstr(": ") #ret += build_single_block(v, leadCnt+1) - ret += build_pair_block(k, v, leadCnt + 1) + ret += build_pair_block(k, v, leadCnt + 1, C._AS_DICT_ELEMENT_) # } ret += _b(S.leading * leadCnt + '}' + pstr(tail + '\n')) diff --git a/tests/data/tests_complicate_data.txt b/tests/data/tests_complicate_data.txt new file mode 100644 index 0000000..946056e --- /dev/null +++ b/tests/data/tests_complicate_data.txt @@ -0,0 +1,29 @@ +[ + 1, + 1.1, + u's', + u'us', + u'a中文', + u'a中文', + [1], + (1, 2), + function(f), + class(CE), + class(CE2), + class(c): + mth: function(mth), + static_props: 1, + class(c2): + mth: function(mth), + static_props: 1, + instance(c): + static_props: 1, + instance(c2): + static_props: 1, + method(mth), + method(mth), + { + u'key': u'val', + u'key2': u'val', + }, +] diff --git a/tests/data/tests_complicate_data2.txt b/tests/data/tests_complicate_data2.txt new file mode 100644 index 0000000..52e5fce --- /dev/null +++ b/tests/data/tests_complicate_data2.txt @@ -0,0 +1,29 @@ +[ + 1, + 1.1, + u's', + u'us', + u'a中文', + u'a中文', + [1], + (1, 2), + function(f), + class(CE), + class(CE2), + class(c): + mth: function(mth), + static_props: 1, + class(c2): + mth: function(mth), + static_props: 1, + instance(c): + static_props: 1, + instance(c2): + static_props: 1, + method(mth), + method(mth), + { + u'key2': u'val', + u'key': u'val', + }, +] diff --git a/tests/t.py b/tests/t.py index d7862e1..a6e6d2b 100644 --- a/tests/t.py +++ b/tests/t.py @@ -124,6 +124,9 @@ def builtin_test(): def main(): if len(sys.argv) == 1: # S.debug_level = 9 + S.str_display_not_prefix_u = False + S.str_display_not_prefix_b = False + pp(values) # pp([ic.mth, ic2.mth]) return diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index cf735c9..66c8788 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -12,6 +12,46 @@ from beeprint import settings as S +def f(): pass + +class CE: pass +class CE2(object): pass + +class c: + def mth():pass + static_props = 1 + +class c2(object): + def mth():pass + static_props = 1 + +ic = c() +ic2 = c2() + +values = [ + 1, + 1.1, + "s", + u"us", + "a中文", + u"a中文", + [1], + (1,2), + f, + CE, + CE2, + c, + c2, + ic, + ic2, + ic.mth, + ic2.mth, + { + 'key': 'val', + u'key2': u'val', + }, +] + class TestSimpleTypes(unittest.TestCase): def setUp(self): @@ -55,6 +95,24 @@ def test_string(self): s = u'\\'.encode("utf8") self.assertEqual(beeprint(s, output=False), u"b'\\'\n") + def test_complicate_data(self): + S.str_display_not_prefix_u = False + S.str_display_not_prefix_b = False + + ans = u"" + with open(os.path.join(CUR_SCRIPT_PATH, + 'data/tests_complicate_data.txt')) as fp: + ans = fp.read() + + # to prevent comparing fail in unordered keys to dict + ans2 = u"" + with open(os.path.join(CUR_SCRIPT_PATH, + 'data/tests_complicate_data2.txt')) as fp: + ans2 = fp.read() + + res = beeprint(values, output=False) + self.assertEqual(res == ans or res == ans2, True) + if __name__ == '__main__': unittest.main() From 76c26fcea1b158078413bb3e6a1893fad37ea28e Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Wed, 15 Jun 2016 20:26:05 +0800 Subject: [PATCH 08/10] improve: handle all sort of method --- beeprint/printer.py | 33 +++++++------ beeprint/settings.py | 5 +- beeprint/utils.py | 34 +++++++++++++ tests/__init__.py | 0 tests/data/tests_complicate_data.txt | 21 ++++---- tests/data/tests_complicate_data2.txt | 21 ++++---- tests/definition.py | 43 +++++++++++++++++ tests/t.py | 69 +++++++-------------------- tests/test_default_beeprint.py | 59 ++++++----------------- tests/test_utils.py | 42 ++++++++++++++++ 10 files changed, 200 insertions(+), 127 deletions(-) create mode 100644 beeprint/utils.py create mode 100644 tests/__init__.py create mode 100644 tests/definition.py create mode 100644 tests/test_utils.py diff --git a/beeprint/printer.py b/beeprint/printer.py index 3624335..788f783 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -8,17 +8,16 @@ import types import inspect -if sys.version_info < (3, 0): +from .utils import pyv + +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') - - pyv = 2 else: unicode = str - pyv = 3 from . import settings as S from . import constants as C @@ -39,12 +38,13 @@ def object_attr_default_filter(obj, name, val): return True # filter is a type # if type(prop) == types.TypeType: - elif isinstance(prop, type): + if isinstance(prop, type): if type(val) == prop: return True # filter is callable elif hasattr(prop, '__call__'): - return prop(name, val) + if prop(name, val): + return True return False @@ -208,12 +208,12 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): position & C._AS_ELEMENT_): ret += _b(pstr('\n')) leadCnt = leadCnt + 1 - 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_VALUE_ + position |= C._AS_VALUE_ ret += build_single_block(val, leadCnt, position) else: @@ -221,6 +221,7 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): ret += _b(pstr(" %s\n" % tail)) else: ret += _b(pstr(" ") + typeval(val) + pstr(tail + '\n')) + return ret @@ -311,7 +312,7 @@ def build_dict_block(o, leadCnt=0, position=C._AS_VALUE_): ret += build_pair_block(k, v, leadCnt + 1, C._AS_DICT_ELEMENT_) # } - ret += _b(S.leading * leadCnt + '}' + pstr(tail + '\n')) + ret += _b(S.leading * leadCnt + '}' + pstr(tail + u'\n')) return ret @@ -323,6 +324,8 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): % (o, leadCnt, position))) ret = pstr('') + tail = tail_symbol(position) + # { _leading = pstr('') if position & C._AS_ELEMENT_: @@ -374,13 +377,15 @@ def build_class_block(o, leadCnt=0, position=C._AS_ELEMENT_): leadCnt + 1, position | C._AS_CLASS_ELEMENT_) + # } if filter_count == props_cnt: - ret = ret[:-2] + pstr(',\n') - # ret += pstr((leadCnt + 1) * S.leading) + \ - # pstr("<... %d props>\n" % filter_count) + # right strip ':\n' + ret = ret[:-2] + else: + # right strip ',\n' which belongs to last element of class + ret = ret[:-2] - # } - #ret += S.leading*leadCnt + '}' + pstr('\n') + ret += pstr(tail + u'\n') return ret diff --git a/beeprint/settings.py b/beeprint/settings.py index 193308e..c35fb68 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -8,6 +8,7 @@ from . import constants as C +from . import utils outfile = sys.stdout encoding = 'utf-8' @@ -21,7 +22,7 @@ # 过滤以 x 开头的属性 prop_leading_filters = ["__", "func_"] # 根据类型过滤对象的属性 -prop_filters = [types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType, 'im_func', 'im_self', 'im_class'] +prop_filters = [utils.is_pan_function, 'im_func', 'im_self', 'im_class'] # >> 优先策略 priority_strategy = C._PS_CONTENT_FIRST @@ -36,3 +37,5 @@ united_str_coding_representation = True str_display_not_prefix_u = True str_display_not_prefix_b = True + +element_display_last_with_comma = True diff --git a/beeprint/utils.py b/beeprint/utils.py new file mode 100644 index 0000000..8595e4f --- /dev/null +++ b/beeprint/utils.py @@ -0,0 +1,34 @@ +# -*- 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 + + +if sys.version_info < (3, 0): + pyv = 2 +else: + pyv = 3 + +def is_class_method(name, val): + if pyv == 2: + return isinstance(val, types.MethodType) and val.im_self is None + elif pyv == 3: + # in python 2, a class method is unbound method type + # in python 3, a class method is function type as well as a function + raise Exception("python 3 only has function type and bound method type") + +def is_instance_method(name, val): + if pyv == 3: + return inspect.ismethod(val) + elif pyv == 2: + return isinstance(val, types.MethodType) and val.im_self is not None + +def is_pan_function(name, val): + """detect pan-function which including function and bound method in python 3 + function and unbound method and bound method in python 2 + """ + return inspect.isfunction(val) or inspect.ismethod(val) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/tests_complicate_data.txt b/tests/data/tests_complicate_data.txt index 946056e..f4d3bdc 100644 --- a/tests/data/tests_complicate_data.txt +++ b/tests/data/tests_complicate_data.txt @@ -1,29 +1,34 @@ [ 1, 1.1, - u's', - u'us', - u'a中文', - u'a中文', + 's', + 'us', + 'a中文', + 'a中文', [1], (1, 2), function(f), class(CE), class(CE2), class(c): - mth: function(mth), static_props: 1, class(c2): - mth: function(mth), + dicts: { + }, + lists: [], static_props: 1, instance(c): static_props: 1, instance(c2): + dicts: { + }, + lists: [], static_props: 1, method(mth), method(mth), { - u'key': u'val', - u'key2': u'val', + 'key': [], + 'key2': { + }, }, ] diff --git a/tests/data/tests_complicate_data2.txt b/tests/data/tests_complicate_data2.txt index 52e5fce..d8b02a6 100644 --- a/tests/data/tests_complicate_data2.txt +++ b/tests/data/tests_complicate_data2.txt @@ -1,29 +1,34 @@ [ 1, 1.1, - u's', - u'us', - u'a中文', - u'a中文', + 's', + 'us', + 'a中文', + 'a中文', [1], (1, 2), function(f), class(CE), class(CE2), class(c): - mth: function(mth), static_props: 1, class(c2): - mth: function(mth), + dicts: { + }, + lists: [], static_props: 1, instance(c): static_props: 1, instance(c2): + dicts: { + }, + lists: [], static_props: 1, method(mth), method(mth), { - u'key2': u'val', - u'key': u'val', + 'key2': { + }, + 'key': [], }, ] diff --git a/tests/definition.py b/tests/definition.py new file mode 100644 index 0000000..c6612e9 --- /dev/null +++ b/tests/definition.py @@ -0,0 +1,43 @@ +# -*- coding:utf-8 -*- + +def f(): pass + +class CE: pass +class CE2(object): pass + +class c: + def mth():pass + static_props = 1 + +class c2(object): + def mth():pass + static_props = 1 + lists = [] + dicts = {} + +ic = c() +ic2 = c2() + +values = [ + 1, + 1.1, + "s", + u"us", + "a中文", + u"a中文", + [1], + (1,2), + f, + CE, + CE2, + c, + c2, + ic, + ic2, + ic.mth, + ic2.mth, + { + 'key': [], + u'key2': {}, + }, +] diff --git a/tests/t.py b/tests/t.py index a6e6d2b..282db4e 100644 --- a/tests/t.py +++ b/tests/t.py @@ -1,5 +1,8 @@ # -*- coding:utf-8 -*- from __future__ import print_function +from __future__ import absolute_import + +from imp import reload import unittest import os @@ -7,6 +10,7 @@ import types import inspect + CUR_SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) BEEPRINT_PATH = os.path.abspath(os.path.join(CUR_SCRIPT_PATH, '..')) sys.path.append(BEEPRINT_PATH) @@ -14,7 +18,6 @@ if sys.version_info < (3, 0): # 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') @@ -26,53 +29,13 @@ from beeprint.printer import beeprint as pp, pyv from beeprint import settings as S -d = { - "key": "val", - "list": [1, 2, 3, "string", 1.2], - "dict": { - "key": "val", - }, -} +try: + from .definition import values + from .definition import ic, ic2, c, c2, f +except: + from definition import values + from definition import ic, ic2, c, c2, f -def f(): pass - -class CE: pass -class CE2(object): pass - -class c: - def mth():pass - static_props = 1 - -class c2(object): - def mth():pass - static_props = 1 - -ic = c() -ic2 = c2() - -values = [ - 1, - 1.1, - "s", - u"us", - "a中文", - u"a中文", - [1], - (1,2), - f, - CE, - CE2, - c, - c2, - ic, - ic2, - ic.mth, - ic2.mth, - { - 'key': 'val', - u'key2': u'val', - }, -] # >> utilities def detect_same_attrs(*args): @@ -112,8 +75,12 @@ def inst_test(): print('%40s: %s' % (attr, v)) def builtin_test(): - for v in [ic.mth.im_func, ic2.mth.im_func]: - print('%40s: %s' % (v, inspect.ismethod(v))) + for v in [f, c.mth, c2.mth, ic.mth, ic2.mth]: + # print('%40s: %s' % (v, isinstance(v, types.MethodType))) py2 all true + # print('%40s: %s' % (v, inspect.ismethod(v))) py2 all true + # print('%40s: %s' % (v, inspect.isbuiltin(v))) py2 all false + # print('%40s: %s' % (v, inspect.ismethod(v))) py3 FFTT + print('%40s: %s, %s' % (v, v.__qualname__, inspect.getargspec(v).args)) args = { "class_test": class_test, @@ -124,8 +91,8 @@ def builtin_test(): def main(): if len(sys.argv) == 1: # S.debug_level = 9 - S.str_display_not_prefix_u = False - S.str_display_not_prefix_b = False + # S.str_display_not_prefix_u = False + # S.str_display_not_prefix_b = False pp(values) # pp([ic.mth, ic2.mth]) diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index 66c8788..c2f5a52 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -3,6 +3,7 @@ import unittest import os import sys +import codecs CUR_SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) BEEPRINT_PATH = os.path.abspath(os.path.join(CUR_SCRIPT_PATH, '..')) @@ -11,46 +12,11 @@ from beeprint.printer import beeprint, pyv from beeprint import settings as S +try: + from .definition import values +except: + from definition import values -def f(): pass - -class CE: pass -class CE2(object): pass - -class c: - def mth():pass - static_props = 1 - -class c2(object): - def mth():pass - static_props = 1 - -ic = c() -ic2 = c2() - -values = [ - 1, - 1.1, - "s", - u"us", - "a中文", - u"a中文", - [1], - (1,2), - f, - CE, - CE2, - c, - c2, - ic, - ic2, - ic.mth, - ic2.mth, - { - 'key': 'val', - u'key2': u'val', - }, -] class TestSimpleTypes(unittest.TestCase): @@ -96,22 +62,25 @@ def test_string(self): self.assertEqual(beeprint(s, output=False), u"b'\\'\n") def test_complicate_data(self): - S.str_display_not_prefix_u = False - S.str_display_not_prefix_b = False + # S.str_display_not_prefix_u = False + # S.str_display_not_prefix_b = False ans = u"" - with open(os.path.join(CUR_SCRIPT_PATH, - 'data/tests_complicate_data.txt')) as fp: + data_path = os.path.join(CUR_SCRIPT_PATH, + 'data/tests_complicate_data.txt') + with codecs.open(data_path, encoding="utf8") as fp: ans = fp.read() # to prevent comparing fail in unordered keys to dict ans2 = u"" - with open(os.path.join(CUR_SCRIPT_PATH, - 'data/tests_complicate_data2.txt')) as fp: + 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) + # self.assertEqual(res, ans) if __name__ == '__main__': diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..d370a04 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,42 @@ +# -*- coding:utf-8 -*- + +import unittest +import os +import sys +import codecs + +CUR_SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) +BEEPRINT_PATH = os.path.abspath(os.path.join(CUR_SCRIPT_PATH, '..')) +sys.path.append(BEEPRINT_PATH) + +from beeprint.printer import beeprint, pyv +from beeprint import settings as S +from beeprint.utils import is_class_method, is_instance_method + +try: + from .definition import values, c, c2, ic, ic2, f +except: + from definition import values, c, c2, ic, ic2, f + +class TestUtils(unittest.TestCase): + + def setUp(self): + pass + + def test_class_method(self): + self.assertEqual(is_class_method('', f), False) + self.assertEqual(is_class_method('', c.mth), True) + self.assertEqual(is_class_method('', c2.mth), True) + self.assertEqual(is_class_method('', ic.mth), False) + self.assertEqual(is_class_method('', ic2.mth), False) + + def test_instance_method(self): + self.assertEqual(is_instance_method('', f), False) + self.assertEqual(is_instance_method('', c.mth), False) + self.assertEqual(is_instance_method('', c2.mth), False) + self.assertEqual(is_instance_method('', ic.mth), True) + self.assertEqual(is_instance_method('', ic2.mth), True) + + +if __name__ == '__main__': + unittest.main() From a77d51dc87643c00f340805ab85eee0f7baf949d Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Thu, 16 Jun 2016 16:08:13 +0800 Subject: [PATCH 09/10] prepare for distributing --- README.md | 1 + README.txt | 45 +++++++++++++++++++++++++-- beeprint/constants.py | 8 ++--- beeprint/printer.py | 24 +++++++------- beeprint/settings.py | 17 +++++++--- tests/data/tests_complicate_data.txt | 14 ++++----- tests/data/tests_complicate_data2.txt | 14 ++++----- tests/definition.py | 32 +++++++++---------- tests/t.py | 10 +++--- 9 files changed, 108 insertions(+), 57 deletions(-) create mode 120000 README.md diff --git a/README.md b/README.md new file mode 120000 index 0000000..c3ca074 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +README.txt \ No newline at end of file diff --git a/README.txt b/README.txt index 9790e9a..4b7c3fd 100644 --- a/README.txt +++ b/README.txt @@ -1,12 +1,53 @@ -beeprint: Beautiful Print +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 === -Plain Variables +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/beeprint/constants.py b/beeprint/constants.py index a22c181..50bd586 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -28,10 +28,10 @@ _AS_CLASS_ELEMENT_ = 16 # string type -ST_LITERAL = 1 # string literal depends on script's coding -ST_UNICODE = 2 -ST_BYTES = 4 -ST_UNDEFINED = 0 +_ST_LITERAL_ = 1 # string literal depends on script's coding +_ST_UNICODE_ = 2 +_ST_BYTES_ = 4 +_ST_UNDEFINED_ = 0 # debug level _DL_MODULE_ = 1 diff --git a/beeprint/printer.py b/beeprint/printer.py index 788f783..1d3cb6b 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -55,8 +55,8 @@ def dict_key_filter(obj, name, val): def _b(s): if S.write_to_buffer_when_execute: - S.bufferHandle.write(s) - S.bufferHandle.flush() + S.buffer_handler.write(s) + S.buffer_handler.flush() return s def pstr(s): @@ -151,7 +151,7 @@ def build_single_block(obj, leadCnt=0, position=C._AS_ELEMENT_): tail = tail_symbol(position) - if S.maxDeep < leadCnt: + if S.max_depth < leadCnt: if S.newline or position & C._AS_ELEMENT_: ret = pstr(leadCnt * S.leading) + pstr("\n") else: @@ -199,7 +199,7 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): else: name = typeval(name) ret += _b(S.leading * leadCnt + name + ':') - if is_extendable(val) and S.maxDeep > leadCnt: + if is_extendable(val) and S.max_depth > leadCnt: # value need to be dispalyed on new line # including: # class type & class instance @@ -217,7 +217,7 @@ def build_pair_block(name, val, leadCnt=0, position=C._AS_ELEMENT_): ret += build_single_block(val, leadCnt, position) else: - if S.maxDeep <= leadCnt: + if S.max_depth <= leadCnt: ret += _b(pstr(" %s\n" % tail)) else: ret += _b(pstr(" ") + typeval(val) + pstr(tail + '\n')) @@ -394,12 +394,12 @@ def typeval(v): if S.united_str_coding_representation: st = string_type(v) ret = u'' - if st & C.ST_UNICODE != 0: + 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: + elif st & C._ST_BYTES_ != 0: # in py3, printed string will enclose with b'' # ret = pstr(v) if S.str_display_not_prefix_b: @@ -432,20 +432,20 @@ def string_type(s): # 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 + return C._ST_UNICODE_ elif isinstance(s, str): # same as isinstance(v, bytes) - return C.ST_LITERAL | C.ST_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 + return C._ST_BYTES_ elif isinstance(s, str): - return C.ST_LITERAL | C.ST_UNICODE + return C._ST_LITERAL_ | C._ST_UNICODE_ - return C.ST_UNDEFINED + return C._ST_UNDEFINED_ def is_newline_obj(o): diff --git a/beeprint/settings.py b/beeprint/settings.py index c35fb68..0e748c7 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -10,21 +10,30 @@ from . import constants as C from . import utils -outfile = sys.stdout +# >> coding encoding = 'utf-8' -maxDeep = 5 + +# >> representation +max_depth = 5 leading = u' ' newline = False -write_to_buffer_when_execute = False -bufferHandle = sys.stdout tuple_in_line = True list_in_line = True + +# >> buffer +buffer_handler = sys.stdout +# use buffer_handler.flush() every print +write_to_buffer_when_execute = False + +# >> class controll # 过滤以 x 开头的属性 prop_leading_filters = ["__", "func_"] # 根据类型过滤对象的属性 prop_filters = [utils.is_pan_function, 'im_func', 'im_self', 'im_class'] # >> 优先策略 +# to raise exception or not when errors happened +# _PS_CONTENT_FIRST will keep content printing despite of any error priority_strategy = C._PS_CONTENT_FIRST # debug = False diff --git a/tests/data/tests_complicate_data.txt b/tests/data/tests_complicate_data.txt index f4d3bdc..8398a40 100644 --- a/tests/data/tests_complicate_data.txt +++ b/tests/data/tests_complicate_data.txt @@ -7,19 +7,19 @@ 'a中文', [1], (1, 2), - function(f), - class(CE), - class(CE2), - class(c): + function(EmptyFunc), + class(EmptyClassOldStyle), + class(EmptyClassNewStyle), + class(NormalClassOldStyle): static_props: 1, - class(c2): + class(NormalClassNewStyle): dicts: { }, lists: [], static_props: 1, - instance(c): + instance(NormalClassOldStyle): static_props: 1, - instance(c2): + instance(NormalClassNewStyle): dicts: { }, lists: [], diff --git a/tests/data/tests_complicate_data2.txt b/tests/data/tests_complicate_data2.txt index d8b02a6..7a3cac2 100644 --- a/tests/data/tests_complicate_data2.txt +++ b/tests/data/tests_complicate_data2.txt @@ -7,19 +7,19 @@ 'a中文', [1], (1, 2), - function(f), - class(CE), - class(CE2), - class(c): + function(EmptyFunc), + class(EmptyClassOldStyle), + class(EmptyClassNewStyle), + class(NormalClassOldStyle): static_props: 1, - class(c2): + class(NormalClassNewStyle): dicts: { }, lists: [], static_props: 1, - instance(c): + instance(NormalClassOldStyle): static_props: 1, - instance(c2): + instance(NormalClassNewStyle): dicts: { }, lists: [], diff --git a/tests/definition.py b/tests/definition.py index c6612e9..1d51fe2 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -1,22 +1,22 @@ # -*- coding:utf-8 -*- -def f(): pass +def EmptyFunc(): pass -class CE: pass -class CE2(object): pass +class EmptyClassOldStyle: pass +class EmptyClassNewStyle(object): pass -class c: +class NormalClassOldStyle: def mth():pass static_props = 1 -class c2(object): +class NormalClassNewStyle(object): def mth():pass static_props = 1 lists = [] dicts = {} -ic = c() -ic2 = c2() +inst_of_normal_class_old_style = NormalClassOldStyle() +inst_of_normal_class_new_style = NormalClassNewStyle() values = [ 1, @@ -27,15 +27,15 @@ def mth():pass u"a中文", [1], (1,2), - f, - CE, - CE2, - c, - c2, - ic, - ic2, - ic.mth, - ic2.mth, + 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 282db4e..61825f6 100644 --- a/tests/t.py +++ b/tests/t.py @@ -31,10 +31,10 @@ try: from .definition import values - from .definition import ic, ic2, c, c2, f + from .definition import inst_of_normal_class_old_style, inst_of_normal_class_new_style, NormalClassOldStyle, NormalClassNewStyle, EmptyFunc except: from definition import values - from definition import ic, ic2, c, c2, f + from definition import inst_of_normal_class_old_style, inst_of_normal_class_new_style, NormalClassOldStyle, NormalClassNewStyle, EmptyFunc # >> utilities @@ -70,12 +70,12 @@ def inst_test(): else: print('%40s: %s' % (v, isinstance(v, object))) - same_attrs = detect_same_attrs(ic, ic2) + same_attrs = detect_same_attrs(inst_of_normal_class_old_style, inst_of_normal_class_new_style) for attr, v in same_attrs: print('%40s: %s' % (attr, v)) def builtin_test(): - for v in [f, c.mth, c2.mth, ic.mth, ic2.mth]: + for v in [EmptyFunc, NormalClassOldStyle.mth, NormalClassNewStyle.mth, inst_of_normal_class_old_style.mth, inst_of_normal_class_new_style.mth]: # print('%40s: %s' % (v, isinstance(v, types.MethodType))) py2 all true # print('%40s: %s' % (v, inspect.ismethod(v))) py2 all true # print('%40s: %s' % (v, inspect.isbuiltin(v))) py2 all false @@ -95,7 +95,7 @@ def main(): # S.str_display_not_prefix_b = False pp(values) - # pp([ic.mth, ic2.mth]) + # pp([inst_of_normal_class_old_style.mth, inst_of_normal_class_new_style.mth]) return for i in range(1, len(sys.argv)): From 62b0ef1e635544661a7e097b49e53e4f48ae89bf Mon Sep 17 00:00:00 2001 From: pyy <568397440@qq.com> Date: Thu, 16 Jun 2016 17:05:15 +0800 Subject: [PATCH 10/10] improved: long string handle - step 1 --- beeprint/constants.py | 5 ++ beeprint/printer.py | 2 +- beeprint/settings.py | 7 ++- beeprint/terminal_size.py | 96 ++++++++++++++++++++++++++++++++++ beeprint/utils.py | 7 +++ tests/definition.py | 26 +++++++++ tests/t.py | 6 ++- tests/test_default_beeprint.py | 9 +++- 8 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 beeprint/terminal_size.py diff --git a/beeprint/constants.py b/beeprint/constants.py index 50bd586..ef28ab9 100644 --- a/beeprint/constants.py +++ b/beeprint/constants.py @@ -37,3 +37,8 @@ _DL_MODULE_ = 1 _DL_FUNC_ = 2 _DL_STATEMENT = 3 + +# long string +_LS_WRAP_BY_NONE = 0 +_LS_WRAP_BY_TERMINAL = 1 +_LS_WRAP_BY_80_COLUMN = 2 diff --git a/beeprint/printer.py b/beeprint/printer.py index 1d3cb6b..e48f9e2 100644 --- a/beeprint/printer.py +++ b/beeprint/printer.py @@ -412,7 +412,7 @@ def typeval(v): ret = ret.replace(u'\n', u'\\n') ret = ret.replace(u'\r', u'\\r') else: - ret = u'' + ret = u'' except Exception as e: if S.priority_strategy == C._PS_CORRECTNESS_FIRST: diff --git a/beeprint/settings.py b/beeprint/settings.py index 0e748c7..b1abece 100644 --- a/beeprint/settings.py +++ b/beeprint/settings.py @@ -25,7 +25,7 @@ # use buffer_handler.flush() every print write_to_buffer_when_execute = False -# >> class controll +# >> class control # 过滤以 x 开头的属性 prop_leading_filters = ["__", "func_"] # 根据类型过滤对象的属性 @@ -39,6 +39,7 @@ # debug = False debug_level = 0 +# >> string control # united_str_coding_representation # In spite of python version # unicode string will be displayed as u'' @@ -46,5 +47,9 @@ united_str_coding_representation = True str_display_not_prefix_u = True str_display_not_prefix_b = True +str_display_escape_special_char = True element_display_last_with_comma = True + +# >> long string control +long_str_wrap_method = C._LS_WRAP_BY_TERMINAL diff --git a/beeprint/terminal_size.py b/beeprint/terminal_size.py new file mode 100644 index 0000000..fdeae45 --- /dev/null +++ b/beeprint/terminal_size.py @@ -0,0 +1,96 @@ +# -*- coding:utf-8 -*- +""" +copied from https://gist.github.com/jtriley/1108174 +""" +from __future__ import print_function +from __future__ import absolute_import +from __future__ import unicode_literals +from __future__ import division +import os +import shlex +import struct +import platform +import subprocess + + +def get_terminal_size(): + """ getTerminalSize() + - get width and height of console + - works on linux,os x,windows,cygwin(windows) + originally retrieved from: + http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python + """ + current_os = platform.system() + tuple_xy = None + if current_os == 'Windows': + tuple_xy = _get_terminal_size_windows() + if tuple_xy is None: + tuple_xy = _get_terminal_size_tput() + # needed for window's python in cygwin's xterm! + if current_os in ['Linux', 'Darwin'] or current_os.startswith('CYGWIN'): + tuple_xy = _get_terminal_size_linux() + if tuple_xy is None: + print("default") + tuple_xy = (80, 25) # default value + return tuple_xy + + +def _get_terminal_size_windows(): + try: + from ctypes import windll, create_string_buffer + # stdin handle is -10 + # stdout handle is -11 + # stderr handle is -12 + h = windll.kernel32.GetStdHandle(-12) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + if res: + (bufx, bufy, curx, cury, wattr, + left, top, right, bottom, + maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + sizex = right - left + 1 + sizey = bottom - top + 1 + return sizex, sizey + except: + pass + + +def _get_terminal_size_tput(): + # get terminal width + # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window + try: + cols = int(subprocess.check_call(shlex.split('tput cols'))) + rows = int(subprocess.check_call(shlex.split('tput lines'))) + return (cols, rows) + except: + pass + + +def _get_terminal_size_linux(): + def ioctl_GWINSZ(fd): + try: + import fcntl + import termios + cr = struct.unpack('hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234')) + return cr + except: + pass + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + try: + cr = (os.environ['LINES'], os.environ['COLUMNS']) + except: + return None + return int(cr[1]), int(cr[0]) + +if __name__ == "__main__": + sizex, sizey = get_terminal_size() + print('width =', sizex, 'height =', sizey) diff --git a/beeprint/utils.py b/beeprint/utils.py index 8595e4f..5604e2a 100644 --- a/beeprint/utils.py +++ b/beeprint/utils.py @@ -7,6 +7,8 @@ import sys import types +from . import constants as C + if sys.version_info < (3, 0): pyv = 2 @@ -32,3 +34,8 @@ def is_pan_function(name, val): function and unbound method and bound method in python 2 """ return inspect.isfunction(val) or inspect.ismethod(val) + +def long_string_wrapper(ls, how): + if how == C._LS_WRAP_BY_80_COLUMN: + pass + pass diff --git a/tests/definition.py b/tests/definition.py index 1d51fe2..0ba554b 100644 --- a/tests/definition.py +++ b/tests/definition.py @@ -41,3 +41,29 @@ def mth():pass u'key2': {}, }, ] + +long_text_en = """ +The sky and the earth were at first one blurred1 entity2 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. + +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. + +In all, the universe and Pangu combine in one. +""" + +long_text_cn = """ +据民间神话传说古时盘古生在黑暗团中,他不能忍受黑暗,用神斧劈向四方,逐渐使天空高远,大地辽阔。他为不使天地会重新合并,继续施展法术。每当盘古的身体长高一尺,天空就随之增高一尺,经过1.8万多年的努力,盘古变成一位顶天立地的巨人,而天空也升得高不可及,大地也变得厚实无比。盘古生前完成开天辟地的伟大业绩,死后永远留给后人无穷无尽的宝藏,成为中华民族崇拜的英雄。 +""" + +long_text_in_list = [ + [ + long_text_en, + long_text_cn, + ], +] + +long_text_in_dict = [ + {"english version": long_text_en}, + {"simplify chinese versino": long_text_cn}, +] diff --git a/tests/t.py b/tests/t.py index 61825f6..860ab51 100644 --- a/tests/t.py +++ b/tests/t.py @@ -32,9 +32,11 @@ try: from .definition import values from .definition import inst_of_normal_class_old_style, inst_of_normal_class_new_style, NormalClassOldStyle, NormalClassNewStyle, EmptyFunc + from . import definition as df except: from definition import values from definition import inst_of_normal_class_old_style, inst_of_normal_class_new_style, NormalClassOldStyle, NormalClassNewStyle, EmptyFunc + import definition as df # >> utilities @@ -82,6 +84,7 @@ def builtin_test(): # print('%40s: %s' % (v, inspect.ismethod(v))) py3 FFTT print('%40s: %s, %s' % (v, v.__qualname__, inspect.getargspec(v).args)) + args = { "class_test": class_test, "inst_test": inst_test, @@ -94,7 +97,8 @@ def main(): # S.str_display_not_prefix_u = False # S.str_display_not_prefix_b = False - pp(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 diff --git a/tests/test_default_beeprint.py b/tests/test_default_beeprint.py index c2f5a52..8d7826a 100644 --- a/tests/test_default_beeprint.py +++ b/tests/test_default_beeprint.py @@ -13,9 +13,9 @@ from beeprint import settings as S try: - from .definition import values + from .definition import values, long_text_en except: - from definition import values + from definition import values, long_text_en class TestSimpleTypes(unittest.TestCase): @@ -82,6 +82,11 @@ def test_complicate_data(self): self.assertEqual(res == ans or res == ans2, True) # self.assertEqual(res, ans) + def test_long_text(self): + pass + # res = beeprint(long_text_en, output=False) + # self.assertEqual(res, ans) + if __name__ == '__main__': unittest.main()