summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2014-01-13 00:14:00 +0100
committerGeorg Brandl <georg@python.org>2014-01-13 00:14:00 +0100
commit8aec27617e3ba2bf13ba9f96bb6fa57602f13b91 (patch)
treeb5a8bfe70f7ecd52ba3d64a19c375a5b8ac6288d /sphinx
parent0ce3e1b81e8105ccf99e497b6c82c1ef8d9201ba (diff)
parentdd1c23518d9f91edeb5a19e796a5be2700c93f34 (diff)
downloadsphinx-8aec27617e3ba2bf13ba9f96bb6fa57602f13b91.tar.gz
merge with stable
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/builders/linkcheck.py90
-rw-r--r--sphinx/domains/c.py7
-rw-r--r--sphinx/ext/autosummary/generate.py17
-rw-r--r--sphinx/ext/mathbase.py4
-rw-r--r--sphinx/search/__init__.py14
-rw-r--r--sphinx/texinputs/sphinxmanual.cls3
-rw-r--r--sphinx/themes/basic/static/searchtools.js_t2
-rw-r--r--sphinx/writers/html.py22
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):