1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9  """ 
 10  Extract API documentation about python objects by directly introspecting 
 11  their values. 
 12   
 13  L{DocIntrospecter} is a processing class that examines Python objects via 
 14  introspection, and uses the information it finds to create L{APIDoc} 
 15  objects containing the API documentation for those objects. 
 16   
 17  C{DocIntrospecter} can be subclassed to extend the set of object types 
 18  that it supports. 
 19  """ 
 20  __docformat__ = 'epytext en' 
 21   
 22   
 23   
 24   
 25   
 26  import inspect, re, sys, os.path, imp 
 27   
 28  from epydoc.apidoc import * 
 29   
 30  from types import * 
 31   
 32  from epydoc import log 
 33   
 34  from epydoc.util import * 
 35   
 36  import epydoc.docparser 
 37   
 38  import __builtin__ 
 39   
 40  from epydoc.compat import *  
 41   
 42   
 43   
 44   
 45   
 46  _valuedoc_cache = {} 
 47  """A cache containing the API documentation for values that we've 
 48  already seen.  This cache is implemented as a dictionary that maps a 
 49  value's pyid to its L{ValueDoc}. 
 50   
 51  Note that if we encounter a value but decide not to introspect it 
 52  (because it's imported from another module), then C{_valuedoc_cache} 
 53  will contain an entry for the value, but the value will not be listed 
 54  in L{_introspected_values}.""" 
 55   
 56  _introspected_values = {} 
 57  """A record which values we've introspected, encoded as a dictionary from 
 58  pyid to C{bool}.""" 
 59   
 60   
 61   
 62   
 63   
 64   
 65  """ 
 66  An API documentation extractor based on introspecting python values. 
 67  C{DocIntrospecter} examines Python objects via introspection, and uses 
 68  the information it finds to create L{APIDoc} objects containing 
 69  the API documentation for those objects.  The main interface to 
 70  the C{DocIntrospecter} class is the L{introspect} method, which takes a 
 71  Python value, and returns an L{APIDoc} that describes it. 
 72   
 73  Currently, C{DocIntrospecter} can extract documentation information 
 74  from the following object types: 
 75   
 76    - modules 
 77    - classes 
 78    - functions 
 79    - methods 
 80    - class methods 
 81    - static methods 
 82    - builtin routines 
 83    - properties 
 84   
 85  Subclassing 
 86  =========== 
 87  C{DocIntrospecter} can be subclassed, to extend the set of object 
 88  types that it supports.  C{DocIntrospecter} can be extended in 
 89  several different ways: 
 90   
 91    - A subclass can override one of the existing introspection methods 
 92      to modify the behavior for a currently supported object type. 
 93   
 94    - A subclass can add support for a new type by adding a new 
 95      introspection method; and extending L{introspecter_method()} to 
 96      return that method for values of the new type. 
 97  """ 
 98   
 99 -def introspect_docs(value=None, name=None, filename=None, context=None, 
100                      is_script=False): 
 101      """ 
102      Generate the API documentation for a specified object by 
103      introspecting Python values, and return it as a L{ValueDoc}.  The 
104      object to generate documentation for may be specified using 
105      the C{value} parameter, the C{filename} parameter, I{or} the 
106      C{name} parameter.  (It is an error to specify more than one 
107      of these three parameters, or to not specify any of them.) 
108   
109      @param value: The python object that should be documented. 
110      @param filename: The name of the file that contains the python 
111          source code for a package, module, or script.  If 
112          C{filename} is specified, then C{introspect} will return a 
113          C{ModuleDoc} describing its contents. 
114      @param name: The fully-qualified python dotted name of any 
115          value (including packages, modules, classes, and 
116          functions).  C{DocParser} will automatically figure out 
117          which module(s) it needs to import in order to find the 
118          documentation for the specified object. 
119      @param context: The API documentation for the class of module 
120          that contains C{value} (if available). 
121      """ 
122      if value is None and name is not None and filename is None: 
123          value = get_value_from_name(DottedName(name)) 
124      elif value is None and name is None and filename is not None: 
125          if is_script: 
126              value = get_value_from_scriptname(filename) 
127          else: 
128              value = get_value_from_filename(filename, context) 
129      elif name is None and filename is None: 
130           
131          pass  
132      else: 
133          raise ValueError("Expected exactly one of the following " 
134                           "arguments: value, name, filename") 
135       
136      pyid = id(value) 
137   
138       
139       
140      if pyid in _introspected_values: 
141          return _valuedoc_cache[pyid] 
142   
143       
144      val_doc = _get_valuedoc(value) 
145   
146       
147      _introspected_values[pyid] = True 
148      introspect_func = _get_introspecter(value) 
149      introspect_func(value, val_doc) 
150   
151       
152      if val_doc.canonical_name == UNKNOWN and name is not None: 
153          val_doc.canonical_name = DottedName(name) 
154   
155       
156       
157       
158       
159       
160      if is_script and filename is not None: 
161          val_doc.canonical_name = DottedName(munge_script_name(str(filename))) 
162           
163      if val_doc.canonical_name == UNKNOWN and filename is not None: 
164          shadowed_name = DottedName(value.__name__) 
165          log.warning("Module %s is shadowed by a variable with " 
166                      "the same name." % shadowed_name) 
167          val_doc.canonical_name = DottedName(str(shadowed_name)+"'") 
168   
169      return val_doc 
 170   
