]>
Commit | Line | Data |
---|---|---|
c4372a0b MF |
1 | #!/usr/bin/python |
2 | # Released into the public domain. | |
3 | # Written by Mike Frysinger <vapier>. | |
4 | ||
5 | """Module for diving into python objects.""" | |
6 | ||
7 | from __future__ import print_function | |
8 | ||
9 | ||
10 | class Dump(object): | |
11 | ||
722f573c MF |
12 | try: |
13 | from cStringIO import StringIO | |
14 | except: | |
15 | from io import StringIO | |
c4372a0b MF |
16 | import datetime |
17 | import logging | |
18 | import pydoc | |
19 | import os | |
20 | import sys | |
21 | import types | |
22 | ||
23 | MAX_DEPTH = 1 | |
24 | ||
25 | # Objects that hold multiple objects (can be looped over). | |
26 | TYPES_ITERABLES = ( | |
27 | types.GeneratorType, | |
22072cde MF |
28 | list, |
29 | tuple, | |
c4372a0b MF |
30 | ) |
31 | ||
32 | # Objects that we shouldn't really probe. | |
33 | TYPES_CALLABLE = ( | |
34 | types.FunctionType, | |
35 | types.LambdaType, | |
36 | ) | |
37 | ||
38 | # Simple objects we don't decode further. | |
39 | TYPES_SCALAR = ( | |
22072cde MF |
40 | bool, |
41 | complex, | |
42 | float, | |
43 | int, | |
44 | #types.LongType, | |
45 | type(None), | |
46 | bytes, | |
47 | str, | |
48 | ||
49 | type, | |
c4372a0b MF |
50 | ) |
51 | ||
52 | # Objects that are dictionary based. | |
53 | TYPES_DICT = ( | |
22072cde | 54 | dict, |
c4372a0b MF |
55 | ) |
56 | ||
57 | # Standard python objects we don't normally expand. | |
58 | TYPES_STANDARD = ( | |
59 | datetime.date, | |
60 | datetime.datetime, | |
61 | datetime.time, | |
62 | logging.Logger | |
63 | ) | |
64 | ||
65 | # Color logic. | |
66 | _C = lambda x: '\033[%im' % x | |
67 | def C(self, m, c): | |
68 | return '%s%s%s' % (c, m, self.NORMAL) | |
69 | NORMAL = _C(0) | |
70 | BOLD = _C(1) | |
71 | UNDERLINE = _C(4) | |
72 | #BLACK = _C() | |
73 | ||
74 | PURPLE = _C(95) | |
75 | CYAN = _C(96) | |
76 | DARKCYAN = _C(36) | |
77 | BLUE = _C(94) | |
78 | GREEN = _C(92) | |
79 | YELLOW = _C(93) | |
80 | RED = _C(91) | |
81 | ||
82 | def __init__(self, obj, depth=None, out=None, linelim=150, | |
83 | show_internal=False, show_all=False, show_std=False): | |
84 | if out is None: | |
722f573c | 85 | out = self.StringIO() |
c4372a0b MF |
86 | #out = self.sys.stdout |
87 | self.out = out | |
88 | ||
89 | self.depth = depth if depth else self.MAX_DEPTH | |
90 | self.line_limit = linelim | |
91 | self.show_internal = show_internal | |
92 | self.show_all = show_all | |
93 | self.show_std = show_std | |
94 | ||
95 | self.seen = set() | |
96 | self.dump(obj) | |
97 | ||
98 | if hasattr(out, 'getvalue'): | |
99 | self.pydoc.pager(out.getvalue()) | |
100 | ||
101 | return None | |
102 | ||
103 | def trunc(self, s): | |
104 | """Truncate |s| length to self.line_limit bytes""" | |
105 | if len(s) > self.line_limit: | |
106 | s = '%s %s' % (s[0:self.line_limit], | |
107 | self.C('<truncated>', self.RED)) | |
108 | return s | |
109 | ||
110 | def dump(self, obj, depth=0, name=None): | |
22072cde MF |
111 | """Dump |obj| with |name|. |
112 | ||
113 | Args: | |
114 | obj: The object to dump. | |
115 | depth: How deep to recursively dive. | |
116 | name: The name? | |
117 | """ | |
c4372a0b MF |
118 | indent = ' ' * depth |
119 | def w(msg, indent=indent, color=None): | |
120 | for line in msg.splitlines(): | |
121 | if color: | |
122 | line = self.C(line, color) | |
123 | self.out.write('%s%s\n' % (indent, line)) | |
124 | def d(obj, **kwargs): | |
125 | try: | |
126 | self.dump(obj, depth=depth + 1, **kwargs) | |
127 | except Exception as e: | |
128 | w(' <error probing>: %s' % (e,), color=self.RED) | |
129 | raise | |
130 | ||
131 | if ((not self.show_std and isinstance(obj, self.TYPES_STANDARD)) or | |
132 | isinstance(obj, self.TYPES_SCALAR)): | |
133 | w('%s: %s' % (self.C(name, self.BOLD), obj), indent=indent[:-2]) | |
134 | return | |
135 | ||
136 | try: | |
137 | if obj in self.seen: | |
138 | w('%s: %s' % (self.C(name, self.BOLD), | |
139 | self.C('<loop>', self.RED)), indent=indent[:-2]) | |
140 | return | |
141 | self.seen.add(obj) | |
142 | except TypeError: | |
143 | pass | |
144 | ||
145 | if name: | |
146 | w('%s' % (name,), color=self.BOLD, indent=indent[:-2]) | |
147 | ||
148 | objr = repr(obj) | |
149 | objs = str(obj) | |
150 | if objr == objs: | |
151 | w('%s: %s' % (type(obj), self.trunc(objs),)) | |
152 | else: | |
153 | w('Object.type: %s' % (type(obj),)) | |
154 | w(' .repr: %s' % (self.trunc(objr),)) | |
155 | w(' .str : %s' % (self.trunc(objs),)) | |
156 | ||
157 | if depth > self.depth: | |
158 | w('<stop; depth limit hit at %i>' % (depth,), color=self.RED) | |
159 | return | |
160 | ||
161 | if isinstance(obj, self.TYPES_ITERABLES): | |
162 | # Iterable types. | |
163 | for i, o in enumerate(obj): | |
164 | d(o, name='[%i]' % (i,)) | |
165 | elif isinstance(obj, self.TYPES_SCALAR): | |
166 | # Scalar types; already shown above. | |
167 | pass | |
168 | elif isinstance(obj, self.TYPES_DICT): | |
169 | # Dictionary types. | |
170 | for k, v in obj.items(): | |
171 | d(v, name='{%r}' % (k,)) | |
172 | elif isinstance(obj, self.TYPES_STANDARD): | |
173 | # Standard types; already shown above. | |
174 | pass | |
175 | elif isinstance(obj, self.TYPES_CALLABLE): | |
176 | # Callable functions. | |
177 | doc = getattr(obj, 'func_doc', None) | |
178 | if doc: | |
179 | w('"""%s"""' % (doc.strip(),), indent=indent + ' ', | |
180 | color=self.BLUE) | |
181 | else: | |
182 | # Unknown type; probe it! | |
183 | doc = getattr(obj, '__doc__', None) | |
184 | if doc: | |
185 | w('"""%s"""' % (doc.strip(),), indent=indent + ' ', | |
186 | color=self.BLUE) | |
187 | ||
188 | for k, v in getattr(obj, '__dict__', {}).items(): | |
189 | d(v, name='{%r}' % (k,)) | |
190 | ||
191 | for k in dir(obj): | |
192 | try: | |
193 | s = self.trunc(repr(getattr(obj, k))) | |
194 | except Exception as e: | |
195 | w(' %s: %s' % (self.C(k, self.BOLD), | |
196 | self.C('<error> %s' % str(e), self.RED))) | |
197 | continue | |
198 | ||
199 | if k.startswith('__'): | |
200 | if self.show_all or not k.endswith('__'): | |
201 | w(' %s (skipping): %s' % (self.C(k, self.BOLD), s)) | |
202 | elif self.show_internal or not k.startswith('_'): | |
203 | d(getattr(obj, k), name=k) | |
204 | ||
205 | ||
206 | def dump(*args, **kwargs): | |
207 | d = Dump(*args, **kwargs) | |
208 | del d | |
22072cde | 209 | dump.__doc__ = Dump.dump.__doc__ |