summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakayuki Shimizukawa <shimizukawa+bitbucket@gmail.com>2014-08-23 11:50:18 +0900
committerTakayuki Shimizukawa <shimizukawa+bitbucket@gmail.com>2014-08-23 11:50:18 +0900
commit1e78f3eb218268d38a0d62e43d65fcd515147dee (patch)
tree23b113f4f3cdd1e49aad1a5bb2c19d5e8ac4dc6b
parent5297231779bdf9c04a2972e8d5354c1f126d953d (diff)
parent4399f695af7013f6735455b2fa1c6381b07c6f34 (diff)
downloadsphinx-1e78f3eb218268d38a0d62e43d65fcd515147dee.tar.gz
Merged in bsipocz/sphinx (pull request #275)
Adding the option of multiple inv for the same name & uri
-rw-r--r--CHANGES17
-rw-r--r--doc/config.rst4
-rw-r--r--doc/ext/viewcode.rst20
-rw-r--r--sphinx/domains/c.py45
-rw-r--r--sphinx/environment.py9
-rw-r--r--sphinx/ext/viewcode.py39
-rw-r--r--sphinx/roles.py34
-rw-r--r--sphinx/themes/basic/domainindex.html2
-rw-r--r--sphinx/themes/basic/static/basic.css_t10
-rw-r--r--sphinx/themes/basic/static/websupport.js4
-rw-r--r--sphinx/themes/default/static/default.css_t6
-rw-r--r--sphinx/themes/epub/static/epub.css12
-rw-r--r--sphinx/themes/haiku/static/haiku.css_t2
-rw-r--r--sphinx/themes/nature/static/nature.css_t2
-rw-r--r--sphinx/themes/pyramid/static/epub.css6
-rw-r--r--sphinx/themes/pyramid/static/pyramid.css_t4
-rw-r--r--sphinx/themes/scrolls/static/scrolls.css_t4
-rw-r--r--sphinx/themes/sphinxdoc/static/sphinxdoc.css_t10
-rw-r--r--sphinx/themes/traditional/static/traditional.css_t12
-rw-r--r--sphinx/util/__init__.py14
-rw-r--r--sphinx/writers/html.py14
-rw-r--r--tests/root/markup.txt2
-rw-r--r--tests/roots/test-ext-viewcode/conf.py8
-rw-r--r--tests/roots/test-ext-viewcode/index.rst29
-rw-r--r--tests/roots/test-ext-viewcode/spam/__init__.py7
-rw-r--r--tests/roots/test-ext-viewcode/spam/mod1.py15
-rw-r--r--tests/roots/test-ext-viewcode/spam/mod2.py15
-rw-r--r--tests/test_build.py35
-rw-r--r--tests/test_build_html.py39
-rw-r--r--tests/test_ext_viewcode.py43
-rw-r--r--tests/test_markup.py12
31 files changed, 387 insertions, 88 deletions
diff --git a/CHANGES b/CHANGES
index 9747c906..3123cf67 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,12 @@ Incompatible changes
* A new node, ``sphinx.addnodes.literal_strong``, has been added, for text that
should appear literally (i.e. no smart quotes) in strong font. Custom writers
will have to be adapted to handle this node.
+* PR#269, #1476: replace `<tt>` tag by `<code>`. User customized stylesheets
+ should be updated If the css contain some styles for `<tt>` tag.
+ Thanks to Takeshi Komiya.
+* #1543: :confval:`templates_path` is automatically added to
+ :confval:`exclude_patterns` to avoid reading autosummary rst templates in the
+ templates directory.
Features added
--------------
@@ -52,6 +58,7 @@ Features added
* Automatically compile ``*.mo`` files from ``*.po`` files when
:confval:`gettext_auto_build` is True (default) and ``*.po`` is newer than
``*.mo`` file.
+* #623: :mod:`~sphinx.ext.viewcode` support imported function/class aliases.
Bugs fixed
----------
@@ -196,6 +203,16 @@ Bugs fixed
* PR#268: Fix numbering section does not work at singlehtml mode. It still
ad-hoc fix because there is a issue that section IDs are conflicted.
Thanks to Takeshi Komiya.
+* PR#273, #1536: Fix RuntimeError with numbered circular toctree. Thanks to
+ Takeshi Komiya.
+* PR#274: Set its URL as a default title value if URL appears in toctree.
+ Thanks to Takeshi Komiya.
+* PR#276, #1381: :rst:role:`rfc` and :rst:role:`pep` roles support custom link
+ text. Thanks to Takeshi Komiya.
+* PR#277, #1513: highlights for function pointers in argument list of
+ :rst:dir:`c:function`. Thanks to Takeshi Komiya.
+* PR#278: Fix section entries were shown twice if toctree has been put under
+ only directive. Thanks to Takeshi Komiya.
Documentation
-------------
diff --git a/doc/config.rst b/doc/config.rst
index ead0d444..6c00e6fe 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -120,6 +120,10 @@ General configuration
builtin/theme-specific templates). Relative paths are taken as relative to
the configuration directory.
+ .. versionchanged:: 1.3
+ As these files are not meant to be built, they are automatically added to
+ :confval:`exclude_patterns`.
+
.. confval:: template_bridge
A string with the fully-qualified name of a callable (or simply a class) that
diff --git a/doc/ext/viewcode.rst b/doc/ext/viewcode.rst
index 6e77914d..f2b6c928 100644
--- a/doc/ext/viewcode.rst
+++ b/doc/ext/viewcode.rst
@@ -18,3 +18,23 @@ 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.
+
+There is also an additional config value:
+
+.. confval:: viewcode_import
+
+ If this is ``True``, viewcode extension will follow alias objects that
+ imported from another module such as functions, classes and attributes.
+ 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
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 1c34207f..4d12c141 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -39,6 +39,13 @@ c_funcptr_sig_re = re.compile(
\( (.*) \) # arguments
(\s+const)? $ # const specifier
''', re.VERBOSE)
+c_funcptr_arg_sig_re = re.compile(
+ r'''^\s*([^(,]+?) # return type
+ \( ([^()]+) \) \s* # name in parentheses
+ \( (.*) \) # arguments
+ (\s+const)? # const specifier
+ \s*(?=$|,) # end with comma or end of string
+ ''', re.VERBOSE)
c_funcptr_name_re = re.compile(r'^\(\s*\*\s*(.*?)\s*\)$')
@@ -80,6 +87,24 @@ class CObject(ObjectDescription):
else:
node += tnode
+ def _parse_arglist(self, arglist):
+ while True:
+ m = c_funcptr_arg_sig_re.match(arglist)
+ if m:
+ yield m.group()
+ arglist = c_funcptr_arg_sig_re.sub('', arglist)
+ if ',' in arglist:
+ _, arglist = arglist.split(',', 1)
+ else:
+ break
+ else:
+ if ',' in arglist:
+ arg, arglist = arglist.split(',', 1)
+ yield arg
+ else:
+ yield arglist
+ break
+
def handle_signature(self, sig, signode):
"""Transform a C signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
@@ -122,19 +147,25 @@ class CObject(ObjectDescription):
paramlist = addnodes.desc_parameterlist()
arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
# this messes up function pointer types, but not too badly ;)
- args = arglist.split(',')
- for arg in args:
+ for arg in self._parse_arglist(arglist):
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
- ctype, argname = arg.rsplit(' ', 1)
+ m = c_funcptr_arg_sig_re.match(arg)
+ if m:
+ self._parse_type(param, m.group(1) + '(')
+ param += nodes.emphasis(m.group(2), m.group(2))
+ self._parse_type(param, ')(' + m.group(3) + ')')
+ if m.group(4):
+ param += addnodes.desc_addname(m.group(4), m.group(4))
+ else:
+ ctype, argname = arg.rsplit(' ', 1)
+ self._parse_type(param, ctype)
+ # separate by non-breaking space in the output
+ param += nodes.emphasis(' '+argname, u'\xa0'+argname)
except ValueError:
# no argument name given, only the type
self._parse_type(param, arg)
- else:
- self._parse_type(param, ctype)
- # separate by non-breaking space in the output
- param += nodes.emphasis(' '+argname, u'\xa0'+argname)
paramlist += param
signode += paramlist
if const:
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 72f5d240..d51e7a16 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -339,6 +339,7 @@ class BuildEnvironment:
"""
matchers = compile_matchers(
config.exclude_patterns[:] +
+ config.templates_path +
config.html_extra_path +
['**/_sources', '.#*']
)
@@ -1000,6 +1001,7 @@ class BuildEnvironment:
if blist:
onlynode += blist.children
entries.append(onlynode)
+ continue
if not isinstance(sectionnode, nodes.section):
for toctreenode in traverse_in_section(sectionnode,
addnodes.toctree):
@@ -1217,6 +1219,8 @@ class BuildEnvironment:
try:
refdoc = None
if url_re.match(ref):
+ if title is None:
+ title = ref
reference = nodes.reference('', '', internal=False,
refuri=ref, anchorname='',
*[nodes.Text(title)])
@@ -1444,6 +1448,7 @@ class BuildEnvironment:
# a list of all docnames whose section numbers changed
rewrite_needed = []
+ assigned = set()
old_secnumbers = self.toc_secnumbers
self.toc_secnumbers = {}
@@ -1483,17 +1488,19 @@ class BuildEnvironment:
if depth == 0:
return
for (title, ref) in toctreenode['entries']:
- if url_re.match(ref) or ref == 'self':
+ if url_re.match(ref) or ref == 'self' or ref in assigned:
# don't mess with those
continue
if ref in self.tocs:
secnums = self.toc_secnumbers[ref] = {}
+ assigned.add(ref)
_walk_toc(self.tocs[ref], secnums, depth,
self.titles.get(ref))
if secnums != old_secnumbers.get(ref):
rewrite_needed.append(ref)
for docname in self.numbered_toctrees:
+ assigned.add(docname)
doctree = self.get_doctree(docname)
for toctreenode in doctree.traverse(addnodes.toctree):
depth = toctreenode.get('numbered', 0)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 4a62bf6d..9976ecc4 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -9,21 +9,43 @@
:license: BSD, see LICENSE for details.
"""
+import traceback
+
from six import iteritems, text_type
from docutils import nodes
from sphinx import addnodes
from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer
+from sphinx.util import get_full_modname
from sphinx.util.nodes import make_refnode
+def _get_full_modname(app, modname, attribute):
+ try:
+ return get_full_modname(modname, attribute)
+ except AttributeError:
+ # sphinx.ext.viewcode can't follow class instance attribute
+ # then AttributeError logging output only verbose mode.
+ app.verbose('Didn\'t find %s in %s' % (attribute, modname))
+ return None
+ except Exception as e:
+ # sphinx.ext.viewcode follow python domain directives.
+ # because of that, if there are no real modules exists that specified
+ # by py:function or other directives, viewcode emits a lot of warnings.
+ # It should be displayed only verbose mode.
+ app.verbose(traceback.format_exc().rstrip())
+ app.verbose('viewcode can\'t import %s, failed with error "%s"' %
+ (modname, e))
+ return None
+
+
def doctree_read(app, doctree):
env = app.builder.env
if not hasattr(env, '_viewcode_modules'):
env._viewcode_modules = {}
- def has_tag(modname, fullname, docname):
+ def has_tag(modname, fullname, docname, refname):
entry = env._viewcode_modules.get(modname, None)
try:
analyzer = ModuleAnalyzer.for_module(modname)
@@ -36,11 +58,11 @@ def doctree_read(app, doctree):
code = analyzer.code
if entry is None or entry[0] != code:
analyzer.find_tags()
- entry = code, analyzer.tags, {}
+ entry = code, analyzer.tags, {}, refname
env._viewcode_modules[modname] = entry
elif entry is False:
return
- code, tags, used = entry
+ _, tags, used, _ = entry
if fullname in tags:
used[fullname] = docname
return True
@@ -53,10 +75,14 @@ def doctree_read(app, doctree):
if not isinstance(signode, addnodes.desc_signature):
continue
modname = signode.get('module')
+ fullname = signode.get('fullname')
+ refname = modname
+ if env.config.viewcode_import:
+ modname = _get_full_modname(app, modname, fullname)
if not modname:
continue
fullname = signode.get('fullname')
- if not has_tag(modname, fullname, env.docname):
+ if not has_tag(modname, fullname, env.docname, refname):
continue
if fullname in names:
# only one link per name, please
@@ -95,7 +121,7 @@ def collect_pages(app):
for modname, entry in iteritems(env._viewcode_modules):
if not entry:
continue
- code, tags, used = entry
+ code, tags, used, refname = entry
# construct a page name for the highlighted source
pagename = '_modules/' + modname.replace('.', '/')
# highlight the source using the builder's highlighter
@@ -112,7 +138,7 @@ def collect_pages(app):
maxindex = len(lines) - 1
for name, docname in iteritems(used):
type, start, end = tags[name]
- backlink = urito(pagename, docname) + '#' + modname + '.' + name
+ backlink = urito(pagename, docname) + '#' + refname + '.' + name
lines[start] = (
'<div class="viewcode-block" id="%s"><a class="viewcode-back" '
'href="%s">%s</a>' % (name, backlink, _('[docs]'))
@@ -171,6 +197,7 @@ def collect_pages(app):
def setup(app):
+ app.add_config_value('viewcode_import', True, False)
app.connect('doctree-read', doctree_read)
app.connect('html-collect-pages', collect_pages)
app.connect('missing-reference', missing_reference)
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 122c5285..aaf6272b 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -158,7 +158,7 @@ class XRefRole(object):
return [node], []
-def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
+def indexmarkup_role(typ, rawtext, text, lineno, inliner,
options={}, content=[]):
"""Role for PEP/RFC references that generate an index entry."""
env = inliner.document.settings.env
@@ -166,47 +166,53 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
typ = env.config.default_role
else:
typ = typ.lower()
- text = utils.unescape(etext)
+ has_explicit_title, title, target = split_explicit_title(text)
+ title = utils.unescape(title)
+ target = utils.unescape(target)
targetid = 'index-%s' % env.new_serialno('index')
indexnode = addnodes.index()
targetnode = nodes.target('', '', ids=[targetid])
inliner.document.note_explicit_target(targetnode)
if typ == 'pep':
indexnode['entries'] = [
- ('single', _('Python Enhancement Proposals; PEP %s') % text,
+ ('single', _('Python Enhancement Proposals; PEP %s') % target,
targetid, '')]
anchor = ''
- anchorindex = text.find('#')
+ anchorindex = target.find('#')
if anchorindex > 0:
- text, anchor = text[:anchorindex], text[anchorindex:]
+ target, anchor = target[:anchorindex], target[anchorindex:]
+ if not has_explicit_title:
+ title = "PEP " + utils.unescape(title)
try:
- pepnum = int(text)
+ pepnum = int(target)
except ValueError:
- msg = inliner.reporter.error('invalid PEP number %s' % text,
+ msg = inliner.reporter.error('invalid PEP number %s' % target,
line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
- sn = nodes.strong('PEP '+text, 'PEP '+text)
+ sn = nodes.strong(title, title)
rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
elif typ == 'rfc':
- indexnode['entries'] = [('single', 'RFC; RFC %s' % text, targetid, '')]
+ indexnode['entries'] = [('single', 'RFC; RFC %s' % target, targetid, '')]
anchor = ''
- anchorindex = text.find('#')
+ anchorindex = target.find('#')
if anchorindex > 0:
- text, anchor = text[:anchorindex], text[anchorindex:]
+ target, anchor = target[:anchorindex], target[anchorindex:]
+ if not has_explicit_title:
+ title = "RFC " + utils.unescape(title)
try:
- rfcnum = int(text)
+ rfcnum = int(target)
except ValueError:
- msg = inliner.reporter.error('invalid RFC number %s' % text,
+ msg = inliner.reporter.error('invalid RFC number %s' % target,
line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
- sn = nodes.strong('RFC '+text, 'RFC '+text)
+ sn = nodes.strong(title, title)
rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
classes=[typ])
rn += sn
diff --git a/sphinx/themes/basic/domainindex.html b/sphinx/themes/basic/domainindex.html
index ac5aed9f..4ee62d55 100644
--- a/sphinx/themes/basic/domainindex.html
+++ b/sphinx/themes/basic/domainindex.html
@@ -44,7 +44,7 @@
{%- endif %}</td>
<td>{% if grouptype == 2 %}&nbsp;&nbsp;&nbsp;{% endif %}
{% if page %}<a href="{{ pathto(page) }}#{{ anchor }}">{% endif -%}
- <tt class="xref">{{ name|e }}</tt>
+ <code class="xref">{{ name|e }}</code>
{%- if page %}</a>{% endif %}
{%- if extra %} <em>({{ extra|e }})</em>{% endif -%}
</td><td>{% if qualifier %}<strong>{{ qualifier|e }}:</strong>{% endif %}
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index d4561bfa..cb1a923c 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -480,7 +480,7 @@ div.code-block-filename {
font-size: small;
}
-div.code-block-filename tt {
+div.code-block-filename code {
background-color: transparent;
}
@@ -489,22 +489,22 @@ div.code-block-filename + div.highlight > pre {
margin-top: 0;
}
-tt.descname {
+code.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
-tt.descclassname {
+code.descclassname {
background-color: transparent;
}
-tt.xref, a tt {
+code.xref, a code {
background-color: transparent;
font-weight: bold;
}
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
diff --git a/sphinx/themes/basic/static/websupport.js b/sphinx/themes/basic/static/websupport.js
index 71c0a136..9932afb9 100644
--- a/sphinx/themes/basic/static/websupport.js
+++ b/sphinx/themes/basic/static/websupport.js
@@ -700,8 +700,8 @@
(<a href="#" class="comment-markup" id="ab<%id%>">markup</a>):</p>\
<div class="comment-markup-box" id="mb<%id%>">\
reStructured text markup: <i>*emph*</i>, <b>**strong**</b>, \
- <tt>``code``</tt>, \
- code blocks: <tt>::</tt> and an indented block after blank line</div>\
+ <code>``code``</code>, \
+ code blocks: <code>::</code> and an indented block after blank line</div>\
<form method="post" id="cf<%id%>" class="comment-form" action="">\
<textarea name="comment" cols="80"></textarea>\
<p class="propose-button">\
diff --git a/sphinx/themes/default/static/default.css_t b/sphinx/themes/default/static/default.css_t
index ae4012d0..c0fbdd8c 100644
--- a/sphinx/themes/default/static/default.css_t
+++ b/sphinx/themes/default/static/default.css_t
@@ -281,7 +281,7 @@ pre {
border-right: none;
}
-tt {
+code {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
@@ -291,11 +291,11 @@ th {
background-color: #ede;
}
-.warning tt {
+.warning code {
background: #efc2c2;
}
-.note tt {
+.note code {
background: #d6d6d6;
}
diff --git a/sphinx/themes/epub/static/epub.css b/sphinx/themes/epub/static/epub.css
index 2e90481d..a03c0627 100644
--- a/sphinx/themes/epub/static/epub.css
+++ b/sphinx/themes/epub/static/epub.css
@@ -433,26 +433,26 @@ table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
-tt {
+code {
font-family: monospace;
}
-tt.descname {
+code.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
-tt.descclassname {
+code.descclassname {
background-color: transparent;
}
-tt.xref, a tt {
+code.xref, a code {
background-color: transparent;
font-weight: bold;
}
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
@@ -535,4 +535,4 @@ table .link-target {
src: url("res:///Data/fonts/LiberationNarrow-BoldItalic.otf")
format("opentype");
}
-*/ \ No newline at end of file
+*/
diff --git a/sphinx/themes/haiku/static/haiku.css_t b/sphinx/themes/haiku/static/haiku.css_t
index bd81db0a..c6cb42d8 100644
--- a/sphinx/themes/haiku/static/haiku.css_t
+++ b/sphinx/themes/haiku/static/haiku.css_t
@@ -307,7 +307,7 @@ td {
vertical-align: top;
}
-tt {
+code {
background-color: #e2e2e2;
font-size: 1.0em;
font-family: monospace;
diff --git a/sphinx/themes/nature/static/nature.css_t b/sphinx/themes/nature/static/nature.css_t
index 7909e813..84b37517 100644
--- a/sphinx/themes/nature/static/nature.css_t
+++ b/sphinx/themes/nature/static/nature.css_t
@@ -226,7 +226,7 @@ pre {
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
-tt {
+code {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
diff --git a/sphinx/themes/pyramid/static/epub.css b/sphinx/themes/pyramid/static/epub.css
index 7465a421..60383755 100644
--- a/sphinx/themes/pyramid/static/epub.css
+++ b/sphinx/themes/pyramid/static/epub.css
@@ -281,7 +281,7 @@ pre {
border-right: none;
}
-tt {
+code {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
@@ -291,11 +291,11 @@ th {
background-color: #ede;
}
-.warning tt {
+.warning code {
background: #efc2c2;
}
-.note tt {
+.note code {
background: #d6d6d6;
}
diff --git a/sphinx/themes/pyramid/static/pyramid.css_t b/sphinx/themes/pyramid/static/pyramid.css_t
index 053f61fa..6ffdb714 100644
--- a/sphinx/themes/pyramid/static/pyramid.css_t
+++ b/sphinx/themes/pyramid/static/pyramid.css_t
@@ -295,7 +295,7 @@ pre {
border-left-style: none;
}
-tt {
+code {
background-color: transparent;
color: #222;
font-size: 1.1em;
@@ -336,7 +336,7 @@ a:hover em.std-term {
font-style: normal;
}
-tt.xref {
+code.xref {
font-weight: normal;
font-style: normal;
}
diff --git a/sphinx/themes/scrolls/static/scrolls.css_t b/sphinx/themes/scrolls/static/scrolls.css_t
index 65b25042..9591f045 100644
--- a/sphinx/themes/scrolls/static/scrolls.css_t
+++ b/sphinx/themes/scrolls/static/scrolls.css_t
@@ -195,7 +195,7 @@ pre {
font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
}
-tt {
+code {
font-size: 13px;
font-family: 'Bitstream Vera Sans Mono', 'Monaco', monospace;
color: black;
@@ -204,7 +204,7 @@ tt {
border-bottom: 1px solid #eee;
}
-a.reference:hover tt {
+a.reference:hover code {
border-bottom-color: #aaa;
}
diff --git a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
index 9f90d56f..174b5950 100644
--- a/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
+++ b/sphinx/themes/sphinxdoc/static/sphinxdoc.css_t
@@ -204,20 +204,20 @@ a.headerlink:hover {
color: white!important;
}
-cite, code, tt {
+cite, code, code {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.01em;
}
-tt {
+code {
background-color: #f2f2f2;
border-bottom: 1px solid #ddd;
color: #333;
}
-tt.descname, tt.descclassname, tt.xref {
+code.descname, code.descclassname, code.xref {
border: 0;
}
@@ -226,12 +226,12 @@ hr {
margin: 2em;
}
-a tt {
+a code {
border: 0;
color: #CA7900;
}
-a tt:hover {
+a code:hover {
color: #2491CF;
}
diff --git a/sphinx/themes/traditional/static/traditional.css_t b/sphinx/themes/traditional/static/traditional.css_t
index 4b6e5c41..a82ef53c 100644
--- a/sphinx/themes/traditional/static/traditional.css_t
+++ b/sphinx/themes/traditional/static/traditional.css_t
@@ -318,7 +318,7 @@ div#comments div.comment h4 {
}
div#comments div.comment pre,
-div#comments div.comment tt {
+div#comments div.comment code {
background-color: #ddd;
color: #111;
border: none;
@@ -616,23 +616,23 @@ pre {
border-right: none;
}
-tt {
+code {
font-family: monospace;
background-color: #ecf0f3;
padding: 0 1px 0 1px;
}
-tt.descname {
+code.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
-tt.descclassname {
+code.descclassname {
background-color: transparent;
}
-tt.xref, a tt {
+code.xref, a code {
background-color: transparent;
font-weight: bold;
}
@@ -651,7 +651,7 @@ tt.xref, a tt {
margin-left: 1.5em;
}
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 2c6731a2..0f11a4c5 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -238,6 +238,20 @@ def get_module_source(modname):
return 'file', filename
+def get_full_modname(modname, attribute):
+ __import__(modname)
+ module = sys.modules[modname]
+
+ # Allow an attribute to have multiple parts and incidentially allow
+ # repeated .s in the attribute.
+ value = module
+ for attr in attribute.split('.'):
+ if attr:
+ value = getattr(value, attr)
+
+ return getattr(value, '__module__', None)
+
+
# a regex to recognize coding cookies
_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index abd4ccaa..0874c7ce 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -111,9 +111,9 @@ class HTMLTranslator(BaseTranslator):
self.body.append('</dt>\n')
def visit_desc_addname(self, node):
- self.body.append(self.starttag(node, 'tt', '', CLASS='descclassname'))
+ self.body.append(self.starttag(node, 'code', '', CLASS='descclassname'))
def depart_desc_addname(self, node):
- self.body.append('</tt>')
+ self.body.append('</code>')
def visit_desc_type(self, node):
pass
@@ -126,9 +126,9 @@ class HTMLTranslator(BaseTranslator):
pass
def visit_desc_name(self, node):
- self.body.append(self.starttag(node, 'tt', '', CLASS='descname'))
+ self.body.append(self.starttag(node, 'code', '', CLASS='descname'))
def depart_desc_name(self, node):
- self.body.append('</tt>')
+ self.body.append('</code>')
def visit_desc_parameterlist(self, node):
self.body.append('<span class="sig-paren">(</span>')
@@ -284,7 +284,7 @@ class HTMLTranslator(BaseTranslator):
starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s' % lang)
if 'filename' in node:
- starttag += '<div class="code-block-filename"><tt>%s</tt></div>' % (
+ starttag += '<div class="code-block-filename"><code>%s</code></div>' % (
node['filename'],)
self.body.append(starttag + highlighted + '</div>\n')
raise nodes.SkipNode
@@ -300,12 +300,12 @@ class HTMLTranslator(BaseTranslator):
# overwritten
def visit_literal(self, node):
- self.body.append(self.starttag(node, 'tt', '',
+ self.body.append(self.starttag(node, 'code', '',
CLASS='docutils literal'))
self.protect_literal_text += 1
def depart_literal(self, node):
self.protect_literal_text -= 1
- self.body.append('</tt>')
+ self.body.append('</code>')
def visit_productionlist(self, node):
self.body.append(self.starttag(node, 'pre'))
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index 7ce721ba..f6f955e2 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -132,7 +132,9 @@ Adding \n to test unescaping.
*Linking inline markup*
* :pep:`8`
+* :pep:`Python Enhancement Proposal #8 <8>`
* :rfc:`1`
+* :rfc:`Request for Comments #1 <1>`
* :envvar:`HOME`
* :keyword:`with`
* :token:`try statement <try_stmt>`
diff --git a/tests/roots/test-ext-viewcode/conf.py b/tests/roots/test-ext-viewcode/conf.py
new file mode 100644
index 00000000..946cb786
--- /dev/null
+++ b/tests/roots/test-ext-viewcode/conf.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+import sys
+import os
+
+sys.path.insert(0, os.path.abspath('.'))
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
+master_doc = 'index'
diff --git a/tests/roots/test-ext-viewcode/index.rst b/tests/roots/test-ext-viewcode/index.rst
new file mode 100644
index 00000000..72e94321
--- /dev/null
+++ b/tests/roots/test-ext-viewcode/index.rst
@@ -0,0 +1,29 @@
+viewcode
+========
+
+.. py:module:: spam
+
+.. autofunction:: func1
+
+.. autofunction:: func2
+
+.. autofunction:: spam.mod1.func1
+
+.. autofunction:: spam.mod2.func2
+
+.. autofunction:: Class1
+
+.. autofunction:: Class2
+
+.. autofunction:: spam.mod1.Class1
+
+.. autofunction:: spam.mod2.Class2
+
+
+.. literalinclude:: spam/__init__.py
+ :language: python
+ :pyobject: func1
+
+.. literalinclude:: spam/mod1.py
+ :language: python
+ :pyobject: func1
diff --git a/tests/roots/test-ext-viewcode/spam/__init__.py b/tests/roots/test-ext-viewcode/spam/__init__.py
new file mode 100644
index 00000000..980e9b8a
--- /dev/null
+++ b/tests/roots/test-ext-viewcode/spam/__init__.py
@@ -0,0 +1,7 @@
+from __future__ import absolute_import
+
+from .mod1 import func1, Class1
+from .mod2 import (
+ func2,
+ Class2,
+)
diff --git a/tests/roots/test-ext-viewcode/spam/mod1.py b/tests/roots/test-ext-viewcode/spam/mod1.py
new file mode 100644
index 00000000..e5eb0d47
--- /dev/null
+++ b/tests/roots/test-ext-viewcode/spam/mod1.py
@@ -0,0 +1,15 @@
+"""
+mod1
+"""
+
+def func1(a, b):
+ """
+ this is func1
+ """
+ return a, b
+
+
+class Class1(object):
+ """
+ this is Class1
+ """
diff --git a/tests/roots/test-ext-viewcode/spam/mod2.py b/tests/roots/test-ext-viewcode/spam/mod2.py
new file mode 100644
index 00000000..1841db1e
--- /dev/null
+++ b/tests/roots/test-ext-viewcode/spam/mod2.py
@@ -0,0 +1,15 @@
+"""
+mod2
+"""
+
+def func2(a, b):
+ """
+ this is func2
+ """
+ return a, b
+
+
+class Class2(object):
+ """
+ this is Class2
+ """
diff --git a/tests/test_build.py b/tests/test_build.py
index c8001271..56fdf826 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -79,3 +79,38 @@ def test_nonascii_path():
app = TestApp(buildername=buildername, _copy_to_temp=True)
yield _test_nonascii_path, app
app.cleanup()
+
+
+@with_app(buildername='text', srcdir='(empty)')
+def test_circular_toctree(app):
+ contents = (".. toctree::\n"
+ "\n"
+ " sub\n")
+ (app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
+
+ contents = (".. toctree::\n"
+ "\n"
+ " contents\n")
+ (app.srcdir / 'sub.rst').write_text(contents, encoding='utf-8')
+ app.builder.build_all()
+ warnings = "".join(app._warning.content)
+ assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings
+ assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings
+
+
+@with_app(buildername='text', srcdir='(empty)')
+def test_numbered_circular_toctree(app):
+ contents = (".. toctree::\n"
+ " :numbered:\n"
+ "\n"
+ " sub\n")
+ (app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
+
+ contents = (".. toctree::\n"
+ "\n"
+ " contents\n")
+ (app.srcdir / 'sub.rst').write_text(contents, encoding='utf-8')
+ app.builder.build_all()
+ warnings = "\n".join(app._warning.content)
+ assert 'circular toctree references detected, ignoring: sub <- contents <- sub' in warnings
+ assert 'circular toctree references detected, ignoring: contents <- sub <- contents' in warnings
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 3753d05b..17a09eae 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -122,21 +122,25 @@ HTML_XPATH = {
(".//li/strong", r'^command\\n$'),
(".//li/strong", r'^program\\n$'),
(".//li/em", r'^dfn\\n$'),
- (".//li/tt/span[@class='pre']", r'^kbd\\n$'),
+ (".//li/code/span[@class='pre']", r'^kbd\\n$'),
(".//li/em", u'File \N{TRIANGULAR BULLET} Close'),
- (".//li/tt/span[@class='pre']", '^a/$'),
- (".//li/tt/em/span[@class='pre']", '^varpart$'),
- (".//li/tt/em/span[@class='pre']", '^i$'),
+ (".//li/code/span[@class='pre']", '^a/$'),
+ (".//li/code/em/span[@class='pre']", '^varpart$'),
+ (".//li/code/em/span[@class='pre']", '^i$'),
(".//a[@href='http://www.python.org/dev/peps/pep-0008']"
"[@class='pep reference external']/strong", 'PEP 8'),
+ (".//a[@href='http://www.python.org/dev/peps/pep-0008']"
+ "[@class='pep reference external']/strong", 'Python Enhancement Proposal #8'),
(".//a[@href='http://tools.ietf.org/html/rfc1.html']"
"[@class='rfc reference external']/strong", 'RFC 1'),
+ (".//a[@href='http://tools.ietf.org/html/rfc1.html']"
+ "[@class='rfc reference external']/strong", 'Request for Comments #1'),
(".//a[@href='objects.html#envvar-HOME']"
- "[@class='reference internal']/tt/span[@class='pre']", 'HOME'),
+ "[@class='reference internal']/code/span[@class='pre']", 'HOME'),
(".//a[@href='#with']"
- "[@class='reference internal']/tt/span[@class='pre']", '^with$'),
+ "[@class='reference internal']/code/span[@class='pre']", '^with$'),
(".//a[@href='#grammar-token-try_stmt']"
- "[@class='reference internal']/tt/span", '^statement$'),
+ "[@class='reference internal']/code/span", '^statement$'),
(".//a[@href='subdir/includes.html']"
"[@class='reference internal']/em", 'Including in subdir'),
(".//a[@href='objects.html#cmdoption-python-c']"
@@ -165,7 +169,7 @@ HTML_XPATH = {
(".//dl/dt[@id='term-boson']", 'boson'),
# a production list
(".//pre/strong", 'try_stmt'),
- (".//pre/a[@href='#grammar-token-try1_stmt']/tt/span", 'try1_stmt'),
+ (".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
# tests for ``only`` directive
(".//p", 'A global substitution.'),
(".//p", 'In HTML.'),
@@ -175,8 +179,8 @@ HTML_XPATH = {
'objects.html': [
(".//dt[@id='mod.Cls.meth1']", ''),
(".//dt[@id='errmod.Error']", ''),
- (".//dt/tt", r'long\(parameter,\s* list\)'),
- (".//dt/tt", 'another one'),
+ (".//dt/code", r'long\(parameter,\s* list\)'),
+ (".//dt/code", 'another one'),
(".//a[@href='#mod.Cls'][@class='reference internal']", ''),
(".//dl[@class='userdesc']", ''),
(".//dt[@id='userdesc-myobj']", ''),
@@ -459,3 +463,18 @@ def test_tocdepth_singlehtml(app):
for xpath, check, be_found in paths:
yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@with_app(buildername='html', srcdir='(empty)')
+def test_url_in_toctree(app):
+ contents = (".. toctree::\n"
+ "\n"
+ " http://sphinx-doc.org/\n"
+ " Latest reference <http://sphinx-doc.org/latest/>\n")
+
+ (app.srcdir / 'contents.rst').write_text(contents, encoding='utf-8')
+ app.builder.build_all()
+
+ result = (app.outdir / 'contents.html').text(encoding='utf-8')
+ assert '<a class="reference external" href="http://sphinx-doc.org/">http://sphinx-doc.org/</a>' in result
+ assert '<a class="reference external" href="http://sphinx-doc.org/latest/">Latest reference</a>' in result
diff --git a/tests/test_ext_viewcode.py b/tests/test_ext_viewcode.py
new file mode 100644
index 00000000..60ab7941
--- /dev/null
+++ b/tests/test_ext_viewcode.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""
+ test_ext_viewcode
+ ~~~~~~~~~~~~~~~~~
+
+ Test sphinx.ext.viewcode extension.
+
+ :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from six import StringIO
+
+from util import test_roots, with_app
+
+
+warnfile = StringIO()
+root = test_roots / 'test-ext-viewcode'
+doctreedir = root / '_build' / 'doctree'
+
+
+def teardown_module():
+ (root / '_build').rmtree(True)
+
+
+@with_app(srcdir=root, warning=warnfile)
+def test_simple(app):
+ app.builder.build_all()
+
+ warnings = re.sub(r'\\+', '/', warnfile.getvalue())
+ assert re.findall(
+ r"index.rst:\d+: WARNING: Object named 'func1' not found in include " +
+ r"file .*/spam/__init__.py'",
+ warnings
+ )
+
+ result = (app.outdir / 'index.html').text(encoding='utf-8')
+ assert result.count('href="_modules/spam/mod1.html#func1"') == 2
+ assert result.count('href="_modules/spam/mod2.html#func2"') == 2
+ assert result.count('href="_modules/spam/mod1.html#Class1"') == 2
+ assert result.count('href="_modules/spam/mod2.html#Class2"') == 2
diff --git a/tests/test_markup.py b/tests/test_markup.py
index e58cfe68..81ade641 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -83,16 +83,16 @@ def verify(rst, html_expected, latex_expected):
def test_inline():
# correct interpretation of code with whitespace
- _html = ('<p><tt class="(samp )?docutils literal"><span class="pre">'
- 'code</span>&nbsp;&nbsp; <span class="pre">sample</span></tt></p>')
+ _html = ('<p><code class="(samp )?docutils literal"><span class="pre">'
+ 'code</span>&nbsp;&nbsp; <span class="pre">sample</span></code></p>')
yield verify_re, '``code sample``', _html, r'\\code{code sample}'
yield verify_re, ':samp:`code sample`', _html, r'\\code{code sample}'
# interpolation of braces in samp and file roles (HTML only)
yield (verify, ':samp:`a{b}c`',
- '<p><tt class="samp docutils literal"><span class="pre">a</span>'
+ '<p><code class="samp docutils literal"><span class="pre">a</span>'
'<em><span class="pre">b</span></em>'
- '<span class="pre">c</span></tt></p>',
+ '<span class="pre">c</span></code></p>',
'\\code{a\\emph{b}c}')
# interpolation of arrows in menuselection
@@ -115,8 +115,8 @@ def test_inline():
yield verify, '"John"', '<p>&#8220;John&#8221;</p>', "``John''"
# ... but not in literal text
yield (verify, '``"John"``',
- '<p><tt class="docutils literal"><span class="pre">'
- '&quot;John&quot;</span></tt></p>',
+ '<p><code class="docutils literal"><span class="pre">'
+ '&quot;John&quot;</span></code></p>',
'\\code{"John"}')
# verify classes for inline roles