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
17 changes: 9 additions & 8 deletions python_ls/_ls.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from collections import Container

try:
import pandas as pd
except ImportError:
Expand All @@ -11,7 +9,7 @@
BAD = object()


def ls(obj, attr=None, depth=None, dunder=False, under=True):
def ls(obj, attr=None, depth=None, dunder=False, under=True, unsafe=False):
"""
Run a recursive find for a named attribute
:param obj: Root object to search
Expand All @@ -25,7 +23,7 @@ def ls(obj, attr=None, depth=None, dunder=False, under=True):
depth = 1

for attr, value in iter_ls(obj, attr=attr, depth=depth,
dunder=dunder, under=under):
dunder=dunder, under=under, unsafe=unsafe):
size = ''
if has_pandas and isinstance(value, pd.DataFrame):
size = '{0}x{1}'.format(*value.shape)
Expand All @@ -42,7 +40,7 @@ def xdir(obj, attr=None, depth=None, dunder=False, under=True):
dunder=dunder, under=under)))


def iter_ls(obj, attr=None, depth=1, dunder=False, under=True,
def iter_ls(obj, attr=None, depth=1, dunder=False, under=True, unsafe=False,
visited=None, current_depth=1, path=''):
visited = visited or set()

Expand Down Expand Up @@ -96,8 +94,11 @@ def attr_filter_callback(a):
if isinstance(obj, dict) or (has_pandas and isinstance(obj, pd.DataFrame)):
val = obj[a]
else:
val = getattr(obj, a)
except Exception:
if unsafe or not isinstance(getattr(type(obj), a, None), property):
val = getattr(obj, a)
else:
val = BAD
except Exception as exc:
val = BAD

if include(a):
Expand All @@ -109,6 +110,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, unsafe=unsafe, visited=visited,
current_depth=current_depth + 1, path=new_path):
yield sub_a, sub_val
18 changes: 17 additions & 1 deletion tests/test_ls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
from python_ls import iter_ls
import pytest

from python_ls import iter_ls


class Object(object):
pass


class Universe:
@property
def answer(self):
return 41 + 1


@pytest.fixture
def test_obj():
o = Object()
Expand Down Expand Up @@ -51,3 +58,12 @@ 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_ls_unsafe():
actual = list(iter_ls(Universe(), unsafe=True))
assert ('answer', 42) in actual


def test_ls_safe():
actual = list(iter_ls(Universe()))
assert ('answer', 42) not in actual