1
2
3
4
5
6
7
8
9 """
10 Epydoc parser for ReStructuredText strings. ReStructuredText is the
11 standard markup language used by the Docutils project.
12 L{parse_docstring()} provides the primary interface to this module; it
13 returns a L{ParsedRstDocstring}, which supports all of the methods
14 defined by L{ParsedDocstring}.
15
16 L{ParsedRstDocstring} is basically just a L{ParsedDocstring} wrapper
17 for the C{docutils.nodes.document} class.
18
19 Creating C{ParsedRstDocstring}s
20 ===============================
21
22 C{ParsedRstDocstring}s are created by the C{parse_document} function,
23 using the C{docutils.core.publish_string()} method, with the following
24 helpers:
25
26 - An L{_EpydocReader} is used to capture all error messages as it
27 parses the docstring.
28 - A L{_DocumentPseudoWriter} is used to extract the document itself,
29 without actually writing any output. The document is saved for
30 further processing. The settings for the writer are copied from
31 C{docutils.writers.html4css1.Writer}, since those settings will
32 be used when we actually write the docstring to html.
33
34 Using C{ParsedRstDocstring}s
35 ============================
36
37 C{ParsedRstDocstring}s support all of the methods defined by
38 C{ParsedDocstring}; but only the following four methods have
39 non-default behavior:
40
41 - L{to_html()<ParsedRstDocstring.to_html>} uses an
42 L{_EpydocHTMLTranslator} to translate the C{ParsedRstDocstring}'s
43 document into an HTML segment.
44 - L{split_fields()<ParsedRstDocstring.split_fields>} uses a
45 L{_SplitFieldsTranslator} to divide the C{ParsedRstDocstring}'s
46 document into its main body and its fields. Special handling
47 is done to account for consolidated fields.
48 - L{summary()<ParsedRstDocstring.summary>} uses a
49 L{_SummaryExtractor} to extract the first sentence from
50 the C{ParsedRstDocstring}'s document.
51 - L{to_plaintext()<ParsedRstDocstring.to_plaintext>} uses
52 C{document.astext()} to convert the C{ParsedRstDocstring}'s
53 document to plaintext.
54
55 @todo: Add ParsedRstDocstring.to_latex()
56 @var CONSOLIDATED_FIELDS: A dictionary encoding the set of
57 'consolidated fields' that can be used. Each consolidated field is
58 marked by a single tag, and contains a single bulleted list, where
59 each list item starts with an identifier, marked as interpreted text
60 (C{`...`}). This module automatically splits these consolidated
61 fields into individual fields. The keys of C{CONSOLIDATED_FIELDS} are
62 the names of possible consolidated fields; and the values are the
63 names of the field tags that should be used for individual entries in
64 the list.
65 """
66 __docformat__ = 'epytext en'
67
68
69 import re, os, os.path
70 from xml.dom.minidom import *
71
72 from docutils.core import publish_string
73 from docutils.writers import Writer
74 from docutils.writers.html4css1 import HTMLTranslator, Writer as HTMLWriter
75 from docutils.writers.latex2e import LaTeXTranslator, Writer as LaTeXWriter
76 from docutils.readers.standalone import Reader as StandaloneReader
77 from docutils.utils import new_document
78 from docutils.nodes import NodeVisitor, Text, SkipChildren
79 from docutils.nodes import SkipNode, TreeCopyVisitor
80 from docutils.frontend import OptionParser
81 from docutils.parsers.rst import directives, roles
82 import docutils.nodes
83 import docutils.transforms.frontmatter
84 import docutils.transforms
85 import docutils.utils
86
87 from epydoc.compat import *
88 from epydoc.markup import *
89 from epydoc.apidoc import ModuleDoc, ClassDoc
90 from epydoc.docwriter.dotgraph import *
91 from epydoc.docwriter.xlink import ApiLinkReader
92 from epydoc.util import wordwrap, plaintext_to_html, plaintext_to_latex
93 from epydoc.markup.doctest import doctest_to_html, doctest_to_latex, \
94 HTMLDoctestColorizer, \
95 LaTeXDoctestColorizer
96
97
98
99
100 CONSOLIDATED_FIELDS = {
101 'parameters': 'param',
102 'arguments': 'arg',
103 'exceptions': 'except',
104 'variables': 'var',
105 'ivariables': 'ivar',
106 'cvariables': 'cvar',
107 'groups': 'group',
108 'types': 'type',
109 'keywords': 'keyword',
110 }
111
112
113
114
115
116 CONSOLIDATED_DEFLIST_FIELDS = ['param', 'arg', 'var', 'ivar', 'cvar', 'keyword']
117
119 """
120 Parse the given docstring, which is formatted using
121 ReStructuredText; and return a L{ParsedDocstring} representation
122 of its contents.
123 @param docstring: The docstring to parse
124 @type docstring: C{string}
125 @param errors: A list where any errors generated during parsing
126 will be stored.
127 @type errors: C{list} of L{ParseError}
128 @param options: Extra options. Unknown options are ignored.
129 Currently, no extra options are defined.
130 @rtype: L{ParsedDocstring}
131 """
132 writer = _DocumentPseudoWriter()
133 reader = _EpydocReader(errors)
134 publish_string(docstring, writer=writer, reader=reader,
135 settings_overrides={'report_level':10000,
136 'halt_level':10000,
137 'warning_stream':None})
138 return ParsedRstDocstring(writer.document)
139
141 """A reporter that ignores all debug messages. This is used to
142 shave a couple seconds off of epydoc's run time, since docutils
143 isn't very fast about processing its own debug messages."""
144 - def debug(self, *args, **kwargs): pass
145
147 """
148 An encoded version of a ReStructuredText docstring. The contents
149 of the docstring are encoded in the L{_document} instance
150 variable.
151
152 @ivar _document: A ReStructuredText document, encoding the
153 docstring.
154 @type _document: C{docutils.nodes.document}
155 """
167
169
170 if errors is None: errors = []
171 visitor = _SplitFieldsTranslator(self._document, errors)
172 self._document.walk(visitor)
173 if len(self._document.children) > 0:
174 return self, visitor.fields
175 else:
176 return None, visitor.fields
177
179
180 visitor = _SummaryExtractor(self._document)
181 try: self._document.walk(visitor)
182 except docutils.nodes.NodeFound: pass
183 return visitor.summary, bool(visitor.other_docs)
184
185
186
187
188
189
190
191
192
193
194 - def to_html(self, docstring_linker, directory=None,
195 docindex=None, context=None, **options):
196
197 visitor = _EpydocHTMLTranslator(self._document, docstring_linker,
198 directory, docindex, context)
199 self._document.walkabout(visitor)
200 return ''.join(visitor.body)
201
202 - def to_latex(self, docstring_linker, directory=None,
203 docindex=None, context=None, **options):
204
205 visitor = _EpydocLaTeXTranslator(self._document, docstring_linker,
206 directory, docindex, context)
207 self._document.walkabout(visitor)
208 return ''.join(visitor.body).strip()+'\n'
209
210 - def to_plaintext(self, docstring_linker, **options):
211
212 return self._document.astext()
213
214 - def __repr__(self): return '<ParsedRstDocstring: ...>'
215
217 visitor = _TermsExtractor(self._document)
218 self._document.walkabout(visitor)
219 return visitor.terms
220
222 """
223 A reader that captures all errors that are generated by parsing,
224 and appends them to a list.
225 """
226
227
228
229
230
231 version = [int(v) for v in docutils.__version__.split('.')]
232 version += [ 0 ] * (3 - len(version))
233 if version < [0,4,0]:
234 default_transforms = list(ApiLinkReader.default_transforms)
235 try: default_transforms.remove(docutils.transforms.frontmatter.DocInfo)
236 except ValueError: pass
237 else:
241 del version
242
246
256
267
269 """
270 A pseudo-writer for the docutils framework, that can be used to
271 access the document itself. The output of C{_DocumentPseudoWriter}
272 is just an empty string; but after it has been used, the most
273 recently processed document is available as the instance variable
274 C{document}
275
276 @type document: C{docutils.nodes.document}
277 @ivar document: The most recently processed document.
278 """
282
285
287 """
288 A docutils node visitor that extracts the first sentence from
289 the first paragraph in a document.
290 """
295
298
299 _SUMMARY_RE = re.compile(r'(\s*[\w\W]*?\.)(\s|$)')
301 if self.summary is not None:
302
303 self.other_docs = True
304 raise docutils.nodes.NodeFound('Found summary')
305
306 summary_pieces = []
307
308
309 for child in node:
310 if isinstance(child, docutils.nodes.Text):
311 m = self._SUMMARY_RE.match(child.data)
312 if m:
313 summary_pieces.append(docutils.nodes.Text(m.group(1)))
314 other = child.data[m.end():]
315 if other and not other.isspace():
316 self.other_docs = True
317 break
318 summary_pieces.append(child)
319
320 summary_doc = self.document.copy()
321 summary_para = node.copy()
322 summary_doc[:] = [summary_para]
323 summary_para[:] = summary_pieces
324 self.summary = ParsedRstDocstring(summary_doc)
325
328
330 'Ignore all unknown nodes'
331
333 """
334 A docutils node visitor that extracts the terms from documentation.
335
336 Terms are created using the C{:term:} interpreted text role.
337 """
339 NodeVisitor.__init__(self, document)
340
341 self.terms = None
342 """
343 The terms currently found.
344 @type: C{list}
345 """
346
348 self.terms = []
349 self._in_term = False
350
352 if 'term' in node.get('classes'):
353 self._in_term = True
354
356 if 'term' in node.get('classes'):
357 self._in_term = False
358
360 if self._in_term:
361 doc = self.document.copy()
362 doc[:] = [node.copy()]
363 self.terms.append(ParsedRstDocstring(doc))
364
366 'Ignore all unknown nodes'
367
369 'Ignore all unknown nodes'
370
372 """
373 A docutils translator that removes all fields from a document, and
374 collects them into the instance variable C{fields}
375
376 @ivar fields: The fields of the most recently walked document.
377 @type fields: C{list} of L{Field<markup.Field>}
378 """
379
380 ALLOW_UNMARKED_ARG_IN_CONSOLIDATED_FIELD = True
381 """If true, then consolidated fields are not required to mark
382 arguments with C{`backticks`}. (This is currently only
383 implemented for consolidated fields expressed as definition lists;
384 consolidated fields expressed as unordered lists still require
385 backticks for now."""
386
388 NodeVisitor.__init__(self, document)
389 self._errors = errors
390 self.fields = []
391 self._newfields = {}
392
395
397
398 node.parent.remove(node)
399
400
401 tag = node[0].astext().split(None, 1)
402 tagname = tag[0]
403 if len(tag)>1: arg = tag[1]
404 else: arg = None
405
406
407 fbody = node[1]
408 if arg is None:
409 for (list_tag, entry_tag) in CONSOLIDATED_FIELDS.items():
410 if tagname.lower() == list_tag:
411 try:
412 self.handle_consolidated_field(fbody, entry_tag)
413 return
414 except ValueError, e:
415 estr = 'Unable to split consolidated field '
416 estr += '"%s" - %s' % (tagname, e)
417 self._errors.append(ParseError(estr, node.line,
418 is_fatal=0))
419
420
421 if tagname.lower() not in self._newfields:
422 newfield = Field('newfield', tagname.lower(),
423 parse(tagname, 'plaintext'))
424 self.fields.append(newfield)
425 self._newfields[tagname.lower()] = 1
426
427 self._add_field(tagname, arg, fbody)
428
430 field_doc = self.document.copy()
431 for child in fbody: field_doc.append(child)
432 field_pdoc = ParsedRstDocstring(field_doc)
433 self.fields.append(Field(tagname, arg, field_pdoc))
434
436
437
438 node.parent.remove(node)
439
456
458
459
460
461 n = 0
462 _BAD_ITEM = ("list item %d is not well formed. Each item must "
463 "consist of a single marked identifier (e.g., `x`), "
464 "optionally followed by a colon or dash and a "
465 "description.")
466 for item in items:
467 n += 1
468 if item.tagname != 'list_item' or len(item) == 0:
469 raise ValueError('bad bulleted list (bad child %d).' % n)
470 if item[0].tagname != 'paragraph':
471 if item[0].tagname == 'definition_list':
472 raise ValueError(('list item %d contains a definition '+
473 'list (it\'s probably indented '+
474 'wrong).') % n)
475 else:
476 raise ValueError(_BAD_ITEM % n)
477 if len(item[0]) == 0:
478 raise ValueError(_BAD_ITEM % n)
479 if item[0][0].tagname != 'title_reference':
480 raise ValueError(_BAD_ITEM % n)
481
482
483 for item in items:
484
485 arg = item[0][0].astext()
486
487
488 fbody = item[:]
489 fbody[0] = fbody[0].copy()
490 fbody[0][:] = item[0][1:]
491
492
493 if (len(fbody[0]) > 0 and
494 isinstance(fbody[0][0], docutils.nodes.Text)):
495 child = fbody[0][0]
496 if child.data[:1] in ':-':
497 child.data = child.data[1:].lstrip()
498 elif child.data[:2] in (' -', ' :'):
499 child.data = child.data[2:].lstrip()
500
501
502 self._add_field(tagname, arg, fbody)
503
505
506 n = 0
507 _BAD_ITEM = ("item %d is not well formed. Each item's term must "
508 "consist of a single marked identifier (e.g., `x`), "
509 "optionally followed by a space, colon, space, and "
510 "a type description.")
511 for item in items:
512 n += 1
513 if (item.tagname != 'definition_list_item' or len(item) < 2 or
514 item[0].tagname != 'term' or
515 item[-1].tagname != 'definition'):
516 raise ValueError('bad definition list (bad child %d).' % n)
517 if len(item) > 3:
518 raise ValueError(_BAD_ITEM % n)
519 if not ((item[0][0].tagname == 'title_reference') or
520 (self.ALLOW_UNMARKED_ARG_IN_CONSOLIDATED_FIELD and
521 isinstance(item[0][0], docutils.nodes.Text))):
522 raise ValueError(_BAD_ITEM % n)
523 for child in item[0][1:]:
524 if child.astext() != '':
525 raise ValueError(_BAD_ITEM % n)
526
527
528 for item in items:
529
530 arg = item[0][0].astext()
531 fbody = item[-1]
532 self._add_field(tagname, arg, fbody)
533
534 if len(item) == 3:
535 type_descr = item[1]
536 self._add_field('type', arg, type_descr)
537
539 'Ignore all unknown nodes'
540
545
546 _TARGET_RE = re.compile(r'^(.*?)\s*<(?:URI:|URL:)?([^<>]+)>$')
547
549 SECTIONS = ['EpydocUserSection',
550 'EpydocUserSubsection',
551 'EpydocUserSubsubsection']
557
559 settings = None
562
563 if self.settings is None:
564 settings = OptionParser([LaTeXWriter()]).get_default_values()
565 settings.output_encoding = 'utf-8'
566
567
568
569 settings.use_latex_toc = True
570
571 self.__class__.settings = settings
572 document.settings = self.settings
573
574 LaTeXTranslator.__init__(self, document)
575 self._linker = docstring_linker
576 self._directory = directory
577 self._docindex = docindex
578 self._context = context
579
580
581 self.d_class = _EpydocDocumentClass()
582
583
585 m = _TARGET_RE.match(node.astext())
586 if m: text, target = m.groups()
587 else: target = text = node.astext()
588 text = plaintext_to_latex(text)
589 xref = self._linker.translate_identifier_xref(target, text)
590 self.body.append(xref)
591 raise SkipNode()
592
595
597 if self._directory is None: raise SkipNode()
598
599
600 graph = node.graph(self._docindex, self._context, self._linker)
601 if graph is None: raise SkipNode()
602
603
604 self.body.append(graph.to_latex(self._directory))
605 raise SkipNode()
606
608 pysrc = node[0].astext()
609 if node.get('codeblock'):
610 self.body.append(LaTeXDoctestColorizer().colorize_codeblock(pysrc))
611 else:
612 self.body.append(doctest_to_latex(pysrc))
613 raise SkipNode()
614
616 self.body.append('\\begin{reSTadmonition}[%s]\n' %
617 self.language.labels[name])
618
620 self.body.append('\\end{reSTadmonition}\n');
621
623 settings = None
624 - def __init__(self, document, docstring_linker, directory,
625 docindex, context):
639
640
649
655
658
659 - def starttag(self, node, tagname, suffix='\n', **attributes):
660 """
661 This modified version of starttag makes a few changes to HTML
662 tags, to prevent them from conflicting with epydoc. In particular:
663 - existing class attributes are prefixed with C{'rst-'}
664 - existing names are prefixed with C{'rst-'}
665 - hrefs starting with C{'#'} are prefixed with C{'rst-'}
666 - hrefs not starting with C{'#'} are given target='_top'
667 - all headings (C{<hM{n}>}) are given the css class C{'heading'}
668 """
669
670 attr_dicts = [attributes]
671 if isinstance(node, docutils.nodes.Node):
672 attr_dicts.append(node.attributes)
673 if isinstance(node, dict):
674 attr_dicts.append(node)
675
676
677
678 for attr_dict in attr_dicts:
679 for (key, val) in attr_dict.items():
680
681
682 if key.lower() in ('class', 'id', 'name'):
683 attr_dict[key] = 'rst-%s' % val
684 elif key.lower() in ('classes', 'ids', 'names'):
685 attr_dict[key] = ['rst-%s' % cls for cls in val]
686 elif key.lower() == 'href':
687 if attr_dict[key][:1]=='#':
688 attr_dict[key] = '#rst-%s' % attr_dict[key][1:]
689 else:
690
691
692 attr_dict['target'] = '_top'
693
694
695 if re.match(r'^h\d+$', tagname):
696 attributes['class'] = ' '.join([attributes.get('class',''),
697 'heading']).strip()
698
699 return HTMLTranslator.starttag(self, node, tagname, suffix,
700 **attributes)
701
703 if self._directory is None: raise SkipNode()
704
705
706 graph = node.graph(self._docindex, self._context, self._linker)
707 if graph is None: raise SkipNode()
708
709
710 self.body.append(graph.to_html(self._directory))
711 raise SkipNode()
712
720
731
732 -def python_code_directive(name, arguments, options, content, lineno,
733 content_offset, block_text, state, state_machine):
734 """
735 A custom restructuredtext directive which can be used to display
736 syntax-highlighted Python code blocks. This directive takes no
737 arguments, and the body should contain only Python code. This
738 directive can be used instead of doctest blocks when it is
739 inconvenient to list prompts on each line, or when you would
740 prefer that the output not contain prompts (e.g., to make
741 copy/paste easier).
742 """
743 required_arguments = 0
744 optional_arguments = 0
745
746 text = '\n'.join(content)
747 node = docutils.nodes.doctest_block(text, text, codeblock=True)
748 return [ node ]
749
750 python_code_directive.arguments = (0, 0, 0)
751 python_code_directive.content = True
752
753 directives.register_directive('python', python_code_directive)
754
755 -def term_role(name, rawtext, text, lineno, inliner,
756 options={}, content=[]):
757
758 text = docutils.utils.unescape(text)
759 node = docutils.nodes.emphasis(rawtext, text, **options)
760 node.attributes['classes'].append('term')
761
762 return [node], []
763
764 roles.register_local_role('term', term_role)
765
766
767
768
769
770
772 """
773 A custom docutils node that should be rendered using Graphviz dot.
774 This node does not directly store the graph; instead, it stores a
775 pointer to a function that can be used to generate the graph.
776 This allows the graph to be built based on information that might
777 not be available yet at parse time. This graph generation
778 function has the following signature:
779
780 >>> def generate_graph(docindex, context, linker, *args):
781 ... 'generates and returns a new DotGraph'
782
783 Where C{docindex} is a docindex containing the documentation that
784 epydoc has built; C{context} is the C{APIDoc} whose docstring
785 contains this dotgraph node; C{linker} is a L{DocstringLinker}
786 that can be used to resolve crossreferences; and C{args} is any
787 extra arguments that are passed to the C{dotgraph} constructor.
788 """
789 - def __init__(self, generate_graph_func, *generate_graph_args):
790 docutils.nodes.image.__init__(self)
791 self.graph_func = generate_graph_func
792 self.args = generate_graph_args
793 - def graph(self, docindex, context, linker):
794 return self.graph_func(docindex, context, linker, *self.args)
795
797 """A directive option spec for the orientation of a graph."""
798 argument = argument.lower().strip()
799 if argument == 'right': return 'LR'
800 if argument == 'left': return 'RL'
801 if argument == 'down': return 'TB'
802 if argument == 'up': return 'BT'
803 raise ValueError('%r unknown; choose from left, right, up, down' %
804 argument)
805
806 -def digraph_directive(name, arguments, options, content, lineno,
807 content_offset, block_text, state, state_machine):
808 """
809 A custom restructuredtext directive which can be used to display
810 Graphviz dot graphs. This directive takes a single argument,
811 which is used as the graph's name. The contents of the directive
812 are used as the body of the graph. Any href attributes whose
813 value has the form <name> will be replaced by the URL of the object
814 with that name. Here's a simple example::
815
816 .. digraph:: example_digraph
817 a -> b -> c
818 c -> a [dir=\"none\"]
819 """
820 if arguments: title = arguments[0]
821 else: title = ''
822 return [ dotgraph(_construct_digraph, title, options.get('caption'),
823 '\n'.join(content)) ]
824 digraph_directive.arguments = (0, 1, True)
825 digraph_directive.options = {'caption': directives.unchanged}
826 digraph_directive.content = True
827 directives.register_directive('digraph', digraph_directive)
828
835
836 -def classtree_directive(name, arguments, options, content, lineno,
837 content_offset, block_text, state, state_machine):
838 """
839 A custom restructuredtext directive which can be used to
840 graphically display a class hierarchy. If one or more arguments
841 are given, then those classes and all their descendants will be
842 displayed. If no arguments are given, and the directive is in a
843 class's docstring, then that class and all its descendants will be
844 displayed. It is an error to use this directive with no arguments
845 in a non-class docstring.
846
847 Options:
848 - C{:dir:} -- Specifies the orientation of the graph. One of
849 C{down}, C{right} (default), C{left}, C{up}.
850 """
851 return [ dotgraph(_construct_classtree, arguments, options) ]
852 classtree_directive.arguments = (0, 1, True)
853 classtree_directive.options = {'dir': _dir_option}
854 classtree_directive.content = False
855 directives.register_directive('classtree', classtree_directive)
856
858 """Graph generator for L{classtree_directive}"""
859 if len(arguments) == 1:
860 bases = [docindex.find(name, context) for name in
861 arguments[0].replace(',',' ').split()]
862 bases = [d for d in bases if isinstance(d, ClassDoc)]
863 elif isinstance(context, ClassDoc):
864 bases = [context]
865 else:
866 log.warning("Could not construct class tree: you must "
867 "specify one or more base classes.")
868 return None
869
870 return class_tree_graph(bases, linker, context, **options)
871
872 -def packagetree_directive(name, arguments, options, content, lineno,
873 content_offset, block_text, state, state_machine):
874 """
875 A custom restructuredtext directive which can be used to
876 graphically display a package hierarchy. If one or more arguments
877 are given, then those packages and all their submodules will be
878 displayed. If no arguments are given, and the directive is in a
879 package's docstring, then that package and all its submodules will
880 be displayed. It is an error to use this directive with no
881 arguments in a non-package docstring.
882
883 Options:
884 - C{:dir:} -- Specifies the orientation of the graph. One of
885 C{down}, C{right} (default), C{left}, C{up}.
886 """
887 return [ dotgraph(_construct_packagetree, arguments, options) ]
888 packagetree_directive.arguments = (0, 1, True)
889 packagetree_directive.options = {
890 'dir': _dir_option,
891 'style': lambda a:directives.choice(a.lower(), ('uml', 'tree'))}
892 packagetree_directive.content = False
893 directives.register_directive('packagetree', packagetree_directive)
894
896 """Graph generator for L{packagetree_directive}"""
897 if len(arguments) == 1:
898 packages = [docindex.find(name, context) for name in
899 arguments[0].replace(',',' ').split()]
900 packages = [d for d in packages if isinstance(d, ModuleDoc)]
901 elif isinstance(context, ModuleDoc):
902 packages = [context]
903 else:
904 log.warning("Could not construct package tree: you must "
905 "specify one or more root packages.")
906 return None
907
908 return package_tree_graph(packages, linker, context, **options)
909
910 -def importgraph_directive(name, arguments, options, content, lineno,
911 content_offset, block_text, state, state_machine):
913 importgraph_directive.arguments = (0, 1, True)
914 importgraph_directive.options = {'dir': _dir_option}
915 importgraph_directive.content = False
916 directives.register_directive('importgraph', importgraph_directive)
917
919 """Graph generator for L{importgraph_directive}"""
920 if len(arguments) == 1:
921 modules = [ docindex.find(name, context)
922 for name in arguments[0].replace(',',' ').split() ]
923 modules = [d for d in modules if isinstance(d, ModuleDoc)]
924 else:
925 modules = [d for d in docindex.root if isinstance(d, ModuleDoc)]
926
927 return import_graph(modules, docindex, linker, context, **options)
928
929 -def callgraph_directive(name, arguments, options, content, lineno,
930 content_offset, block_text, state, state_machine):
932 callgraph_directive.arguments = (0, 1, True)
933 callgraph_directive.options = {'dir': _dir_option,
934 'add_callers': directives.flag,
935 'add_callees': directives.flag}
936 callgraph_directive.content = False
937 directives.register_directive('callgraph', callgraph_directive)
938
940 """Graph generator for L{callgraph_directive}"""
941 if len(arguments) == 1:
942 docs = [docindex.find(name, context) for name in
943 arguments[0].replace(',',' ').split()]
944 docs = [doc for doc in docs if doc is not None]
945 else:
946 docs = [context]
947 return call_graph(docs, docindex, linker, context, **options)
948