summaryrefslogtreecommitdiff
path: root/docutils/readers/python/__init__.py
blob: ba147a3d5c27758c9c8e9c11aefe34876f5a6c81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Author: David Goodger
# Contact: goodger@users.sourceforge.net
# Revision: $Revision$
# Date: $Date$
# Copyright: This module has been placed in the public domain.

"""
This package contains the Python Source Reader modules.

It requires Python 2.2 or higher (`moduleparser` depends on the
`compiler` and `tokenize` modules).
"""

__docformat__ = 'reStructuredText'


import sys
import docutils.readers
from docutils.readers.python import moduleparser
from docutils import parsers
from docutils import nodes
from docutils.readers.python import pynodes
from docutils import readers

class Reader(docutils.readers.Reader):

    config_section = 'python reader'
    config_section_dependencies = ('readers',)

    default_parser = 'restructuredtext'

    def parse(self):
        """Parse `self.input` into a document tree."""
        self.document = document = self.new_document()
        module_section = moduleparser.parse_module(self.input,
                                                   self.source.source_path)
        module_section.walk(DocformatVisitor(self.document))
        visitor = DocstringFormattingVisitor(
            document=document,
            default_parser=self.default_parser)
        module_section.walk(visitor)
        self.document.append(module_section)


class DocformatVisitor(nodes.SparseNodeVisitor):

    """
    This sets docformat attributes in a module.  Wherever an assignment
    to __docformat__ is found, we look for the enclosing scope -- a class,
    a module, or a function -- and set the docformat attribute there.

    We can't do this during the DocstringFormattingVisitor walking,
    because __docformat__ may appear below a docstring in that format
    (typically below the module docstring).
    """

    def visit_attribute(self, node):
        assert isinstance(node[0], pynodes.object_name)
        name = node[0][0].data
        if name != '__docformat__':
            return
        value = None
        for child in children:
            if isinstance(child, pynodes.expression_value):
                value = child[0].data
                break
        assert value.startswith("'") or value.startswith('"'), "__docformat__ must be assigned a string literal (not %s); line: %s" % (value, node['lineno'])
        name = name[1:-1]
        looking_in = node.parent
        while not isinstance(looking_in, (pynodes.module_section,
                                          pynodes.function_section,
                                          pynodes.class_section)):
            looking_in = looking_in.parent
        looking_in['docformat'] = name


class DocstringFormattingVisitor(nodes.SparseNodeVisitor):

    def __init__(self, document, default_parser):
        self.document = document
        self.default_parser = default_parser
        self.parsers = {}

    def visit_docstring(self, node):
        text = node[0].data
        docformat = self.find_docformat(node)
        del node[0]
        node['docformat'] = docformat
        parser = self.get_parser(docformat)
        parser.parse(text, self.document)
        for child in self.document.children:
            node.append(child)
        self.document.current_source = self.document.current_line = None
        del self.document[:]

    def get_parser(self, parser_name):
        """
        Get a parser based on its name.  We reuse parsers during this
        visitation, so parser instances are cached.
        """
        parser_name = parsers._parser_aliases.get(parser_name, parser_name)
        if not self.parsers.has_key(parser_name):
            cls = parsers.get_parser_class(parser_name)
            self.parsers[parser_name] = cls()
        return self.parsers[parser_name]

    def find_docformat(self, node):
        """
        Find the __docformat__ closest to this node (i.e., look in the
        class or module)
        """
        while node:
            if node.get('docformat'):
                return node['docformat']
            node = node.parent
        return self.default_parser


if __name__ == '__main__':
    try:
        import locale
        locale.setlocale(locale.LC_ALL, '')
    except:
        pass

    from docutils.core import publish_cmdline, default_description

    description = ('Generates pseudo-XML from Python modules '
                   '(for testing purposes).  ' + default_description)

    publish_cmdline(description=description,
                    reader=Reader())