Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions python_ls/_ls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from collections import Container
from numbers import Number

try:
import pandas as pd
Expand All @@ -21,16 +22,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 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))

Expand All @@ -43,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))

Expand Down Expand Up @@ -109,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
31 changes: 28 additions & 3 deletions tests/test_ls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from python_ls import iter_ls
from python_ls import iter_ls, ls
import pytest


Expand All @@ -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)
o.foo.bar._something_else = lambda: None
o.foo.bar.someconstructor_obj = dict
o.foo.baz = {'something_weird': 'going on', 'blah': 'bleh'}
o.lala = Object()
o.lala.lele = Object()
Expand Down Expand Up @@ -47,7 +48,31 @@ 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.someconstructor_obj()']
actual = [x[0] for x in iter_ls(test_obj, 'someconstructor', 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()', '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, 'someconstructor')
out, err = capsys.readouterr()
expect = [['foo.bar.someconstructor_obj()', 'type']]
assert expect == [line.split() for line in out.splitlines()]