From 900be4f6b8c62beeadc8abd38a22b0b3034836ea Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Mon, 16 Nov 2020 13:01:40 +0000 Subject: [PATCH 1/5] change default ls to infinite depth --- python_ls/_ls.py | 10 ++++++---- tests/test_ls.py | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/python_ls/_ls.py b/python_ls/_ls.py index d5fe516..522e545 100644 --- a/python_ls/_ls.py +++ b/python_ls/_ls.py @@ -21,16 +21,18 @@ def ls(obj, attr=None, depth=None, dunder=False, under=True): :param under: If True single underscore prefixed attributes are ignored, default is enabled :return: None """ - if depth is None: - depth = 1 - for attr, value in iter_ls(obj, attr=attr, depth=depth, dunder=dunder, under=under): size = '' if has_pandas and isinstance(value, pd.DataFrame): size = '{0}x{1}'.format(*value.shape) elif hasattr(value, '__len__'): - size = len(value) + try: + size = len(value) + except TypeError as exc: + # certain objects such as the dict type have __len__ + # method which raises TypeError + pass type_name = type(value).__name__ print('{:<60}{:>20}{:>7}'.format(attr, type_name, size)) diff --git a/tests/test_ls.py b/tests/test_ls.py index 1a71368..6007b4a 100644 --- a/tests/test_ls.py +++ b/tests/test_ls.py @@ -1,4 +1,4 @@ -from python_ls import iter_ls +from python_ls import iter_ls, ls import pytest @@ -14,7 +14,8 @@ def test_obj(): o.foo.bar.something = Object() o.foo.bar.aaa = Object() o.foo.bar.bbb = Object() - o.foo.bar._something_else = dict # a callable (lambda recurses infinitely in Python 2.7 when depth=None) + # a callable (lambda recurses infinitely in Python 2.7 when depth=None) + o.foo.bar._something_else = dict o.foo.baz = {'something_weird': 'going on', 'blah': 'bleh'} o.lala = Object() o.lala.lele = Object() @@ -51,3 +52,15 @@ def test_depth_is_None(test_obj): actual = [x[0] for x in iter_ls(test_obj, 'something', depth=None)] assert actual == expected + +def test_basic_ls_usage(test_obj, capsys): + ls(test_obj, 'something') + out, err = capsys.readouterr() + expect = [ + ['foo.bar._something_else()', 'type'], + ['foo.bar.something', 'Object'], + ["foo.baz['something_weird']", 'str', '8'], + ['lala.something', 'Object'] + ] + assert expect == [line.split() for line in out.splitlines()] + From f4ffcacfeb1a7003f6eead36b2c491f832a69930 Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Mon, 16 Nov 2020 13:18:23 +0000 Subject: [PATCH 2/5] constructor object as attributes --- python_ls/_ls.py | 4 ++-- tests/test_ls.py | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/python_ls/_ls.py b/python_ls/_ls.py index 522e545..2d17de3 100644 --- a/python_ls/_ls.py +++ b/python_ls/_ls.py @@ -30,8 +30,8 @@ def ls(obj, attr=None, depth=None, dunder=False, under=True): try: size = len(value) except TypeError as exc: - # certain objects such as the dict type have __len__ - # method which raises TypeError + # certain constructor object such as dict, list have a + # __len__ method but it throws a TypeError pass type_name = type(value).__name__ print('{:<60}{:>20}{:>7}'.format(attr, type_name, size)) diff --git a/tests/test_ls.py b/tests/test_ls.py index 6007b4a..b9ee357 100644 --- a/tests/test_ls.py +++ b/tests/test_ls.py @@ -14,8 +14,8 @@ def test_obj(): o.foo.bar.something = Object() o.foo.bar.aaa = Object() o.foo.bar.bbb = Object() - # a callable (lambda recurses infinitely in Python 2.7 when depth=None) - o.foo.bar._something_else = dict + o.foo.bar._something_else = lambda: None + o.foo.bar.constructor_obj = dict o.foo.baz = {'something_weird': 'going on', 'blah': 'bleh'} o.lala = Object() o.lala.lele = Object() @@ -53,14 +53,28 @@ def test_depth_is_None(test_obj): assert actual == expected +def test_iter_ls_constructor_obj(test_obj): + expected = ['foo.bar.constructor_obj()'] + + actual = [x[0] for x in iter_ls(test_obj, 'constructor', depth=None)] + assert actual == expected + + def test_basic_ls_usage(test_obj, capsys): ls(test_obj, 'something') out, err = capsys.readouterr() expect = [ - ['foo.bar._something_else()', 'type'], + ['foo.bar._something_else()', 'function'], ['foo.bar.something', 'Object'], ["foo.baz['something_weird']", 'str', '8'], ['lala.something', 'Object'] ] assert expect == [line.split() for line in out.splitlines()] + +def test_ls_constructor_obj(test_obj, capsys): + ls(test_obj, 'constructor') + out, err = capsys.readouterr() + expect = [['foo.bar.constructor_obj()', 'type']] + assert expect == [line.split() for line in out.splitlines()] + From 9b9765018d82fef48f8bd7069d8a723efd9c4e45 Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Mon, 16 Nov 2020 16:00:51 +0000 Subject: [PATCH 3/5] in python 2.7 longs have attributes that produce the same long but ID of that long may be different resulting in an infinite loop --- python_ls/_ls.py | 11 +++++++++-- tests/test_ls.py | 14 +++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/python_ls/_ls.py b/python_ls/_ls.py index 2d17de3..c92d4c0 100644 --- a/python_ls/_ls.py +++ b/python_ls/_ls.py @@ -1,4 +1,5 @@ from collections import Container +from numbers import Number try: import pandas as pd @@ -45,10 +46,16 @@ def xdir(obj, attr=None, depth=None, dunder=False, under=True): def iter_ls(obj, attr=None, depth=1, dunder=False, under=True, - visited=None, current_depth=1, path=''): + visited=None, numbers=None, current_depth=1, path=''): visited = visited or set() + numbers = numbers or set() if (depth is None) or (current_depth <= depth): + if isinstance(obj, Number): + if obj in numbers: + return + else: + numbers.add(obj) if id(obj) not in visited: visited.add(id(obj)) @@ -111,6 +118,6 @@ def attr_filter_callback(a): if val is not BAD and not a.startswith('__'): for sub_a, sub_val in iter_ls(val, attr=attr, depth=depth, dunder=dunder, - under=under, visited=visited, + under=under, visited=visited, numbers=numbers, current_depth=current_depth + 1, path=new_path): yield sub_a, sub_val diff --git a/tests/test_ls.py b/tests/test_ls.py index b9ee357..b97a3fb 100644 --- a/tests/test_ls.py +++ b/tests/test_ls.py @@ -15,7 +15,8 @@ def test_obj(): o.foo.bar.aaa = Object() o.foo.bar.bbb = Object() o.foo.bar._something_else = lambda: None - o.foo.bar.constructor_obj = dict + o.foo.bar.someconstructor_obj = dict + o.foo.bar._lambda = lambda: None o.foo.baz = {'something_weird': 'going on', 'blah': 'bleh'} o.lala = Object() o.lala.lele = Object() @@ -48,15 +49,13 @@ def test_depth_is_None(test_obj): "foo.baz['something_weird']", 'lala.something', ] - actual = [x[0] for x in iter_ls(test_obj, 'something', depth=None)] assert actual == expected def test_iter_ls_constructor_obj(test_obj): - expected = ['foo.bar.constructor_obj()'] - - actual = [x[0] for x in iter_ls(test_obj, 'constructor', depth=None)] + expected = ['foo.bar.someconstructor_obj()'] + actual = [x[0] for x in iter_ls(test_obj, 'someconstructor', depth=None)] assert actual == expected @@ -73,8 +72,9 @@ def test_basic_ls_usage(test_obj, capsys): def test_ls_constructor_obj(test_obj, capsys): - ls(test_obj, 'constructor') + ls(test_obj, 'someconstructor') out, err = capsys.readouterr() - expect = [['foo.bar.constructor_obj()', 'type']] + expect = [['foo.bar.someconstructor_obj()', 'type']] + import pdb; pdb.set_trace() assert expect == [line.split() for line in out.splitlines()] From 98ba7c3bdfc5f48fe2f2bde1838a1b523f5cab7d Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Mon, 16 Nov 2020 17:23:40 +0000 Subject: [PATCH 4/5] remove pdb --- tests/test_ls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_ls.py b/tests/test_ls.py index b97a3fb..13bc241 100644 --- a/tests/test_ls.py +++ b/tests/test_ls.py @@ -75,6 +75,5 @@ def test_ls_constructor_obj(test_obj, capsys): ls(test_obj, 'someconstructor') out, err = capsys.readouterr() expect = [['foo.bar.someconstructor_obj()', 'type']] - import pdb; pdb.set_trace() assert expect == [line.split() for line in out.splitlines()] From 5148fcdfc768e1170649b603347dcdc97711b2b5 Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Mon, 16 Nov 2020 17:52:31 +0000 Subject: [PATCH 5/5] remove unused line --- tests/test_ls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_ls.py b/tests/test_ls.py index 13bc241..7fc31c8 100644 --- a/tests/test_ls.py +++ b/tests/test_ls.py @@ -16,7 +16,6 @@ def test_obj(): o.foo.bar.bbb = Object() o.foo.bar._something_else = lambda: None o.foo.bar.someconstructor_obj = dict - o.foo.bar._lambda = lambda: None o.foo.baz = {'something_weird': 'going on', 'blah': 'bleh'} o.lala = Object() o.lala.lele = Object()