172      """ 
173      If a C{ValueDoc} for the given value exists in the valuedoc 
174      cache, then return it; otherwise, create a new C{ValueDoc}, 
175      add it to the cache, and return it.  When possible, the new 
176      C{ValueDoc}'s C{pyval}, C{repr}, and C{canonical_name} 
177      attributes will be set appropriately. 
178      """ 
179      pyid = id(value) 
180      val_doc = _valuedoc_cache.get(pyid) 
181      if val_doc is None: 
182          try: canonical_name = get_canonical_name(value) 
183          except DottedName.InvalidDottedName: canonical_name = UNKNOWN 
184          val_doc = ValueDoc(pyval=value, canonical_name = canonical_name, 
185                             docs_extracted_by='introspecter') 
186          _valuedoc_cache[pyid] = val_doc 
187           
188           
189           
190           
191          if inspect.ismodule(value): 
192              introspect_module(value, val_doc, preliminary=True) 
193              val_doc.defining_module = val_doc 
194          else: 
195              module_name = str(get_containing_module(value)) 
196              module = sys.modules.get(module_name) 
197              if module is not None and inspect.ismodule(module): 
198                  val_doc.defining_module = _get_valuedoc(module) 
199               
200      return val_doc 
 201   
202   
203   
204   
205   
206   
207   
208  UNDOCUMENTED_MODULE_VARS = ( 
209      '__builtins__', '__doc__', '__all__', '__file__', '__path__', 
210      '__name__', '__extra_epydoc_fields__', '__docformat__') 
211   
213      """ 
214      Add API documentation information about the module C{module} 
215      to C{module_doc}. 
216      """ 
217      module_doc.specialize_to(ModuleDoc) 
218   
219       
220      if hasattr(module, '__docformat__'): 
221          module_doc.docformat = unicode(module.__docformat__) 
222                                     
223       
224      if hasattr(module, '__file__'): 
225          try: module_doc.filename = unicode(module.__file__) 
226          except KeyboardInterrupt: raise 
227          except: pass 
228   
229       
230       
231       
232       
233      if preliminary: return 
234   
235       
236      if hasattr(module, '__doc__'): 
237          module_doc.docstring = get_docstring(module) 
238   
239       
240       
241      if hasattr(module, '__path__'): 
242          module_doc.is_package = True 
243          try: module_doc.path = [unicode(p) for p in module.__path__] 
244          except KeyboardInterrupt: raise 
245          except: pass 
246      else: 
247          module_doc.is_package = False 
248   
249       
250      dotted_name = module_doc.canonical_name 
251      if dotted_name is UNKNOWN: 
252          dotted_name = DottedName(module.__name__) 
253           
254       
255      if len(dotted_name) > 1: 
256          package_name = str(dotted_name.container()) 
257          package = sys.modules.get(package_name) 
258          if package is not None: 
259              module_doc.package = introspect_docs(package) 
260      else: 
261          module_doc.package = None 
262   
263       
264      module_doc.submodules = [] 
265   
266       
267      if module_doc.package not in (None, UNKNOWN): 
268          module_doc.package.submodules.append(module_doc) 
269   
270       
271      module_doc.variables = {} 
272      for child_name in dir(module): 
273          if child_name in UNDOCUMENTED_MODULE_VARS: continue 
274          child = getattr(module, child_name) 
275   
276           
277           
278          container = get_containing_module(child) 
279          if container != None and container == module_doc.canonical_name: 
280               
281              child_val_doc = introspect_docs(child, context=module_doc) 
282              child_var_doc = VariableDoc(name=child_name, 
283                                          value=child_val_doc, 
284                                          is_imported=False, 
285                                          container=module_doc, 
286                                          docs_extracted_by='introspecter') 
287          elif container is None or module_doc.canonical_name is UNKNOWN: 
288               
289              child_val_doc = introspect_docs(child, context=module_doc) 
290              child_var_doc = VariableDoc(name=child_name, 
291                                          value=child_val_doc, 
292                                          container=module_doc, 
293                                          docs_extracted_by='introspecter') 
294          else: 
295               
296              child_val_doc = _get_valuedoc(child) 
297              child_var_doc = VariableDoc(name=child_name, 
298                                          value=child_val_doc, 
299                                          is_imported=True, 
300                                          container=module_doc, 
301                                          docs_extracted_by='introspecter') 
302   
303          module_doc.variables[child_name] = child_var_doc 
304   
305       
306      if hasattr(module, '__all__'): 
307          try: 
308              public_names = set([str(name) for name in module.__all__]) 
309              for name, var_doc in module_doc.variables.items(): 
310                  if name in public_names: 
311                      var_doc.is_public = True 
312                      if not isinstance(var_doc, ModuleDoc): 
313                          var_doc.is_imported = False 
314                  else: 
315                      var_doc.is_public = False 
316          except KeyboardInterrupt: raise 
317          except: pass 
318   
319      return module_doc 
 320   
