##// END OF EJS Templates
oinspect.find_file: Additional safety if file cannot be found....
Bradley M. Froehle -
Show More
@@ -1,825 +1,826 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tools for inspecting Python objects.
2 """Tools for inspecting Python objects.
3
3
4 Uses syntax highlighting for presenting the various information elements.
4 Uses syntax highlighting for presenting the various information elements.
5
5
6 Similar in spirit to the inspect module, but all calls take a name argument to
6 Similar in spirit to the inspect module, but all calls take a name argument to
7 reference the name under which an object is being read.
7 reference the name under which an object is being read.
8 """
8 """
9
9
10 #*****************************************************************************
10 #*****************************************************************************
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
11 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #*****************************************************************************
15 #*****************************************************************************
16
16
17 __all__ = ['Inspector','InspectColors']
17 __all__ = ['Inspector','InspectColors']
18
18
19 # stdlib modules
19 # stdlib modules
20 import __builtin__
20 import __builtin__
21 import inspect
21 import inspect
22 import linecache
22 import linecache
23 import os
23 import os
24 import sys
24 import sys
25 import types
25 import types
26 from collections import namedtuple
26 from collections import namedtuple
27 try:
27 try:
28 from itertools import izip_longest
28 from itertools import izip_longest
29 except ImportError:
29 except ImportError:
30 from itertools import zip_longest as izip_longest
30 from itertools import zip_longest as izip_longest
31
31
32 # IPython's own
32 # IPython's own
33 from IPython.core import page
33 from IPython.core import page
34 from IPython.testing.skipdoctest import skip_doctest_py3
34 from IPython.testing.skipdoctest import skip_doctest_py3
35 from IPython.utils import PyColorize
35 from IPython.utils import PyColorize
36 from IPython.utils import io
36 from IPython.utils import io
37 from IPython.utils import py3compat
37 from IPython.utils import py3compat
38 from IPython.utils.text import indent
38 from IPython.utils.text import indent
39 from IPython.utils.wildcard import list_namespace
39 from IPython.utils.wildcard import list_namespace
40 from IPython.utils.coloransi import *
40 from IPython.utils.coloransi import *
41
41
42 #****************************************************************************
42 #****************************************************************************
43 # Builtin color schemes
43 # Builtin color schemes
44
44
45 Colors = TermColors # just a shorthand
45 Colors = TermColors # just a shorthand
46
46
47 # Build a few color schemes
47 # Build a few color schemes
48 NoColor = ColorScheme(
48 NoColor = ColorScheme(
49 'NoColor',{
49 'NoColor',{
50 'header' : Colors.NoColor,
50 'header' : Colors.NoColor,
51 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
51 'normal' : Colors.NoColor # color off (usu. Colors.Normal)
52 } )
52 } )
53
53
54 LinuxColors = ColorScheme(
54 LinuxColors = ColorScheme(
55 'Linux',{
55 'Linux',{
56 'header' : Colors.LightRed,
56 'header' : Colors.LightRed,
57 'normal' : Colors.Normal # color off (usu. Colors.Normal)
57 'normal' : Colors.Normal # color off (usu. Colors.Normal)
58 } )
58 } )
59
59
60 LightBGColors = ColorScheme(
60 LightBGColors = ColorScheme(
61 'LightBG',{
61 'LightBG',{
62 'header' : Colors.Red,
62 'header' : Colors.Red,
63 'normal' : Colors.Normal # color off (usu. Colors.Normal)
63 'normal' : Colors.Normal # color off (usu. Colors.Normal)
64 } )
64 } )
65
65
66 # Build table of color schemes (needed by the parser)
66 # Build table of color schemes (needed by the parser)
67 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
67 InspectColors = ColorSchemeTable([NoColor,LinuxColors,LightBGColors],
68 'Linux')
68 'Linux')
69
69
70 #****************************************************************************
70 #****************************************************************************
71 # Auxiliary functions and objects
71 # Auxiliary functions and objects
72
72
73 # See the messaging spec for the definition of all these fields. This list
73 # See the messaging spec for the definition of all these fields. This list
74 # effectively defines the order of display
74 # effectively defines the order of display
75 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
75 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
76 'length', 'file', 'definition', 'docstring', 'source',
76 'length', 'file', 'definition', 'docstring', 'source',
77 'init_definition', 'class_docstring', 'init_docstring',
77 'init_definition', 'class_docstring', 'init_docstring',
78 'call_def', 'call_docstring',
78 'call_def', 'call_docstring',
79 # These won't be printed but will be used to determine how to
79 # These won't be printed but will be used to determine how to
80 # format the object
80 # format the object
81 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
81 'ismagic', 'isalias', 'isclass', 'argspec', 'found', 'name'
82 ]
82 ]
83
83
84
84
85 def object_info(**kw):
85 def object_info(**kw):
86 """Make an object info dict with all fields present."""
86 """Make an object info dict with all fields present."""
87 infodict = dict(izip_longest(info_fields, [None]))
87 infodict = dict(izip_longest(info_fields, [None]))
88 infodict.update(kw)
88 infodict.update(kw)
89 return infodict
89 return infodict
90
90
91
91
92 def getdoc(obj):
92 def getdoc(obj):
93 """Stable wrapper around inspect.getdoc.
93 """Stable wrapper around inspect.getdoc.
94
94
95 This can't crash because of attribute problems.
95 This can't crash because of attribute problems.
96
96
97 It also attempts to call a getdoc() method on the given object. This
97 It also attempts to call a getdoc() method on the given object. This
98 allows objects which provide their docstrings via non-standard mechanisms
98 allows objects which provide their docstrings via non-standard mechanisms
99 (like Pyro proxies) to still be inspected by ipython's ? system."""
99 (like Pyro proxies) to still be inspected by ipython's ? system."""
100 # Allow objects to offer customized documentation via a getdoc method:
100 # Allow objects to offer customized documentation via a getdoc method:
101 try:
101 try:
102 ds = obj.getdoc()
102 ds = obj.getdoc()
103 except Exception:
103 except Exception:
104 pass
104 pass
105 else:
105 else:
106 # if we get extra info, we add it to the normal docstring.
106 # if we get extra info, we add it to the normal docstring.
107 if isinstance(ds, basestring):
107 if isinstance(ds, basestring):
108 return inspect.cleandoc(ds)
108 return inspect.cleandoc(ds)
109
109
110 try:
110 try:
111 return inspect.getdoc(obj)
111 return inspect.getdoc(obj)
112 except Exception:
112 except Exception:
113 # Harden against an inspect failure, which can occur with
113 # Harden against an inspect failure, which can occur with
114 # SWIG-wrapped extensions.
114 # SWIG-wrapped extensions.
115 return None
115 return None
116
116
117
117
118 def getsource(obj,is_binary=False):
118 def getsource(obj,is_binary=False):
119 """Wrapper around inspect.getsource.
119 """Wrapper around inspect.getsource.
120
120
121 This can be modified by other projects to provide customized source
121 This can be modified by other projects to provide customized source
122 extraction.
122 extraction.
123
123
124 Inputs:
124 Inputs:
125
125
126 - obj: an object whose source code we will attempt to extract.
126 - obj: an object whose source code we will attempt to extract.
127
127
128 Optional inputs:
128 Optional inputs:
129
129
130 - is_binary: whether the object is known to come from a binary source.
130 - is_binary: whether the object is known to come from a binary source.
131 This implementation will skip returning any output for binary objects, but
131 This implementation will skip returning any output for binary objects, but
132 custom extractors may know how to meaningfully process them."""
132 custom extractors may know how to meaningfully process them."""
133
133
134 if is_binary:
134 if is_binary:
135 return None
135 return None
136 else:
136 else:
137 # get source if obj was decorated with @decorator
137 # get source if obj was decorated with @decorator
138 if hasattr(obj,"__wrapped__"):
138 if hasattr(obj,"__wrapped__"):
139 obj = obj.__wrapped__
139 obj = obj.__wrapped__
140 try:
140 try:
141 src = inspect.getsource(obj)
141 src = inspect.getsource(obj)
142 except TypeError:
142 except TypeError:
143 if hasattr(obj,'__class__'):
143 if hasattr(obj,'__class__'):
144 src = inspect.getsource(obj.__class__)
144 src = inspect.getsource(obj.__class__)
145 return src
145 return src
146
146
147 def getargspec(obj):
147 def getargspec(obj):
148 """Get the names and default values of a function's arguments.
148 """Get the names and default values of a function's arguments.
149
149
150 A tuple of four things is returned: (args, varargs, varkw, defaults).
150 A tuple of four things is returned: (args, varargs, varkw, defaults).
151 'args' is a list of the argument names (it may contain nested lists).
151 'args' is a list of the argument names (it may contain nested lists).
152 'varargs' and 'varkw' are the names of the * and ** arguments or None.
152 'varargs' and 'varkw' are the names of the * and ** arguments or None.
153 'defaults' is an n-tuple of the default values of the last n arguments.
153 'defaults' is an n-tuple of the default values of the last n arguments.
154
154
155 Modified version of inspect.getargspec from the Python Standard
155 Modified version of inspect.getargspec from the Python Standard
156 Library."""
156 Library."""
157
157
158 if inspect.isfunction(obj):
158 if inspect.isfunction(obj):
159 func_obj = obj
159 func_obj = obj
160 elif inspect.ismethod(obj):
160 elif inspect.ismethod(obj):
161 func_obj = obj.im_func
161 func_obj = obj.im_func
162 elif hasattr(obj, '__call__'):
162 elif hasattr(obj, '__call__'):
163 func_obj = obj.__call__
163 func_obj = obj.__call__
164 else:
164 else:
165 raise TypeError('arg is not a Python function')
165 raise TypeError('arg is not a Python function')
166 args, varargs, varkw = inspect.getargs(func_obj.func_code)
166 args, varargs, varkw = inspect.getargs(func_obj.func_code)
167 return args, varargs, varkw, func_obj.func_defaults
167 return args, varargs, varkw, func_obj.func_defaults
168
168
169
169
170 def format_argspec(argspec):
170 def format_argspec(argspec):
171 """Format argspect, convenience wrapper around inspect's.
171 """Format argspect, convenience wrapper around inspect's.
172
172
173 This takes a dict instead of ordered arguments and calls
173 This takes a dict instead of ordered arguments and calls
174 inspect.format_argspec with the arguments in the necessary order.
174 inspect.format_argspec with the arguments in the necessary order.
175 """
175 """
176 return inspect.formatargspec(argspec['args'], argspec['varargs'],
176 return inspect.formatargspec(argspec['args'], argspec['varargs'],
177 argspec['varkw'], argspec['defaults'])
177 argspec['varkw'], argspec['defaults'])
178
178
179
179
180 def call_tip(oinfo, format_call=True):
180 def call_tip(oinfo, format_call=True):
181 """Extract call tip data from an oinfo dict.
181 """Extract call tip data from an oinfo dict.
182
182
183 Parameters
183 Parameters
184 ----------
184 ----------
185 oinfo : dict
185 oinfo : dict
186
186
187 format_call : bool, optional
187 format_call : bool, optional
188 If True, the call line is formatted and returned as a string. If not, a
188 If True, the call line is formatted and returned as a string. If not, a
189 tuple of (name, argspec) is returned.
189 tuple of (name, argspec) is returned.
190
190
191 Returns
191 Returns
192 -------
192 -------
193 call_info : None, str or (str, dict) tuple.
193 call_info : None, str or (str, dict) tuple.
194 When format_call is True, the whole call information is formattted as a
194 When format_call is True, the whole call information is formattted as a
195 single string. Otherwise, the object's name and its argspec dict are
195 single string. Otherwise, the object's name and its argspec dict are
196 returned. If no call information is available, None is returned.
196 returned. If no call information is available, None is returned.
197
197
198 docstring : str or None
198 docstring : str or None
199 The most relevant docstring for calling purposes is returned, if
199 The most relevant docstring for calling purposes is returned, if
200 available. The priority is: call docstring for callable instances, then
200 available. The priority is: call docstring for callable instances, then
201 constructor docstring for classes, then main object's docstring otherwise
201 constructor docstring for classes, then main object's docstring otherwise
202 (regular functions).
202 (regular functions).
203 """
203 """
204 # Get call definition
204 # Get call definition
205 argspec = oinfo.get('argspec')
205 argspec = oinfo.get('argspec')
206 if argspec is None:
206 if argspec is None:
207 call_line = None
207 call_line = None
208 else:
208 else:
209 # Callable objects will have 'self' as their first argument, prune
209 # Callable objects will have 'self' as their first argument, prune
210 # it out if it's there for clarity (since users do *not* pass an
210 # it out if it's there for clarity (since users do *not* pass an
211 # extra first argument explicitly).
211 # extra first argument explicitly).
212 try:
212 try:
213 has_self = argspec['args'][0] == 'self'
213 has_self = argspec['args'][0] == 'self'
214 except (KeyError, IndexError):
214 except (KeyError, IndexError):
215 pass
215 pass
216 else:
216 else:
217 if has_self:
217 if has_self:
218 argspec['args'] = argspec['args'][1:]
218 argspec['args'] = argspec['args'][1:]
219
219
220 call_line = oinfo['name']+format_argspec(argspec)
220 call_line = oinfo['name']+format_argspec(argspec)
221
221
222 # Now get docstring.
222 # Now get docstring.
223 # The priority is: call docstring, constructor docstring, main one.
223 # The priority is: call docstring, constructor docstring, main one.
224 doc = oinfo.get('call_docstring')
224 doc = oinfo.get('call_docstring')
225 if doc is None:
225 if doc is None:
226 doc = oinfo.get('init_docstring')
226 doc = oinfo.get('init_docstring')
227 if doc is None:
227 if doc is None:
228 doc = oinfo.get('docstring','')
228 doc = oinfo.get('docstring','')
229
229
230 return call_line, doc
230 return call_line, doc
231
231
232
232
233 def find_file(obj):
233 def find_file(obj):
234 """Find the absolute path to the file where an object was defined.
234 """Find the absolute path to the file where an object was defined.
235
235
236 This is essentially a robust wrapper around `inspect.getabsfile`.
236 This is essentially a robust wrapper around `inspect.getabsfile`.
237
237
238 Returns None if no file can be found.
238 Returns None if no file can be found.
239
239
240 Parameters
240 Parameters
241 ----------
241 ----------
242 obj : any Python object
242 obj : any Python object
243
243
244 Returns
244 Returns
245 -------
245 -------
246 fname : str
246 fname : str
247 The absolute path to the file where the object was defined.
247 The absolute path to the file where the object was defined.
248 """
248 """
249 # get source if obj was decorated with @decorator
249 # get source if obj was decorated with @decorator
250 if hasattr(obj, '__wrapped__'):
250 if hasattr(obj, '__wrapped__'):
251 obj = obj.__wrapped__
251 obj = obj.__wrapped__
252
252
253 fname = None
253 try:
254 try:
254 fname = inspect.getabsfile(obj)
255 fname = inspect.getabsfile(obj)
255 except TypeError:
256 except TypeError:
256 # For an instance, the file that matters is where its class was
257 # For an instance, the file that matters is where its class was
257 # declared.
258 # declared.
258 if hasattr(obj, '__class__'):
259 if hasattr(obj, '__class__'):
259 try:
260 try:
260 fname = inspect.getabsfile(obj.__class__)
261 fname = inspect.getabsfile(obj.__class__)
261 except TypeError:
262 except TypeError:
262 # Can happen for builtins
263 # Can happen for builtins
263 fname = None
264 pass
264 except:
265 except:
265 fname = None
266 pass
266 return fname
267 return fname
267
268
268
269
269 def find_source_lines(obj):
270 def find_source_lines(obj):
270 """Find the line number in a file where an object was defined.
271 """Find the line number in a file where an object was defined.
271
272
272 This is essentially a robust wrapper around `inspect.getsourcelines`.
273 This is essentially a robust wrapper around `inspect.getsourcelines`.
273
274
274 Returns None if no file can be found.
275 Returns None if no file can be found.
275
276
276 Parameters
277 Parameters
277 ----------
278 ----------
278 obj : any Python object
279 obj : any Python object
279
280
280 Returns
281 Returns
281 -------
282 -------
282 lineno : int
283 lineno : int
283 The line number where the object definition starts.
284 The line number where the object definition starts.
284 """
285 """
285 # get source if obj was decorated with @decorator
286 # get source if obj was decorated with @decorator
286 if hasattr(obj, '__wrapped__'):
287 if hasattr(obj, '__wrapped__'):
287 obj = obj.__wrapped__
288 obj = obj.__wrapped__
288
289
289 try:
290 try:
290 try:
291 try:
291 lineno = inspect.getsourcelines(obj)[1]
292 lineno = inspect.getsourcelines(obj)[1]
292 except TypeError:
293 except TypeError:
293 # For instances, try the class object like getsource() does
294 # For instances, try the class object like getsource() does
294 if hasattr(obj, '__class__'):
295 if hasattr(obj, '__class__'):
295 lineno = inspect.getsourcelines(obj.__class__)[1]
296 lineno = inspect.getsourcelines(obj.__class__)[1]
296 except:
297 except:
297 return None
298 return None
298
299
299 return lineno
300 return lineno
300
301
301
302
302 class Inspector:
303 class Inspector:
303 def __init__(self, color_table=InspectColors,
304 def __init__(self, color_table=InspectColors,
304 code_color_table=PyColorize.ANSICodeColors,
305 code_color_table=PyColorize.ANSICodeColors,
305 scheme='NoColor',
306 scheme='NoColor',
306 str_detail_level=0):
307 str_detail_level=0):
307 self.color_table = color_table
308 self.color_table = color_table
308 self.parser = PyColorize.Parser(code_color_table,out='str')
309 self.parser = PyColorize.Parser(code_color_table,out='str')
309 self.format = self.parser.format
310 self.format = self.parser.format
310 self.str_detail_level = str_detail_level
311 self.str_detail_level = str_detail_level
311 self.set_active_scheme(scheme)
312 self.set_active_scheme(scheme)
312
313
313 def _getdef(self,obj,oname=''):
314 def _getdef(self,obj,oname=''):
314 """Return the definition header for any callable object.
315 """Return the definition header for any callable object.
315
316
316 If any exception is generated, None is returned instead and the
317 If any exception is generated, None is returned instead and the
317 exception is suppressed."""
318 exception is suppressed."""
318
319
319 try:
320 try:
320 # We need a plain string here, NOT unicode!
321 # We need a plain string here, NOT unicode!
321 hdef = oname + inspect.formatargspec(*getargspec(obj))
322 hdef = oname + inspect.formatargspec(*getargspec(obj))
322 return py3compat.unicode_to_str(hdef, 'ascii')
323 return py3compat.unicode_to_str(hdef, 'ascii')
323 except:
324 except:
324 return None
325 return None
325
326
326 def __head(self,h):
327 def __head(self,h):
327 """Return a header string with proper colors."""
328 """Return a header string with proper colors."""
328 return '%s%s%s' % (self.color_table.active_colors.header,h,
329 return '%s%s%s' % (self.color_table.active_colors.header,h,
329 self.color_table.active_colors.normal)
330 self.color_table.active_colors.normal)
330
331
331 def set_active_scheme(self, scheme):
332 def set_active_scheme(self, scheme):
332 self.color_table.set_active_scheme(scheme)
333 self.color_table.set_active_scheme(scheme)
333 self.parser.color_table.set_active_scheme(scheme)
334 self.parser.color_table.set_active_scheme(scheme)
334
335
335 def noinfo(self, msg, oname):
336 def noinfo(self, msg, oname):
336 """Generic message when no information is found."""
337 """Generic message when no information is found."""
337 print 'No %s found' % msg,
338 print 'No %s found' % msg,
338 if oname:
339 if oname:
339 print 'for %s' % oname
340 print 'for %s' % oname
340 else:
341 else:
341 print
342 print
342
343
343 def pdef(self, obj, oname=''):
344 def pdef(self, obj, oname=''):
344 """Print the definition header for any callable object.
345 """Print the definition header for any callable object.
345
346
346 If the object is a class, print the constructor information."""
347 If the object is a class, print the constructor information."""
347
348
348 if not callable(obj):
349 if not callable(obj):
349 print 'Object is not callable.'
350 print 'Object is not callable.'
350 return
351 return
351
352
352 header = ''
353 header = ''
353
354
354 if inspect.isclass(obj):
355 if inspect.isclass(obj):
355 header = self.__head('Class constructor information:\n')
356 header = self.__head('Class constructor information:\n')
356 obj = obj.__init__
357 obj = obj.__init__
357 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
358 elif (not py3compat.PY3) and type(obj) is types.InstanceType:
358 obj = obj.__call__
359 obj = obj.__call__
359
360
360 output = self._getdef(obj,oname)
361 output = self._getdef(obj,oname)
361 if output is None:
362 if output is None:
362 self.noinfo('definition header',oname)
363 self.noinfo('definition header',oname)
363 else:
364 else:
364 print >>io.stdout, header,self.format(output),
365 print >>io.stdout, header,self.format(output),
365
366
366 # In Python 3, all classes are new-style, so they all have __init__.
367 # In Python 3, all classes are new-style, so they all have __init__.
367 @skip_doctest_py3
368 @skip_doctest_py3
368 def pdoc(self,obj,oname='',formatter = None):
369 def pdoc(self,obj,oname='',formatter = None):
369 """Print the docstring for any object.
370 """Print the docstring for any object.
370
371
371 Optional:
372 Optional:
372 -formatter: a function to run the docstring through for specially
373 -formatter: a function to run the docstring through for specially
373 formatted docstrings.
374 formatted docstrings.
374
375
375 Examples
376 Examples
376 --------
377 --------
377
378
378 In [1]: class NoInit:
379 In [1]: class NoInit:
379 ...: pass
380 ...: pass
380
381
381 In [2]: class NoDoc:
382 In [2]: class NoDoc:
382 ...: def __init__(self):
383 ...: def __init__(self):
383 ...: pass
384 ...: pass
384
385
385 In [3]: %pdoc NoDoc
386 In [3]: %pdoc NoDoc
386 No documentation found for NoDoc
387 No documentation found for NoDoc
387
388
388 In [4]: %pdoc NoInit
389 In [4]: %pdoc NoInit
389 No documentation found for NoInit
390 No documentation found for NoInit
390
391
391 In [5]: obj = NoInit()
392 In [5]: obj = NoInit()
392
393
393 In [6]: %pdoc obj
394 In [6]: %pdoc obj
394 No documentation found for obj
395 No documentation found for obj
395
396
396 In [5]: obj2 = NoDoc()
397 In [5]: obj2 = NoDoc()
397
398
398 In [6]: %pdoc obj2
399 In [6]: %pdoc obj2
399 No documentation found for obj2
400 No documentation found for obj2
400 """
401 """
401
402
402 head = self.__head # For convenience
403 head = self.__head # For convenience
403 lines = []
404 lines = []
404 ds = getdoc(obj)
405 ds = getdoc(obj)
405 if formatter:
406 if formatter:
406 ds = formatter(ds)
407 ds = formatter(ds)
407 if ds:
408 if ds:
408 lines.append(head("Class Docstring:"))
409 lines.append(head("Class Docstring:"))
409 lines.append(indent(ds))
410 lines.append(indent(ds))
410 if inspect.isclass(obj) and hasattr(obj, '__init__'):
411 if inspect.isclass(obj) and hasattr(obj, '__init__'):
411 init_ds = getdoc(obj.__init__)
412 init_ds = getdoc(obj.__init__)
412 if init_ds is not None:
413 if init_ds is not None:
413 lines.append(head("Constructor Docstring:"))
414 lines.append(head("Constructor Docstring:"))
414 lines.append(indent(init_ds))
415 lines.append(indent(init_ds))
415 elif hasattr(obj,'__call__'):
416 elif hasattr(obj,'__call__'):
416 call_ds = getdoc(obj.__call__)
417 call_ds = getdoc(obj.__call__)
417 if call_ds:
418 if call_ds:
418 lines.append(head("Calling Docstring:"))
419 lines.append(head("Calling Docstring:"))
419 lines.append(indent(call_ds))
420 lines.append(indent(call_ds))
420
421
421 if not lines:
422 if not lines:
422 self.noinfo('documentation',oname)
423 self.noinfo('documentation',oname)
423 else:
424 else:
424 page.page('\n'.join(lines))
425 page.page('\n'.join(lines))
425
426
426 def psource(self,obj,oname=''):
427 def psource(self,obj,oname=''):
427 """Print the source code for an object."""
428 """Print the source code for an object."""
428
429
429 # Flush the source cache because inspect can return out-of-date source
430 # Flush the source cache because inspect can return out-of-date source
430 linecache.checkcache()
431 linecache.checkcache()
431 try:
432 try:
432 src = getsource(obj)
433 src = getsource(obj)
433 except:
434 except:
434 self.noinfo('source',oname)
435 self.noinfo('source',oname)
435 else:
436 else:
436 page.page(self.format(py3compat.unicode_to_str(src)))
437 page.page(self.format(py3compat.unicode_to_str(src)))
437
438
438 def pfile(self, obj, oname=''):
439 def pfile(self, obj, oname=''):
439 """Show the whole file where an object was defined."""
440 """Show the whole file where an object was defined."""
440
441
441 lineno = find_source_lines(obj)
442 lineno = find_source_lines(obj)
442 if lineno is None:
443 if lineno is None:
443 self.noinfo('file', oname)
444 self.noinfo('file', oname)
444 return
445 return
445
446
446 ofile = find_file(obj)
447 ofile = find_file(obj)
447 # run contents of file through pager starting at line where the object
448 # run contents of file through pager starting at line where the object
448 # is defined, as long as the file isn't binary and is actually on the
449 # is defined, as long as the file isn't binary and is actually on the
449 # filesystem.
450 # filesystem.
450 if ofile.endswith(('.so', '.dll', '.pyd')):
451 if ofile.endswith(('.so', '.dll', '.pyd')):
451 print 'File %r is binary, not printing.' % ofile
452 print 'File %r is binary, not printing.' % ofile
452 elif not os.path.isfile(ofile):
453 elif not os.path.isfile(ofile):
453 print 'File %r does not exist, not printing.' % ofile
454 print 'File %r does not exist, not printing.' % ofile
454 else:
455 else:
455 # Print only text files, not extension binaries. Note that
456 # Print only text files, not extension binaries. Note that
456 # getsourcelines returns lineno with 1-offset and page() uses
457 # getsourcelines returns lineno with 1-offset and page() uses
457 # 0-offset, so we must adjust.
458 # 0-offset, so we must adjust.
458 page.page(self.format(open(ofile).read()), lineno-1)
459 page.page(self.format(open(ofile).read()), lineno-1)
459
460
460 def _format_fields(self, fields, title_width=12):
461 def _format_fields(self, fields, title_width=12):
461 """Formats a list of fields for display.
462 """Formats a list of fields for display.
462
463
463 Parameters
464 Parameters
464 ----------
465 ----------
465 fields : list
466 fields : list
466 A list of 2-tuples: (field_title, field_content)
467 A list of 2-tuples: (field_title, field_content)
467 title_width : int
468 title_width : int
468 How many characters to pad titles to. Default 12.
469 How many characters to pad titles to. Default 12.
469 """
470 """
470 out = []
471 out = []
471 header = self.__head
472 header = self.__head
472 for title, content in fields:
473 for title, content in fields:
473 if len(content.splitlines()) > 1:
474 if len(content.splitlines()) > 1:
474 title = header(title + ":") + "\n"
475 title = header(title + ":") + "\n"
475 else:
476 else:
476 title = header((title+":").ljust(title_width))
477 title = header((title+":").ljust(title_width))
477 out.append(title + content)
478 out.append(title + content)
478 return "\n".join(out)
479 return "\n".join(out)
479
480
480 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
481 # The fields to be displayed by pinfo: (fancy_name, key_in_info_dict)
481 pinfo_fields1 = [("Type", "type_name"),
482 pinfo_fields1 = [("Type", "type_name"),
482 ("Base Class", "base_class"),
483 ("Base Class", "base_class"),
483 ("String Form", "string_form"),
484 ("String Form", "string_form"),
484 ("Namespace", "namespace"),
485 ("Namespace", "namespace"),
485 ("Length", "length"),
486 ("Length", "length"),
486 ("File", "file"),
487 ("File", "file"),
487 ("Definition", "definition")]
488 ("Definition", "definition")]
488
489
489 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
490 pinfo_fields_obj = [("Class Docstring", "class_docstring"),
490 ("Constructor Docstring","init_docstring"),
491 ("Constructor Docstring","init_docstring"),
491 ("Call def", "call_def"),
492 ("Call def", "call_def"),
492 ("Call docstring", "call_docstring")]
493 ("Call docstring", "call_docstring")]
493
494
494 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
495 def pinfo(self,obj,oname='',formatter=None,info=None,detail_level=0):
495 """Show detailed information about an object.
496 """Show detailed information about an object.
496
497
497 Optional arguments:
498 Optional arguments:
498
499
499 - oname: name of the variable pointing to the object.
500 - oname: name of the variable pointing to the object.
500
501
501 - formatter: special formatter for docstrings (see pdoc)
502 - formatter: special formatter for docstrings (see pdoc)
502
503
503 - info: a structure with some information fields which may have been
504 - info: a structure with some information fields which may have been
504 precomputed already.
505 precomputed already.
505
506
506 - detail_level: if set to 1, more information is given.
507 - detail_level: if set to 1, more information is given.
507 """
508 """
508 info = self.info(obj, oname=oname, formatter=formatter,
509 info = self.info(obj, oname=oname, formatter=formatter,
509 info=info, detail_level=detail_level)
510 info=info, detail_level=detail_level)
510 displayfields = []
511 displayfields = []
511 for title, key in self.pinfo_fields1:
512 for title, key in self.pinfo_fields1:
512 field = info[key]
513 field = info[key]
513 if field is not None:
514 if field is not None:
514 displayfields.append((title, field.rstrip()))
515 displayfields.append((title, field.rstrip()))
515
516
516 # Source or docstring, depending on detail level and whether
517 # Source or docstring, depending on detail level and whether
517 # source found.
518 # source found.
518 if detail_level > 0 and info['source'] is not None:
519 if detail_level > 0 and info['source'] is not None:
519 displayfields.append(("Source", self.format(py3compat.cast_bytes_py2(info['source']))))
520 displayfields.append(("Source", self.format(py3compat.cast_bytes_py2(info['source']))))
520 elif info['docstring'] is not None:
521 elif info['docstring'] is not None:
521 displayfields.append(("Docstring", info["docstring"]))
522 displayfields.append(("Docstring", info["docstring"]))
522
523
523 # Constructor info for classes
524 # Constructor info for classes
524 if info['isclass']:
525 if info['isclass']:
525 if info['init_definition'] or info['init_docstring']:
526 if info['init_definition'] or info['init_docstring']:
526 displayfields.append(("Constructor information", ""))
527 displayfields.append(("Constructor information", ""))
527 if info['init_definition'] is not None:
528 if info['init_definition'] is not None:
528 displayfields.append((" Definition",
529 displayfields.append((" Definition",
529 info['init_definition'].rstrip()))
530 info['init_definition'].rstrip()))
530 if info['init_docstring'] is not None:
531 if info['init_docstring'] is not None:
531 displayfields.append((" Docstring",
532 displayfields.append((" Docstring",
532 indent(info['init_docstring'])))
533 indent(info['init_docstring'])))
533
534
534 # Info for objects:
535 # Info for objects:
535 else:
536 else:
536 for title, key in self.pinfo_fields_obj:
537 for title, key in self.pinfo_fields_obj:
537 field = info[key]
538 field = info[key]
538 if field is not None:
539 if field is not None:
539 displayfields.append((title, field.rstrip()))
540 displayfields.append((title, field.rstrip()))
540
541
541 # Finally send to printer/pager:
542 # Finally send to printer/pager:
542 if displayfields:
543 if displayfields:
543 page.page(self._format_fields(displayfields))
544 page.page(self._format_fields(displayfields))
544
545
545 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
546 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
546 """Compute a dict with detailed information about an object.
547 """Compute a dict with detailed information about an object.
547
548
548 Optional arguments:
549 Optional arguments:
549
550
550 - oname: name of the variable pointing to the object.
551 - oname: name of the variable pointing to the object.
551
552
552 - formatter: special formatter for docstrings (see pdoc)
553 - formatter: special formatter for docstrings (see pdoc)
553
554
554 - info: a structure with some information fields which may have been
555 - info: a structure with some information fields which may have been
555 precomputed already.
556 precomputed already.
556
557
557 - detail_level: if set to 1, more information is given.
558 - detail_level: if set to 1, more information is given.
558 """
559 """
559
560
560 obj_type = type(obj)
561 obj_type = type(obj)
561
562
562 header = self.__head
563 header = self.__head
563 if info is None:
564 if info is None:
564 ismagic = 0
565 ismagic = 0
565 isalias = 0
566 isalias = 0
566 ospace = ''
567 ospace = ''
567 else:
568 else:
568 ismagic = info.ismagic
569 ismagic = info.ismagic
569 isalias = info.isalias
570 isalias = info.isalias
570 ospace = info.namespace
571 ospace = info.namespace
571
572
572 # Get docstring, special-casing aliases:
573 # Get docstring, special-casing aliases:
573 if isalias:
574 if isalias:
574 if not callable(obj):
575 if not callable(obj):
575 try:
576 try:
576 ds = "Alias to the system command:\n %s" % obj[1]
577 ds = "Alias to the system command:\n %s" % obj[1]
577 except:
578 except:
578 ds = "Alias: " + str(obj)
579 ds = "Alias: " + str(obj)
579 else:
580 else:
580 ds = "Alias to " + str(obj)
581 ds = "Alias to " + str(obj)
581 if obj.__doc__:
582 if obj.__doc__:
582 ds += "\nDocstring:\n" + obj.__doc__
583 ds += "\nDocstring:\n" + obj.__doc__
583 else:
584 else:
584 ds = getdoc(obj)
585 ds = getdoc(obj)
585 if ds is None:
586 if ds is None:
586 ds = '<no docstring>'
587 ds = '<no docstring>'
587 if formatter is not None:
588 if formatter is not None:
588 ds = formatter(ds)
589 ds = formatter(ds)
589
590
590 # store output in a dict, we initialize it here and fill it as we go
591 # store output in a dict, we initialize it here and fill it as we go
591 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
592 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic)
592
593
593 string_max = 200 # max size of strings to show (snipped if longer)
594 string_max = 200 # max size of strings to show (snipped if longer)
594 shalf = int((string_max -5)/2)
595 shalf = int((string_max -5)/2)
595
596
596 if ismagic:
597 if ismagic:
597 obj_type_name = 'Magic function'
598 obj_type_name = 'Magic function'
598 elif isalias:
599 elif isalias:
599 obj_type_name = 'System alias'
600 obj_type_name = 'System alias'
600 else:
601 else:
601 obj_type_name = obj_type.__name__
602 obj_type_name = obj_type.__name__
602 out['type_name'] = obj_type_name
603 out['type_name'] = obj_type_name
603
604
604 try:
605 try:
605 bclass = obj.__class__
606 bclass = obj.__class__
606 out['base_class'] = str(bclass)
607 out['base_class'] = str(bclass)
607 except: pass
608 except: pass
608
609
609 # String form, but snip if too long in ? form (full in ??)
610 # String form, but snip if too long in ? form (full in ??)
610 if detail_level >= self.str_detail_level:
611 if detail_level >= self.str_detail_level:
611 try:
612 try:
612 ostr = str(obj)
613 ostr = str(obj)
613 str_head = 'string_form'
614 str_head = 'string_form'
614 if not detail_level and len(ostr)>string_max:
615 if not detail_level and len(ostr)>string_max:
615 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
616 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
616 ostr = ("\n" + " " * len(str_head.expandtabs())).\
617 ostr = ("\n" + " " * len(str_head.expandtabs())).\
617 join(q.strip() for q in ostr.split("\n"))
618 join(q.strip() for q in ostr.split("\n"))
618 out[str_head] = ostr
619 out[str_head] = ostr
619 except:
620 except:
620 pass
621 pass
621
622
622 if ospace:
623 if ospace:
623 out['namespace'] = ospace
624 out['namespace'] = ospace
624
625
625 # Length (for strings and lists)
626 # Length (for strings and lists)
626 try:
627 try:
627 out['length'] = str(len(obj))
628 out['length'] = str(len(obj))
628 except: pass
629 except: pass
629
630
630 # Filename where object was defined
631 # Filename where object was defined
631 binary_file = False
632 binary_file = False
632 fname = find_file(obj)
633 fname = find_file(obj)
633 if fname is None:
634 if fname is None:
634 # if anything goes wrong, we don't want to show source, so it's as
635 # if anything goes wrong, we don't want to show source, so it's as
635 # if the file was binary
636 # if the file was binary
636 binary_file = True
637 binary_file = True
637 else:
638 else:
638 if fname.endswith(('.so', '.dll', '.pyd')):
639 if fname.endswith(('.so', '.dll', '.pyd')):
639 binary_file = True
640 binary_file = True
640 elif fname.endswith('<string>'):
641 elif fname.endswith('<string>'):
641 fname = 'Dynamically generated function. No source code available.'
642 fname = 'Dynamically generated function. No source code available.'
642 out['file'] = fname
643 out['file'] = fname
643
644
644 # reconstruct the function definition and print it:
645 # reconstruct the function definition and print it:
645 defln = self._getdef(obj, oname)
646 defln = self._getdef(obj, oname)
646 if defln:
647 if defln:
647 out['definition'] = self.format(defln)
648 out['definition'] = self.format(defln)
648
649
649 # Docstrings only in detail 0 mode, since source contains them (we
650 # Docstrings only in detail 0 mode, since source contains them (we
650 # avoid repetitions). If source fails, we add them back, see below.
651 # avoid repetitions). If source fails, we add them back, see below.
651 if ds and detail_level == 0:
652 if ds and detail_level == 0:
652 out['docstring'] = ds
653 out['docstring'] = ds
653
654
654 # Original source code for any callable
655 # Original source code for any callable
655 if detail_level:
656 if detail_level:
656 # Flush the source cache because inspect can return out-of-date
657 # Flush the source cache because inspect can return out-of-date
657 # source
658 # source
658 linecache.checkcache()
659 linecache.checkcache()
659 source = None
660 source = None
660 try:
661 try:
661 try:
662 try:
662 source = getsource(obj, binary_file)
663 source = getsource(obj, binary_file)
663 except TypeError:
664 except TypeError:
664 if hasattr(obj, '__class__'):
665 if hasattr(obj, '__class__'):
665 source = getsource(obj.__class__, binary_file)
666 source = getsource(obj.__class__, binary_file)
666 if source is not None:
667 if source is not None:
667 out['source'] = source.rstrip()
668 out['source'] = source.rstrip()
668 except Exception:
669 except Exception:
669 pass
670 pass
670
671
671 if ds and source is None:
672 if ds and source is None:
672 out['docstring'] = ds
673 out['docstring'] = ds
673
674
674
675
675 # Constructor docstring for classes
676 # Constructor docstring for classes
676 if inspect.isclass(obj):
677 if inspect.isclass(obj):
677 out['isclass'] = True
678 out['isclass'] = True
678 # reconstruct the function definition and print it:
679 # reconstruct the function definition and print it:
679 try:
680 try:
680 obj_init = obj.__init__
681 obj_init = obj.__init__
681 except AttributeError:
682 except AttributeError:
682 init_def = init_ds = None
683 init_def = init_ds = None
683 else:
684 else:
684 init_def = self._getdef(obj_init,oname)
685 init_def = self._getdef(obj_init,oname)
685 init_ds = getdoc(obj_init)
686 init_ds = getdoc(obj_init)
686 # Skip Python's auto-generated docstrings
687 # Skip Python's auto-generated docstrings
687 if init_ds and \
688 if init_ds and \
688 init_ds.startswith('x.__init__(...) initializes'):
689 init_ds.startswith('x.__init__(...) initializes'):
689 init_ds = None
690 init_ds = None
690
691
691 if init_def or init_ds:
692 if init_def or init_ds:
692 if init_def:
693 if init_def:
693 out['init_definition'] = self.format(init_def)
694 out['init_definition'] = self.format(init_def)
694 if init_ds:
695 if init_ds:
695 out['init_docstring'] = init_ds
696 out['init_docstring'] = init_ds
696
697
697 # and class docstring for instances:
698 # and class docstring for instances:
698 else:
699 else:
699 # First, check whether the instance docstring is identical to the
700 # First, check whether the instance docstring is identical to the
700 # class one, and print it separately if they don't coincide. In
701 # class one, and print it separately if they don't coincide. In
701 # most cases they will, but it's nice to print all the info for
702 # most cases they will, but it's nice to print all the info for
702 # objects which use instance-customized docstrings.
703 # objects which use instance-customized docstrings.
703 if ds:
704 if ds:
704 try:
705 try:
705 cls = getattr(obj,'__class__')
706 cls = getattr(obj,'__class__')
706 except:
707 except:
707 class_ds = None
708 class_ds = None
708 else:
709 else:
709 class_ds = getdoc(cls)
710 class_ds = getdoc(cls)
710 # Skip Python's auto-generated docstrings
711 # Skip Python's auto-generated docstrings
711 if class_ds and \
712 if class_ds and \
712 (class_ds.startswith('function(code, globals[,') or \
713 (class_ds.startswith('function(code, globals[,') or \
713 class_ds.startswith('instancemethod(function, instance,') or \
714 class_ds.startswith('instancemethod(function, instance,') or \
714 class_ds.startswith('module(name[,') ):
715 class_ds.startswith('module(name[,') ):
715 class_ds = None
716 class_ds = None
716 if class_ds and ds != class_ds:
717 if class_ds and ds != class_ds:
717 out['class_docstring'] = class_ds
718 out['class_docstring'] = class_ds
718
719
719 # Next, try to show constructor docstrings
720 # Next, try to show constructor docstrings
720 try:
721 try:
721 init_ds = getdoc(obj.__init__)
722 init_ds = getdoc(obj.__init__)
722 # Skip Python's auto-generated docstrings
723 # Skip Python's auto-generated docstrings
723 if init_ds and \
724 if init_ds and \
724 init_ds.startswith('x.__init__(...) initializes'):
725 init_ds.startswith('x.__init__(...) initializes'):
725 init_ds = None
726 init_ds = None
726 except AttributeError:
727 except AttributeError:
727 init_ds = None
728 init_ds = None
728 if init_ds:
729 if init_ds:
729 out['init_docstring'] = init_ds
730 out['init_docstring'] = init_ds
730
731
731 # Call form docstring for callable instances
732 # Call form docstring for callable instances
732 if hasattr(obj, '__call__'):
733 if hasattr(obj, '__call__'):
733 call_def = self._getdef(obj.__call__, oname)
734 call_def = self._getdef(obj.__call__, oname)
734 if call_def is not None:
735 if call_def is not None:
735 out['call_def'] = self.format(call_def)
736 out['call_def'] = self.format(call_def)
736 call_ds = getdoc(obj.__call__)
737 call_ds = getdoc(obj.__call__)
737 # Skip Python's auto-generated docstrings
738 # Skip Python's auto-generated docstrings
738 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
739 if call_ds and call_ds.startswith('x.__call__(...) <==> x(...)'):
739 call_ds = None
740 call_ds = None
740 if call_ds:
741 if call_ds:
741 out['call_docstring'] = call_ds
742 out['call_docstring'] = call_ds
742
743
743 # Compute the object's argspec as a callable. The key is to decide
744 # Compute the object's argspec as a callable. The key is to decide
744 # whether to pull it from the object itself, from its __init__ or
745 # whether to pull it from the object itself, from its __init__ or
745 # from its __call__ method.
746 # from its __call__ method.
746
747
747 if inspect.isclass(obj):
748 if inspect.isclass(obj):
748 # Old-style classes need not have an __init__
749 # Old-style classes need not have an __init__
749 callable_obj = getattr(obj, "__init__", None)
750 callable_obj = getattr(obj, "__init__", None)
750 elif callable(obj):
751 elif callable(obj):
751 callable_obj = obj
752 callable_obj = obj
752 else:
753 else:
753 callable_obj = None
754 callable_obj = None
754
755
755 if callable_obj:
756 if callable_obj:
756 try:
757 try:
757 args, varargs, varkw, defaults = getargspec(callable_obj)
758 args, varargs, varkw, defaults = getargspec(callable_obj)
758 except (TypeError, AttributeError):
759 except (TypeError, AttributeError):
759 # For extensions/builtins we can't retrieve the argspec
760 # For extensions/builtins we can't retrieve the argspec
760 pass
761 pass
761 else:
762 else:
762 out['argspec'] = dict(args=args, varargs=varargs,
763 out['argspec'] = dict(args=args, varargs=varargs,
763 varkw=varkw, defaults=defaults)
764 varkw=varkw, defaults=defaults)
764
765
765 return object_info(**out)
766 return object_info(**out)
766
767
767
768
768 def psearch(self,pattern,ns_table,ns_search=[],
769 def psearch(self,pattern,ns_table,ns_search=[],
769 ignore_case=False,show_all=False):
770 ignore_case=False,show_all=False):
770 """Search namespaces with wildcards for objects.
771 """Search namespaces with wildcards for objects.
771
772
772 Arguments:
773 Arguments:
773
774
774 - pattern: string containing shell-like wildcards to use in namespace
775 - pattern: string containing shell-like wildcards to use in namespace
775 searches and optionally a type specification to narrow the search to
776 searches and optionally a type specification to narrow the search to
776 objects of that type.
777 objects of that type.
777
778
778 - ns_table: dict of name->namespaces for search.
779 - ns_table: dict of name->namespaces for search.
779
780
780 Optional arguments:
781 Optional arguments:
781
782
782 - ns_search: list of namespace names to include in search.
783 - ns_search: list of namespace names to include in search.
783
784
784 - ignore_case(False): make the search case-insensitive.
785 - ignore_case(False): make the search case-insensitive.
785
786
786 - show_all(False): show all names, including those starting with
787 - show_all(False): show all names, including those starting with
787 underscores.
788 underscores.
788 """
789 """
789 #print 'ps pattern:<%r>' % pattern # dbg
790 #print 'ps pattern:<%r>' % pattern # dbg
790
791
791 # defaults
792 # defaults
792 type_pattern = 'all'
793 type_pattern = 'all'
793 filter = ''
794 filter = ''
794
795
795 cmds = pattern.split()
796 cmds = pattern.split()
796 len_cmds = len(cmds)
797 len_cmds = len(cmds)
797 if len_cmds == 1:
798 if len_cmds == 1:
798 # Only filter pattern given
799 # Only filter pattern given
799 filter = cmds[0]
800 filter = cmds[0]
800 elif len_cmds == 2:
801 elif len_cmds == 2:
801 # Both filter and type specified
802 # Both filter and type specified
802 filter,type_pattern = cmds
803 filter,type_pattern = cmds
803 else:
804 else:
804 raise ValueError('invalid argument string for psearch: <%s>' %
805 raise ValueError('invalid argument string for psearch: <%s>' %
805 pattern)
806 pattern)
806
807
807 # filter search namespaces
808 # filter search namespaces
808 for name in ns_search:
809 for name in ns_search:
809 if name not in ns_table:
810 if name not in ns_table:
810 raise ValueError('invalid namespace <%s>. Valid names: %s' %
811 raise ValueError('invalid namespace <%s>. Valid names: %s' %
811 (name,ns_table.keys()))
812 (name,ns_table.keys()))
812
813
813 #print 'type_pattern:',type_pattern # dbg
814 #print 'type_pattern:',type_pattern # dbg
814 search_result, namespaces_seen = set(), set()
815 search_result, namespaces_seen = set(), set()
815 for ns_name in ns_search:
816 for ns_name in ns_search:
816 ns = ns_table[ns_name]
817 ns = ns_table[ns_name]
817 # Normally, locals and globals are the same, so we just check one.
818 # Normally, locals and globals are the same, so we just check one.
818 if id(ns) in namespaces_seen:
819 if id(ns) in namespaces_seen:
819 continue
820 continue
820 namespaces_seen.add(id(ns))
821 namespaces_seen.add(id(ns))
821 tmp_res = list_namespace(ns, type_pattern, filter,
822 tmp_res = list_namespace(ns, type_pattern, filter,
822 ignore_case=ignore_case, show_all=show_all)
823 ignore_case=ignore_case, show_all=show_all)
823 search_result.update(tmp_res)
824 search_result.update(tmp_res)
824
825
825 page.page('\n'.join(sorted(search_result)))
826 page.page('\n'.join(sorted(search_result)))
General Comments 0
You need to be logged in to leave comments. Login now