-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy path_ls.py
More file actions
123 lines (101 loc) · 4.12 KB
/
Copy path_ls.py
File metadata and controls
123 lines (101 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from collections import Container
from numbers import Number
try:
import pandas as pd
except ImportError:
has_pandas = False
else:
has_pandas = True
# sentinel
BAD = object()
def ls(obj, attr=None, depth=None, dunder=False, under=True):
"""
Run a recursive find for a named attribute
:param obj: Root object to search
:param attr: Name of the attribute to search for
:param depth: Maximum search depth, defaults to unlimited
:param dunder: If True double underscore prefixed attributes are ignored, default is disabled
:param under: If True single underscore prefixed attributes are ignored, default is enabled
:return: None
"""
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__'):
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))
def xdir(obj, attr=None, depth=None, dunder=False, under=True):
if depth is None and attr is None:
depth = 1
return list((x[0] for x in iter_ls(obj, attr=attr, depth=depth,
dunder=dunder, under=under)))
def iter_ls(obj, attr=None, depth=1, dunder=False, under=True,
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))
callbacks = []
def include(a):
for c in callbacks:
if not c(a):
return False
return True
def exclude_dunders(a):
return not a.startswith('__')
def exclude_unders(a):
return not a.startswith('_')
def attr_filter_callback(a):
return attr in a
if attr:
callbacks.append(attr_filter_callback)
if not dunder:
callbacks.append(exclude_dunders)
if not under:
callbacks.append(exclude_unders)
if isinstance(obj, dict):
attrs = [str(k) for k in obj.keys()]
elif has_pandas and isinstance(obj, pd.DataFrame):
attrs = [str(c) for c in obj.columns]
else:
attrs = dir(obj)
for a in attrs:
if isinstance(obj, dict) or (has_pandas and isinstance(obj, pd.DataFrame)):
new_path = path + '[%r]' % a
else:
if path:
new_path = '.'.join([path, a])
else:
new_path = a
try:
if isinstance(obj, dict) or (has_pandas and isinstance(obj, pd.DataFrame)):
val = obj[a]
else:
val = getattr(obj, a)
except Exception:
val = BAD
if include(a):
suffix = ''
if val is not BAD:
if callable(val):
suffix = '()'
yield new_path + suffix, val
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, numbers=numbers,
current_depth=current_depth + 1, path=new_path):
yield sub_a, sub_val