##// END OF EJS Templates
Python 3 print functions...
MinRK -
Show More
@@ -1,66 +1,66
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Script to auto-generate our API docs.
2 """Script to auto-generate our API docs.
3 """
3 """
4 # stdlib imports
4 # stdlib imports
5 import os
5 import os
6 import sys
6 import sys
7
7
8 # local imports
8 # local imports
9 sys.path.append(os.path.abspath('sphinxext'))
9 sys.path.append(os.path.abspath('sphinxext'))
10 from apigen import ApiDocWriter
10 from apigen import ApiDocWriter
11
11
12 #*****************************************************************************
12 #*****************************************************************************
13 if __name__ == '__main__':
13 if __name__ == '__main__':
14 pjoin = os.path.join
14 pjoin = os.path.join
15 package = 'IPython'
15 package = 'IPython'
16 outdir = pjoin('source','api','generated')
16 outdir = pjoin('source','api','generated')
17 docwriter = ApiDocWriter(package,rst_extension='.txt')
17 docwriter = ApiDocWriter(package,rst_extension='.txt')
18 # You have to escape the . here because . is a special char for regexps.
18 # You have to escape the . here because . is a special char for regexps.
19 # You must do make clean if you change this!
19 # You must do make clean if you change this!
20 docwriter.package_skip_patterns += [r'\.fixes$',
20 docwriter.package_skip_patterns += [r'\.fixes$',
21 r'\.external$',
21 r'\.external$',
22 r'\.extensions',
22 r'\.extensions',
23 r'\.kernel\.config',
23 r'\.kernel\.config',
24 r'\.attic',
24 r'\.attic',
25 r'\.quarantine',
25 r'\.quarantine',
26 r'\.deathrow',
26 r'\.deathrow',
27 r'\.config\.default',
27 r'\.config\.default',
28 r'\.config\.profile',
28 r'\.config\.profile',
29 r'\.frontend',
29 r'\.frontend',
30 r'\.gui',
30 r'\.gui',
31 r'\.kernel',
31 r'\.kernel',
32 # For now, the zmq code has
32 # For now, the zmq code has
33 # unconditional top-level code so it's
33 # unconditional top-level code so it's
34 # not import safe. This needs fixing
34 # not import safe. This needs fixing
35 r'\.zmq',
35 r'\.zmq',
36 ]
36 ]
37
37
38 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
38 docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
39 r'\.testing\.iptest',
39 r'\.testing\.iptest',
40 # Keeping these disabled is OK
40 # Keeping these disabled is OK
41 r'\.parallel\.controller\.mongodb',
41 r'\.parallel\.controller\.mongodb',
42 r'\.lib\.inputhookwx',
42 r'\.lib\.inputhookwx',
43 r'\.lib\.inputhookgtk',
43 r'\.lib\.inputhookgtk',
44 r'\.cocoa',
44 r'\.cocoa',
45 r'\.ipdoctest',
45 r'\.ipdoctest',
46 r'\.Gnuplot',
46 r'\.Gnuplot',
47 r'\.frontend\.process\.winprocess',
47 r'\.frontend\.process\.winprocess',
48 r'\.frontend',
48 r'\.frontend',
49 r'\.Shell',
49 r'\.Shell',
50 ]
50 ]
51
51
52 # If we don't have pexpect, we can't load irunner, so skip any code that
52 # If we don't have pexpect, we can't load irunner, so skip any code that
53 # depends on it
53 # depends on it
54 try:
54 try:
55 import pexpect
55 import pexpect
56 except ImportError:
56 except ImportError:
57 docwriter.module_skip_patterns += [r'\.lib\.irunner',
57 docwriter.module_skip_patterns += [r'\.lib\.irunner',
58 r'\.testing\.mkdoctests']
58 r'\.testing\.mkdoctests']
59 # Now, generate the outputs
59 # Now, generate the outputs
60 docwriter.write_api_docs(outdir)
60 docwriter.write_api_docs(outdir)
61 # Write index with .rst extension - we can include it, but Sphinx won't try
61 # Write index with .rst extension - we can include it, but Sphinx won't try
62 # to compile it
62 # to compile it
63 docwriter.write_index(outdir, 'gen.rst',
63 docwriter.write_index(outdir, 'gen.rst',
64 relative_to = pjoin('source','api')
64 relative_to = pjoin('source','api')
65 )
65 )
66 print '%d files written' % len(docwriter.written_modules)
66 print ('%d files written' % len(docwriter.written_modules))
@@ -1,411 +1,413
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
21
20 # Stdlib imports
22 # Stdlib imports
21 import ast
23 import ast
22 import os
24 import os
23 import re
25 import re
24
26
25 class Obj(object):
27 class Obj(object):
26 '''Namespace to hold arbitrary information.'''
28 '''Namespace to hold arbitrary information.'''
27 def __init__(self, **kwargs):
29 def __init__(self, **kwargs):
28 for k, v in kwargs.items():
30 for k, v in kwargs.items():
29 setattr(self, k, v)
31 setattr(self, k, v)
30
32
31 class FuncClsScanner(ast.NodeVisitor):
33 class FuncClsScanner(ast.NodeVisitor):
32 """Scan a module for top-level functions and classes.
34 """Scan a module for top-level functions and classes.
33
35
34 Skips objects with an @undoc decorator, or a name starting with '_'.
36 Skips objects with an @undoc decorator, or a name starting with '_'.
35 """
37 """
36 def __init__(self):
38 def __init__(self):
37 ast.NodeVisitor.__init__(self)
39 ast.NodeVisitor.__init__(self)
38 self.classes = []
40 self.classes = []
39 self.classes_seen = set()
41 self.classes_seen = set()
40 self.functions = []
42 self.functions = []
41
43
42 @staticmethod
44 @staticmethod
43 def has_undoc_decorator(node):
45 def has_undoc_decorator(node):
44 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
46 return any(isinstance(d, ast.Name) and d.id == 'undoc' \
45 for d in node.decorator_list)
47 for d in node.decorator_list)
46
48
47 def visit_FunctionDef(self, node):
49 def visit_FunctionDef(self, node):
48 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
50 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
49 and node.name not in self.functions:
51 and node.name not in self.functions:
50 self.functions.append(node.name)
52 self.functions.append(node.name)
51
53
52 def visit_ClassDef(self, node):
54 def visit_ClassDef(self, node):
53 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
55 if not (node.name.startswith('_') or self.has_undoc_decorator(node)) \
54 and node.name not in self.classes_seen:
56 and node.name not in self.classes_seen:
55 cls = Obj(name=node.name)
57 cls = Obj(name=node.name)
56 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
58 cls.has_init = any(isinstance(n, ast.FunctionDef) and \
57 n.name=='__init__' for n in node.body)
59 n.name=='__init__' for n in node.body)
58 self.classes.append(cls)
60 self.classes.append(cls)
59 self.classes_seen.add(node.name)
61 self.classes_seen.add(node.name)
60
62
61 def scan(self, mod):
63 def scan(self, mod):
62 self.visit(mod)
64 self.visit(mod)
63 return self.functions, self.classes
65 return self.functions, self.classes
64
66
65 # Functions and classes
67 # Functions and classes
66 class ApiDocWriter(object):
68 class ApiDocWriter(object):
67 ''' Class for automatic detection and parsing of API docs
69 ''' Class for automatic detection and parsing of API docs
68 to Sphinx-parsable reST format'''
70 to Sphinx-parsable reST format'''
69
71
70 # only separating first two levels
72 # only separating first two levels
71 rst_section_levels = ['*', '=', '-', '~', '^']
73 rst_section_levels = ['*', '=', '-', '~', '^']
72
74
73 def __init__(self,
75 def __init__(self,
74 package_name,
76 package_name,
75 rst_extension='.rst',
77 rst_extension='.rst',
76 package_skip_patterns=None,
78 package_skip_patterns=None,
77 module_skip_patterns=None,
79 module_skip_patterns=None,
78 ):
80 ):
79 ''' Initialize package for parsing
81 ''' Initialize package for parsing
80
82
81 Parameters
83 Parameters
82 ----------
84 ----------
83 package_name : string
85 package_name : string
84 Name of the top-level package. *package_name* must be the
86 Name of the top-level package. *package_name* must be the
85 name of an importable package
87 name of an importable package
86 rst_extension : string, optional
88 rst_extension : string, optional
87 Extension for reST files, default '.rst'
89 Extension for reST files, default '.rst'
88 package_skip_patterns : None or sequence of {strings, regexps}
90 package_skip_patterns : None or sequence of {strings, regexps}
89 Sequence of strings giving URIs of packages to be excluded
91 Sequence of strings giving URIs of packages to be excluded
90 Operates on the package path, starting at (including) the
92 Operates on the package path, starting at (including) the
91 first dot in the package path, after *package_name* - so,
93 first dot in the package path, after *package_name* - so,
92 if *package_name* is ``sphinx``, then ``sphinx.util`` will
94 if *package_name* is ``sphinx``, then ``sphinx.util`` will
93 result in ``.util`` being passed for earching by these
95 result in ``.util`` being passed for earching by these
94 regexps. If is None, gives default. Default is:
96 regexps. If is None, gives default. Default is:
95 ['\.tests$']
97 ['\.tests$']
96 module_skip_patterns : None or sequence
98 module_skip_patterns : None or sequence
97 Sequence of strings giving URIs of modules to be excluded
99 Sequence of strings giving URIs of modules to be excluded
98 Operates on the module name including preceding URI path,
100 Operates on the module name including preceding URI path,
99 back to the first dot after *package_name*. For example
101 back to the first dot after *package_name*. For example
100 ``sphinx.util.console`` results in the string to search of
102 ``sphinx.util.console`` results in the string to search of
101 ``.util.console``
103 ``.util.console``
102 If is None, gives default. Default is:
104 If is None, gives default. Default is:
103 ['\.setup$', '\._']
105 ['\.setup$', '\._']
104 '''
106 '''
105 if package_skip_patterns is None:
107 if package_skip_patterns is None:
106 package_skip_patterns = ['\\.tests$']
108 package_skip_patterns = ['\\.tests$']
107 if module_skip_patterns is None:
109 if module_skip_patterns is None:
108 module_skip_patterns = ['\\.setup$', '\\._']
110 module_skip_patterns = ['\\.setup$', '\\._']
109 self.package_name = package_name
111 self.package_name = package_name
110 self.rst_extension = rst_extension
112 self.rst_extension = rst_extension
111 self.package_skip_patterns = package_skip_patterns
113 self.package_skip_patterns = package_skip_patterns
112 self.module_skip_patterns = module_skip_patterns
114 self.module_skip_patterns = module_skip_patterns
113
115
114 def get_package_name(self):
116 def get_package_name(self):
115 return self._package_name
117 return self._package_name
116
118
117 def set_package_name(self, package_name):
119 def set_package_name(self, package_name):
118 ''' Set package_name
120 ''' Set package_name
119
121
120 >>> docwriter = ApiDocWriter('sphinx')
122 >>> docwriter = ApiDocWriter('sphinx')
121 >>> import sphinx
123 >>> import sphinx
122 >>> docwriter.root_path == sphinx.__path__[0]
124 >>> docwriter.root_path == sphinx.__path__[0]
123 True
125 True
124 >>> docwriter.package_name = 'docutils'
126 >>> docwriter.package_name = 'docutils'
125 >>> import docutils
127 >>> import docutils
126 >>> docwriter.root_path == docutils.__path__[0]
128 >>> docwriter.root_path == docutils.__path__[0]
127 True
129 True
128 '''
130 '''
129 # It's also possible to imagine caching the module parsing here
131 # It's also possible to imagine caching the module parsing here
130 self._package_name = package_name
132 self._package_name = package_name
131 self.root_module = __import__(package_name)
133 self.root_module = __import__(package_name)
132 self.root_path = self.root_module.__path__[0]
134 self.root_path = self.root_module.__path__[0]
133 self.written_modules = None
135 self.written_modules = None
134
136
135 package_name = property(get_package_name, set_package_name, None,
137 package_name = property(get_package_name, set_package_name, None,
136 'get/set package_name')
138 'get/set package_name')
137
139
138 def _uri2path(self, uri):
140 def _uri2path(self, uri):
139 ''' Convert uri to absolute filepath
141 ''' Convert uri to absolute filepath
140
142
141 Parameters
143 Parameters
142 ----------
144 ----------
143 uri : string
145 uri : string
144 URI of python module to return path for
146 URI of python module to return path for
145
147
146 Returns
148 Returns
147 -------
149 -------
148 path : None or string
150 path : None or string
149 Returns None if there is no valid path for this URI
151 Returns None if there is no valid path for this URI
150 Otherwise returns absolute file system path for URI
152 Otherwise returns absolute file system path for URI
151
153
152 Examples
154 Examples
153 --------
155 --------
154 >>> docwriter = ApiDocWriter('sphinx')
156 >>> docwriter = ApiDocWriter('sphinx')
155 >>> import sphinx
157 >>> import sphinx
156 >>> modpath = sphinx.__path__[0]
158 >>> modpath = sphinx.__path__[0]
157 >>> res = docwriter._uri2path('sphinx.builder')
159 >>> res = docwriter._uri2path('sphinx.builder')
158 >>> res == os.path.join(modpath, 'builder.py')
160 >>> res == os.path.join(modpath, 'builder.py')
159 True
161 True
160 >>> res = docwriter._uri2path('sphinx')
162 >>> res = docwriter._uri2path('sphinx')
161 >>> res == os.path.join(modpath, '__init__.py')
163 >>> res == os.path.join(modpath, '__init__.py')
162 True
164 True
163 >>> docwriter._uri2path('sphinx.does_not_exist')
165 >>> docwriter._uri2path('sphinx.does_not_exist')
164
166
165 '''
167 '''
166 if uri == self.package_name:
168 if uri == self.package_name:
167 return os.path.join(self.root_path, '__init__.py')
169 return os.path.join(self.root_path, '__init__.py')
168 path = uri.replace('.', os.path.sep)
170 path = uri.replace('.', os.path.sep)
169 path = path.replace(self.package_name + os.path.sep, '')
171 path = path.replace(self.package_name + os.path.sep, '')
170 path = os.path.join(self.root_path, path)
172 path = os.path.join(self.root_path, path)
171 # XXX maybe check for extensions as well?
173 # XXX maybe check for extensions as well?
172 if os.path.exists(path + '.py'): # file
174 if os.path.exists(path + '.py'): # file
173 path += '.py'
175 path += '.py'
174 elif os.path.exists(os.path.join(path, '__init__.py')):
176 elif os.path.exists(os.path.join(path, '__init__.py')):
175 path = os.path.join(path, '__init__.py')
177 path = os.path.join(path, '__init__.py')
176 else:
178 else:
177 return None
179 return None
178 return path
180 return path
179
181
180 def _path2uri(self, dirpath):
182 def _path2uri(self, dirpath):
181 ''' Convert directory path to uri '''
183 ''' Convert directory path to uri '''
182 relpath = dirpath.replace(self.root_path, self.package_name)
184 relpath = dirpath.replace(self.root_path, self.package_name)
183 if relpath.startswith(os.path.sep):
185 if relpath.startswith(os.path.sep):
184 relpath = relpath[1:]
186 relpath = relpath[1:]
185 return relpath.replace(os.path.sep, '.')
187 return relpath.replace(os.path.sep, '.')
186
188
187 def _parse_module(self, uri):
189 def _parse_module(self, uri):
188 ''' Parse module defined in *uri* '''
190 ''' Parse module defined in *uri* '''
189 filename = self._uri2path(uri)
191 filename = self._uri2path(uri)
190 if filename is None:
192 if filename is None:
191 # nothing that we could handle here.
193 # nothing that we could handle here.
192 return ([],[])
194 return ([],[])
193 with open(filename, 'rb') as f:
195 with open(filename, 'rb') as f:
194 mod = ast.parse(f.read())
196 mod = ast.parse(f.read())
195 return FuncClsScanner().scan(mod)
197 return FuncClsScanner().scan(mod)
196
198
197 def generate_api_doc(self, uri):
199 def generate_api_doc(self, uri):
198 '''Make autodoc documentation template string for a module
200 '''Make autodoc documentation template string for a module
199
201
200 Parameters
202 Parameters
201 ----------
203 ----------
202 uri : string
204 uri : string
203 python location of module - e.g 'sphinx.builder'
205 python location of module - e.g 'sphinx.builder'
204
206
205 Returns
207 Returns
206 -------
208 -------
207 S : string
209 S : string
208 Contents of API doc
210 Contents of API doc
209 '''
211 '''
210 # get the names of all classes and functions
212 # get the names of all classes and functions
211 functions, classes = self._parse_module(uri)
213 functions, classes = self._parse_module(uri)
212 if not len(functions) and not len(classes):
214 if not len(functions) and not len(classes):
213 print 'WARNING: Empty -',uri # dbg
215 print ('WARNING: Empty -', uri) # dbg
214 return ''
216 return ''
215
217
216 # Make a shorter version of the uri that omits the package name for
218 # Make a shorter version of the uri that omits the package name for
217 # titles
219 # titles
218 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
220 uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
219
221
220 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
222 ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
221
223
222 # Set the chapter title to read 'Module:' for all modules except for the
224 # Set the chapter title to read 'Module:' for all modules except for the
223 # main packages
225 # main packages
224 if '.' in uri:
226 if '.' in uri:
225 chap_title = 'Module: :mod:`' + uri_short + '`'
227 chap_title = 'Module: :mod:`' + uri_short + '`'
226 else:
228 else:
227 chap_title = ':mod:`' + uri_short + '`'
229 chap_title = ':mod:`' + uri_short + '`'
228 ad += chap_title + '\n' + self.rst_section_levels[1] * len(chap_title)
230 ad += chap_title + '\n' + self.rst_section_levels[1] * len(chap_title)
229
231
230 ad += '\n.. automodule:: ' + uri + '\n'
232 ad += '\n.. automodule:: ' + uri + '\n'
231 ad += '\n.. currentmodule:: ' + uri + '\n'
233 ad += '\n.. currentmodule:: ' + uri + '\n'
232
234
233 if classes:
235 if classes:
234 subhead = str(len(classes)) + (' Classes' if len(classes) > 1 else ' Class')
236 subhead = str(len(classes)) + (' Classes' if len(classes) > 1 else ' Class')
235 ad += '\n'+ subhead + '\n' + \
237 ad += '\n'+ subhead + '\n' + \
236 self.rst_section_levels[2] * len(subhead) + '\n'
238 self.rst_section_levels[2] * len(subhead) + '\n'
237
239
238 for c in classes:
240 for c in classes:
239 ad += '\n.. autoclass:: ' + c.name + '\n'
241 ad += '\n.. autoclass:: ' + c.name + '\n'
240 # must NOT exclude from index to keep cross-refs working
242 # must NOT exclude from index to keep cross-refs working
241 ad += ' :members:\n' \
243 ad += ' :members:\n' \
242 ' :show-inheritance:\n'
244 ' :show-inheritance:\n'
243 if c.has_init:
245 if c.has_init:
244 ad += '\n .. automethod:: __init__\n'
246 ad += '\n .. automethod:: __init__\n'
245
247
246 if functions:
248 if functions:
247 subhead = str(len(functions)) + (' Functions' if len(functions) > 1 else ' Function')
249 subhead = str(len(functions)) + (' Functions' if len(functions) > 1 else ' Function')
248 ad += '\n'+ subhead + '\n' + \
250 ad += '\n'+ subhead + '\n' + \
249 self.rst_section_levels[2] * len(subhead) + '\n'
251 self.rst_section_levels[2] * len(subhead) + '\n'
250 for f in functions:
252 for f in functions:
251 # must NOT exclude from index to keep cross-refs working
253 # must NOT exclude from index to keep cross-refs working
252 ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
254 ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
253 return ad
255 return ad
254
256
255 def _survives_exclude(self, matchstr, match_type):
257 def _survives_exclude(self, matchstr, match_type):
256 ''' Returns True if *matchstr* does not match patterns
258 ''' Returns True if *matchstr* does not match patterns
257
259
258 ``self.package_name`` removed from front of string if present
260 ``self.package_name`` removed from front of string if present
259
261
260 Examples
262 Examples
261 --------
263 --------
262 >>> dw = ApiDocWriter('sphinx')
264 >>> dw = ApiDocWriter('sphinx')
263 >>> dw._survives_exclude('sphinx.okpkg', 'package')
265 >>> dw._survives_exclude('sphinx.okpkg', 'package')
264 True
266 True
265 >>> dw.package_skip_patterns.append('^\\.badpkg$')
267 >>> dw.package_skip_patterns.append('^\\.badpkg$')
266 >>> dw._survives_exclude('sphinx.badpkg', 'package')
268 >>> dw._survives_exclude('sphinx.badpkg', 'package')
267 False
269 False
268 >>> dw._survives_exclude('sphinx.badpkg', 'module')
270 >>> dw._survives_exclude('sphinx.badpkg', 'module')
269 True
271 True
270 >>> dw._survives_exclude('sphinx.badmod', 'module')
272 >>> dw._survives_exclude('sphinx.badmod', 'module')
271 True
273 True
272 >>> dw.module_skip_patterns.append('^\\.badmod$')
274 >>> dw.module_skip_patterns.append('^\\.badmod$')
273 >>> dw._survives_exclude('sphinx.badmod', 'module')
275 >>> dw._survives_exclude('sphinx.badmod', 'module')
274 False
276 False
275 '''
277 '''
276 if match_type == 'module':
278 if match_type == 'module':
277 patterns = self.module_skip_patterns
279 patterns = self.module_skip_patterns
278 elif match_type == 'package':
280 elif match_type == 'package':
279 patterns = self.package_skip_patterns
281 patterns = self.package_skip_patterns
280 else:
282 else:
281 raise ValueError('Cannot interpret match type "%s"'
283 raise ValueError('Cannot interpret match type "%s"'
282 % match_type)
284 % match_type)
283 # Match to URI without package name
285 # Match to URI without package name
284 L = len(self.package_name)
286 L = len(self.package_name)
285 if matchstr[:L] == self.package_name:
287 if matchstr[:L] == self.package_name:
286 matchstr = matchstr[L:]
288 matchstr = matchstr[L:]
287 for pat in patterns:
289 for pat in patterns:
288 try:
290 try:
289 pat.search
291 pat.search
290 except AttributeError:
292 except AttributeError:
291 pat = re.compile(pat)
293 pat = re.compile(pat)
292 if pat.search(matchstr):
294 if pat.search(matchstr):
293 return False
295 return False
294 return True
296 return True
295
297
296 def discover_modules(self):
298 def discover_modules(self):
297 ''' Return module sequence discovered from ``self.package_name``
299 ''' Return module sequence discovered from ``self.package_name``
298
300
299
301
300 Parameters
302 Parameters
301 ----------
303 ----------
302 None
304 None
303
305
304 Returns
306 Returns
305 -------
307 -------
306 mods : sequence
308 mods : sequence
307 Sequence of module names within ``self.package_name``
309 Sequence of module names within ``self.package_name``
308
310
309 Examples
311 Examples
310 --------
312 --------
311 >>> dw = ApiDocWriter('sphinx')
313 >>> dw = ApiDocWriter('sphinx')
312 >>> mods = dw.discover_modules()
314 >>> mods = dw.discover_modules()
313 >>> 'sphinx.util' in mods
315 >>> 'sphinx.util' in mods
314 True
316 True
315 >>> dw.package_skip_patterns.append('\.util$')
317 >>> dw.package_skip_patterns.append('\.util$')
316 >>> 'sphinx.util' in dw.discover_modules()
318 >>> 'sphinx.util' in dw.discover_modules()
317 False
319 False
318 >>>
320 >>>
319 '''
321 '''
320 modules = [self.package_name]
322 modules = [self.package_name]
321 # raw directory parsing
323 # raw directory parsing
322 for dirpath, dirnames, filenames in os.walk(self.root_path):
324 for dirpath, dirnames, filenames in os.walk(self.root_path):
323 # Check directory names for packages
325 # Check directory names for packages
324 root_uri = self._path2uri(os.path.join(self.root_path,
326 root_uri = self._path2uri(os.path.join(self.root_path,
325 dirpath))
327 dirpath))
326 for dirname in dirnames[:]: # copy list - we modify inplace
328 for dirname in dirnames[:]: # copy list - we modify inplace
327 package_uri = '.'.join((root_uri, dirname))
329 package_uri = '.'.join((root_uri, dirname))
328 if (self._uri2path(package_uri) and
330 if (self._uri2path(package_uri) and
329 self._survives_exclude(package_uri, 'package')):
331 self._survives_exclude(package_uri, 'package')):
330 modules.append(package_uri)
332 modules.append(package_uri)
331 else:
333 else:
332 dirnames.remove(dirname)
334 dirnames.remove(dirname)
333 # Check filenames for modules
335 # Check filenames for modules
334 for filename in filenames:
336 for filename in filenames:
335 module_name = filename[:-3]
337 module_name = filename[:-3]
336 module_uri = '.'.join((root_uri, module_name))
338 module_uri = '.'.join((root_uri, module_name))
337 if (self._uri2path(module_uri) and
339 if (self._uri2path(module_uri) and
338 self._survives_exclude(module_uri, 'module')):
340 self._survives_exclude(module_uri, 'module')):
339 modules.append(module_uri)
341 modules.append(module_uri)
340 return sorted(modules)
342 return sorted(modules)
341
343
342 def write_modules_api(self, modules,outdir):
344 def write_modules_api(self, modules,outdir):
343 # write the list
345 # write the list
344 written_modules = []
346 written_modules = []
345 for m in modules:
347 for m in modules:
346 api_str = self.generate_api_doc(m)
348 api_str = self.generate_api_doc(m)
347 if not api_str:
349 if not api_str:
348 continue
350 continue
349 # write out to file
351 # write out to file
350 outfile = os.path.join(outdir,
352 outfile = os.path.join(outdir,
351 m + self.rst_extension)
353 m + self.rst_extension)
352 fileobj = open(outfile, 'wt')
354 fileobj = open(outfile, 'wt')
353 fileobj.write(api_str)
355 fileobj.write(api_str)
354 fileobj.close()
356 fileobj.close()
355 written_modules.append(m)
357 written_modules.append(m)
356 self.written_modules = written_modules
358 self.written_modules = written_modules
357
359
358 def write_api_docs(self, outdir):
360 def write_api_docs(self, outdir):
359 """Generate API reST files.
361 """Generate API reST files.
360
362
361 Parameters
363 Parameters
362 ----------
364 ----------
363 outdir : string
365 outdir : string
364 Directory name in which to store files
366 Directory name in which to store files
365 We create automatic filenames for each module
367 We create automatic filenames for each module
366
368
367 Returns
369 Returns
368 -------
370 -------
369 None
371 None
370
372
371 Notes
373 Notes
372 -----
374 -----
373 Sets self.written_modules to list of written modules
375 Sets self.written_modules to list of written modules
374 """
376 """
375 if not os.path.exists(outdir):
377 if not os.path.exists(outdir):
376 os.mkdir(outdir)
378 os.mkdir(outdir)
377 # compose list of modules
379 # compose list of modules
378 modules = self.discover_modules()
380 modules = self.discover_modules()
379 self.write_modules_api(modules,outdir)
381 self.write_modules_api(modules,outdir)
380
382
381 def write_index(self, outdir, path='gen.rst', relative_to=None):
383 def write_index(self, outdir, path='gen.rst', relative_to=None):
382 """Make a reST API index file from written files
384 """Make a reST API index file from written files
383
385
384 Parameters
386 Parameters
385 ----------
387 ----------
386 outdir : string
388 outdir : string
387 Directory to which to write generated index file
389 Directory to which to write generated index file
388 path : string
390 path : string
389 Filename to write index to
391 Filename to write index to
390 relative_to : string
392 relative_to : string
391 path to which written filenames are relative. This
393 path to which written filenames are relative. This
392 component of the written file path will be removed from
394 component of the written file path will be removed from
393 outdir, in the generated index. Default is None, meaning,
395 outdir, in the generated index. Default is None, meaning,
394 leave path as it is.
396 leave path as it is.
395 """
397 """
396 if self.written_modules is None:
398 if self.written_modules is None:
397 raise ValueError('No modules written')
399 raise ValueError('No modules written')
398 # Get full filename path
400 # Get full filename path
399 path = os.path.join(outdir, path)
401 path = os.path.join(outdir, path)
400 # Path written into index is relative to rootpath
402 # Path written into index is relative to rootpath
401 if relative_to is not None:
403 if relative_to is not None:
402 relpath = outdir.replace(relative_to + os.path.sep, '')
404 relpath = outdir.replace(relative_to + os.path.sep, '')
403 else:
405 else:
404 relpath = outdir
406 relpath = outdir
405 idx = open(path,'wt')
407 idx = open(path,'wt')
406 w = idx.write
408 w = idx.write
407 w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
409 w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
408 w('.. toctree::\n\n')
410 w('.. toctree::\n\n')
409 for f in self.written_modules:
411 for f in self.written_modules:
410 w(' %s\n' % os.path.join(relpath,f))
412 w(' %s\n' % os.path.join(relpath,f))
411 idx.close()
413 idx.close()
General Comments 0
You need to be logged in to leave comments. Login now