321   
322   
323   
324   
325   
326   
327  UNDOCUMENTED_CLASS_VARS = ( 
328      '__doc__', '__module__', '__dict__', '__weakref__') 
329   
331      """ 
332      Add API documentation information about the class C{cls} 
333      to C{class_doc}. 
334      """ 
335      class_doc.specialize_to(ClassDoc) 
336   
337       
338      class_doc.docstring = get_docstring(cls) 
339   
340       
341      if hasattr(cls, '__all__'): 
342          try: 
343              public_names = [str(name) for name in cls.__all__] 
344              class_doc.public_names = public_names 
345          except KeyboardInterrupt: raise 
346          except: pass 
347   
348       
349      class_doc.subclasses = [] 
350   
351       
352       
353       
354       
355       
356       
357       
358       
359       
360       
361       
362       
363      base_children = {} 
364       
365       
366       
367      if hasattr(cls, '__bases__'): 
368          class_doc.bases = [] 
369          for base in cls.__bases__: 
370              basedoc = introspect_docs(base) 
371              class_doc.bases.append(basedoc) 
372              basedoc.subclasses.append(class_doc) 
373               
374          bases = list(cls.__bases__) 
375          bases.reverse() 
376          for base in bases: 
377              if hasattr(base, '__dict__'): 
378                  base_children.update(base.__dict__) 
379   
380       
381      class_doc.variables = {} 
382      private_prefix = '_%s__' % cls.__name__ 
383      if hasattr(cls, '__dict__'): 
384          for child_name, child in cls.__dict__.items(): 
385              if (child_name in base_children 
386                  and base_children[child_name] == child): 
387                  continue 
388   
389              if child_name.startswith(private_prefix): 
390                  child_name = child_name[len(private_prefix)-2:] 
391              if child_name in UNDOCUMENTED_CLASS_VARS: continue 
392               
393               
394              val_doc = introspect_docs(child, context=class_doc) 
395              var_doc = VariableDoc(name=child_name, value=val_doc, 
396                                    container=class_doc, 
397                                    docs_extracted_by='introspecter') 
398              class_doc.variables[child_name] = var_doc 
399   
400      return class_doc 
 401   
