diff options
author | Takayuki Shimizukawa <shimizukawa+bitbucket@gmail.com> | 2014-08-23 11:50:18 +0900 |
---|---|---|
committer | Takayuki Shimizukawa <shimizukawa+bitbucket@gmail.com> | 2014-08-23 11:50:18 +0900 |
commit | 1e78f3eb218268d38a0d62e43d65fcd515147dee (patch) | |
tree | 23b113f4f3cdd1e49aad1a5bb2c19d5e8ac4dc6b | |
parent | 5297231779bdf9c04a2972e8d5354c1f126d953d (diff) | |
parent | 4399f695af7013f6735455b2fa1c6381b07c6f34 (diff) | |
download | sphinx-1e78f3eb218268d38a0d62e43d65fcd515147dee.tar.gz |
Merged in bsipocz/sphinx (pull request #275)
Adding the option of multiple inv for the same name & uri
31 files changed, 387 insertions, 88 deletions
@@ -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 %} {% 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> <span class="pre">sample</span></tt></p>') + _html = ('<p><code class="(samp )?docutils literal"><span class="pre">' + 'code</span> <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>“John”</p>', "``John''" # ... but not in literal text yield (verify, '``"John"``', - '<p><tt class="docutils literal"><span class="pre">' - '"John"</span></tt></p>', + '<p><code class="docutils literal"><span class="pre">' + '"John"</span></code></p>', '\\code{"John"}') # verify classes for inline roles |