mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
merge
This commit is contained in:
commit
07f295df7c
4
CHANGES
4
CHANGES
@ -7,6 +7,10 @@ Release 1.0 (in development)
|
|||||||
|
|
||||||
* Support for docutils 0.4 has been removed.
|
* Support for docutils 0.4 has been removed.
|
||||||
|
|
||||||
|
* Added the ``viewcode`` extension.
|
||||||
|
|
||||||
|
* Added ``html-collect-pages`` event.
|
||||||
|
|
||||||
* Added ``tab-width`` option to ``literalinclude`` directive.
|
* Added ``tab-width`` option to ``literalinclude`` directive.
|
||||||
|
|
||||||
* The ``html_sidebars`` config value can now contain patterns as
|
* The ``html_sidebars`` config value can now contain patterns as
|
||||||
|
@ -7,7 +7,7 @@ import sys, os, re
|
|||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.addons.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
|
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
|
||||||
'sphinx.ext.autosummary']
|
'sphinx.ext.autosummary', 'sphinx.ext.viewcode']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
@ -360,7 +360,15 @@ registered event handlers.
|
|||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
|
|
||||||
.. event:: page-context (app, pagename, templatename, context, doctree)
|
.. event:: html-collect-pages (app)
|
||||||
|
|
||||||
|
Emitted when the HTML builder is starting to write non-document pages. You
|
||||||
|
can add pages to write by returning an iterable from this event consisting of
|
||||||
|
``(pagename, context, templatename)``.
|
||||||
|
|
||||||
|
.. versionadded:: 1.0
|
||||||
|
|
||||||
|
.. event:: html-page-context (app, pagename, templatename, context, doctree)
|
||||||
|
|
||||||
Emitted when the HTML builder has created a context dictionary to render a
|
Emitted when the HTML builder has created a context dictionary to render a
|
||||||
template with -- this can be used to add custom elements to the context.
|
template with -- this can be used to add custom elements to the context.
|
||||||
|
19
doc/ext/viewcode.rst
Normal file
19
doc/ext/viewcode.rst
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
:mod:`sphinx.ext.viewcode` -- Add links to highlighted source code
|
||||||
|
==================================================================
|
||||||
|
|
||||||
|
.. module:: sphinx.ext.viewcode
|
||||||
|
:synopsis: Add links to a highlighted version of the source code.
|
||||||
|
.. moduleauthor:: Georg Brandl
|
||||||
|
|
||||||
|
.. versionadded:: 1.0
|
||||||
|
|
||||||
|
|
||||||
|
This extension looks at your Python object descriptions (``.. class::``,
|
||||||
|
``.. function::`` etc.) and tries to find the source files where the objects are
|
||||||
|
contained. When found, a separate HTML page will be output for each module with
|
||||||
|
a highlighted version of the source code, and a link will be added to all object
|
||||||
|
descriptions that leads to the source code of the described object. A link back
|
||||||
|
from the source to the description will also be inserted.
|
||||||
|
|
||||||
|
There are currently no configuration values for this extension; you just need to
|
||||||
|
add ``'sphinx.ext.viewcode'`` to your :confval:`extensions` value for it to work.
|
@ -52,6 +52,7 @@ These extensions are built in and can be activated by respective entries in the
|
|||||||
ext/coverage
|
ext/coverage
|
||||||
ext/todo
|
ext/todo
|
||||||
ext/extlinks
|
ext/extlinks
|
||||||
|
ext/viewcode
|
||||||
|
|
||||||
|
|
||||||
Third-party extensions
|
Third-party extensions
|
||||||
|
@ -47,6 +47,7 @@ events = {
|
|||||||
'missing-reference': 'env, node, contnode',
|
'missing-reference': 'env, node, contnode',
|
||||||
'doctree-resolved': 'doctree, docname',
|
'doctree-resolved': 'doctree, docname',
|
||||||
'env-updated': 'env',
|
'env-updated': 'env',
|
||||||
|
'html-collect-pages': 'builder',
|
||||||
'html-page-context': 'pagename, context, doctree or None',
|
'html-page-context': 'pagename, context, doctree or None',
|
||||||
'build-finished': 'exception',
|
'build-finished': 'exception',
|
||||||
}
|
}
|
||||||
|
@ -381,8 +381,12 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
def finish(self):
|
def finish(self):
|
||||||
self.info(bold('writing additional files...'), nonl=1)
|
self.info(bold('writing additional files...'), nonl=1)
|
||||||
|
|
||||||
# the global general index
|
# pages from extensions
|
||||||
|
for pagelist in self.app.emit('html-collect-pages'):
|
||||||
|
for pagename, context, template in pagelist:
|
||||||
|
self.handle_page(pagename, context, template)
|
||||||
|
|
||||||
|
# the global general index
|
||||||
if self.config.html_use_index:
|
if self.config.html_use_index:
|
||||||
# the total count of lines for each index letter, used to distribute
|
# the total count of lines for each index letter, used to distribute
|
||||||
# the entries into two columns
|
# the entries into two columns
|
||||||
|
@ -190,7 +190,7 @@ class ObjectDescription(Directive):
|
|||||||
return [strip_backslash_re.sub('', sig.strip())
|
return [strip_backslash_re.sub('', sig.strip())
|
||||||
for sig in self.arguments[0].split('\n')]
|
for sig in self.arguments[0].split('\n')]
|
||||||
|
|
||||||
def parse_signature(self, sig, signode):
|
def handle_signature(self, sig, signode):
|
||||||
"""
|
"""
|
||||||
Parse the signature *sig* into individual nodes and append them to
|
Parse the signature *sig* into individual nodes and append them to
|
||||||
*signode*. If ValueError is raised, parsing is aborted and the whole
|
*signode*. If ValueError is raised, parsing is aborted and the whole
|
||||||
@ -242,8 +242,10 @@ class ObjectDescription(Directive):
|
|||||||
signode['first'] = False
|
signode['first'] = False
|
||||||
node.append(signode)
|
node.append(signode)
|
||||||
try:
|
try:
|
||||||
# name can also be a tuple, e.g. (classname, objname)
|
# name can also be a tuple, e.g. (classname, objname);
|
||||||
name = self.parse_signature(sig, signode)
|
# this is strictly domain-specific (i.e. no assumptions may
|
||||||
|
# be made in this base class)
|
||||||
|
name = self.handle_signature(sig, signode)
|
||||||
except ValueError, err:
|
except ValueError, err:
|
||||||
# signature parsing failed
|
# signature parsing failed
|
||||||
signode.clear()
|
signode.clear()
|
||||||
|
@ -215,7 +215,8 @@ class VersionChange(Directive):
|
|||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
env.versionchanges.setdefault(node['version'], []).append(
|
env.versionchanges.setdefault(node['version'], []).append(
|
||||||
(node['type'], env.doc_read_data['docname'], self.lineno,
|
(node['type'], env.doc_read_data['docname'], self.lineno,
|
||||||
env.doc_read_data.get('py_module'),
|
# XXX: python domain specific
|
||||||
|
env.doc_read_data.get('py:module'),
|
||||||
env.doc_read_data.get('object'),
|
env.doc_read_data.get('object'),
|
||||||
node.astext()))
|
node.astext()))
|
||||||
return ret
|
return ret
|
||||||
|
@ -66,7 +66,7 @@ class CObject(ObjectDescription):
|
|||||||
else:
|
else:
|
||||||
node += tnode
|
node += tnode
|
||||||
|
|
||||||
def parse_signature(self, sig, signode):
|
def handle_signature(self, sig, signode):
|
||||||
"""Transform a C (or C++) signature into RST nodes."""
|
"""Transform a C (or C++) signature into RST nodes."""
|
||||||
# first try the function pointer signature regex, it's more specific
|
# first try the function pointer signature regex, it's more specific
|
||||||
m = c_funcptr_sig_re.match(sig)
|
m = c_funcptr_sig_re.match(sig)
|
||||||
|
@ -53,7 +53,7 @@ class PyObject(ObjectDescription):
|
|||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def parse_signature(self, sig, signode):
|
def handle_signature(self, sig, signode):
|
||||||
"""
|
"""
|
||||||
Transform a Python signature into RST nodes.
|
Transform a Python signature into RST nodes.
|
||||||
Returns (fully qualified name of the thing, classname if any).
|
Returns (fully qualified name of the thing, classname if any).
|
||||||
@ -65,37 +65,49 @@ class PyObject(ObjectDescription):
|
|||||||
m = py_sig_re.match(sig)
|
m = py_sig_re.match(sig)
|
||||||
if m is None:
|
if m is None:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
classname, name, arglist, retann = m.groups()
|
name_prefix, name, arglist, retann = m.groups()
|
||||||
|
|
||||||
currclass = self.env.doc_read_data.get('py_class')
|
# determine module and class name (if applicable), as well as full name
|
||||||
if currclass:
|
modname = self.options.get(
|
||||||
|
'module', self.env.doc_read_data.get('py:module'))
|
||||||
|
classname = self.env.doc_read_data.get('py:class')
|
||||||
|
if classname:
|
||||||
add_module = False
|
add_module = False
|
||||||
if classname and classname.startswith(currclass):
|
if name_prefix and name_prefix.startswith(classname):
|
||||||
fullname = classname + name
|
fullname = name_prefix + name
|
||||||
# class name is given again in the signature
|
# class name is given again in the signature
|
||||||
classname = classname[len(currclass):].lstrip('.')
|
name_prefix = name_prefix[len(classname):].lstrip('.')
|
||||||
elif classname:
|
elif name_prefix:
|
||||||
# class name is given in the signature, but different
|
# class name is given in the signature, but different
|
||||||
# (shouldn't happen)
|
# (shouldn't happen)
|
||||||
fullname = currclass + '.' + classname + name
|
fullname = classname + '.' + name_prefix + name
|
||||||
else:
|
else:
|
||||||
# class name is not given in the signature
|
# class name is not given in the signature
|
||||||
fullname = currclass + '.' + name
|
fullname = classname + '.' + name
|
||||||
else:
|
else:
|
||||||
add_module = True
|
add_module = True
|
||||||
fullname = classname and classname + name or name
|
if name_prefix:
|
||||||
|
classname = name_prefix.rstrip('.')
|
||||||
|
fullname = name_prefix + name
|
||||||
|
else:
|
||||||
|
classname = ''
|
||||||
|
fullname = name
|
||||||
|
|
||||||
prefix = self.get_signature_prefix(sig)
|
signode['module'] = modname
|
||||||
if prefix:
|
signode['class'] = classname
|
||||||
signode += addnodes.desc_annotation(prefix, prefix)
|
signode['fullname'] = fullname
|
||||||
|
|
||||||
if classname:
|
sig_prefix = self.get_signature_prefix(sig)
|
||||||
signode += addnodes.desc_addname(classname, classname)
|
if sig_prefix:
|
||||||
|
signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
|
||||||
|
|
||||||
|
if name_prefix:
|
||||||
|
signode += addnodes.desc_addname(name_prefix, name_prefix)
|
||||||
# exceptions are a special case, since they are documented in the
|
# exceptions are a special case, since they are documented in the
|
||||||
# 'exceptions' module.
|
# 'exceptions' module.
|
||||||
elif add_module and self.env.config.add_module_names:
|
elif add_module and self.env.config.add_module_names:
|
||||||
modname = self.options.get(
|
modname = self.options.get(
|
||||||
'module', self.env.doc_read_data.get('py_module'))
|
'module', self.env.doc_read_data.get('py:module'))
|
||||||
if modname and modname != 'exceptions':
|
if modname and modname != 'exceptions':
|
||||||
nodetext = modname + '.'
|
nodetext = modname + '.'
|
||||||
signode += addnodes.desc_addname(nodetext, nodetext)
|
signode += addnodes.desc_addname(nodetext, nodetext)
|
||||||
@ -107,7 +119,7 @@ class PyObject(ObjectDescription):
|
|||||||
signode += addnodes.desc_parameterlist()
|
signode += addnodes.desc_parameterlist()
|
||||||
if retann:
|
if retann:
|
||||||
signode += addnodes.desc_returns(retann, retann)
|
signode += addnodes.desc_returns(retann, retann)
|
||||||
return fullname, classname
|
return fullname, name_prefix
|
||||||
signode += addnodes.desc_parameterlist()
|
signode += addnodes.desc_parameterlist()
|
||||||
|
|
||||||
stack = [signode[-1]]
|
stack = [signode[-1]]
|
||||||
@ -130,7 +142,7 @@ class PyObject(ObjectDescription):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
if retann:
|
if retann:
|
||||||
signode += addnodes.desc_returns(retann, retann)
|
signode += addnodes.desc_returns(retann, retann)
|
||||||
return fullname, classname
|
return fullname, name_prefix
|
||||||
|
|
||||||
def get_index_text(self, modname, name):
|
def get_index_text(self, modname, name):
|
||||||
"""
|
"""
|
||||||
@ -140,7 +152,7 @@ class PyObject(ObjectDescription):
|
|||||||
|
|
||||||
def add_target_and_index(self, name_cls, sig, signode):
|
def add_target_and_index(self, name_cls, sig, signode):
|
||||||
modname = self.options.get(
|
modname = self.options.get(
|
||||||
'module', self.env.doc_read_data.get('py_module'))
|
'module', self.env.doc_read_data.get('py:module'))
|
||||||
fullname = (modname and modname + '.' or '') + name_cls[0]
|
fullname = (modname and modname + '.' or '') + name_cls[0]
|
||||||
# note target
|
# note target
|
||||||
if fullname not in self.state.document.ids:
|
if fullname not in self.state.document.ids:
|
||||||
@ -169,7 +181,7 @@ class PyObject(ObjectDescription):
|
|||||||
|
|
||||||
def after_content(self):
|
def after_content(self):
|
||||||
if self.clsname_set:
|
if self.clsname_set:
|
||||||
self.env.doc_read_data['py_class'] = None
|
self.env.doc_read_data['py:class'] = None
|
||||||
|
|
||||||
|
|
||||||
class PyModulelevel(PyObject):
|
class PyModulelevel(PyObject):
|
||||||
@ -214,7 +226,7 @@ class PyClasslike(PyObject):
|
|||||||
def before_content(self):
|
def before_content(self):
|
||||||
PyObject.before_content(self)
|
PyObject.before_content(self)
|
||||||
if self.names:
|
if self.names:
|
||||||
self.env.doc_read_data['py_class'] = self.names[0][0]
|
self.env.doc_read_data['py:class'] = self.names[0][0]
|
||||||
self.clsname_set = True
|
self.clsname_set = True
|
||||||
|
|
||||||
|
|
||||||
@ -292,8 +304,8 @@ class PyClassmember(PyObject):
|
|||||||
def before_content(self):
|
def before_content(self):
|
||||||
PyObject.before_content(self)
|
PyObject.before_content(self)
|
||||||
lastname = self.names and self.names[-1][1]
|
lastname = self.names and self.names[-1][1]
|
||||||
if lastname and not self.env.doc_read_data.get('py_class'):
|
if lastname and not self.env.doc_read_data.get('py:class'):
|
||||||
self.env.doc_read_data['py_class'] = lastname.strip('.')
|
self.env.doc_read_data['py:class'] = lastname.strip('.')
|
||||||
self.clsname_set = True
|
self.clsname_set = True
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +329,7 @@ class PyModule(Directive):
|
|||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
modname = self.arguments[0].strip()
|
modname = self.arguments[0].strip()
|
||||||
noindex = 'noindex' in self.options
|
noindex = 'noindex' in self.options
|
||||||
env.doc_read_data['py_module'] = modname
|
env.doc_read_data['py:module'] = modname
|
||||||
env.domaindata['py']['modules'][modname] = \
|
env.domaindata['py']['modules'][modname] = \
|
||||||
(env.docname, self.options.get('synopsis', ''),
|
(env.docname, self.options.get('synopsis', ''),
|
||||||
self.options.get('platform', ''), 'deprecated' in self.options)
|
self.options.get('platform', ''), 'deprecated' in self.options)
|
||||||
@ -361,16 +373,16 @@ class PyCurrentModule(Directive):
|
|||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
modname = self.arguments[0].strip()
|
modname = self.arguments[0].strip()
|
||||||
if modname == 'None':
|
if modname == 'None':
|
||||||
env.doc_read_data['py_module'] = None
|
env.doc_read_data['py:module'] = None
|
||||||
else:
|
else:
|
||||||
env.doc_read_data['py_module'] = modname
|
env.doc_read_data['py:module'] = modname
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
class PyXRefRole(XRefRole):
|
class PyXRefRole(XRefRole):
|
||||||
def process_link(self, env, refnode, has_explicit_title, title, target):
|
def process_link(self, env, refnode, has_explicit_title, title, target):
|
||||||
refnode['py_module'] = env.doc_read_data.get('py_module')
|
refnode['py:module'] = env.doc_read_data.get('py:module')
|
||||||
refnode['py_class'] = env.doc_read_data.get('py_class')
|
refnode['py:class'] = env.doc_read_data.get('py:class')
|
||||||
if not has_explicit_title:
|
if not has_explicit_title:
|
||||||
title = title.lstrip('.') # only has a meaning for the target
|
title = title.lstrip('.') # only has a meaning for the target
|
||||||
target = target.lstrip('~') # only has a meaning for the title
|
target = target.lstrip('~') # only has a meaning for the title
|
||||||
@ -497,8 +509,8 @@ class PythonDomain(Domain):
|
|||||||
return make_refnode(builder, fromdocname, docname,
|
return make_refnode(builder, fromdocname, docname,
|
||||||
'module-' + target, contnode, title)
|
'module-' + target, contnode, title)
|
||||||
else:
|
else:
|
||||||
modname = node.get('py_module')
|
modname = node.get('py:module')
|
||||||
clsname = node.get('py_class')
|
clsname = node.get('py:class')
|
||||||
searchorder = node.hasattr('refspecific') and 1 or 0
|
searchorder = node.hasattr('refspecific') and 1 or 0
|
||||||
name, obj = self.find_obj(env, modname, clsname,
|
name, obj = self.find_obj(env, modname, clsname,
|
||||||
target, typ, searchorder)
|
target, typ, searchorder)
|
||||||
|
@ -35,7 +35,7 @@ class GenericObject(ObjectDescription):
|
|||||||
indextemplate = ''
|
indextemplate = ''
|
||||||
parse_node = None
|
parse_node = None
|
||||||
|
|
||||||
def parse_signature(self, sig, signode):
|
def handle_signature(self, sig, signode):
|
||||||
if self.parse_node:
|
if self.parse_node:
|
||||||
name = self.parse_node(self.env, sig, signode)
|
name = self.parse_node(self.env, sig, signode)
|
||||||
else:
|
else:
|
||||||
@ -146,7 +146,7 @@ class Cmdoption(ObjectDescription):
|
|||||||
|
|
||||||
def add_target_and_index(self, name, sig, signode):
|
def add_target_and_index(self, name, sig, signode):
|
||||||
targetname = name.replace('/', '-')
|
targetname = name.replace('/', '-')
|
||||||
currprogram = self.env.doc_read_data.get('std_program')
|
currprogram = self.env.doc_read_data.get('std:program')
|
||||||
if currprogram:
|
if currprogram:
|
||||||
targetname = '-' + currprogram + targetname
|
targetname = '-' + currprogram + targetname
|
||||||
targetname = 'cmdoption' + targetname
|
targetname = 'cmdoption' + targetname
|
||||||
@ -175,9 +175,9 @@ class Program(Directive):
|
|||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
program = ws_re.sub('-', self.arguments[0].strip())
|
program = ws_re.sub('-', self.arguments[0].strip())
|
||||||
if program == 'None':
|
if program == 'None':
|
||||||
env.doc_read_data['std_program'] = None
|
env.doc_read_data['std:program'] = None
|
||||||
else:
|
else:
|
||||||
env.doc_read_data['std_program'] = program
|
env.doc_read_data['std:program'] = program
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ class OptionXRefRole(XRefRole):
|
|||||||
innernodeclass = addnodes.literal_emphasis
|
innernodeclass = addnodes.literal_emphasis
|
||||||
|
|
||||||
def process_link(self, env, refnode, has_explicit_title, title, target):
|
def process_link(self, env, refnode, has_explicit_title, title, target):
|
||||||
program = env.doc_read_data.get('std_program')
|
program = env.doc_read_data.get('std:program')
|
||||||
if not has_explicit_title:
|
if not has_explicit_title:
|
||||||
if ' ' in title and not (title.startswith('/') or
|
if ' ' in title and not (title.startswith('/') or
|
||||||
title.startswith('-')):
|
title.startswith('-')):
|
||||||
|
@ -713,12 +713,12 @@ class BuildEnvironment:
|
|||||||
@property
|
@property
|
||||||
def currmodule(self):
|
def currmodule(self):
|
||||||
"""Backwards compatible alias."""
|
"""Backwards compatible alias."""
|
||||||
return self.doc_read_data.get('py_module')
|
return self.doc_read_data.get('py:module')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def currclass(self):
|
def currclass(self):
|
||||||
"""Backwards compatible alias."""
|
"""Backwards compatible alias."""
|
||||||
return self.doc_read_data.get('py_class')
|
return self.doc_read_data.get('py:class')
|
||||||
|
|
||||||
def new_serialno(self, category=''):
|
def new_serialno(self, category=''):
|
||||||
"""Return a serial number, e.g. for index entry targets."""
|
"""Return a serial number, e.g. for index entry targets."""
|
||||||
|
@ -46,3 +46,11 @@ class ExtensionError(SphinxError):
|
|||||||
|
|
||||||
class ThemeError(SphinxError):
|
class ThemeError(SphinxError):
|
||||||
category = 'Theme error'
|
category = 'Theme error'
|
||||||
|
|
||||||
|
|
||||||
|
class PycodeError(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
res = self.args[0]
|
||||||
|
if len(self.args) > 1:
|
||||||
|
res += ' (exception was: %r)' % self.args[1]
|
||||||
|
return res
|
||||||
|
@ -562,9 +562,9 @@ class Documenter(object):
|
|||||||
do all members, else those given by *self.options.members*.
|
do all members, else those given by *self.options.members*.
|
||||||
"""
|
"""
|
||||||
# set current namespace for finding members
|
# set current namespace for finding members
|
||||||
self.env.doc_read_data['autodoc_module'] = self.modname
|
self.env.doc_read_data['autodoc:module'] = self.modname
|
||||||
if self.objpath:
|
if self.objpath:
|
||||||
self.env.doc_read_data['autodoc_class'] = self.objpath[0]
|
self.env.doc_read_data['autodoc:class'] = self.objpath[0]
|
||||||
|
|
||||||
want_all = all_members or self.options.inherited_members or \
|
want_all = all_members or self.options.inherited_members or \
|
||||||
self.options.members is ALL
|
self.options.members is ALL
|
||||||
@ -605,8 +605,8 @@ class Documenter(object):
|
|||||||
check_module=members_check_module)
|
check_module=members_check_module)
|
||||||
|
|
||||||
# reset current objects
|
# reset current objects
|
||||||
self.env.doc_read_data['autodoc_module'] = None
|
self.env.doc_read_data['autodoc:module'] = None
|
||||||
self.env.doc_read_data['autodoc_class'] = None
|
self.env.doc_read_data['autodoc:class'] = None
|
||||||
|
|
||||||
def generate(self, more_content=None, real_modname=None,
|
def generate(self, more_content=None, real_modname=None,
|
||||||
check_module=False, all_members=False):
|
check_module=False, all_members=False):
|
||||||
@ -764,10 +764,10 @@ class ModuleLevelDocumenter(Documenter):
|
|||||||
else:
|
else:
|
||||||
# if documenting a toplevel object without explicit module,
|
# if documenting a toplevel object without explicit module,
|
||||||
# it can be contained in another auto directive ...
|
# it can be contained in another auto directive ...
|
||||||
modname = self.env.doc_read_data.get('autodoc_module')
|
modname = self.env.doc_read_data.get('autodoc:module')
|
||||||
# ... or in the scope of a module directive
|
# ... or in the scope of a module directive
|
||||||
if not modname:
|
if not modname:
|
||||||
modname = self.env.doc_read_data.get('py_module')
|
modname = self.env.doc_read_data.get('py:module')
|
||||||
# ... else, it stays None, which means invalid
|
# ... else, it stays None, which means invalid
|
||||||
return modname, parents + [base]
|
return modname, parents + [base]
|
||||||
|
|
||||||
@ -786,10 +786,10 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
# if documenting a class-level object without path,
|
# if documenting a class-level object without path,
|
||||||
# there must be a current class, either from a parent
|
# there must be a current class, either from a parent
|
||||||
# auto directive ...
|
# auto directive ...
|
||||||
mod_cls = self.env.doc_read_data.get('autodoc_class')
|
mod_cls = self.env.doc_read_data.get('autodoc:class')
|
||||||
# ... or from a class directive
|
# ... or from a class directive
|
||||||
if mod_cls is None:
|
if mod_cls is None:
|
||||||
mod_cls = self.env.doc_read_data.get('py_class')
|
mod_cls = self.env.doc_read_data.get('py:class')
|
||||||
# ... if still None, there's no way to know
|
# ... if still None, there's no way to know
|
||||||
if mod_cls is None:
|
if mod_cls is None:
|
||||||
return None, []
|
return None, []
|
||||||
@ -797,9 +797,9 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
parents = [cls]
|
parents = [cls]
|
||||||
# if the module name is still missing, get it like above
|
# if the module name is still missing, get it like above
|
||||||
if not modname:
|
if not modname:
|
||||||
modname = self.env.doc_read_data.get('autodoc_module')
|
modname = self.env.doc_read_data.get('autodoc:module')
|
||||||
if not modname:
|
if not modname:
|
||||||
modname = self.env.doc_read_data.get('py_module')
|
modname = self.env.doc_read_data.get('py:module')
|
||||||
# ... else, it stays None, which means invalid
|
# ... else, it stays None, which means invalid
|
||||||
return modname, parents + [base]
|
return modname, parents + [base]
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ class Autosummary(Directive):
|
|||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
|
|
||||||
prefixes = ['']
|
prefixes = ['']
|
||||||
currmodule = env.doc_read_data.get('py_module')
|
currmodule = env.doc_read_data.get('py:module')
|
||||||
if currmodule:
|
if currmodule:
|
||||||
prefixes.insert(0, currmodule)
|
prefixes.insert(0, currmodule)
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ class InheritanceDiagram(Directive):
|
|||||||
# Create a graph starting with the list of classes
|
# Create a graph starting with the list of classes
|
||||||
try:
|
try:
|
||||||
graph = InheritanceGraph(class_names,
|
graph = InheritanceGraph(class_names,
|
||||||
env.doc_read_data.get('py_module'))
|
env.doc_read_data.get('py:module'))
|
||||||
except InheritanceException, err:
|
except InheritanceException, err:
|
||||||
return [node.document.reporter.warning(err.args[0],
|
return [node.document.reporter.warning(err.args[0],
|
||||||
line=self.lineno)]
|
line=self.lineno)]
|
||||||
|
162
sphinx/ext/viewcode.py
Normal file
162
sphinx/ext/viewcode.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
sphinx.ext.viewcode
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Add links to module code in Python object descriptions.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.util import make_refnode
|
||||||
|
from sphinx.pycode import ModuleAnalyzer
|
||||||
|
|
||||||
|
|
||||||
|
def doctree_read(app, doctree):
|
||||||
|
env = app.builder.env
|
||||||
|
if not hasattr(env, '_viewcode_modules'):
|
||||||
|
env._viewcode_modules = {}
|
||||||
|
|
||||||
|
def has_tag(modname, fullname, docname):
|
||||||
|
entry = env._viewcode_modules.get(modname, None)
|
||||||
|
if entry is None:
|
||||||
|
try:
|
||||||
|
analyzer = ModuleAnalyzer.for_module(modname)
|
||||||
|
except Exception:
|
||||||
|
env._viewcode_modules[modname] = False
|
||||||
|
return
|
||||||
|
analyzer.find_tags()
|
||||||
|
entry = analyzer.code, analyzer.tags, {}
|
||||||
|
env._viewcode_modules[modname] = entry
|
||||||
|
elif entry is False:
|
||||||
|
return
|
||||||
|
code, tags, used = entry
|
||||||
|
if fullname in tags:
|
||||||
|
used[fullname] = docname
|
||||||
|
return True
|
||||||
|
|
||||||
|
for objnode in doctree.traverse(addnodes.desc):
|
||||||
|
if objnode['domain'] != 'py':
|
||||||
|
continue
|
||||||
|
names = set()
|
||||||
|
for signode in objnode.traverse(addnodes.desc_signature):
|
||||||
|
modname = signode['module']
|
||||||
|
if not modname:
|
||||||
|
continue
|
||||||
|
fullname = signode['fullname']
|
||||||
|
if not has_tag(modname, fullname, env.docname):
|
||||||
|
continue
|
||||||
|
if fullname in names:
|
||||||
|
# only one link per name, please
|
||||||
|
continue
|
||||||
|
names.add(fullname)
|
||||||
|
pagename = '_modules/' + modname.replace('.', '/')
|
||||||
|
onlynode = addnodes.only(expr='html')
|
||||||
|
onlynode += addnodes.pending_xref(
|
||||||
|
'', reftype='viewcode', refdomain='std', refexplicit=False,
|
||||||
|
reftarget=pagename, refid=fullname,
|
||||||
|
refdoc=env.docname)
|
||||||
|
onlynode[0] += nodes.inline('', _('[source]'),
|
||||||
|
classes=['viewcode-link'])
|
||||||
|
signode += onlynode
|
||||||
|
|
||||||
|
|
||||||
|
def missing_reference(app, env, node, contnode):
|
||||||
|
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||||
|
if node['reftype'] == 'viewcode':
|
||||||
|
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||||
|
node['refid'], contnode)
|
||||||
|
|
||||||
|
|
||||||
|
def collect_pages(app):
|
||||||
|
env = app.builder.env
|
||||||
|
if not hasattr(env, '_viewcode_modules'):
|
||||||
|
return
|
||||||
|
highlighter = app.builder.highlighter
|
||||||
|
urito = app.builder.get_relative_uri
|
||||||
|
|
||||||
|
modnames = set(env._viewcode_modules)
|
||||||
|
|
||||||
|
for modname, (code, tags, used) in env._viewcode_modules.iteritems():
|
||||||
|
# construct a page name for the highlighted source
|
||||||
|
pagename = '_modules/' + modname.replace('.', '/')
|
||||||
|
# highlight the source using the builder's highlighter
|
||||||
|
highlighted = highlighter.highlight_block(code, 'python', False)
|
||||||
|
# split the code into lines
|
||||||
|
lines = highlighted.splitlines()
|
||||||
|
# split off wrap markup from the first line of the actual code
|
||||||
|
before, after = lines[0].split('<pre>')
|
||||||
|
lines[0:1] = [before + '<pre>', after]
|
||||||
|
# nothing to do for the last line; it always starts with </pre> anyway
|
||||||
|
# now that we have code lines (starting at index 1), insert anchors for
|
||||||
|
# the collected tags (HACK: this only works if the tag boundaries are
|
||||||
|
# properly nested!)
|
||||||
|
for name, docname in used.iteritems():
|
||||||
|
type, start, end = tags[name]
|
||||||
|
backlink = urito(pagename, docname) + '#' + modname + '.' + name
|
||||||
|
lines[start] = (
|
||||||
|
'<div class="viewcode-block" id="%s"><a class="viewcode-back" '
|
||||||
|
'href="%s">%s</a>' % (name, backlink, _('[docs]')) + lines[start])
|
||||||
|
lines[end - 1] += '</div>'
|
||||||
|
# try to find parents (for submodules)
|
||||||
|
parents = []
|
||||||
|
parent = modname
|
||||||
|
while '.' in parent:
|
||||||
|
parent = parent.rsplit('.', 1)[0]
|
||||||
|
if parent in modnames:
|
||||||
|
parents.append({
|
||||||
|
'link': urito(pagename, '_modules/' +
|
||||||
|
parent.replace('.', '/')),
|
||||||
|
'title': parent})
|
||||||
|
parents.append({'link': urito(pagename, '_modules/index'),
|
||||||
|
'title': _('Module code')})
|
||||||
|
parents.reverse()
|
||||||
|
# putting it all together
|
||||||
|
context = {
|
||||||
|
'parents': parents,
|
||||||
|
'title': modname,
|
||||||
|
'body': _('<h2>Source code for %s</h2>') % modname + '\n'.join(lines)
|
||||||
|
}
|
||||||
|
app.builder.info(' '+pagename, nonl=1)
|
||||||
|
yield (pagename, context, 'page.html')
|
||||||
|
|
||||||
|
if not modnames:
|
||||||
|
return
|
||||||
|
|
||||||
|
app.builder.info(' _modules/index')
|
||||||
|
html = ['\n']
|
||||||
|
# the stack logic is needed for using nested lists for submodules
|
||||||
|
stack = ['']
|
||||||
|
for modname in sorted(modnames):
|
||||||
|
if modname.startswith(stack[-1]):
|
||||||
|
stack.append(modname + '.')
|
||||||
|
html.append('<ul>')
|
||||||
|
else:
|
||||||
|
stack.pop()
|
||||||
|
while not modname.startswith(stack[-1]):
|
||||||
|
stack.pop()
|
||||||
|
html.append('</ul>')
|
||||||
|
stack.append(modname + '.')
|
||||||
|
html.append('<li><a href="%s">%s</a></li>\n' % (
|
||||||
|
urito('_modules/index', '_modules/' + modname.replace('.', '/')),
|
||||||
|
modname))
|
||||||
|
html.append('</ul>' * (len(stack) - 1))
|
||||||
|
context = {
|
||||||
|
'title': _('Overview: module code'),
|
||||||
|
'body': _('<h2>All modules for which code is available</h2>') + \
|
||||||
|
''.join(html),
|
||||||
|
}
|
||||||
|
|
||||||
|
yield ('_modules/index', context, 'page.html')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.connect('doctree-read', doctree_read)
|
||||||
|
app.connect('html-collect-pages', collect_pages)
|
||||||
|
app.connect('missing-reference', missing_reference)
|
||||||
|
#app.add_config_value('viewcode_include_modules', [], 'env')
|
||||||
|
#app.add_config_value('viewcode_exclude_modules', [], 'env')
|
@ -14,8 +14,10 @@ import sys
|
|||||||
from os import path
|
from os import path
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
from sphinx.errors import PycodeError
|
||||||
from sphinx.pycode import nodes
|
from sphinx.pycode import nodes
|
||||||
from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals
|
from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals
|
||||||
|
from sphinx.util import get_module_source
|
||||||
from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc
|
from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc
|
||||||
|
|
||||||
|
|
||||||
@ -136,14 +138,6 @@ class AttrDocVisitor(nodes.NodeVisitor):
|
|||||||
self.collected[namespace, name] = docstring
|
self.collected[namespace, name] = docstring
|
||||||
|
|
||||||
|
|
||||||
class PycodeError(Exception):
|
|
||||||
def __str__(self):
|
|
||||||
res = self.args[0]
|
|
||||||
if len(self.args) > 1:
|
|
||||||
res += ' (exception was: %r)' % self.args[1]
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleAnalyzer(object):
|
class ModuleAnalyzer(object):
|
||||||
# cache for analyzer objects -- caches both by module and file name
|
# cache for analyzer objects -- caches both by module and file name
|
||||||
cache = {}
|
cache = {}
|
||||||
@ -173,33 +167,11 @@ class ModuleAnalyzer(object):
|
|||||||
return entry
|
return entry
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if modname not in sys.modules:
|
type, source = get_module_source(modname)
|
||||||
try:
|
if type == 'string':
|
||||||
__import__(modname)
|
|
||||||
except ImportError, err:
|
|
||||||
raise PycodeError('error importing %r' % modname, err)
|
|
||||||
mod = sys.modules[modname]
|
|
||||||
if hasattr(mod, '__loader__'):
|
|
||||||
try:
|
|
||||||
source = mod.__loader__.get_source(modname)
|
|
||||||
except Exception, err:
|
|
||||||
raise PycodeError('error getting source for %r' % modname,
|
|
||||||
err)
|
|
||||||
obj = cls.for_string(source, modname)
|
obj = cls.for_string(source, modname)
|
||||||
cls.cache['module', modname] = obj
|
else:
|
||||||
return obj
|
obj = cls.for_file(source, modname)
|
||||||
filename = getattr(mod, '__file__', None)
|
|
||||||
if filename is None:
|
|
||||||
raise PycodeError('no source found for module %r' % modname)
|
|
||||||
filename = path.normpath(path.abspath(filename))
|
|
||||||
lfilename = filename.lower()
|
|
||||||
if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'):
|
|
||||||
filename = filename[:-1]
|
|
||||||
elif not lfilename.endswith('.py'):
|
|
||||||
raise PycodeError('source is not a .py file: %r' % filename)
|
|
||||||
if not path.isfile(filename):
|
|
||||||
raise PycodeError('source file is not present: %r' % filename)
|
|
||||||
obj = cls.for_file(filename, modname)
|
|
||||||
except PycodeError, err:
|
except PycodeError, err:
|
||||||
cls.cache['module', modname] = err
|
cls.cache['module', modname] = err
|
||||||
raise
|
raise
|
||||||
@ -214,6 +186,11 @@ class ModuleAnalyzer(object):
|
|||||||
# file-like object yielding source lines
|
# file-like object yielding source lines
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
|
# cache the source code as well
|
||||||
|
pos = self.source.tell()
|
||||||
|
self.code = self.source.read()
|
||||||
|
self.source.seek(pos)
|
||||||
|
|
||||||
# will be filled by tokenize()
|
# will be filled by tokenize()
|
||||||
self.tokens = None
|
self.tokens = None
|
||||||
# will be filled by parse()
|
# will be filled by parse()
|
||||||
|
@ -368,6 +368,22 @@ dl.glossary dt {
|
|||||||
margin-left: 1.5em;
|
margin-left: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.viewcode-link {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewcode-back {
|
||||||
|
float: right;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.viewcode-block:target {
|
||||||
|
background-color: #f4debf;
|
||||||
|
border: 1px solid #D5BB73;
|
||||||
|
margin: -1px -10px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
/* -- code displays --------------------------------------------------------- */
|
/* -- code displays --------------------------------------------------------- */
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
@ -508,6 +508,38 @@ def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_source(modname):
|
||||||
|
"""Try to find the source code for a module.
|
||||||
|
|
||||||
|
Can return ('file', 'filename') in which case the source is in the given
|
||||||
|
file, or ('string', 'source') which which case the source is the string.
|
||||||
|
"""
|
||||||
|
if modname not in sys.modules:
|
||||||
|
try:
|
||||||
|
__import__(modname)
|
||||||
|
except Exception, err:
|
||||||
|
raise PycodeError('error importing %r' % modname, err)
|
||||||
|
mod = sys.modules[modname]
|
||||||
|
if hasattr(mod, '__loader__'):
|
||||||
|
try:
|
||||||
|
source = mod.__loader__.get_source(modname)
|
||||||
|
except Exception, err:
|
||||||
|
raise PycodeError('error getting source for %r' % modname, err)
|
||||||
|
return 'string', source
|
||||||
|
filename = getattr(mod, '__file__', None)
|
||||||
|
if filename is None:
|
||||||
|
raise PycodeError('no source found for module %r' % modname)
|
||||||
|
filename = path.normpath(path.abspath(filename))
|
||||||
|
lfilename = filename.lower()
|
||||||
|
if lfilename.endswith('.pyo') or lfilename.endswith('.pyc'):
|
||||||
|
filename = filename[:-1]
|
||||||
|
elif not lfilename.endswith('.py'):
|
||||||
|
raise PycodeError('source is not a .py file: %r' % filename)
|
||||||
|
if not path.isfile(filename):
|
||||||
|
raise PycodeError('source file is not present: %r' % filename)
|
||||||
|
return 'file', filename
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
any = any
|
any = any
|
||||||
except NameError:
|
except NameError:
|
||||||
|
@ -97,28 +97,28 @@ def test_parse_name():
|
|||||||
verify('function', 'util.raises', ('util', ['raises'], None, None))
|
verify('function', 'util.raises', ('util', ['raises'], None, None))
|
||||||
verify('function', 'util.raises(exc) -> None',
|
verify('function', 'util.raises(exc) -> None',
|
||||||
('util', ['raises'], 'exc', 'None'))
|
('util', ['raises'], 'exc', 'None'))
|
||||||
directive.env.doc_read_data['autodoc_module'] = 'util'
|
directive.env.doc_read_data['autodoc:module'] = 'util'
|
||||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||||
del directive.env.doc_read_data['autodoc_module']
|
del directive.env.doc_read_data['autodoc:module']
|
||||||
directive.env.doc_read_data['py_module'] = 'util'
|
directive.env.doc_read_data['py:module'] = 'util'
|
||||||
verify('function', 'raises', ('util', ['raises'], None, None))
|
verify('function', 'raises', ('util', ['raises'], None, None))
|
||||||
verify('class', 'TestApp', ('util', ['TestApp'], None, None))
|
verify('class', 'TestApp', ('util', ['TestApp'], None, None))
|
||||||
|
|
||||||
# for members
|
# for members
|
||||||
directive.env.doc_read_data['py_module'] = 'foo'
|
directive.env.doc_read_data['py:module'] = 'foo'
|
||||||
verify('method', 'util.TestApp.cleanup',
|
verify('method', 'util.TestApp.cleanup',
|
||||||
('util', ['TestApp', 'cleanup'], None, None))
|
('util', ['TestApp', 'cleanup'], None, None))
|
||||||
directive.env.doc_read_data['py_module'] = 'util'
|
directive.env.doc_read_data['py:module'] = 'util'
|
||||||
directive.env.doc_read_data['py_class'] = 'Foo'
|
directive.env.doc_read_data['py:class'] = 'Foo'
|
||||||
directive.env.doc_read_data['autodoc_class'] = 'TestApp'
|
directive.env.doc_read_data['autodoc:class'] = 'TestApp'
|
||||||
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
|
verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
|
||||||
verify('method', 'TestApp.cleanup',
|
verify('method', 'TestApp.cleanup',
|
||||||
('util', ['TestApp', 'cleanup'], None, None))
|
('util', ['TestApp', 'cleanup'], None, None))
|
||||||
|
|
||||||
# and clean up
|
# and clean up
|
||||||
del directive.env.doc_read_data['py_module']
|
del directive.env.doc_read_data['py:module']
|
||||||
del directive.env.doc_read_data['py_class']
|
del directive.env.doc_read_data['py:class']
|
||||||
del directive.env.doc_read_data['autodoc_class']
|
del directive.env.doc_read_data['autodoc:class']
|
||||||
|
|
||||||
|
|
||||||
def test_format_signature():
|
def test_format_signature():
|
||||||
@ -353,7 +353,7 @@ def test_generate():
|
|||||||
'function', 'util.foobar', more_content=None)
|
'function', 'util.foobar', more_content=None)
|
||||||
|
|
||||||
# test auto and given content mixing
|
# test auto and given content mixing
|
||||||
directive.env.doc_read_data['py_module'] = 'test_autodoc'
|
directive.env.doc_read_data['py:module'] = 'test_autodoc'
|
||||||
assert_result_contains(' Function.', 'method', 'Class.meth')
|
assert_result_contains(' Function.', 'method', 'Class.meth')
|
||||||
add_content = ViewList()
|
add_content = ViewList()
|
||||||
add_content.append('Content.', '', 0)
|
add_content.append('Content.', '', 0)
|
||||||
@ -437,7 +437,7 @@ def test_generate():
|
|||||||
'attribute', 'test_autodoc.Class.descr')
|
'attribute', 'test_autodoc.Class.descr')
|
||||||
|
|
||||||
# test generation for C modules (which have no source file)
|
# test generation for C modules (which have no source file)
|
||||||
directive.env.doc_read_data['py_module'] = 'time'
|
directive.env.doc_read_data['py:module'] = 'time'
|
||||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user