##// END OF EJS Templates
Use Sphinx autosummary extension for API docs index
Thomas Kluyver -
Show More
@@ -1,239 +1,240 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 #
2 #
3 # IPython documentation build configuration file.
3 # IPython documentation build configuration file.
4
4
5 # NOTE: This file has been edited manually from the auto-generated one from
5 # NOTE: This file has been edited manually from the auto-generated one from
6 # sphinx. Do NOT delete and re-generate. If any changes from sphinx are
6 # sphinx. Do NOT delete and re-generate. If any changes from sphinx are
7 # needed, generate a scratch one and merge by hand any new fields needed.
7 # needed, generate a scratch one and merge by hand any new fields needed.
8
8
9 #
9 #
10 # This file is execfile()d with the current directory set to its containing dir.
10 # This file is execfile()d with the current directory set to its containing dir.
11 #
11 #
12 # The contents of this file are pickled, so don't put values in the namespace
12 # The contents of this file are pickled, so don't put values in the namespace
13 # that aren't pickleable (module imports are okay, they're removed automatically).
13 # that aren't pickleable (module imports are okay, they're removed automatically).
14 #
14 #
15 # All configuration values have a default value; values that are commented out
15 # All configuration values have a default value; values that are commented out
16 # serve to show the default value.
16 # serve to show the default value.
17
17
18 import sys, os
18 import sys, os
19
19
20 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
20 ON_RTD = os.environ.get('READTHEDOCS', None) == 'True'
21
21
22 if ON_RTD:
22 if ON_RTD:
23 # Mock the presence of matplotlib, which we don't have on RTD
23 # Mock the presence of matplotlib, which we don't have on RTD
24 # see
24 # see
25 # http://read-the-docs.readthedocs.org/en/latest/faq.html
25 # http://read-the-docs.readthedocs.org/en/latest/faq.html
26 tags.add('rtd')
26 tags.add('rtd')
27
27
28 # If your extensions are in another directory, add it here. If the directory
28 # If your extensions are in another directory, add it here. If the directory
29 # is relative to the documentation root, use os.path.abspath to make it
29 # is relative to the documentation root, use os.path.abspath to make it
30 # absolute, like shown here.
30 # absolute, like shown here.
31 sys.path.insert(0, os.path.abspath('../sphinxext'))
31 sys.path.insert(0, os.path.abspath('../sphinxext'))
32
32
33 # We load the ipython release info into a dict by explicit execution
33 # We load the ipython release info into a dict by explicit execution
34 iprelease = {}
34 iprelease = {}
35 execfile('../../IPython/core/release.py',iprelease)
35 execfile('../../IPython/core/release.py',iprelease)
36
36
37 # General configuration
37 # General configuration
38 # ---------------------
38 # ---------------------
39
39
40 # Add any Sphinx extension module names here, as strings. They can be extensions
40 # Add any Sphinx extension module names here, as strings. They can be extensions
41 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
41 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
42 extensions = [
42 extensions = [
43 'matplotlib.sphinxext.mathmpl',
43 'matplotlib.sphinxext.mathmpl',
44 'matplotlib.sphinxext.only_directives',
44 'matplotlib.sphinxext.only_directives',
45 'matplotlib.sphinxext.plot_directive',
45 'matplotlib.sphinxext.plot_directive',
46 'sphinx.ext.autodoc',
46 'sphinx.ext.autodoc',
47 'sphinx.ext.autosummary',
47 'sphinx.ext.doctest',
48 'sphinx.ext.doctest',
48 'sphinx.ext.inheritance_diagram',
49 'sphinx.ext.inheritance_diagram',
49 'sphinx.ext.intersphinx',
50 'sphinx.ext.intersphinx',
50 'IPython.sphinxext.ipython_console_highlighting',
51 'IPython.sphinxext.ipython_console_highlighting',
51 'IPython.sphinxext.ipython_directive',
52 'IPython.sphinxext.ipython_directive',
52 'numpydoc', # to preprocess docstrings
53 'numpydoc', # to preprocess docstrings
53 'github', # for easy GitHub links
54 'github', # for easy GitHub links
54 ]
55 ]
55
56
56 if ON_RTD:
57 if ON_RTD:
57 # Remove extensions not currently supported on RTD
58 # Remove extensions not currently supported on RTD
58 extensions.remove('matplotlib.sphinxext.only_directives')
59 extensions.remove('matplotlib.sphinxext.only_directives')
59 extensions.remove('matplotlib.sphinxext.mathmpl')
60 extensions.remove('matplotlib.sphinxext.mathmpl')
60 extensions.remove('matplotlib.sphinxext.plot_directive')
61 extensions.remove('matplotlib.sphinxext.plot_directive')
61 extensions.remove('IPython.sphinxext.ipython_directive')
62 extensions.remove('IPython.sphinxext.ipython_directive')
62 extensions.remove('IPython.sphinxext.ipython_console_highlighting')
63 extensions.remove('IPython.sphinxext.ipython_console_highlighting')
63
64
64 # Add any paths that contain templates here, relative to this directory.
65 # Add any paths that contain templates here, relative to this directory.
65 templates_path = ['_templates']
66 templates_path = ['_templates']
66
67
67 # The suffix of source filenames.
68 # The suffix of source filenames.
68 source_suffix = '.rst'
69 source_suffix = '.rst'
69
70
70 if iprelease['_version_extra']:
71 if iprelease['_version_extra']:
71 rst_prolog = """
72 rst_prolog = """
72 .. note::
73 .. note::
73
74
74 This documentation is for a development version of IPython. There may be
75 This documentation is for a development version of IPython. There may be
75 significant differences from the latest stable release (1.1.0).
76 significant differences from the latest stable release (1.1.0).
76
77
77 """
78 """
78
79
79 # The master toctree document.
80 # The master toctree document.
80 master_doc = 'index'
81 master_doc = 'index'
81
82
82 # General substitutions.
83 # General substitutions.
83 project = 'IPython'
84 project = 'IPython'
84 copyright = '2008, The IPython Development Team'
85 copyright = '2008, The IPython Development Team'
85
86
86 # ghissue config
87 # ghissue config
87 github_project_url = "https://github.com/ipython/ipython"
88 github_project_url = "https://github.com/ipython/ipython"
88
89
89 # The default replacements for |version| and |release|, also used in various
90 # The default replacements for |version| and |release|, also used in various
90 # other places throughout the built documents.
91 # other places throughout the built documents.
91 #
92 #
92 # The full version, including alpha/beta/rc tags.
93 # The full version, including alpha/beta/rc tags.
93 codename = iprelease['codename']
94 codename = iprelease['codename']
94 release = "%s: %s" % (iprelease['version'], codename)
95 release = "%s: %s" % (iprelease['version'], codename)
95 # Just the X.Y.Z part, no '-dev'
96 # Just the X.Y.Z part, no '-dev'
96 version = iprelease['version'].split('-', 1)[0]
97 version = iprelease['version'].split('-', 1)[0]
97
98
98
99
99 # There are two options for replacing |today|: either, you set today to some
100 # There are two options for replacing |today|: either, you set today to some
100 # non-false value, then it is used:
101 # non-false value, then it is used:
101 #today = ''
102 #today = ''
102 # Else, today_fmt is used as the format for a strftime call.
103 # Else, today_fmt is used as the format for a strftime call.
103 today_fmt = '%B %d, %Y'
104 today_fmt = '%B %d, %Y'
104
105
105 # List of documents that shouldn't be included in the build.
106 # List of documents that shouldn't be included in the build.
106 #unused_docs = []
107 #unused_docs = []
107
108
108 # List of directories, relative to source directories, that shouldn't be searched
109 # List of directories, relative to source directories, that shouldn't be searched
109 # for source files.
110 # for source files.
110 exclude_dirs = ['attic']
111 exclude_dirs = ['attic']
111
112
112 # If true, '()' will be appended to :func: etc. cross-reference text.
113 # If true, '()' will be appended to :func: etc. cross-reference text.
113 #add_function_parentheses = True
114 #add_function_parentheses = True
114
115
115 # If true, the current module name will be prepended to all description
116 # If true, the current module name will be prepended to all description
116 # unit titles (such as .. function::).
117 # unit titles (such as .. function::).
117 #add_module_names = True
118 #add_module_names = True
118
119
119 # If true, sectionauthor and moduleauthor directives will be shown in the
120 # If true, sectionauthor and moduleauthor directives will be shown in the
120 # output. They are ignored by default.
121 # output. They are ignored by default.
121 #show_authors = False
122 #show_authors = False
122
123
123 # The name of the Pygments (syntax highlighting) style to use.
124 # The name of the Pygments (syntax highlighting) style to use.
124 pygments_style = 'sphinx'
125 pygments_style = 'sphinx'
125
126
126
127
127 # Options for HTML output
128 # Options for HTML output
128 # -----------------------
129 # -----------------------
129
130
130 # The style sheet to use for HTML and HTML Help pages. A file of that name
131 # The style sheet to use for HTML and HTML Help pages. A file of that name
131 # must exist either in Sphinx' static/ path, or in one of the custom paths
132 # must exist either in Sphinx' static/ path, or in one of the custom paths
132 # given in html_static_path.
133 # given in html_static_path.
133 html_style = 'default.css'
134 html_style = 'default.css'
134
135
135 # The name for this set of Sphinx documents. If None, it defaults to
136 # The name for this set of Sphinx documents. If None, it defaults to
136 # "<project> v<release> documentation".
137 # "<project> v<release> documentation".
137 #html_title = None
138 #html_title = None
138
139
139 # The name of an image file (within the static path) to place at the top of
140 # The name of an image file (within the static path) to place at the top of
140 # the sidebar.
141 # the sidebar.
141 #html_logo = None
142 #html_logo = None
142
143
143 # Add any paths that contain custom static files (such as style sheets) here,
144 # Add any paths that contain custom static files (such as style sheets) here,
144 # relative to this directory. They are copied after the builtin static files,
145 # relative to this directory. They are copied after the builtin static files,
145 # so a file named "default.css" will overwrite the builtin "default.css".
146 # so a file named "default.css" will overwrite the builtin "default.css".
146 html_static_path = ['_static']
147 html_static_path = ['_static']
147
148
148 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
149 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
149 # using the given strftime format.
150 # using the given strftime format.
150 html_last_updated_fmt = '%b %d, %Y'
151 html_last_updated_fmt = '%b %d, %Y'
151
152
152 # If true, SmartyPants will be used to convert quotes and dashes to
153 # If true, SmartyPants will be used to convert quotes and dashes to
153 # typographically correct entities.
154 # typographically correct entities.
154 #html_use_smartypants = True
155 #html_use_smartypants = True
155
156
156 # Custom sidebar templates, maps document names to template names.
157 # Custom sidebar templates, maps document names to template names.
157 #html_sidebars = {}
158 #html_sidebars = {}
158
159
159 # Additional templates that should be rendered to pages, maps page names to
160 # Additional templates that should be rendered to pages, maps page names to
160 # template names.
161 # template names.
161 html_additional_pages = {
162 html_additional_pages = {
162 'interactive/htmlnotebook': 'htmlnotebook.html',
163 'interactive/htmlnotebook': 'htmlnotebook.html',
163 }
164 }
164
165
165 # If false, no module index is generated.
166 # If false, no module index is generated.
166 #html_use_modindex = True
167 #html_use_modindex = True
167
168
168 # If true, the reST sources are included in the HTML build as _sources/<name>.
169 # If true, the reST sources are included in the HTML build as _sources/<name>.
169 #html_copy_source = True
170 #html_copy_source = True
170
171
171 # If true, an OpenSearch description file will be output, and all pages will
172 # If true, an OpenSearch description file will be output, and all pages will
172 # contain a <link> tag referring to it. The value of this option must be the
173 # contain a <link> tag referring to it. The value of this option must be the
173 # base URL from which the finished HTML is served.
174 # base URL from which the finished HTML is served.
174 #html_use_opensearch = ''
175 #html_use_opensearch = ''
175
176
176 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
177 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
177 #html_file_suffix = ''
178 #html_file_suffix = ''
178
179
179 # Output file base name for HTML help builder.
180 # Output file base name for HTML help builder.
180 htmlhelp_basename = 'ipythondoc'
181 htmlhelp_basename = 'ipythondoc'
181
182
182 intersphinx_mapping = {'python': ('http://docs.python.org/2/', None)}
183 intersphinx_mapping = {'python': ('http://docs.python.org/2/', None)}
183
184
184 # Options for LaTeX output
185 # Options for LaTeX output
185 # ------------------------
186 # ------------------------
186
187
187 # The paper size ('letter' or 'a4').
188 # The paper size ('letter' or 'a4').
188 latex_paper_size = 'letter'
189 latex_paper_size = 'letter'
189
190
190 # The font size ('10pt', '11pt' or '12pt').
191 # The font size ('10pt', '11pt' or '12pt').
191 latex_font_size = '11pt'
192 latex_font_size = '11pt'
192
193
193 # Grouping the document tree into LaTeX files. List of tuples
194 # Grouping the document tree into LaTeX files. List of tuples
194 # (source start file, target name, title, author, document class [howto/manual]).
195 # (source start file, target name, title, author, document class [howto/manual]).
195
196
196 latex_documents = [
197 latex_documents = [
197 ('index', 'ipython.tex', 'IPython Documentation',
198 ('index', 'ipython.tex', 'IPython Documentation',
198 ur"""The IPython Development Team""", 'manual', True),
199 ur"""The IPython Development Team""", 'manual', True),
199 ('parallel/winhpc_index', 'winhpc_whitepaper.tex',
200 ('parallel/winhpc_index', 'winhpc_whitepaper.tex',
200 'Using IPython on Windows HPC Server 2008',
201 'Using IPython on Windows HPC Server 2008',
201 ur"Brian E. Granger", 'manual', True)
202 ur"Brian E. Granger", 'manual', True)
202 ]
203 ]
203
204
204 # The name of an image file (relative to this directory) to place at the top of
205 # The name of an image file (relative to this directory) to place at the top of
205 # the title page.
206 # the title page.
206 #latex_logo = None
207 #latex_logo = None
207
208
208 # For "manual" documents, if this is true, then toplevel headings are parts,
209 # For "manual" documents, if this is true, then toplevel headings are parts,
209 # not chapters.
210 # not chapters.
210 #latex_use_parts = False
211 #latex_use_parts = False
211
212
212 # Additional stuff for the LaTeX preamble.
213 # Additional stuff for the LaTeX preamble.
213 #latex_preamble = ''
214 #latex_preamble = ''
214
215
215 # Documents to append as an appendix to all manuals.
216 # Documents to append as an appendix to all manuals.
216 #latex_appendices = []
217 #latex_appendices = []
217
218
218 # If false, no module index is generated.
219 # If false, no module index is generated.
219 latex_use_modindex = True
220 latex_use_modindex = True
220
221
221
222
222 # Options for texinfo output
223 # Options for texinfo output
223 # --------------------------
224 # --------------------------
224
225
225 texinfo_documents = [
226 texinfo_documents = [
226 (master_doc, 'ipython', 'IPython Documentation',
227 (master_doc, 'ipython', 'IPython Documentation',
227 'The IPython Development Team',
228 'The IPython Development Team',
228 'IPython',
229 'IPython',
229 'IPython Documentation',
230 'IPython Documentation',
230 'Programming',
231 'Programming',
231 1),
232 1),
232 ]
233 ]
233
234
234
235
235 # Cleanup
236 # Cleanup
236 # -------
237 # -------
237 # delete release info to avoid pickling errors from sphinx
238 # delete release info to avoid pickling errors from sphinx
238
239
239 del iprelease
240 del iprelease
@@ -1,421 +1,422 b''
1 """Attempt to generate templates for module reference with Sphinx
1 """Attempt to generate templates for module reference with Sphinx
2
2
3 XXX - we exclude extension modules
3 XXX - we exclude extension modules
4
4
5 To include extension modules, first identify them as valid in the
5 To include extension modules, first identify them as valid in the
6 ``_uri2path`` method, then handle them in the ``_parse_module`` script.
6 ``_uri2path`` method, then handle them in the ``_parse_module`` script.
7
7
8 We get functions and classes by parsing the text of .py files.
8 We get functions and classes by parsing the text of .py files.
9 Alternatively we could import the modules for discovery, and we'd have
9 Alternatively we could import the modules for discovery, and we'd have
10 to do that for extension modules. This would involve changing the
10 to do that for extension modules. This would involve changing the
11 ``_parse_module`` method to work via import and introspection, and
11 ``_parse_module`` method to work via import and introspection, and
12 might involve changing ``discover_modules`` (which determines which
12 might involve changing ``discover_modules`` (which determines which
13 files are modules, and therefore which module URIs will be passed to
13 files are modules, and therefore which module URIs will be passed to
14 ``_parse_module``).
14 ``_parse_module``).
15
15
16 NOTE: this is a modified version of a script originally shipped with the
16 NOTE: this is a modified version of a script originally shipped with the
17 PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed
17 PyMVPA project, which we've adapted for NIPY use. PyMVPA is an MIT-licensed
18 project."""
18 project."""
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 # Stdlib imports
22 # Stdlib imports
23 import ast
23 import ast
24 import os
24 import os
25 import re
25 import re
26
26
27 class Obj(object):
27 class Obj(object):
28 '''Namespace to hold arbitrary information.'''
28 '''Namespace to hold arbitrary information.'''
29 def __init__(self, **kwargs):
29 def __init__(self, **kwargs):
30 for k, v in kwargs.items():
30 for k, v in kwargs.items():
31 setattr(self, k, v)
31 setattr(self, k, v)
32
32
33 class FuncClsScanner(ast.NodeVisitor):
33 class FuncClsScanner(ast.NodeVisitor):
34 """Scan a module for top-level functions and classes.
34 """Scan a module for top-level functions and classes.
35
35
36 Skips objects with an @undoc decorator, or a name starting with '_'.
36 Skips objects with an @undoc decorator, or a name starting with '_'.
37 """
37 """
38 def __init__(self):
38 def __init__(self):
39 ast.NodeVisitor.__init__(self)
39 ast.NodeVisitor.__init__(self)
40 self.classes = []
40 self.classes = []
41 self.classes_seen = set()
41 self.classes_seen = set()
42 self.functions = []
42 self.functions = []
43
43
44 @staticmethod
44 @staticmethod
45 def has_undoc_decorator(node):
45 def has_undoc_decorator(node):
46 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
46 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
47 for d in node.decorator_list)
47 for d in node.decorator_list)
48
48
49 def visit_If(self, node):
49 def visit_If(self, node):
50 if isinstance(node.test, ast.Compare) \
50 if isinstance(node.test, ast.Compare) \
51 and isinstance(node.test.left, ast.Name) \
51 and isinstance(node.test.left, ast.Name) \
52 and node.test.left.id == '__name__':
52 and node.test.left.id == '__name__':
53 return # Ignore classes defined in "if __name__ == '__main__':"
53 return # Ignore classes defined in "if __name__ == '__main__':"
54
54
55 self.generic_visit(node)
55 self.generic_visit(node)
56
56
57 def visit_FunctionDef(self, node):
57 def visit_FunctionDef(self, node):
58 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
58 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
59 and node.name not in self.functions:
59 and node.name not in self.functions:
60 self.functions.append(node.name)
60 self.functions.append(node.name)
61
61
62 def visit_ClassDef(self, node):
62 def visit_ClassDef(self, node):
63 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
63 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
64 and node.name not in self.classes_seen:
64 and node.name not in self.classes_seen:
65 cls = Obj(name=node.name)
65 cls = Obj(name=node.name)
66 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
66 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
67 n.name=='__init__' for n in node.body)
67 n.name=='__init__' for n in node.body)
68 self.classes.append(cls)
68 self.classes.append(cls)
69 self.classes_seen.add(node.name)
69 self.classes_seen.add(node.name)
70
70
71 def scan(self, mod):
71 def scan(self, mod):
72 self.visit(mod)
72 self.visit(mod)
73 return self.functions, self.classes
73 return self.functions, self.classes
74
74
75 # Functions and classes
75 # Functions and classes
76 class ApiDocWriter(object):
76 class ApiDocWriter(object):
77 ''' Class for automatic detection and parsing of API docs
77 ''' Class for automatic detection and parsing of API docs
78 to Sphinx-parsable reST format'''
78 to Sphinx-parsable reST format'''
79
79
80 # only separating first two levels
80 # only separating first two levels
81 rst_section_levels = ['*', '=', '-', '~', '^']
81 rst_section_levels = ['*', '=', '-', '~', '^']
82
82
83 def __init__(self,
83 def __init__(self,
84 package_name,
84 package_name,
85 rst_extension='.rst',
85 rst_extension='.rst',
86 package_skip_patterns=None,
86 package_skip_patterns=None,
87 module_skip_patterns=None,
87 module_skip_patterns=None,
88 ):
88 ):
89 ''' Initialize package for parsing
89 ''' Initialize package for parsing
90
90
91 Parameters
91 Parameters
92 ----------
92 ----------
93 package_name : string
93 package_name : string
94 Name of the top-level package. *package_name* must be the
94 Name of the top-level package. *package_name* must be the
95 name of an importable package
95 name of an importable package
96 rst_extension : string, optional
96 rst_extension : string, optional
97 Extension for reST files, default '.rst'
97 Extension for reST files, default '.rst'
98 package_skip_patterns : None or sequence of {strings, regexps}
98 package_skip_patterns : None or sequence of {strings, regexps}
99 Sequence of strings giving URIs of packages to be excluded
99 Sequence of strings giving URIs of packages to be excluded
100 Operates on the package path, starting at (including) the
100 Operates on the package path, starting at (including) the
101 first dot in the package path, after *package_name* - so,
101 first dot in the package path, after *package_name* - so,
102 if *package_name* is ``sphinx``, then ``sphinx.util`` will
102 if *package_name* is ``sphinx``, then ``sphinx.util`` will
103 result in ``.util`` being passed for earching by these
103 result in ``.util`` being passed for earching by these
104 regexps. If is None, gives default. Default is:
104 regexps. If is None, gives default. Default is:
105 ['\.tests$']
105 ['\.tests$']
106 module_skip_patterns : None or sequence
106 module_skip_patterns : None or sequence
107 Sequence of strings giving URIs of modules to be excluded
107 Sequence of strings giving URIs of modules to be excluded
108 Operates on the module name including preceding URI path,
108 Operates on the module name including preceding URI path,
109 back to the first dot after *package_name*. For example
109 back to the first dot after *package_name*. For example
110 ``sphinx.util.console`` results in the string to search of
110 ``sphinx.util.console`` results in the string to search of
111 ``.util.console``
111 ``.util.console``
112 If is None, gives default. Default is:
112 If is None, gives default. Default is:
113 ['\.setup$', '\._']
113 ['\.setup$', '\._']
114 '''
114 '''
115 if package_skip_patterns is None:
115 if package_skip_patterns is None:
116 package_skip_patterns = ['\\.tests$']
116 package_skip_patterns = ['\\.tests$']
117 if module_skip_patterns is None:
117 if module_skip_patterns is None:
118 module_skip_patterns = ['\\.setup$', '\\._']
118 module_skip_patterns = ['\\.setup$', '\\._']
119 self.package_name = package_name
119 self.package_name = package_name
120 self.rst_extension = rst_extension
120 self.rst_extension = rst_extension
121 self.package_skip_patterns = package_skip_patterns
121 self.package_skip_patterns = package_skip_patterns
122 self.module_skip_patterns = module_skip_patterns
122 self.module_skip_patterns = module_skip_patterns
123
123
124 def get_package_name(self):
124 def get_package_name(self):
125 return self._package_name
125 return self._package_name
126
126
127 def set_package_name(self, package_name):
127 def set_package_name(self, package_name):
128 ''' Set package_name
128 ''' Set package_name
129
129
130 >>> docwriter = ApiDocWriter('sphinx')
130 >>> docwriter = ApiDocWriter('sphinx')
131 >>> import sphinx
131 >>> import sphinx
132 >>> docwriter.root_path == sphinx.__path__[0]
132 >>> docwriter.root_path == sphinx.__path__[0]
133 True
133 True
134 >>> docwriter.package_name = 'docutils'
134 >>> docwriter.package_name = 'docutils'
135 >>> import docutils
135 >>> import docutils
136 >>> docwriter.root_path == docutils.__path__[0]
136 >>> docwriter.root_path == docutils.__path__[0]
137 True
137 True
138 '''
138 '''
139 # It's also possible to imagine caching the module parsing here
139 # It's also possible to imagine caching the module parsing here
140 self._package_name = package_name
140 self._package_name = package_name
141 self.root_module = __import__(package_name)
141 self.root_module = __import__(package_name)
142 self.root_path = self.root_module.__path__[0]
142 self.root_path = self.root_module.__path__[0]
143 self.written_modules = None
143 self.written_modules = None
144
144
145 package_name = property(get_package_name, set_package_name, None,
145 package_name = property(get_package_name, set_package_name, None,
146 'get/set package_name')
146 'get/set package_name')
147
147
148 def _uri2path(self, uri):
148 def _uri2path(self, uri):
149 ''' Convert uri to absolute filepath
149 ''' Convert uri to absolute filepath
150
150
151 Parameters
151 Parameters
152 ----------
152 ----------
153 uri : string
153 uri : string
154 URI of python module to return path for
154 URI of python module to return path for
155
155
156 Returns
156 Returns
157 -------
157 -------
158 path : None or string
158 path : None or string
159 Returns None if there is no valid path for this URI
159 Returns None if there is no valid path for this URI
160 Otherwise returns absolute file system path for URI
160 Otherwise returns absolute file system path for URI
161
161
162 Examples
162 Examples
163 --------
163 --------
164 >>> docwriter = ApiDocWriter('sphinx')
164 >>> docwriter = ApiDocWriter('sphinx')
165 >>> import sphinx
165 >>> import sphinx
166 >>> modpath = sphinx.__path__[0]
166 >>> modpath = sphinx.__path__[0]
167 >>> res = docwriter._uri2path('sphinx.builder')
167 >>> res = docwriter._uri2path('sphinx.builder')
168 >>> res == os.path.join(modpath, 'builder.py')
168 >>> res == os.path.join(modpath, 'builder.py')
169 True
169 True
170 >>> res = docwriter._uri2path('sphinx')
170 >>> res = docwriter._uri2path('sphinx')
171 >>> res == os.path.join(modpath, '__init__.py')
171 >>> res == os.path.join(modpath, '__init__.py')
172 True
172 True
173 >>> docwriter._uri2path('sphinx.does_not_exist')
173 >>> docwriter._uri2path('sphinx.does_not_exist')
174
174
175 '''
175 '''
176 if uri == self.package_name:
176 if uri == self.package_name:
177 return os.path.join(self.root_path, '__init__.py')
177 return os.path.join(self.root_path, '__init__.py')
178 path = uri.replace('.', os.path.sep)
178 path = uri.replace('.', os.path.sep)
179 path = path.replace(self.package_name + os.path.sep, '')
179 path = path.replace(self.package_name + os.path.sep, '')
180 path = os.path.join(self.root_path, path)
180 path = os.path.join(self.root_path, path)
181 # XXX maybe check for extensions as well?
181 # XXX maybe check for extensions as well?
182 if os.path.exists(path + '.py'): # file
182 if os.path.exists(path + '.py'): # file
183 path += '.py'
183 path += '.py'
184 elif os.path.exists(os.path.join(path, '__init__.py')):
184 elif os.path.exists(os.path.join(path, '__init__.py')):
185 path = os.path.join(path, '__init__.py')
185 path = os.path.join(path, '__init__.py')
186 else:
186 else:
187 return None
187 return None
188 return path
188 return path
189
189
190 def _path2uri(self, dirpath):
190 def _path2uri(self, dirpath):
191 ''' Convert directory path to uri '''
191 ''' Convert directory path to uri '''
192 relpath = dirpath.replace(self.root_path, self.package_name)
192 relpath = dirpath.replace(self.root_path, self.package_name)
193 if relpath.startswith(os.path.sep):
193 if relpath.startswith(os.path.sep):
194 relpath = relpath[1:]
194 relpath = relpath[1:]
195 return relpath.replace(os.path.sep, '.')
195 return relpath.replace(os.path.sep, '.')
196
196
197 def _parse_module(self, uri):
197 def _parse_module(self, uri):
198 ''' Parse module defined in *uri* '''
198 ''' Parse module defined in *uri* '''
199 filename = self._uri2path(uri)
199 filename = self._uri2path(uri)
200 if filename is None:
200 if filename is None:
201 # nothing that we could handle here.
201 # nothing that we could handle here.
202 return ([],[])
202 return ([],[])
203 with open(filename, 'rb') as f:
203 with open(filename, 'rb') as f:
204 mod = ast.parse(f.read())
204 mod = ast.parse(f.read())
205 return FuncClsScanner().scan(mod)
205 return FuncClsScanner().scan(mod)
206
206
207 def generate_api_doc(self, uri):
207 def generate_api_doc(self, uri):
208 '''Make autodoc documentation template string for a module
208 '''Make autodoc documentation template string for a module
209
209
210 Parameters
210 Parameters
211 ----------
211 ----------
212 uri : string
212 uri : string
213 python location of module - e.g 'sphinx.builder'
213 python location of module - e.g 'sphinx.builder'
214
214
215 Returns
215 Returns
216 -------
216 -------
217 S : string
217 S : string
218 Contents of API doc
218 Contents of API doc
219 '''
219 '''
220 # get the names of all classes and functions
220 # get the names of all classes and functions
221 functions, classes = self._parse_module(uri)
221 functions, classes = self._parse_module(uri)
222 if not len(functions) and not len(classes):
222 if not len(functions) and not len(classes):
223 #print ('WARNING: Empty -', uri) # dbg
223 #print ('WARNING: Empty -', uri) # dbg
224 return ''
224 return ''
225
225
226 # Make a shorter version of the uri that omits the package name for
226 # Make a shorter version of the uri that omits the package name for
227 # titles
227 # titles
228 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
228 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
229
229
230 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
230 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
231
231
232 # Set the chapter title to read 'Module:' for all modules except for the
232 # Set the chapter title to read 'Module:' for all modules except for the
233 # main packages
233 # main packages
234 if '.' in uri:
234 if '.' in uri:
235 chap_title = 'Module: :mod:`' + uri_short + '`'
235 chap_title = 'Module: :mod:`' + uri_short + '`'
236 else:
236 else:
237 chap_title = ':mod:`' + uri_short + '`'
237 chap_title = ':mod:`' + uri_short + '`'
238 ad += chap_title + '\n' + self.rst_section_levels[1] * len(chap_title)
238 ad += chap_title + '\n' + self.rst_section_levels[1] * len(chap_title)
239
239
240 ad += '\n.. automodule:: ' + uri + '\n'
240 ad += '\n.. automodule:: ' + uri + '\n'
241 ad += '\n.. currentmodule:: ' + uri + '\n'
241 ad += '\n.. currentmodule:: ' + uri + '\n'
242
242
243 if classes:
243 if classes:
244 subhead = str(len(classes)) + (' Classes' if len(classes) > 1 else ' Class')
244 subhead = str(len(classes)) + (' Classes' if len(classes) > 1 else ' Class')
245 ad += '\n'+ subhead + '\n' + \
245 ad += '\n'+ subhead + '\n' + \
246 self.rst_section_levels[2] * len(subhead) + '\n'
246 self.rst_section_levels[2] * len(subhead) + '\n'
247
247
248 for c in classes:
248 for c in classes:
249 ad += '\n.. autoclass:: ' + c.name + '\n'
249 ad += '\n.. autoclass:: ' + c.name + '\n'
250 # must NOT exclude from index to keep cross-refs working
250 # must NOT exclude from index to keep cross-refs working
251 ad += ' :members:\n' \
251 ad += ' :members:\n' \
252 ' :show-inheritance:\n'
252 ' :show-inheritance:\n'
253 if c.has_init:
253 if c.has_init:
254 ad += '\n .. automethod:: __init__\n'
254 ad += '\n .. automethod:: __init__\n'
255
255
256 if functions:
256 if functions:
257 subhead = str(len(functions)) + (' Functions' if len(functions) > 1 else ' Function')
257 subhead = str(len(functions)) + (' Functions' if len(functions) > 1 else ' Function')
258 ad += '\n'+ subhead + '\n' + \
258 ad += '\n'+ subhead + '\n' + \
259 self.rst_section_levels[2] * len(subhead) + '\n'
259 self.rst_section_levels[2] * len(subhead) + '\n'
260 for f in functions:
260 for f in functions:
261 # must NOT exclude from index to keep cross-refs working
261 # must NOT exclude from index to keep cross-refs working
262 ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
262 ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
263 return ad
263 return ad
264
264
265 def _survives_exclude(self, matchstr, match_type):
265 def _survives_exclude(self, matchstr, match_type):
266 ''' Returns True if *matchstr* does not match patterns
266 ''' Returns True if *matchstr* does not match patterns
267
267
268 ``self.package_name`` removed from front of string if present
268 ``self.package_name`` removed from front of string if present
269
269
270 Examples
270 Examples
271 --------
271 --------
272 >>> dw = ApiDocWriter('sphinx')
272 >>> dw = ApiDocWriter('sphinx')
273 >>> dw._survives_exclude('sphinx.okpkg', 'package')
273 >>> dw._survives_exclude('sphinx.okpkg', 'package')
274 True
274 True
275 >>> dw.package_skip_patterns.append('^\\.badpkg$')
275 >>> dw.package_skip_patterns.append('^\\.badpkg$')
276 >>> dw._survives_exclude('sphinx.badpkg', 'package')
276 >>> dw._survives_exclude('sphinx.badpkg', 'package')
277 False
277 False
278 >>> dw._survives_exclude('sphinx.badpkg', 'module')
278 >>> dw._survives_exclude('sphinx.badpkg', 'module')
279 True
279 True
280 >>> dw._survives_exclude('sphinx.badmod', 'module')
280 >>> dw._survives_exclude('sphinx.badmod', 'module')
281 True
281 True
282 >>> dw.module_skip_patterns.append('^\\.badmod$')
282 >>> dw.module_skip_patterns.append('^\\.badmod$')
283 >>> dw._survives_exclude('sphinx.badmod', 'module')
283 >>> dw._survives_exclude('sphinx.badmod', 'module')
284 False
284 False
285 '''
285 '''
286 if match_type == 'module':
286 if match_type == 'module':
287 patterns = self.module_skip_patterns
287 patterns = self.module_skip_patterns
288 elif match_type == 'package':
288 elif match_type == 'package':
289 patterns = self.package_skip_patterns
289 patterns = self.package_skip_patterns
290 else:
290 else:
291 raise ValueError('Cannot interpret match type "%s"'
291 raise ValueError('Cannot interpret match type "%s"'
292 % match_type)
292 % match_type)
293 # Match to URI without package name
293 # Match to URI without package name
294 L = len(self.package_name)
294 L = len(self.package_name)
295 if matchstr[:L] == self.package_name:
295 if matchstr[:L] == self.package_name:
296 matchstr = matchstr[L:]
296 matchstr = matchstr[L:]
297 for pat in patterns:
297 for pat in patterns:
298 try:
298 try:
299 pat.search
299 pat.search
300 except AttributeError:
300 except AttributeError:
301 pat = re.compile(pat)
301 pat = re.compile(pat)
302 if pat.search(matchstr):
302 if pat.search(matchstr):
303 return False
303 return False
304 return True
304 return True
305
305
306 def discover_modules(self):
306 def discover_modules(self):
307 ''' Return module sequence discovered from ``self.package_name``
307 ''' Return module sequence discovered from ``self.package_name``
308
308
309
309
310 Parameters
310 Parameters
311 ----------
311 ----------
312 None
312 None
313
313
314 Returns
314 Returns
315 -------
315 -------
316 mods : sequence
316 mods : sequence
317 Sequence of module names within ``self.package_name``
317 Sequence of module names within ``self.package_name``
318
318
319 Examples
319 Examples
320 --------
320 --------
321 >>> dw = ApiDocWriter('sphinx')
321 >>> dw = ApiDocWriter('sphinx')
322 >>> mods = dw.discover_modules()
322 >>> mods = dw.discover_modules()
323 >>> 'sphinx.util' in mods
323 >>> 'sphinx.util' in mods
324 True
324 True
325 >>> dw.package_skip_patterns.append('\.util$')
325 >>> dw.package_skip_patterns.append('\.util$')
326 >>> 'sphinx.util' in dw.discover_modules()
326 >>> 'sphinx.util' in dw.discover_modules()
327 False
327 False
328 >>>
328 >>>
329 '''
329 '''
330 modules = [self.package_name]
330 modules = [self.package_name]
331 # raw directory parsing
331 # raw directory parsing
332 for dirpath, dirnames, filenames in os.walk(self.root_path):
332 for dirpath, dirnames, filenames in os.walk(self.root_path):
333 # Check directory names for packages
333 # Check directory names for packages
334 root_uri = self._path2uri(os.path.join(self.root_path,
334 root_uri = self._path2uri(os.path.join(self.root_path,
335 dirpath))
335 dirpath))
336 for dirname in dirnames[:]: # copy list - we modify inplace
336 for dirname in dirnames[:]: # copy list - we modify inplace
337 package_uri = '.'.join((root_uri, dirname))
337 package_uri = '.'.join((root_uri, dirname))
338 if (self._uri2path(package_uri) and
338 if (self._uri2path(package_uri) and
339 self._survives_exclude(package_uri, 'package')):
339 self._survives_exclude(package_uri, 'package')):
340 modules.append(package_uri)
340 modules.append(package_uri)
341 else:
341 else:
342 dirnames.remove(dirname)
342 dirnames.remove(dirname)
343 # Check filenames for modules
343 # Check filenames for modules
344 for filename in filenames:
344 for filename in filenames:
345 module_name = filename[:-3]
345 module_name = filename[:-3]
346 module_uri = '.'.join((root_uri, module_name))
346 module_uri = '.'.join((root_uri, module_name))
347 if (self._uri2path(module_uri) and
347 if (self._uri2path(module_uri) and
348 self._survives_exclude(module_uri, 'module')):
348 self._survives_exclude(module_uri, 'module')):
349 modules.append(module_uri)
349 modules.append(module_uri)
350 return sorted(modules)
350 return sorted(modules)
351
351
352 def write_modules_api(self, modules,outdir):
352 def write_modules_api(self, modules,outdir):
353 # write the list
353 # write the list
354 written_modules = []
354 written_modules = []
355 for m in modules:
355 for m in modules:
356 api_str = self.generate_api_doc(m)
356 api_str = self.generate_api_doc(m)
357 if not api_str:
357 if not api_str:
358 continue
358 continue
359 # write out to file
359 # write out to file
360 outfile = os.path.join(outdir,
360 outfile = os.path.join(outdir,
361 m + self.rst_extension)
361 m + self.rst_extension)
362 fileobj = open(outfile, 'wt')
362 fileobj = open(outfile, 'wt')
363 fileobj.write(api_str)
363 fileobj.write(api_str)
364 fileobj.close()
364 fileobj.close()
365 written_modules.append(m)
365 written_modules.append(m)
366 self.written_modules = written_modules
366 self.written_modules = written_modules
367
367
368 def write_api_docs(self, outdir):
368 def write_api_docs(self, outdir):
369 """Generate API reST files.
369 """Generate API reST files.
370
370
371 Parameters
371 Parameters
372 ----------
372 ----------
373 outdir : string
373 outdir : string
374 Directory name in which to store files
374 Directory name in which to store files
375 We create automatic filenames for each module
375 We create automatic filenames for each module
376
376
377 Returns
377 Returns
378 -------
378 -------
379 None
379 None
380
380
381 Notes
381 Notes
382 -----
382 -----
383 Sets self.written_modules to list of written modules
383 Sets self.written_modules to list of written modules
384 """
384 """
385 if not os.path.exists(outdir):
385 if not os.path.exists(outdir):
386 os.mkdir(outdir)
386 os.mkdir(outdir)
387 # compose list of modules
387 # compose list of modules
388 modules = self.discover_modules()
388 modules = self.discover_modules()
389 self.write_modules_api(modules,outdir)
389 self.write_modules_api(modules,outdir)
390
390
391 def write_index(self, outdir, path='gen.rst', relative_to=None):
391 def write_index(self, outdir, path='gen.rst', relative_to=None):
392 """Make a reST API index file from written files
392 """Make a reST API index file from written files
393
393
394 Parameters
394 Parameters
395 ----------
395 ----------
396 outdir : string
396 outdir : string
397 Directory to which to write generated index file
397 Directory to which to write generated index file
398 path : string
398 path : string
399 Filename to write index to
399 Filename to write index to
400 relative_to : string
400 relative_to : string
401 path to which written filenames are relative. This
401 path to which written filenames are relative. This
402 component of the written file path will be removed from
402 component of the written file path will be removed from
403 outdir, in the generated index. Default is None, meaning,
403 outdir, in the generated index. Default is None, meaning,
404 leave path as it is.
404 leave path as it is.
405 """
405 """
406 if self.written_modules is None:
406 if self.written_modules is None:
407 raise ValueError('No modules written')
407 raise ValueError('No modules written')
408 # Get full filename path
408 # Get full filename path
409 path = os.path.join(outdir, path)
409 path = os.path.join(outdir, path)
410 # Path written into index is relative to rootpath
410 # Path written into index is relative to rootpath
411 if relative_to is not None:
411 if relative_to is not None:
412 relpath = outdir.replace(relative_to + os.path.sep, '')
412 relpath = outdir.replace(relative_to + os.path.sep, '')
413 else:
413 else:
414 relpath = outdir
414 relpath = outdir
415 idx = open(path,'wt')
415 idx = open(path,'wt')
416 w = idx.write
416 w = idx.write
417 w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
417 w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
418 w('.. toctree::\n\n')
418 w('.. autosummary::\n'
419 for f in self.written_modules:
419 ' :toctree: %s\n\n' % relpath)
420 w(' %s\n' % os.path.join(relpath,f))
420 for mod in self.written_modules:
421 w(' %s\n' % mod)
421 idx.close()
422 idx.close()
General Comments 0
You need to be logged in to leave comments. Login now