402   
403   
404   
405   
407      """Add API documentation information about the function 
408      C{routine} to C{routine_doc} (specializing it to C{Routine_doc}).""" 
409      routine_doc.specialize_to(RoutineDoc) 
410       
411       
412      if isinstance(routine, MethodType): 
413          func = routine.im_func 
414      elif isinstance(routine, staticmethod): 
415          func = routine.__get__(0) 
416      elif isinstance(routine, classmethod): 
417          func = routine.__get__(0).im_func 
418      else: 
419          func = routine 
420   
421       
422      routine_doc.docstring = get_docstring(func) 
423   
424       
425      if isinstance(func, FunctionType): 
426          (args, vararg, kwarg, defaults) = inspect.getargspec(func) 
427   
428           
429          routine_doc.posargs = args 
430          routine_doc.vararg = vararg 
431          routine_doc.kwarg = kwarg 
432   
433           
434          routine_doc.posarg_defaults = [None]*len(args) 
435          if defaults is not None: 
436              offset = len(args)-len(defaults) 
437              for i in range(len(defaults)): 
438                  default_val = introspect_docs(defaults[i]) 
439                  routine_doc.posarg_defaults[i+offset] = default_val 
440   
441           
442          if isinstance(routine, MethodType) and routine.im_self is not None: 
443              routine_doc.posargs = routine_doc.posargs[1:] 
444              routine_doc.posarg_defaults = routine_doc.posarg_defaults[1:] 
445   
446           
447          if hasattr(func, 'func_code'): 
448              routine_doc.lineno = func.func_code.co_firstlineno 
449   
450      else: 
451           
452          routine_doc.posargs = ['...'] 
453          routine_doc.posarg_defaults = [None] 
454          routine_doc.kwarg = None 
455          routine_doc.vararg = None 
456   
457       
458      if isinstance(routine, staticmethod): 
459          routine_doc.specialize_to(StaticMethodDoc) 
460      if isinstance(routine, classmethod): 
461          routine_doc.specialize_to(ClassMethodDoc) 
462           
463      return routine_doc 
 464   
465   
466   
467   
468   
483   
484   
485   
486   
487   
492   
493   
494   
495   
496   
498      """ 
499      Return the docstring for the given value; or C{None} if it 
500      does not have a docstring. 
501      @rtype: C{unicode} 
502      """ 
503      docstring = getattr(value, '__doc__', None) 
504      if docstring is None: 
505          return None 
506      elif isinstance(docstring, unicode): 
507          return docstring 
508      elif isinstance(docstring, str): 
509          try: return unicode(docstring, 'ascii') 
510          except UnicodeDecodeError: 
511              module_name = get_containing_module(value) 
512              if module_name is not None: 
513                  try: 
514                      module = get_value_from_name(module_name) 
515                      filename = py_src_filename(module.__file__) 
516                      encoding = epydoc.docparser.get_module_encoding(filename) 
517                      return unicode(docstring, encoding) 
518                  except KeyboardInterrupt: raise 
519                  except Exception: pass 
520              if hasattr(value, '__name__'): name = value.__name__ 
521              else: name = `value` 
522              log.warning("%s's docstring is not a unicode string, but it " 
523                          "contains non-ascii data -- treating it as " 
524                          "latin-1." % name) 
525              return unicode(docstring, 'latin-1') 
526          return None 
527      elif value is BuiltinMethodType: 
528           
529          return None 
530      else: 
531          if hasattr(value, '__name__'): name = value.__name__ 
532          else: name = `value` 
533          log.warning("%s's docstring is not a string -- ignoring it." % 
534                      name) 
535          return None 
 536   
538      """ 
539      @return: the canonical name for C{value}, or C{UNKNOWN} if no 
540      canonical name can be found.  Currently, C{get_canonical_name} 
541      can find canonical names for: modules; functions; non-nested 
542      classes; methods of non-nested classes; and some class methods 
543      of non-nested classes. 
544       
545      @rtype: L{DottedName} or C{None} 
546      """ 
547      if not hasattr(value, '__name__'): return UNKNOWN 
548   
549       
550      if isinstance(value, ModuleType): 
551          dotted_name = DottedName(value.__name__) 
552           
553      elif isinstance(value, (ClassType, TypeType)): 
554          if value.__module__ == '__builtin__': 
555              dotted_name = DottedName(value.__name__) 
556          else: 
557              dotted_name = DottedName(value.__module__, value.__name__) 
558               
559      elif (inspect.ismethod(value) and value.im_self is not None and 
560            value.im_class is ClassType and 
561            not value.__name__.startswith('<')):  
562          class_name = get_canonical_name(value.im_self) 
563          if class_name is None: return UNKNOWN 
564          dotted_name = DottedName(class_name, value.__name__) 
565      elif (inspect.ismethod(value) and 
566            not value.__name__.startswith('<')): 
567          class_name = get_canonical_name(value.im_class) 
568          if class_name is None: return UNKNOWN 
569          dotted_name = DottedName(class_name, value.__name__) 
570      elif (isinstance(value, FunctionType) and 
571            not value.__name__.startswith('<')): 
572          module_name = _find_function_module(value) 
573          if module_name is None: return UNKNOWN 
574          dotted_name = DottedName(module_name, value.__name__) 
575      else: 
576          return UNKNOWN 
577   
578      return verify_name(value, dotted_name) 
 579   
