1
2
3
4
5
6
7
8
9 """
10 Extract API documentation about python objects by directly introspecting
11 their values.
12
13 The function L{introspect_docs()}, which provides the main interface
14 of this module, examines a Python objects via introspection, and uses
15 the information it finds to create an L{APIDoc} objects containing the
16 API documentation for that objects.
17
18 The L{register_introspecter()} method can be used to extend the
19 functionality of C{docintrospector}, by providing methods that handle
20 special value types.
21 """
22 __docformat__ = 'epytext en'
23
24
25
26
27
28 import inspect, re, sys, os.path, imp
29
30 from epydoc.apidoc import *
31
32 from types import *
33
34 from epydoc import log
35
36 from epydoc.util import *
37
38 import epydoc.docparser
39
40 import __builtin__
41
42 from epydoc.compat import *
43
44
45
46
47
48 _valuedoc_cache = {}
49 """A cache containing the API documentation for values that we've
50 already seen. This cache is implemented as a dictionary that maps a
51 value's pyid to its L{ValueDoc}.
52
53 Note that if we encounter a value but decide not to introspect it
54 (because it's imported from another module), then C{_valuedoc_cache}
55 will contain an entry for the value, but the value will not be listed
56 in L{_introspected_values}."""
57
58 _introspected_values = {}
59 """A record which values we've introspected, encoded as a dictionary from
60 pyid to C{bool}."""
61
69
70
71
72
73
74 -def introspect_docs(value=None, name=None, filename=None, context=None,
75 is_script=False, module_name=None):
76 """
77 Generate the API documentation for a specified object by
78 introspecting Python values, and return it as a L{ValueDoc}. The
79 object to generate documentation for may be specified using
80 the C{value} parameter, the C{filename} parameter, I{or} the
81 C{name} parameter. (It is an error to specify more than one
82 of these three parameters, or to not specify any of them.)
83
84 @param value: The python object that should be documented.
85 @param filename: The name of the file that contains the python
86 source code for a package, module, or script. If
87 C{filename} is specified, then C{introspect} will return a
88 C{ModuleDoc} describing its contents.
89 @param name: The fully-qualified python dotted name of any
90 value (including packages, modules, classes, and
91 functions). C{DocParser} will automatically figure out
92 which module(s) it needs to import in order to find the
93 documentation for the specified object.
94 @param context: The API documentation for the class of module
95 that contains C{value} (if available).
96 @param module_name: The name of the module where the value is defined.
97 Useful to retrieve the docstring encoding if there is no way to
98 detect the module by introspection (such as in properties)
99 """
100 if value is None and name is not None and filename is None:
101 value = get_value_from_name(DottedName(name))
102 elif value is None and name is None and filename is not None:
103 if is_script:
104 value = get_value_from_scriptname(filename)
105 else:
106 value = get_value_from_filename(filename, context)
107 elif name is None and filename is None:
108
109 pass
110 else:
111 raise ValueError("Expected exactly one of the following "
112 "arguments: value, name, filename")
113
114 pyid = id(value)
115
116
117
118 if pyid in _introspected_values:
119 return _valuedoc_cache[pyid]
120
121
122 val_doc = _get_valuedoc(value)
123
124
125 _introspected_values[pyid] = True
126 introspect_func = _get_introspecter(value)
127 introspect_func(value, val_doc, module_name=module_name)
128
129
130 if val_doc.canonical_name is UNKNOWN and name is not None:
131 val_doc.canonical_name = DottedName(name)
132
133
134
135
136
137
138 if is_script and filename is not None:
139 val_doc.canonical_name = DottedName(munge_script_name(str(filename)))
140
141 if val_doc.canonical_name is UNKNOWN and filename is not None:
142 shadowed_name = DottedName(value.__name__)
143 log.warning("Module %s is shadowed by a variable with "
144 "the same name." % shadowed_name)
145 val_doc.canonical_name = DottedName(str(shadowed_name)+"'")
146
147 return val_doc
148
150 """
151 If a C{ValueDoc} for the given value exists in the valuedoc
152 cache, then return it; otherwise, create a new C{ValueDoc},
153 add it to the cache, and return it. When possible, the new
154 C{ValueDoc}'s C{pyval}, C{repr}, and C{canonical_name}
155 attributes will be set appropriately.
156 """
157 pyid = id(value)
158 val_doc = _valuedoc_cache.get(pyid)
159 if val_doc is None:
160 try: canonical_name = get_canonical_name(value)
161 except DottedName.InvalidDottedName: canonical_name = UNKNOWN
162 val_doc = ValueDoc(pyval=value, canonical_name = canonical_name,
163 docs_extracted_by='introspecter')
164 _valuedoc_cache[pyid] = val_doc
165
166
167
168
169 if inspect.ismodule(value):
170 introspect_module(value, val_doc, preliminary=True)
171 val_doc.defining_module = val_doc
172 else:
173 module_name = str(get_containing_module(value))
174 module = sys.modules.get(module_name)
175 if module is not None and inspect.ismodule(module):
176 val_doc.defining_module = _get_valuedoc(module)
177
178 return val_doc
179
180
181
182
183
184
185
186 UNDOCUMENTED_MODULE_VARS = (
187 '__builtins__', '__doc__', '__all__', '__file__', '__path__',
188 '__name__', '__extra_epydoc_fields__', '__docformat__')
189
191 """
192 Add API documentation information about the module C{module}
193 to C{module_doc}.
194 """
195 module_doc.specialize_to(ModuleDoc)
196
197
198 if hasattr(module, '__docformat__'):
199 module_doc.docformat = unicode(module.__docformat__)
200
201
202 if hasattr(module, '__file__'):
203 try: module_doc.filename = unicode(module.__file__)
204 except KeyboardInterrupt: raise
205 except: pass
206 if module_doc.filename is not UNKNOWN:
207 try: module_doc.filename = py_src_filename(module_doc.filename)
208 except ValueError: pass
209
210
211
212
213
214 if preliminary: return
215
216
217 if hasattr(module, '__doc__'):
218 module_doc.docstring = get_docstring(module)
219
220
221
222 if hasattr(module, '__path__'):
223 module_doc.is_package = True
224 try: module_doc.path = [unicode(p) for p in module.__path__]
225 except KeyboardInterrupt: raise
226 except: pass
227 else:
228 module_doc.is_package = False
229
230
231 dotted_name = module_doc.canonical_name
232 if dotted_name is UNKNOWN:
233 dotted_name = DottedName(module.__name__)
234
235
236 if len(dotted_name) > 1:
237 package_name = str(dotted_name.container())
238 package = sys.modules.get(package_name)
239 if package is not None:
240 module_doc.package = introspect_docs(package)
241 else:
242 module_doc.package = None
243
244
245 module_doc.submodules = []
246
247
248 if module_doc.package not in (None, UNKNOWN):
249 module_doc.package.submodules.append(module_doc)
250
251
252 public_names = None
253 if hasattr(module, '__all__'):
254 try:
255 public_names = set([str(name) for name in module.__all__])
256 except KeyboardInterrupt: raise
257 except: pass
258
259
260 module_doc.variables = {}
261 for child_name in dir(module):
262 if child_name in UNDOCUMENTED_MODULE_VARS: continue
263 child = getattr(module, child_name)
264
265
266
267 container = get_containing_module(child)
268 if ((container is not None and
269 container == module_doc.canonical_name) or
270 (public_names is not None and
271 child_name in public_names)):
272
273 child_val_doc = introspect_docs(child, context=module_doc,
274 module_name=dotted_name)
275 child_var_doc = VariableDoc(name=child_name,
276 value=child_val_doc,
277 is_imported=False,
278 container=module_doc,
279 docs_extracted_by='introspecter')
280 elif container is None or module_doc.canonical_name is UNKNOWN:
281
282
283 if is_future_feature(child): continue
284
285
286 child_val_doc = introspect_docs(child, context=module_doc)
287 child_var_doc = VariableDoc(name=child_name,
288 value=child_val_doc,
289 container=module_doc,
290 docs_extracted_by='introspecter')
291 else:
292
293 child_val_doc = _get_valuedoc(child)
294 child_var_doc = VariableDoc(name=child_name,
295 value=child_val_doc,
296 is_imported=True,
297 container=module_doc,
298 docs_extracted_by='introspecter')
299
300
301
302 if public_names is not None:
303 if child_name in public_names:
304 child_var_doc.is_public = True
305 if not isinstance(child_var_doc, ModuleDoc):
306 child_var_doc.is_imported = False
307 else:
308 child_var_doc.is_public = False
309
310 module_doc.variables[child_name] = child_var_doc
311
312 return module_doc
313
314
315
316
317
318
319
320 UNDOCUMENTED_CLASS_VARS = (
321 '__doc__', '__module__', '__dict__', '__weakref__', '__slots__',
322 '__pyx_vtable__')
323
325 """
326 Add API documentation information about the class C{cls}
327 to C{class_doc}.
328 """
329 class_doc.specialize_to(ClassDoc)
330
331
332 class_doc.docstring = get_docstring(cls)
333
334
335 if hasattr(cls, '__all__'):
336 try:
337 public_names = [str(name) for name in cls.__all__]
338 class_doc.public_names = public_names
339 except KeyboardInterrupt: raise
340 except: pass
341
342
343 class_doc.subclasses = []
344
345
346
347
348
349
350
351
352
353
354
355
356
357 base_children = {}
358
359
360
361 if hasattr(cls, '__bases__'):
362 try: bases = list(cls.__bases__)
363 except:
364 bases = None
365 log.warning("Class '%s' defines __bases__, but it does not "
366 "contain an iterable; ignoring base list."
367 % getattr(cls, '__name__', '??'))
368 if bases is not None:
369 class_doc.bases = []
370 for base in bases:
371 basedoc = introspect_docs(base)
372 class_doc.bases.append(basedoc)
373 basedoc.subclasses.append(class_doc)
374
375 bases.reverse()
376 for base in bases:
377 if hasattr(base, '__dict__'):
378 base_children.update(base.__dict__)
379
380
381
382 if module_name is None and class_doc.defining_module not in (None, UNKNOWN):
383 module_name = class_doc.defining_module.canonical_name
384
385
386 class_doc.variables = {}
387 if hasattr(cls, '__dict__'):
388 private_prefix = '_%s__' % getattr(cls, '__name__', '<none>')
389 for child_name, child in cls.__dict__.items():
390 if (child_name in base_children
391 and base_children[child_name] == child):
392 continue
393
394 if child_name.startswith(private_prefix):
395 child_name = child_name[len(private_prefix)-2:]
396 if child_name in UNDOCUMENTED_CLASS_VARS: continue
397
398
399 val_doc = introspect_docs(child, context=class_doc,
400 module_name=module_name)
401 var_doc = VariableDoc(name=child_name, value=val_doc,
402 container=class_doc,
403 docs_extracted_by='introspecter')
404 class_doc.variables[child_name] = var_doc
405
406 return class_doc
407
408
409
410
411
413 """Add API documentation information about the function
414 C{routine} to C{routine_doc} (specializing it to C{Routine_doc})."""
415 routine_doc.specialize_to(RoutineDoc)
416
417
418 if isinstance(routine, MethodType):
419 func = routine.im_func
420 elif isinstance(routine, staticmethod):
421 func = routine.__get__(0)
422 elif isinstance(routine, classmethod):
423 func = routine.__get__(0).im_func
424 else:
425 func = routine
426
427
428 routine_doc.docstring = get_docstring(func)
429
430
431 if isinstance(func, FunctionType):
432 (args, vararg, kwarg, defaults) = inspect.getargspec(func)
433
434
435 routine_doc.posargs = args
436 routine_doc.vararg = vararg
437 routine_doc.kwarg = kwarg
438
439
440 routine_doc.posarg_defaults = [None]*len(args)
441 if defaults is not None:
442 offset = len(args)-len(defaults)
443 for i in range(len(defaults)):
444 default_val = introspect_docs(defaults[i])
445 routine_doc.posarg_defaults[i+offset] = default_val
446
447
448 if isinstance(routine, MethodType) and routine.im_self is not None:
449 routine_doc.posargs = routine_doc.posargs[1:]
450 routine_doc.posarg_defaults = routine_doc.posarg_defaults[1:]
451
452
453 if hasattr(func, 'func_code'):
454 routine_doc.lineno = func.func_code.co_firstlineno
455
456 else:
457
458
459
460
461 routine_doc.posargs = ['...']
462 routine_doc.posarg_defaults = [None]
463 routine_doc.kwarg = None
464 routine_doc.vararg = None
465
466
467 if isinstance(routine, staticmethod):
468 routine_doc.specialize_to(StaticMethodDoc)
469 if isinstance(routine, classmethod):
470 routine_doc.specialize_to(ClassMethodDoc)
471
472 return routine_doc
473
474
475
476
477
493
494
495
496
497
502
503
504
505
506
508 """
509 Return true if the given object is a class. In particular, return
510 true if object is an instance of C{types.TypeType} or of
511 C{types.ClassType}. This is used instead of C{inspect.isclass()},
512 because the latter returns true for objects that are not classes
513 (in particular, it returns true for any object that has a
514 C{__bases__} attribute, including objects that define
515 C{__getattr__} to always return a value).
516 """
517 return isinstance(object, tuple(_CLASS_TYPES))
518
519 _CLASS_TYPES = set([TypeType, ClassType])
520 """A list of types that should be treated as classes."""
521
523 """Add a type to the lists of types that should be treated as
524 classes. By default, this list contains C{TypeType} and
525 C{ClassType}."""
526 _CLASS_TYPES.add(typ)
527
528 __future_check_works = None
529
552
554 """
555 Return the docstring for the given value; or C{None} if it
556 does not have a docstring.
557 @rtype: C{unicode}
558 """
559 docstring = getattr(value, '__doc__', None)
560 if docstring is None:
561 return None
562 elif isinstance(docstring, unicode):
563 return docstring
564 elif isinstance(docstring, str):
565 try: return unicode(docstring, 'ascii')
566 except UnicodeDecodeError:
567 if module_name is None:
568 module_name = get_containing_module(value)
569 if module_name is not None:
570 try:
571 module = get_value_from_name(module_name)
572 filename = py_src_filename(module.__file__)
573 encoding = epydoc.docparser.get_module_encoding(filename)
574 return unicode(docstring, encoding)
575 except KeyboardInterrupt: raise
576 except Exception: pass
577 if hasattr(value, '__name__'): name = value.__name__
578 else: name = repr(value)
579 log.warning("%s's docstring is not a unicode string, but it "
580 "contains non-ascii data -- treating it as "
581 "latin-1." % name)
582 return unicode(docstring, 'latin-1')
583 return None
584 elif value is BuiltinMethodType:
585
586 return None
587 else:
588 if hasattr(value, '__name__'): name = value.__name__
589 else: name = repr(value)
590 log.warning("%s's docstring is not a string -- ignoring it." %
591 name)
592 return None
593
595 """
596 @return: the canonical name for C{value}, or C{UNKNOWN} if no
597 canonical name can be found. Currently, C{get_canonical_name}
598 can find canonical names for: modules; functions; non-nested
599 classes; methods of non-nested classes; and some class methods
600 of non-nested classes.
601
602 @rtype: L{DottedName} or C{UNKNOWN}
603 """
604 if not hasattr(value, '__name__'): return UNKNOWN
605
606
607 if isinstance(value, ModuleType):
608 dotted_name = DottedName(value.__name__)
609
610 elif isclass(value):
611 if value.__module__ == '__builtin__':
612 dotted_name = DottedName(value.__name__)
613 else:
614 dotted_name = DottedName(value.__module__, value.__name__)
615
616 elif (inspect.ismethod(value) and value.im_self is not None and
617 value.im_class is ClassType and
618 not value.__name__.startswith('<')):
619 class_name = get_canonical_name(value.im_self)
620 if class_name is UNKNOWN: return UNKNOWN
621 dotted_name = DottedName(class_name, value.__name__)
622 elif (inspect.ismethod(value) and
623 not value.__name__.startswith('<')):
624 class_name = get_canonical_name(value.im_class)
625 if class_name is UNKNOWN: return UNKNOWN
626 dotted_name = DottedName(class_name, value.__name__)
627 elif (isinstance(value, FunctionType) and
628 not value.__name__.startswith('<')):
629 module_name = _find_function_module(value)
630 if module_name is None: return UNKNOWN
631 dotted_name = DottedName(module_name, value.__name__)
632 else:
633 return UNKNOWN
634
635 return verify_name(value, dotted_name)
636
638 """
639 Verify the name. E.g., if it's a nested class, then we won't be
640 able to find it with the name we constructed.
641 """
642 if dotted_name is UNKNOWN: return UNKNOWN
643 if len(dotted_name) == 1 and hasattr(__builtin__, dotted_name[0]):
644 return dotted_name
645 named_value = sys.modules.get(dotted_name[0])
646 if named_value is None: return UNKNOWN
647 for identifier in dotted_name[1:]:
648 try: named_value = getattr(named_value, identifier)
649 except: return UNKNOWN
650 if value is named_value:
651 return dotted_name
652 else:
653 return UNKNOWN
654
655
664
666 """
667 Return the name of the module containing the given value, or
668 C{None} if the module name can't be determined.
669 @rtype: L{DottedName}
670 """
671 if inspect.ismodule(value):
672 return DottedName(value.__name__)
673 elif isclass(value):
674 return DottedName(value.__module__)
675 elif (inspect.ismethod(value) and value.im_self is not None and
676 value.im_class is ClassType):
677 return DottedName(value.im_self.__module__)
678 elif inspect.ismethod(value):
679 return DottedName(value.im_class.__module__)
680 elif inspect.isroutine(value):
681 module = _find_function_module(value)
682 if module is None: return None
683 return DottedName(module)
684 else:
685 return None
686
688 """
689 @return: The module that defines the given function.
690 @rtype: C{module}
691 @param func: The function whose module should be found.
692 @type func: C{function}
693 """
694 if hasattr(func, '__module__'):
695 return func.__module__
696 try:
697 module = inspect.getmodule(func)
698 if module: return module.__name__
699 except KeyboardInterrupt: raise
700 except: pass
701
702
703
704
705
706 for module in sys.modules.values():
707 if (hasattr(module, '__dict__') and
708 hasattr(func, 'func_globals') and
709 func.func_globals is module.__dict__):
710 return module.__name__
711 return None
712
713
714
715
716
717 _introspecter_registry = []
719 """
720 Register an introspecter function. Introspecter functions take
721 two arguments, a python value and a C{ValueDoc} object, and should
722 add information about the given value to the the C{ValueDoc}.
723 Usually, the first line of an inspecter function will specialize
724 it to a sublass of C{ValueDoc}, using L{ValueDoc.specialize_to()}:
725
726 >>> def typical_introspecter(value, value_doc):
727 ... value_doc.specialize_to(SomeSubclassOfValueDoc)
728 ... <add info to value_doc>
729
730 @param priority: The priority of this introspecter, which determines
731 the order in which introspecters are tried -- introspecters with lower
732 numbers are tried first. The standard introspecters have priorities
733 ranging from 20 to 30. The default priority (10) will place new
734 introspecters before standard introspecters.
735 """
736 _introspecter_registry.append( (priority, applicability_test,
737 introspecter) )
738 _introspecter_registry.sort()
739
746
747
751 register_introspecter(inspect.ismodule, introspect_module, priority=20)
752 register_introspecter(isclass, introspect_class, priority=24)
753 register_introspecter(inspect.isroutine, introspect_routine, priority=28)
754 register_introspecter(is_property, introspect_property, priority=30)
755
756
757 try:
758 import array
759 getset_type = type(array.array.typecode)
760 del array
761 - def is_getset(v): return isinstance(v, getset_type)
762 register_introspecter(is_getset, introspect_property, priority=32)
763 except:
764 pass
765
766
767 try:
768 import datetime
769 member_type = type(datetime.timedelta.days)
770 del datetime
771 - def is_member(v): return isinstance(v, member_type)
772 register_introspecter(is_member, introspect_property, priority=34)
773 except:
774 pass
775
776
777
778
779
831
835
837 """
838 Given a name, return the corresponding value.
839
840 @param globs: A namespace to check for the value, if there is no
841 module containing the named value. Defaults to __builtin__.
842 """
843 name = DottedName(name)
844
845
846
847 try:
848 module = _import(name[0])
849 except ImportError, e:
850 if globs is None: globs = __builtin__.__dict__
851 if name[0] in globs:
852 try: return _lookup(globs[name[0]], name[1:])
853 except: raise e
854 else:
855 raise
856
857
858 for i in range(1, len(name)):
859 try: return _lookup(module, name[i:])
860 except ImportError: pass
861 module = _import('.'.join(name[:i+1]))
862 module = _lookup(module, name[1:i+1])
863 return module
864
866 val = module
867 for i, identifier in enumerate(name):
868 try: val = getattr(val, identifier)
869 except AttributeError:
870 exc_msg = ('no variable named %s in %s' %
871 (identifier, '.'.join(name[:1+i])))
872 raise ImportError(exc_msg)
873 return val
874
876 """
877 Run the given callable in a 'sandboxed' environment.
878 Currently, this includes saving and restoring the contents of
879 sys and __builtins__; and supressing stdin, stdout, and stderr.
880 """
881
882
883
884 old_sys = sys.__dict__.copy()
885 old_sys_path = sys.path[:]
886 old_builtins = __builtin__.__dict__.copy()
887
888
889
890
891
892 sys.path.append('')
893
894
895
896 sys.stdin = sys.stdout = sys.stderr = _dev_null
897 sys.__stdin__ = sys.__stdout__ = sys.__stderr__ = _dev_null
898
899
900 sys.argv = ['(imported)']
901
902 try:
903 try:
904 if filename is None:
905 return __import__(name)
906 else:
907
908 return imp.load_source(name, filename)
909 except KeyboardInterrupt: raise
910 except:
911 exc_typ, exc_val, exc_tb = sys.exc_info()
912 if exc_val is None:
913 estr = '%s' % (exc_typ,)
914 else:
915 estr = '%s: %s' % (exc_typ.__name__, exc_val)
916 if exc_tb.tb_next is not None:
917 estr += ' (line %d)' % (exc_tb.tb_next.tb_lineno,)
918 raise ImportError(estr)
919 finally:
920
921 __builtin__.__dict__.clear()
922 __builtin__.__dict__.update(old_builtins)
923 sys.__dict__.clear()
924 sys.__dict__.update(old_sys)
925 sys.path = old_sys_path
926
928 """
929 Try to determine the line number on which the given item's
930 docstring begins. Return the line number, or C{None} if the line
931 number can't be determined. The line number of the first line in
932 the file is 1.
933 """
934 if api_doc.docstring_lineno is not UNKNOWN:
935 return api_doc.docstring_lineno
936 if isinstance(api_doc, ValueDoc) and api_doc.pyval is not UNKNOWN:
937 try:
938 lines, lineno = inspect.findsource(api_doc.pyval)
939 if not isinstance(api_doc, ModuleDoc): lineno += 1
940 for lineno in range(lineno, len(lines)):
941 if lines[lineno].split('#', 1)[0].strip():
942 api_doc.docstring_lineno = lineno + 1
943 return lineno + 1
944 except IOError: pass
945 except TypeError: pass
946 return None
947
949 """
950 A "file-like" object that discards anything that is written and
951 always reports end-of-file when read. C{_DevNull} is used by
952 L{_import()} to discard output when importing modules; and to
953 ensure that stdin appears closed.
954 """
956 self.closed = 1
957 self.mode = 'r+'
958 self.softspace = 0
959 self.name='</dev/null>'
962 - def read(self, size=0): return ''
965 - def seek(self, offset, whence=0): pass
966 - def tell(self): return 0L
968 - def write(self, str): pass
970 xreadlines = readlines
971 _dev_null = _DevNull()
972
973
974
975
976
977 try:
978 from zope.interface.interface import InterfaceClass as _ZopeInterfaceClass
979 register_class_type(_ZopeInterfaceClass)
980 except:
981 pass
982
983
984
985
986
987 try:
988
989 from ExtensionClass import ExtensionClass as _ExtensionClass
990 _ZopeType = type(_ExtensionClass)
993 register_introspecter(_is_zope_type, introspect_class)
994
995
996 from ExtensionClass import PythonMethodType as _ZopeMethodType
997 from ExtensionClass import ExtensionMethodType as _ZopeCMethodType
999 return isinstance(val, (_ZopeMethodType, _ZopeCMethodType))
1000 register_introspecter(_is_zope_method, introspect_routine)
1001 except:
1002 pass
1003
1004
1005
1006
1007
1008 0
1009 """
1010 ######################################################################
1011 ## Zope Extension...
1012 ######################################################################
1013 class ZopeIntrospecter(Introspecter):
1014 VALUEDOC_CLASSES = Introspecter.VALUEDOC_CLASSES.copy()
1015 VALUEDOC_CLASSES.update({
1016 'module': ZopeModuleDoc,
1017 'class': ZopeClassDoc,
1018 'interface': ZopeInterfaceDoc,
1019 'attribute': ZopeAttributeDoc,
1020 })
1021
1022 def add_module_child(self, child, child_name, module_doc):
1023 if isinstance(child, zope.interfaces.Interface):
1024 module_doc.add_zope_interface(child_name)
1025 else:
1026 Introspecter.add_module_child(self, child, child_name, module_doc)
1027
1028 def add_class_child(self, child, child_name, class_doc):
1029 if isinstance(child, zope.interfaces.Interface):
1030 class_doc.add_zope_interface(child_name)
1031 else:
1032 Introspecter.add_class_child(self, child, child_name, class_doc)
1033
1034 def introspect_zope_interface(self, interface, interfacename):
1035 pass # etc...
1036 """
1037