mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
1
CHANGES
1
CHANGES
@@ -74,6 +74,7 @@ Features added
|
||||
* Improve warning messages during including (refs: #4818)
|
||||
* LaTeX: separate customizability of :rst:role:`guilabel` and
|
||||
:rst:role:`menuselection` (refs: #4830)
|
||||
* Add :event:`viewcode-find-source` event to viewcode extension.
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
@@ -15,6 +15,18 @@ 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.
|
||||
|
||||
.. warning::
|
||||
|
||||
If :confval:`viewcode_import` is True,
|
||||
or if the :event:`viewcode-find-source` event does not find source code
|
||||
for the given module,
|
||||
``viewcode`` will import the modules being linked to.
|
||||
If any modules have side effects on import, these will be
|
||||
executed by ``viewcode`` when ``sphinx-build`` is run.
|
||||
|
||||
If you document scripts (as opposed to library modules), make sure their
|
||||
main routine is protected by a ``if __name__ == '__main__'`` condition.
|
||||
|
||||
This extension works only on HTML related builders like ``html``,
|
||||
``applehelp``, ``devhelp``, ``htmlhelp``, ``qthelp`` and so on except
|
||||
``singlehtml``. By default ``epub`` builder doesn't
|
||||
@@ -29,15 +41,6 @@ There is an additional config value:
|
||||
As side effects, this option
|
||||
else they produce nothing. The default is ``True``.
|
||||
|
||||
.. warning::
|
||||
|
||||
:confval:`viewcode_import` **imports** the modules to be followed real
|
||||
location. If any modules have side effects on import, these will be
|
||||
executed by ``viewcode`` when ``sphinx-build`` is run.
|
||||
|
||||
If you document scripts (as opposed to library modules), make sure their
|
||||
main routine is protected by a ``if __name__ == '__main__'`` condition.
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
.. confval:: viewcode_enable_epub
|
||||
@@ -62,3 +65,17 @@ There is an additional config value:
|
||||
Some reader's rendering result are corrupted and
|
||||
`epubcheck <https://github.com/IDPF/epubcheck>`_'s score
|
||||
becomes worse even if the reader supports.
|
||||
|
||||
.. event:: viewcode-find-source (app, modname)
|
||||
|
||||
.. versionadded:: 1.8
|
||||
|
||||
Find the source code for a module.
|
||||
An event handler for this event should return
|
||||
a tuple of the source code itself and a dictionary of tags.
|
||||
The dictionary maps the name of a class, function, attribute, etc
|
||||
to a tuple of its type, the start line number, and the end line number.
|
||||
The type should be one of "class", "def", or "other".
|
||||
|
||||
:param app: The Sphinx application object.
|
||||
:param modname: The name of the module to find source code for.
|
||||
|
||||
@@ -61,20 +61,29 @@ def doctree_read(app, doctree):
|
||||
|
||||
def has_tag(modname, fullname, docname, refname):
|
||||
entry = env._viewcode_modules.get(modname, None) # type: ignore
|
||||
try:
|
||||
analyzer = ModuleAnalyzer.for_module(modname)
|
||||
except Exception:
|
||||
env._viewcode_modules[modname] = False # type: ignore
|
||||
return
|
||||
if not isinstance(analyzer.code, text_type):
|
||||
code = analyzer.code.decode(analyzer.encoding)
|
||||
else:
|
||||
code = analyzer.code
|
||||
if entry is False:
|
||||
return
|
||||
elif entry is None or entry[0] != code:
|
||||
|
||||
code_tags = app.emit_firstresult('viewcode-find-source', modname)
|
||||
if code_tags is None:
|
||||
try:
|
||||
analyzer = ModuleAnalyzer.for_module(modname)
|
||||
except Exception:
|
||||
env._viewcode_modules[modname] = False # type: ignore
|
||||
return
|
||||
|
||||
if not isinstance(analyzer.code, text_type):
|
||||
code = analyzer.code.decode(analyzer.encoding)
|
||||
else:
|
||||
code = analyzer.code
|
||||
|
||||
analyzer.find_tags()
|
||||
entry = code, analyzer.tags, {}, refname
|
||||
tags = analyzer.tags
|
||||
else:
|
||||
code, tags = code_tags
|
||||
|
||||
if entry is None or entry[0] != code:
|
||||
entry = code, tags, {}, refname
|
||||
env._viewcode_modules[modname] = entry # type: ignore
|
||||
_, tags, used, _ = entry
|
||||
if fullname in tags:
|
||||
@@ -240,6 +249,7 @@ def setup(app):
|
||||
app.connect('missing-reference', missing_reference)
|
||||
# app.add_config_value('viewcode_include_modules', [], 'env')
|
||||
# app.add_config_value('viewcode_exclude_modules', [], 'env')
|
||||
app.add_event('viewcode-find-source')
|
||||
return {
|
||||
'version': sphinx.__display_version__,
|
||||
'env_version': 1,
|
||||
|
||||
9
tests/roots/test-ext-viewcode-find/conf.py
Normal file
9
tests/roots/test-ext-viewcode-find/conf.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
extensions = ['sphinx.ext.viewcode']
|
||||
master_doc = 'index'
|
||||
exclude_patterns = ['_build']
|
||||
viewcode_import = False
|
||||
38
tests/roots/test-ext-viewcode-find/index.rst
Normal file
38
tests/roots/test-ext-viewcode-find/index.rst
Normal file
@@ -0,0 +1,38 @@
|
||||
viewcode
|
||||
========
|
||||
|
||||
.. py:module:: not_a_package
|
||||
|
||||
.. py:function:: func1(a, b)
|
||||
|
||||
This is func1
|
||||
|
||||
.. py:function:: not_a_package.submodule.func1(a, b)
|
||||
|
||||
This is func1
|
||||
|
||||
.. py:module:: not_a_package.submodule
|
||||
|
||||
.. py:class:: Class1
|
||||
|
||||
This is Class1
|
||||
|
||||
.. py:class:: Class3
|
||||
|
||||
This is Class3
|
||||
|
||||
.. py:class:: not_a_package.submodule.Class1
|
||||
|
||||
This is Class1
|
||||
|
||||
.. literalinclude:: not_a_package/__init__.py
|
||||
:language: python
|
||||
:pyobject: func1
|
||||
|
||||
.. literalinclude:: not_a_package/submodule.py
|
||||
:language: python
|
||||
:pyobject: func1
|
||||
|
||||
.. py:attribute:: not_a_package.submodule.Class3.class_attr
|
||||
|
||||
This is the class attribute class_attr
|
||||
@@ -0,0 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .submodule import func1, Class1 # NOQA
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
submodule
|
||||
"""
|
||||
raise RuntimeError('This module should not get imported')
|
||||
|
||||
def decorator(f):
|
||||
return f
|
||||
|
||||
|
||||
@decorator
|
||||
def func1(a, b):
|
||||
"""
|
||||
this is func1
|
||||
"""
|
||||
return a, b
|
||||
|
||||
|
||||
@decorator
|
||||
class Class1(object):
|
||||
"""
|
||||
this is Class1
|
||||
"""
|
||||
|
||||
|
||||
class Class3(object):
|
||||
"""
|
||||
this is Class3
|
||||
"""
|
||||
class_attr = 42
|
||||
"""this is the class attribute class_attr"""
|
||||
@@ -3,7 +3,9 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
source_dir = os.path.abspath('.')
|
||||
if source_dir not in sys.path:
|
||||
sys.path.insert(0, source_dir)
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
|
||||
master_doc = 'index'
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
@@ -60,3 +60,46 @@ def test_linkcode(app, status, warning):
|
||||
assert 'http://foobar/js/' in stuff
|
||||
assert 'http://foobar/c/' in stuff
|
||||
assert 'http://foobar/cpp/' in stuff
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='ext-viewcode-find')
|
||||
def test_local_source_files(app, status, warning):
|
||||
def find_source(app, modname):
|
||||
if modname == 'not_a_package':
|
||||
source = (app.srcdir / 'not_a_package/__init__.py').text()
|
||||
tags = {
|
||||
'func1': ('def', 3, 3),
|
||||
'Class1': ('class', 3, 3),
|
||||
'not_a_package.submodule.func1': ('def', 3, 3),
|
||||
'not_a_package.submodule.Class1': ('class', 3, 3),
|
||||
}
|
||||
else:
|
||||
source = (app.srcdir / 'not_a_package/submodule.py').text()
|
||||
tags = {
|
||||
'not_a_package.submodule.func1': ('def', 11, 15),
|
||||
'Class1': ('class', 19, 22),
|
||||
'not_a_package.submodule.Class1': ('class', 19, 22),
|
||||
'Class3': ('class', 25, 30),
|
||||
'not_a_package.submodule.Class3.class_attr': ('other', 29, 29),
|
||||
}
|
||||
return (source, tags)
|
||||
|
||||
app.connect('viewcode-find-source', find_source)
|
||||
app.builder.build_all()
|
||||
|
||||
warnings = re.sub(r'\\+', '/', warning.getvalue())
|
||||
assert re.findall(
|
||||
r"index.rst:\d+: WARNING: Object named 'func1' not found in include " +
|
||||
r"file .*/not_a_package/__init__.py'",
|
||||
warnings
|
||||
)
|
||||
|
||||
result = (app.outdir / 'index.html').text(encoding='utf-8')
|
||||
assert result.count('href="_modules/not_a_package.html#func1"') == 1
|
||||
assert result.count('href="_modules/not_a_package.html#not_a_package.submodule.func1"') == 1
|
||||
assert result.count('href="_modules/not_a_package/submodule.html#Class1"') == 1
|
||||
assert result.count('href="_modules/not_a_package/submodule.html#Class3"') == 1
|
||||
assert result.count('href="_modules/not_a_package/submodule.html#not_a_package.submodule.Class1"') == 1
|
||||
|
||||
assert result.count('href="_modules/not_a_package/submodule.html#not_a_package.submodule.Class3.class_attr"') == 1
|
||||
assert result.count('This is the class attribute class_attr') == 1
|
||||
|
||||
Reference in New Issue
Block a user