581      """ 
582      Verify the name.  E.g., if it's a nested class, then we won't be 
583      able to find it with the name we constructed. 
584      """ 
585      if dotted_name == UNKNOWN: return UNKNOWN 
586      if len(dotted_name) == 1 and hasattr(__builtin__, dotted_name[0]): 
587          return dotted_name 
588      named_value = sys.modules.get(dotted_name[0]) 
589      if named_value is None: return UNKNOWN 
590      for identifier in dotted_name[1:]: 
591          try: named_value = getattr(named_value, identifier) 
592          except: return UNKNOWN 
593      if value is named_value: 
594          return dotted_name 
595      else: 
596          return UNKNOWN 
 597   
598   
607   
609      """ 
610      Return the name of the module containing the given value, or 
611      C{None} if the module name can't be determined. 
612      @rtype: L{DottedName} 
613      """ 
614      if inspect.ismodule(value): 
615          return DottedName(value.__name__) 
616      elif inspect.isclass(value): 
617          return DottedName(value.__module__) 
618      elif (inspect.ismethod(value) and value.im_self is not None and 
619            value.im_class is ClassType):  
620          return DottedName(value.im_self.__module__) 
621      elif inspect.ismethod(value): 
622          return DottedName(value.im_class.__module__) 
623      elif inspect.isroutine(value): 
624          module = _find_function_module(value) 
625          if module is None: return None 
626          return DottedName(module) 
627      else: 
628          return None 
 629   
631      """ 
632      @return: The module that defines the given function. 
633      @rtype: C{module} 
634      @param func: The function whose module should be found. 
635      @type func: C{function} 
636      """ 
637      if hasattr(func, '__module__'): 
638          return func.__module__ 
639      try: 
640          module = inspect.getmodule(func) 
641          if module: return module.__name__ 
642      except KeyboardInterrupt: raise 
643      except: pass 
644   
645       
646       
647       
648       
649      for module in sys.modules.values(): 
650          if (hasattr(module, '__dict__') and 
651              hasattr(func, 'func_globals') and 
652              func.func_globals is module.__dict__): 
653              return module.__name__ 
654      return None 
655   
656   
657   
658   
659   
660  _introspecter_registry = [] 
662      """ 
663      Register an introspecter function.  Introspecter functions take 
664      two arguments, a python value and a C{ValueDoc} object, and should 
665      add information about the given value to the the C{ValueDoc}. 
666      Usually, the first line of an inspecter function will specialize 
667      it to a sublass of C{ValueDoc}, using L{ValueDoc.specialize_to()}: 
668   
669          >>> def typical_introspecter(value, value_doc): 
670          ...     value_doc.specialize_to(SomeSubclassOfValueDoc) 
671          ...     <add info to value_doc> 
672   
673      @param priority: The priority of this introspecter, which determines 
674      the order in which introspecters are tried -- introspecters with lower 
675      numbers are tried first.  The standard introspecters have priorities 
676      ranging from 20 to 30.  The default priority (10) will place new 
677      introspecters before standard introspecters. 
678      """ 
679      _introspecter_registry.append( (priority, applicability_test, 
680                                      introspecter) ) 
681      _introspecter_registry.sort() 
 682       
689   
690   
694  register_introspecter(inspect.ismodule, introspect_module, priority=20) 
695  register_introspecter(inspect.isclass, introspect_class, priority=24) 
696  register_introspecter(inspect.isroutine, introspect_routine, priority=28) 
697  register_introspecter(is_property, introspect_property, priority=30) 
698   
699   
700   
701   
702   
754   
758   
760      """ 
761      Given a name, return the corresponding value. 
762       
763      @param globals: A namespace to check for the value, if there is no 
764          module containing the named value.  Defaults to __builtin__. 
765      """ 
766      name = DottedName(name) 
767   
768       
769       
770      try: 
771          module = _import(name[0]) 
772      except ImportError, e: 
773          if globs is None: globs = __builtin__.__dict__ 
774          if name[0] in globs: 
775              try: return _lookup(globs[name[0]], name[1:]) 
776              except: raise e 
777          else: 
778              raise 
779   
780       
781      for i in range(1, len(name)): 
782          try: return _lookup(module, name[i:]) 
783          except ImportError: pass 
784          module = _import('.'.join(name[:i+1])) 
785          module = _lookup(module, name[1:i+1]) 
786      return module 
 787   
789      val = module 
790      for i, identifier in enumerate(name): 
791          try: val = getattr(val, identifier) 
792          except AttributeError: 
793              exc_msg = ('no variable named %s in %s' % 
794                         (identifier, '.'.join(name[:1+i]))) 
795              raise ImportError(exc_msg) 
796      return val 
 797               
