1 # $Id: __init__.py 4813 2006-11-13 03:41:08Z wiemann $ |
|
2 # Author: David Goodger <goodger@python.org> |
|
3 # Copyright: This module has been placed in the public domain. |
|
4 |
|
5 """ |
|
6 This package contains the Python Source Reader modules. |
|
7 """ |
|
8 |
|
9 __docformat__ = 'reStructuredText' |
|
10 |
|
11 |
|
12 import sys |
|
13 import docutils.readers |
|
14 from docutils.readers.python import moduleparser |
|
15 from docutils import parsers |
|
16 from docutils import nodes |
|
17 from docutils.readers.python import pynodes |
|
18 from docutils import readers |
|
19 |
|
20 class Reader(docutils.readers.Reader): |
|
21 |
|
22 config_section = 'python reader' |
|
23 config_section_dependencies = ('readers',) |
|
24 |
|
25 default_parser = 'restructuredtext' |
|
26 |
|
27 def parse(self): |
|
28 """Parse `self.input` into a document tree.""" |
|
29 self.document = document = self.new_document() |
|
30 module_section = moduleparser.parse_module(self.input, |
|
31 self.source.source_path) |
|
32 module_section.walk(DocformatVisitor(self.document)) |
|
33 visitor = DocstringFormattingVisitor( |
|
34 document=document, |
|
35 default_parser=self.default_parser) |
|
36 module_section.walk(visitor) |
|
37 self.document.append(module_section) |
|
38 |
|
39 |
|
40 class DocformatVisitor(nodes.SparseNodeVisitor): |
|
41 |
|
42 """ |
|
43 This sets docformat attributes in a module. Wherever an assignment |
|
44 to __docformat__ is found, we look for the enclosing scope -- a class, |
|
45 a module, or a function -- and set the docformat attribute there. |
|
46 |
|
47 We can't do this during the DocstringFormattingVisitor walking, |
|
48 because __docformat__ may appear below a docstring in that format |
|
49 (typically below the module docstring). |
|
50 """ |
|
51 |
|
52 def visit_attribute(self, node): |
|
53 assert isinstance(node[0], pynodes.object_name) |
|
54 name = node[0][0].data |
|
55 if name != '__docformat__': |
|
56 return |
|
57 value = None |
|
58 for child in children: |
|
59 if isinstance(child, pynodes.expression_value): |
|
60 value = child[0].data |
|
61 break |
|
62 assert value.startswith("'") or value.startswith('"'), "__docformat__ must be assigned a string literal (not %s); line: %s" % (value, node['lineno']) |
|
63 name = name[1:-1] |
|
64 looking_in = node.parent |
|
65 while not isinstance(looking_in, (pynodes.module_section, |
|
66 pynodes.function_section, |
|
67 pynodes.class_section)): |
|
68 looking_in = looking_in.parent |
|
69 looking_in['docformat'] = name |
|
70 |
|
71 |
|
72 class DocstringFormattingVisitor(nodes.SparseNodeVisitor): |
|
73 |
|
74 def __init__(self, document, default_parser): |
|
75 self.document = document |
|
76 self.default_parser = default_parser |
|
77 self.parsers = {} |
|
78 |
|
79 def visit_docstring(self, node): |
|
80 text = node[0].data |
|
81 docformat = self.find_docformat(node) |
|
82 del node[0] |
|
83 node['docformat'] = docformat |
|
84 parser = self.get_parser(docformat) |
|
85 parser.parse(text, self.document) |
|
86 for child in self.document.children: |
|
87 node.append(child) |
|
88 self.document.current_source = self.document.current_line = None |
|
89 del self.document[:] |
|
90 |
|
91 def get_parser(self, parser_name): |
|
92 """ |
|
93 Get a parser based on its name. We reuse parsers during this |
|
94 visitation, so parser instances are cached. |
|
95 """ |
|
96 parser_name = parsers._parser_aliases.get(parser_name, parser_name) |
|
97 if not self.parsers.has_key(parser_name): |
|
98 cls = parsers.get_parser_class(parser_name) |
|
99 self.parsers[parser_name] = cls() |
|
100 return self.parsers[parser_name] |
|
101 |
|
102 def find_docformat(self, node): |
|
103 """ |
|
104 Find the __docformat__ closest to this node (i.e., look in the |
|
105 class or module) |
|
106 """ |
|
107 while node: |
|
108 if node.get('docformat'): |
|
109 return node['docformat'] |
|
110 node = node.parent |
|
111 return self.default_parser |
|
112 |
|
113 |
|
114 if __name__ == '__main__': |
|
115 try: |
|
116 import locale |
|
117 locale.setlocale(locale.LC_ALL, '') |
|
118 except: |
|
119 pass |
|
120 |
|
121 from docutils.core import publish_cmdline, default_description |
|
122 |
|
123 description = ('Generates pseudo-XML from Python modules ' |
|
124 '(for testing purposes). ' + default_description) |
|
125 |
|
126 publish_cmdline(description=description, |
|
127 reader=Reader()) |
|