diff options
| author | Georg Brandl <georg@python.org> | 2014-01-13 00:14:00 +0100 |
|---|---|---|
| committer | Georg Brandl <georg@python.org> | 2014-01-13 00:14:00 +0100 |
| commit | 8aec27617e3ba2bf13ba9f96bb6fa57602f13b91 (patch) | |
| tree | b5a8bfe70f7ecd52ba3d64a19c375a5b8ac6288d /sphinx | |
| parent | 0ce3e1b81e8105ccf99e497b6c82c1ef8d9201ba (diff) | |
| parent | dd1c23518d9f91edeb5a19e796a5be2700c93f34 (diff) | |
| download | sphinx-8aec27617e3ba2bf13ba9f96bb6fa57602f13b91.tar.gz | |
merge with stable
Diffstat (limited to 'sphinx')
| -rw-r--r-- | sphinx/builders/linkcheck.py | 90 | ||||
| -rw-r--r-- | sphinx/domains/c.py | 7 | ||||
| -rw-r--r-- | sphinx/ext/autosummary/generate.py | 17 | ||||
| -rw-r--r-- | sphinx/ext/mathbase.py | 4 | ||||
| -rw-r--r-- | sphinx/search/__init__.py | 14 | ||||
| -rw-r--r-- | sphinx/texinputs/sphinxmanual.cls | 3 | ||||
| -rw-r--r-- | sphinx/themes/basic/static/searchtools.js_t | 2 | ||||
| -rw-r--r-- | sphinx/writers/html.py | 22 |
8 files changed, 121 insertions, 38 deletions
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index c567401c..6cbe361b 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -15,17 +15,30 @@ import Queue import socket import threading from os import path -from urllib2 import build_opener, unquote, Request +from urllib2 import build_opener, unquote, Request, \ + HTTPError, HTTPRedirectHandler from HTMLParser import HTMLParser, HTMLParseError from docutils import nodes from sphinx.builders import Builder -from sphinx.util.console import purple, red, darkgreen, darkgray +from sphinx.util.console import purple, red, darkgreen, darkgray, \ + darkred, turquoise + + +class RedirectHandler(HTTPRedirectHandler): + """A RedirectHandler that records the redirect code we got.""" + + def redirect_request(self, req, fp, code, msg, headers, newurl): + new_req = HTTPRedirectHandler.redirect_request(self, req, fp, code, + msg, headers, newurl) + req.redirect_code = code + return new_req # create an opener that will simulate a browser user-agent -opener = build_opener() -opener.addheaders = [('User-agent', 'Mozilla/5.0')] +opener = build_opener(RedirectHandler) +opener.addheaders = [('User-agent', 'Mozilla/5.0 (X11; Linux x86_64; rv:25.0) ' + 'Gecko/20100101 Firefox/25.0')] class HeadRequest(Request): @@ -104,18 +117,18 @@ class CheckExternalLinksBuilder(Builder): # check for various conditions without bothering the network if len(uri) == 0 or uri[0] == '#' or \ uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:': - return 'unchecked', '' + return 'unchecked', '', 0 elif not (uri[0:5] == 'http:' or uri[0:6] == 'https:'): - return 'local', '' + return 'local', '', 0 elif uri in self.good: - return 'working', '' + return 'working', '', 0 elif uri in self.broken: - return 'broken', self.broken[uri] + return 'broken', self.broken[uri], 0 elif uri in self.redirected: - return 'redirected', self.redirected[uri] + return 'redirected', self.redirected[uri][0], self.redirected[uri][1] for rex in self.to_ignore: if rex.match(uri): - return 'ignored', '' + return 'ignored', '', 0 if '#' in uri: req_url, hash = uri.split('#', 1) @@ -127,61 +140,82 @@ class CheckExternalLinksBuilder(Builder): try: if hash and self.app.config.linkcheck_anchors: # Read the whole document and see if #hash exists - f = opener.open(Request(req_url), **kwargs) + req = Request(req_url) + f = opener.open(req, **kwargs) found = check_anchor(f, unquote(hash)) f.close() if not found: raise Exception("Anchor '%s' not found" % hash) else: - f = opener.open(HeadRequest(req_url), **kwargs) - f.close() + try: + # try a HEAD request, which should be easier on + # the server and the network + req = HeadRequest(req_url) + f = opener.open(req, **kwargs) + f.close() + except HTTPError, err: + if err.code != 405: + raise + # retry with GET if that fails, some servers + # don't like HEAD requests and reply with 405 + req = Request(req_url) + f = opener.open(req, **kwargs) + f.close() except Exception, err: self.broken[uri] = str(err) - return 'broken', str(err) + return 'broken', str(err), 0 if f.url.rstrip('/') == req_url.rstrip('/'): self.good.add(uri) - return 'working', 'new' + return 'working', 'new', 0 else: new_url = f.url if hash: new_url += '#' + hash - - self.redirected[uri] = new_url - return 'redirected', new_url + code = getattr(req, 'redirect_code', 0) + self.redirected[uri] = (new_url, code) + return 'redirected', new_url, code while True: uri, docname, lineno = self.wqueue.get() if uri is None: break - status, info = check() - self.rqueue.put((uri, docname, lineno, status, info)) + status, info, code = check() + self.rqueue.put((uri, docname, lineno, status, info, code)) def process_result(self, result): - uri, docname, lineno, status, info = result + uri, docname, lineno, status, info, code = result if status == 'unchecked': return if status == 'working' and info != 'new': return if lineno: - self.info('(line %3d) ' % lineno, nonl=1) + self.info('(line %4d) ' % lineno, nonl=1) if status == 'ignored': - self.info(uri + ' - ' + darkgray('ignored')) + self.info(darkgray('-ignored- ') + uri) elif status == 'local': - self.info(uri + ' - ' + darkgray('local')) + self.info(darkgray('-local- ') + uri) self.write_entry('local', docname, lineno, uri) elif status == 'working': - self.info(uri + ' - ' + darkgreen('working')) + self.info(darkgreen('ok ') + uri) elif status == 'broken': - self.info(uri + ' - ' + red('broken: ') + info) + self.info(red('broken ') + uri + red(' - ' + info)) self.write_entry('broken', docname, lineno, uri + ': ' + info) if self.app.quiet: self.warn('broken link: %s' % uri, '%s:%s' % (self.env.doc2path(docname), lineno)) elif status == 'redirected': - self.info(uri + ' - ' + purple('redirected') + ' to ' + info) - self.write_entry('redirected', docname, lineno, uri + ' to ' + info) + text, color = { + 301: ('permanently', darkred), + 302: ('with Found', purple), + 303: ('with See Other', purple), + 307: ('temporarily', turquoise), + 0: ('with unknown code', purple), + }[code] + self.write_entry('redirected ' + text, docname, lineno, + uri + ' to ' + info) + self.info(color('redirect ') + uri + color(' - ' + text + ' to ' + info)) def get_target_uri(self, docname, typ=None): return '' diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index f9f2e664..aed5b47e 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -59,7 +59,12 @@ class CObject(ObjectDescription): # These C types aren't described anywhere, so don't try to create # a cross-reference to them - stopwords = set(('const', 'void', 'char', 'int', 'long', 'FILE', 'struct')) + stopwords = set(( + 'const', 'void', 'char', 'wchar_t', 'int', 'short', + 'long', 'float', 'double', 'unsigned', 'signed', 'FILE', + 'clock_t', 'time_t', 'ptrdiff_t', 'size_t', 'ssize_t', + 'struct', '_Bool', + )) def _parse_type(self, node, ctype): # add cross-ref nodes for all words diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 0640a332..f45aa085 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -33,6 +33,21 @@ from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.util.osutil import ensuredir from sphinx.util.inspect import safe_getattr +# Add documenters to AutoDirective registry +from sphinx.ext.autodoc import add_documenter, \ + ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \ + FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \ + InstanceAttributeDocumenter +add_documenter(ModuleDocumenter) +add_documenter(ClassDocumenter) +add_documenter(ExceptionDocumenter) +add_documenter(DataDocumenter) +add_documenter(FunctionDocumenter) +add_documenter(MethodDocumenter) +add_documenter(AttributeDocumenter) +add_documenter(InstanceAttributeDocumenter) + + def main(argv=sys.argv): usage = """%prog [OPTIONS] SOURCEFILE ...""" p = optparse.OptionParser(usage.strip()) @@ -101,7 +116,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', new_files = [] # write - for name, path, template_name in sorted(items): + for name, path, template_name in sorted(items, key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py index b7d0d997..0c5eaf8b 100644 --- a/sphinx/ext/mathbase.py +++ b/sphinx/ext/mathbase.py @@ -30,11 +30,15 @@ def wrap_displaymath(math, label): parts = math.split('\n\n') ret = [] for i, part in enumerate(parts): + if not part.strip(): + continue if label is not None and i == 0: ret.append('\\begin{split}%s\\end{split}' % part + (label and '\\label{'+label+'}' or '')) else: ret.append('\\begin{split}%s\\end{split}\\notag' % part) + if not ret: + return '' return '\\begin{gather}\n' + '\\\\'.join(ret) + '\n\\end{gather}' diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 8e26eb25..87377c31 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -10,10 +10,9 @@ """ from __future__ import with_statement import re -import itertools import cPickle as pickle -from docutils.nodes import comment, title, Text, NodeVisitor, SkipNode +from docutils.nodes import raw, comment, title, Text, NodeVisitor, SkipNode from sphinx.util import jsdump, rpartition @@ -146,7 +145,16 @@ class WordCollector(NodeVisitor): def dispatch_visit(self, node): if node.__class__ is comment: raise SkipNode - elif node.__class__ is Text: + if node.__class__ is raw: + # Some people might put content in raw HTML that should be searched, + # so we just amateurishly strip HTML tags and index the remaining + # content + nodetext = re.sub(r'(?is)<style.*?</style>', '', node.astext()) + nodetext = re.sub(r'(?is)<script.*?</script>', '', nodetext) + nodetext = re.sub(r'<[^<]+?>', '', nodetext) + self.found_words.extend(self.lang.split(nodetext)) + raise SkipNode + if node.__class__ is Text: self.found_words.extend(self.lang.split(node.astext())) elif node.__class__ is title: self.found_title_words.extend(self.lang.split(node.astext())) diff --git a/sphinx/texinputs/sphinxmanual.cls b/sphinx/texinputs/sphinxmanual.cls index 26df488e..a6b9b392 100644 --- a/sphinx/texinputs/sphinxmanual.cls +++ b/sphinx/texinputs/sphinxmanual.cls @@ -97,6 +97,7 @@ % \let\py@OldTableofcontents=\tableofcontents \renewcommand{\tableofcontents}{% + \pagenumbering{roman}% \setcounter{page}{1}% \pagebreak% \pagestyle{plain}% @@ -114,7 +115,7 @@ \pagenumbering{arabic}% \@ifundefined{fancyhf}{}{\pagestyle{normal}}% } -\pagenumbering{roman} +\pagenumbering{alph} % This is needed to get the width of the section # area wide enough in the % library reference. Doing it here keeps it the same for all the manuals. diff --git a/sphinx/themes/basic/static/searchtools.js_t b/sphinx/themes/basic/static/searchtools.js_t index 26121366..523ecaaa 100644 --- a/sphinx/themes/basic/static/searchtools.js_t +++ b/sphinx/themes/basic/static/searchtools.js_t @@ -156,7 +156,7 @@ var Search = { continue; } // stem the word - var word = stemmer.stemWord(tmp[i]).toLowerCase(); + var word = stemmer.stemWord(tmp[i].toLowerCase()); var toAppend; // select the correct list if (word[0] == '-') { diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 612496e5..308e1f5b 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -131,24 +131,40 @@ class HTMLTranslator(BaseTranslator): def visit_desc_parameterlist(self, node): self.body.append('<big>(</big>') self.first_param = 1 + self.optional_param_level = 0 + # How many required parameters are left. + self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) self.param_separator = node.child_text_separator def depart_desc_parameterlist(self, node): self.body.append('<big>)</big>') + # If required parameters are still to come, then put the comma after + # the parameter. Otherwise, put the comma before. This ensures that + # signatures like the following render correctly (see issue #1001): + # + # foo([a, ]b, c[, d]) + # def visit_desc_parameter(self, node): - if not self.first_param: - self.body.append(self.param_separator) - else: + if self.first_param: self.first_param = 0 + elif not self.required_params_left: + self.body.append(self.param_separator) + if self.optional_param_level == 0: + self.required_params_left -= 1 if not node.hasattr('noemph'): self.body.append('<em>') def depart_desc_parameter(self, node): if not node.hasattr('noemph'): self.body.append('</em>') + if self.required_params_left: + self.body.append(self.param_separator) def visit_desc_optional(self, node): + self.optional_param_level += 1 self.body.append('<span class="optional">[</span>') def depart_desc_optional(self, node): + self.optional_param_level -= 1 self.body.append('<span class="optional">]</span>') def visit_desc_annotation(self, node): |