799      """ 
800      Run the given callable in a 'sandboxed' environment. 
801      Currently, this includes saving and restoring the contents of 
802      sys and __builtins__; and supressing stdin, stdout, and stderr. 
803      """ 
804       
805       
806       
807      old_sys = sys.__dict__.copy() 
808      old_sys_path = sys.path[:] 
809      old_builtins = __builtin__.__dict__.copy() 
810   
811       
812       
813      sys.path.insert(0, '') 
814   
815       
816       
817      sys.stdin = sys.stdout = sys.stderr = _dev_null 
818      sys.__stdin__ = sys.__stdout__ = sys.__stderr__ = _dev_null 
819   
820       
821      sys.argv = ['(imported)'] 
822   
823      try: 
824          try: 
825              if filename is None: 
826                  return __import__(name) 
827              else: 
828                   
829                  return imp.load_source(name, filename) 
830          except KeyboardInterrupt: raise 
831          except: 
832              exc_typ, exc_val, exc_tb = sys.exc_info() 
833              if exc_val is None: 
834                  estr = '%s' % (exc_typ,) 
835              else: 
836                  estr = '%s: %s' % (exc_typ.__name__, exc_val) 
837              if exc_tb.tb_next is not None: 
838                  estr += ' (line %d)' % (exc_tb.tb_next.tb_lineno,) 
839              raise ImportError(estr) 
840      finally: 
841           
842          __builtin__.__dict__.clear() 
843          __builtin__.__dict__.update(old_builtins) 
844          sys.__dict__.clear() 
845          sys.__dict__.update(old_sys) 
846          sys.path = old_sys_path 
 847           
849      """ 
850      Try to determine the line number on which the given item's 
851      docstring begins.  Return the line number, or C{None} if the line 
852      number can't be determined.  The line number of the first line in 
853      the file is 1. 
854      """ 
855      if api_doc.docstring_lineno is not UNKNOWN: 
856          return api_doc.docstring_lineno 
857      if isinstance(api_doc, ValueDoc) and api_doc.pyval != UNKNOWN: 
858          try: 
859              lines, lineno = inspect.findsource(api_doc.pyval) 
860              if not isinstance(api_doc, ModuleDoc): lineno += 1 
861              for lineno in range(lineno, len(lines)): 
862                  if lines[lineno].split('#', 1)[0].strip(): 
863                      api_doc.docstring_lineno = lineno + 1 
864                      return lineno + 1 
865          except IOError: pass 
866          except TypeError: pass 
867      return None 
 868   
870      """ 
871      A "file-like" object that discards anything that is written and 
872      always reports end-of-file when read.  C{_DevNull} is used by 
873      L{_import()} to discard output when importing modules; and to 
874      ensure that stdin appears closed. 
875      """ 
877          self.closed = 1 
878          self.mode = 'r+' 
879          self.softspace = 0 
880          self.name='</dev/null>' 
 883 -    def read(self, size=0): return '' 
 886 -    def seek(self, offset, whence=0): pass 
 887 -    def tell(self): return 0L 
 889 -    def write(self, str): pass 
 891      xreadlines = readlines 
 892  _dev_null = _DevNull() 
893       
894   
895   
896   
897   
898       
899   
900  0  
901  """ 
902  ###################################################################### 
903  ## Zope Extension... 
904  ###################################################################### 
905  class ZopeIntrospecter(Introspecter): 
906      VALUEDOC_CLASSES = Introspecter.VALUEDOC_CLASSES.copy() 
907      VALUEDOC_CLASSES.update({ 
908          'module': ZopeModuleDoc, 
909          'class': ZopeClassDoc, 
910          'interface': ZopeInterfaceDoc, 
911          'attribute': ZopeAttributeDoc, 
912          }) 
913       
914      def add_module_child(self, child, child_name, module_doc): 
915          if isinstance(child, zope.interfaces.Interface): 
916              module_doc.add_zope_interface(child_name) 
917          else: 
918              Introspecter.add_module_child(self, child, child_name, module_doc) 
919   
920      def add_class_child(self, child, child_name, class_doc): 
921          if isinstance(child, zope.interfaces.Interface): 
922              class_doc.add_zope_interface(child_name) 
923          else: 
924              Introspecter.add_class_child(self, child, child_name, class_doc) 
925   
926      def introspect_zope_interface(self, interface, interfacename): 
927          pass # etc... 
928  """ 
929