Commits

ericholscher committed fb26da7 Merge

Test commit.

Comments (0)

Files changed (32)

 * John Waltman -- Texinfo builder
 * Barry Warsaw -- setup command improvements
 * Sebastian Wiesner -- image handling, distutils support
+* Joel Wurtz -- cellspanning support in LaTeX
 
 Many thanks for all contributions!
 
     requests and allow configuring the timeout.  New config values:
     :confval:`linkcheck_timeout` and :confval:`linkcheck_workers`.
   - #521: Added :confval:`linkcheck_ignore` config value.
+  - #28: Support row/colspans in tables in the LaTeX builder.
 
 * Configuration and extensibility:
 
   - #537: Added :confval:`nitpick_ignore`.
   - #306: Added :event:`env-get-outdated` event.
+  - :meth:`.Application.add_stylesheet` now accepts full URIs.
 
 * Autodoc:
 
   - #367: Added automatic exclusion of hidden members in inheritance
     diagrams, and an option to selectively enable it.
   - Added :confval:`pngmath_add_tooltips`.
+  - The math extension displaymath directives now support ``name`` in
+    addition to ``label`` for giving the equation label, for compatibility
+    with Docutils.
 
 * New locales:
 
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 ----------------------------------------------------------------------
+
+The included Underscore JavaScript library is available under the MIT
+license:
+
+----------------------------------------------------------------------
+Copyright (c) 2009 Jeremy Ashkenas, DocumentCloud
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
 Sphinx documentation contents
 =============================
 
+All the things.
+
 .. toctree::
    :maxdepth: 2
 

doc/ext/appapi.rst

 
    Add *filename* to the list of CSS files that the default HTML template will
    include.  Like for :meth:`add_javascript`, the filename must be relative to
-   the HTML static path.
+   the HTML static path, or a full URI with scheme.
 
    .. versionadded:: 1.0
 

doc/ext/autosummary.rst

    contain links to the documented items, and short summary blurbs extracted
    from their docstrings.
 
-2. The convenience script :program:`sphinx-autogen` or the new
+2. Optionally, the convenience script :program:`sphinx-autogen` or the new
    :confval:`autosummary_generate` config value can be used to generate short
    "stub" files for the entries listed in the :rst:dir:`autosummary` directives.
-   These by default contain only the corresponding :mod:`sphinx.ext.autodoc`
-   directive.
+   These files by default contain only the corresponding :mod:`sphinx.ext.autodoc`
+   directive, but can be customized with templates.
 
 
 .. rst:directive:: autosummary
 
    Insert a table that contains links to documented items, and a short summary
-   blurb (the first sentence of the docstring) for each of them.  The
-   :rst:dir:`autosummary` directive can also optionally serve as a :rst:dir:`toctree`
-   entry for the included items.
+   blurb (the first sentence of the docstring) for each of them.  
+
+   The :rst:dir:`autosummary` directive can also optionally serve as a
+   :rst:dir:`toctree` entry for the included items. Optionally, stub
+   ``.rst`` files for these items can also be automatically generated.
 
    For example, ::
 
 ======================
 
 .. module:: sphinx.ext.mathbase
-   :synopsis: Common math support for pngmath and jsmath.
+   :synopsis: Common math support for pngmath and mathjax / jsmath.
 
 .. versionadded:: 0.5
 
 Since mathematical notation isn't natively supported by HTML in any way, Sphinx
-supports math in documentation with two extensions.
+supports math in documentation with several extensions.
 
-The basic math support that is common to both extensions is contained in
-:mod:`sphinx.ext.mathbase`.  Other math support extensions should,
-if possible, reuse that support too.
+The basic math support is contained in :mod:`sphinx.ext.mathbase`. Other math
+support extensions should, if possible, reuse that support too.
 
 .. note::
 
    :mod:`.mathbase` is not meant to be added to the :confval:`extensions` config
    value, instead, use either :mod:`sphinx.ext.pngmath` or
-   :mod:`sphinx.ext.jsmath` as described below.
+   :mod:`sphinx.ext.mathjax` as described below.
 
 The input language for mathematics is LaTeX markup.  This is the de-facto
 standard for plain-text math notation and has the added advantage that no
 course means that the computer where the docs are built must have both programs
 available.
 
-There are various config values you can set to influence how the images are built:
+There are various config values you can set to influence how the images are
+built:
 
 .. confval:: pngmath_latex
 
 .. confval:: mathjax_path
 
    The path to the JavaScript file to include in the HTML files in order to load
-   JSMath.
+   MathJax.
 
    The default is the ``http://`` URL that loads the JS files from the `MathJax
    CDN <http://www.mathjax.org/docs/1.1/start.html>`_.  If you want MathJax to
    The path can be absolute or relative; if it is relative, it is relative to
    the ``_static`` directory of the built docs.
 
-   For example, if you put JSMath into the static path of the Sphinx docs, this
+   For example, if you put MathJax into the static path of the Sphinx docs, this
    value would be ``MathJax/MathJax.js``.  If you host more than one Sphinx
    documentation set on one server, it is advisable to install MathJax in a
    shared location.
 to reStructuredText/Sphinx from other documentation systems.
 
 * Gerard Flanagan has written a script to convert pure HTML to reST; it can be
-  found at `BitBucket
-  <https://bitbucket.org/djerdo/musette/src/tip/musette/html/html2rest.py>`_.
+  found at the `Python Package Index <http://pypi.python.org/pypi/html2rest>`_.
 
 * For converting the old Python docs to Sphinx, a converter was written which
   can be found at `the Python SVN repository

doc/markup/para.rst

 subdocuments, is described in :ref:`toctree-directive`.
 
 For local tables of contents, use the standard reST :dudir:`contents directive
-<contents>`.
+<table-of-contents>`.
 
 
 Glossary
 
 * Additional body elements:
 
-  - :dudir:`contents` (a local, i.e. for the current file only, table of
-    contents)
+  - :dudir:`contents <table-of-contents>` (a local, i.e. for the current file
+    only, table of contents)
   - :dudir:`container` (a container with a custom class, useful to generate an
     outer ``<div>`` in HTML)
   - :dudir:`rubric` (a heading without relation to the document sectioning)

sphinx/application.py

 
     def add_stylesheet(self, filename):
         from sphinx.builders.html import StandaloneHTMLBuilder
-        StandaloneHTMLBuilder.css_files.append(
-            posixpath.join('_static', filename))
+        if '://' in filename:
+            StandaloneHTMLBuilder.css_files.append(filename)
+        else:
+            StandaloneHTMLBuilder.css_files.append(
+                posixpath.join('_static', filename))
 
     def add_lexer(self, alias, lexer):
         from sphinx.highlighting import lexers

sphinx/builders/gettext.py

 """[1:]
 
 
+class Catalog(object):
+    """Catalog of translatable messages."""
+
+    def __init__(self):
+        self.messages = []  # retain insertion order, a la OrderedDict
+        self.metadata = {}  # msgid -> file, line, uid
+
+    def add(self, msg, origin):
+        if msg not in self.metadata:  # faster lookup in hash
+            self.messages.append(msg)
+            self.metadata[msg] = []
+        self.metadata[msg].append((origin.source, origin.line, origin.uid))
+
+
 class I18nBuilder(Builder):
     """
     General i18n builder.
 
     def init(self):
         Builder.init(self)
-        self.catalogs = defaultdict(dict)
+        self.catalogs = defaultdict(Catalog)
 
     def get_target_uri(self, docname, typ=None):
         return ''
         catalog = self.catalogs[docname.split(SEP, 1)[0]]
 
         for node, msg in extract_messages(doctree):
-            if not msg in catalog:
-                catalog[msg] = []
-            if node.source and node.line:
-                position = {"source": node.source,
-                            "line": node.line}
-                catalog[msg].append(position)
+            catalog.add(msg, node)
 
 
 class MessageCatalogBuilder(I18nBuilder):
             # XXX should supply tz
             ctime = datetime.now().strftime('%Y-%m-%d %H:%M%z'),
         )
-        for section, messages in self.status_iterator(
+        for section, catalog in self.status_iterator(
                 self.catalogs.iteritems(), "writing message catalogs... ",
                 lambda (section, _):darkgreen(section), len(self.catalogs)):
 
             pofile = open(pofn, 'w', encoding='utf-8')
             try:
                 pofile.write(POHEADER % data)
-                for message, positions in messages.iteritems():
+
+                for message in catalog.messages:
+                    positions = catalog.metadata[message]
+
+                    # generate "#: file1:line1\n#: file2:line2 ..."
+                    pofile.write(u"#: %s\n" % "\n#: ".join("%s:%s" %
+                        (path.relpath(source, self.outdir), line)
+                        for source, line, _ in positions))
+                    # generate "# uuid1\n# uuid2\n ..."
+                    pofile.write(u"# %s\n" % "\n# ".join(uid for _, _, uid
+                        in positions))
+
                     # message contains *one* line of text ready for translation
                     message = message.replace(u'\\', ur'\\'). \
                                       replace(u'"', ur'\"')
-                    for position in positions:
-                        source = path.relpath(position["source"], self.outdir)
-                        line = position["line"]
-                        pofile.write(u'#: %s:%d\n' % (source, line))
                     pofile.write(u'msgid "%s"\nmsgstr ""\n\n' % message)
+
             finally:
                 pofile.close()

sphinx/environment.py

 import codecs
 import imghdr
 import string
-import posixpath
 import unicodedata
 import cPickle as pickle
 from os import path
                                 toplevel[1][:] = subtrees
                     # resolve all sub-toctrees
                     for toctreenode in toc.traverse(addnodes.toctree):
-                        i = toctreenode.parent.index(toctreenode) + 1
-                        for item in _entries_from_toctree(toctreenode,
-                                                          subtree=True):
-                            toctreenode.parent.insert(i, item)
-                            i += 1
-                        toctreenode.parent.remove(toctreenode)
+                        if not (toctreenode.get('hidden', False)
+                                and not includehidden):
+                            i = toctreenode.parent.index(toctreenode) + 1
+                            for item in _entries_from_toctree(toctreenode,
+                                                              subtree=True):
+                                toctreenode.parent.insert(i, item)
+                                i += 1
+                            toctreenode.parent.remove(toctreenode)
                     if separate:
                         entries.append(toc)
                     else:
                 if 'orphan' in self.metadata[docname]:
                     continue
                 self.warn(docname, 'document isn\'t included in any toctree')
+

sphinx/ext/autosummary/__init__.py

 
 # -- autodoc integration -------------------------------------------------------
 
-try:
-    ismemberdescriptor = inspect.ismemberdescriptor
-    isgetsetdescriptor = inspect.isgetsetdescriptor
-except AttributeError:
-    def ismemberdescriptor(obj):
-        return False
-    isgetsetdescriptor = ismemberdescriptor
+class FakeDirective:
+    env = {}
+    genopt = {}
 
 def get_documenter(obj, parent):
     """Get an autodoc.Documenter class suitable for documenting the given
     object.
+
+    *obj* is the Python object to be documented, and *parent* is an
+    another Python object (e.g. a module or a class) to which *obj*
+    belongs to.
     """
-    from sphinx.ext.autodoc import AutoDirective, DataDocumenter
+    from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \
+         ModuleDocumenter
 
+    if inspect.ismodule(obj):
+        # ModuleDocumenter.can_document_member always returns False
+        return ModuleDocumenter
+
+    # Construct a fake documenter for *parent*
+    if parent is not None:
+        parent_doc_cls = get_documenter(parent, None)
+    else:
+        parent_doc_cls = ModuleDocumenter
+
+    if hasattr(parent, '__name__'):
+        parent_doc = parent_doc_cls(FakeDirective(), parent.__name__)
+    else:
+        parent_doc = parent_doc_cls(FakeDirective(), "")
+
+    # Get the corrent documenter class for *obj*
     classes = [cls for cls in AutoDirective._registry.values()
-               if cls.can_document_member(obj, '', False, parent)]
+               if cls.can_document_member(obj, '', False, parent_doc)]
     if classes:
         classes.sort(key=lambda cls: cls.priority)
         return classes[-1]
     """
     Pretty table containing short signatures and summaries of functions etc.
 
-    autosummary also generates a (hidden) toctree:: node.
+    autosummary can also optionally generate a hidden toctree:: node.
     """
 
     required_arguments = 0
         """
         env = self.state.document.settings.env
 
-        prefixes = ['']
-        currmodule = env.temp_data.get('py:module')
-        if currmodule:
-            prefixes.insert(0, currmodule)
+        prefixes = get_import_prefixes_from_env(env)
 
         items = []
 
 
 def mangle_signature(sig, max_chars=30):
     """Reformat a function signature to a more compact form."""
-    sig = re.sub(r"^\((.*)\)$", r"\1", sig) + ", "
-    r = re.compile(r"(?P<name>[a-zA-Z0-9_*]+)(?P<default>=.*?)?, ")
-    items = r.findall(sig)
+    s = re.sub(r"^\((.*)\)$", r"\1", sig).strip()
 
-    args = [name for name, default in items if not default]
-    opts = [name for name, default in items if default]
+    # Strip strings (which can contain things that confuse the code below)
+    s = re.sub(r"\\\\", "", s)
+    s = re.sub(r"\\'", "", s)
+    s = re.sub(r"'[^']*'", "", s)
 
+    # Parse the signature to arguments + options
+    args = []
+    opts = []
+
+    opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)=")
+    while s:
+        m = opt_re.search(s)
+        if not m:
+            # The rest are arguments
+            args = s.split(', ')
+            break
+
+        opts.insert(0, m.group(2))
+        s = m.group(1)[:-2]
+
+    # Produce a more compact signature
     sig = limited_join(", ", args, max_chars=max_chars-2)
     if opts:
         if not sig:
 
 # -- Importing items -----------------------------------------------------------
 
+def get_import_prefixes_from_env(env):
+    """
+    Obtain current Python import prefixes (for `import_by_name`)
+    from ``document.env``
+    """
+    prefixes = [None]
+
+    currmodule = env.temp_data.get('py:module')
+    if currmodule:
+        prefixes.insert(0, currmodule)
+
+    currclass = env.temp_data.get('py:class')
+    if currclass:
+        if currmodule:
+            prefixes.insert(0, currmodule + "." + currclass)
+        else:
+            prefixes.insert(0, currclass)
+
+    return prefixes
+
 def import_by_name(name, prefixes=[None]):
     """Import a Python object that has the given *name*, under one of the
     *prefixes*.  The first name that succeeds is used.
         'obj', rawtext, etext, lineno, inliner, options, content)
     pnode = r[0][0]
 
-    prefixes = [None]
-    #prefixes.insert(0, inliner.document.settings.env.currmodule)
+    prefixes = get_import_prefixes_from_env(env)
     try:
         name, obj, parent = import_by_name(pnode['reftarget'], prefixes)
     except ImportError:

sphinx/ext/autosummary/generate.py

     *template* ``None`` if the directive does not have the
     corresponding options set.
     """
-    autosummary_re = re.compile(r'^\s*\.\.\s+autosummary::\s*')
+    autosummary_re = re.compile(r'^(\s*)\.\.\s+autosummary::\s*')
     automodule_re = re.compile(
         r'^\s*\.\.\s+automodule::\s*([A-Za-z0-9_.]+)\s*$')
     module_re = re.compile(
     template = None
     current_module = module
     in_autosummary = False
+    base_indent = ""
 
     for line in lines:
         if in_autosummary:
                 documented.append((name, toctree, template))
                 continue
 
-            if not line.strip():
+            if not line.strip() or line.startswith(base_indent + " "):
                 continue
 
             in_autosummary = False
         m = autosummary_re.match(line)
         if m:
             in_autosummary = True
+            base_indent = m.group(1)
             toctree = None
             template = None
             continue

sphinx/ext/graphviz.py

 from docutils.parsers.rst import directives
 
 from sphinx.errors import SphinxError
-from sphinx.util.osutil import ensuredir, ENOENT, EPIPE
+from sphinx.util.osutil import ensuredir, ENOENT, EPIPE, EINVAL
 from sphinx.util.compat import Directive
 
 
                           self.builder.config.graphviz_dot)
         self.builder._graphviz_warned_dot = True
         return None, None
+    wentWrong = False
     try:
         # Graphviz may close standard input when an error occurs,
         # resulting in a broken pipe on communicate()
     except OSError, err:
         if err.errno != EPIPE:
             raise
+        wentWrong = True
+    except IOError, err:
+        if err.errno != EINVAL:
+            raise
+        wentWrong = True
+    if wentWrong:
         # in this case, read the standard output and standard error streams
         # directly, to get the error message(s)
         stdout, stderr = p.stdout.read(), p.stderr.read()
     # The object tag works fine on Firefox and WebKit
     # Besides it's a hack, this strategy does not mess with templates.
     imgcss = imgcls and ' class="%s"' % imgcls or ''
-    return '<object type="image/svg+xml" data="%s"%s%s/>\n' % \
+    return '<object type="image/svg+xml" data="%s"%s%s></object>\n' % \
            (svgref, imgcss, style)
 
 
         raise nodes.SkipNode
     if fname is not None:
         self.body.append('\n\n@float\n')
-        if node.get('caption'):
+        caption = node.get('caption')
+        if caption:
             self.body.append('@caption{%s}\n' % self.escape_arg(caption))
         self.body.append('@image{%s,,,[graphviz],png}\n'
                          '@end float\n\n' % fname[:-4])

sphinx/ext/intersphinx.py

             # new format
             name, (uri, inv) = key, value
             if not name.isalnum():
-                env.warn('intersphinx identifier %r is not alphanumeric' % name)
+                env.warn(docname=None, msg='intersphinx identifier %r is not alphanumeric' % name)
         else:
             # old format, no name
             name, uri, inv = None, key, value

sphinx/ext/mathbase.py

     final_argument_whitespace = True
     option_spec = {
         'label': directives.unchanged,
+        'name': directives.unchanged,
         'nowrap': directives.flag,
     }
 
             latex = self.arguments[0] + '\n\n' + latex
         node = displaymath()
         node['latex'] = latex
-        node['label'] = self.options.get('label', None)
+        node['label'] = self.options.get('name', None)
+        if node['label'] is None:
+            node['label'] = self.options.get('label', None)
         node['nowrap'] = 'nowrap' in self.options
         node['docname'] = self.state.document.settings.env.docname
         ret = [node]

sphinx/locale/ja/LC_MESSAGES/sphinx.mo

Binary file modified.

sphinx/locale/ja/LC_MESSAGES/sphinx.po

 # Copyright (C) 2008 ORGANIZATION
 # This file is distributed under the same license as the Sphinx project.
 # Yasushi Masuda <whosaysni@gmail.com>, 2008.
-# Kouhei Sutou <kou@clear-code.com, 2011.
+# Kouhei Sutou <kou@clear-code.com>, 2011.
+# Akitoshi Ohta <fire.kuma8@gmail.com>, 2011.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: Sphinx 1.1pre\n"
 "POT-Creation-Date: 2008-09-11 23:58+0200\n"
-"PO-Revision-Date: 2011-05-15 21:25+0900\n"
-"Last-Translator: Kouhei Sutou <kou@clear-code.com>\n"
+"PO-Revision-Date: 2011-08-27 17:50+0900\n"
+"Last-Translator: Akitoshi Ohta <fire.kuma8@gmail.com>\n"
 "Language-Team: Japanese\n"
 "Plural-Forms: nplurals=1; plural=0\n"
 "MIME-Version: 1.0\n"
 
 #: sphinx/writers/latex.py:677
 msgid "Continued on next page"
-msgstr "総索引"
+msgstr "次のページに続く"
 
 #: sphinx/writers/text.py:430
 msgid "[image]"

sphinx/quickstart.py

 
 # -- Options for LaTeX output --------------------------------------------------
 
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 # If true, show URL addresses after external links.
 #latex_show_urls = False
 
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
 # Documents to append as an appendix to all manuals.
 #latex_appendices = []
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('%(master_str)s', '%(project_manpage)s', u'%(project_doc)s',
+    ('%(master_str)s', '%(project_manpage)s', u'%(project_doc_str)s',
      [u'%(author_str)s'], 1)
 ]
 
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('%(master_str)s', '%(project_fn)s', u'%(project_doc)s', u'%(author_str)s',
+  ('%(master_str)s', '%(project_fn)s', u'%(project_doc_str)s', u'%(author_str)s',
    '%(project_fn)s', 'One line description of project.', 'Miscellaneous'),
 ]
 
 
     # escape backslashes and single quotes in strings that are put into
     # a Python string literal
-    for key in ('project', 'copyright', 'author', 'author_texescaped',
-                'project_doc_texescaped', 'version', 'release', 'master'):
+    for key in ('project', 'project_doc', 'project_doc_texescaped',
+                'author', 'author_texescaped', 'copyright',
+                'version', 'release', 'master'):
         d[key + '_str'] = d[key].replace('\\', '\\\\').replace("'", "\\'")
 
     if not path.isdir(d['path']):

sphinx/themes/basic/static/searchtools.js_t

 
     // lookup as object
     for (var i = 0; i < objectterms.length; i++) {
-      var others = Array.concat(objectterms.slice(0,i),
+      var others = [].concat(objectterms.slice(0,i),
                                 objectterms.slice(i+1, objectterms.length))
       var results = this.performObjectSearch(objectterms[i], others);
       // Assume first word is most likely to be the object,

sphinx/themes/basic/static/underscore.js

+// Underscore.js 0.5.5
+// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the terms of the MIT license.
+// Portions of Underscore are inspired by or borrowed from Prototype.js,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore/
 (function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.5";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
 a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
 var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,

sphinx/util/nodes.py

 explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
 caption_ref_re = explicit_title_re  # b/w compat alias
 
-
+IGNORED_NODES = (
+    nodes.Invisible,
+    nodes.Inline,
+    nodes.literal_block,
+    nodes.doctest_block,
+    #XXX there are probably more
+)
 def extract_messages(doctree):
     """Extract translatable messages from a document tree."""
     for node in doctree.traverse(nodes.TextElement):
-        if isinstance(node, (nodes.Invisible, nodes.Inline)):
+        if not node.source:
+            continue # built-in message
+        if isinstance(node, IGNORED_NODES):
             continue
         # <field_name>orphan</field_name>
         # XXX ignore all metadata (== docinfo)
         if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
             continue
+
         msg = node.rawsource.replace('\n', ' ').strip()
         # XXX nodes rendering empty are likely a bug in sphinx.addnodes
         if msg:

sphinx/util/osutil.py

 EEXIST = getattr(errno, 'EEXIST', 0)
 ENOENT = getattr(errno, 'ENOENT', 0)
 EPIPE  = getattr(errno, 'EPIPE', 0)
+EINVAL = getattr(errno, 'EINVAL', 0)
 
 # SEP separates path elements in the canonical file names
 #

sphinx/writers/latex.py

 %(fncychap)s
 %(longtable)s
 \usepackage{sphinx}
+\usepackage{multirow}
 %(preamble)s
 
 \title{%(title)s}
         self.no_contractions = 0
         self.compact_list = 0
         self.first_param = 0
+        self.previous_spanning_row = 0
+        self.previous_spanning_column = 0
+        self.remember_multirow = {}
 
     def astext(self):
         return (HEADER % self.elements +
     def visit_row(self, node):
         self.table.col = 0
     def depart_row(self, node):
-        self.body.append('\\\\\\hline\n')
+        if self.previous_spanning_row == 1:
+            self.previous_spanning_row = 0
+            self.body.append('\\\\\n')
+        else:
+            self.body.append('\\\\\\hline\n')
         self.table.rowcount += 1
 
     def visit_entry(self, node):
-        if 'morerows' in node or 'morecols' in node:
-            raise UnsupportedError('%s:%s: column or row spanning cells are '
-                                   'not yet implemented.' %
-                                   (self.curfilestack[-1], node.line or ''))
+        if self.remember_multirow.get(0, 0) > 1:
+            self.body.append(' & ')
         if self.table.col > 0:
             self.body.append(' & ')
         self.table.col += 1
+        self.context.append('')
+        if 'morerows' in node:
+            self.body.append(' \multirow{')
+            self.previous_spanning_row = 1
+            self.body.append(str(node.get('morerows') + 1))
+            self.body.append('}{*}{')
+            self.context.append('}')
+            self.remember_multirow[self.table.col] = node.get('morerows') + 1
+        if 'morecols' in node:
+            self.body.append(' \multicolumn{')
+            self.body.append(str(node.get('morecols') + 1))
+            if self.table.col == 1:
+                self.body.append('}{|l|}{')
+            else:
+                self.body.append('}{l|}{')
+            self.context.append('}')
         if isinstance(node.parent.parent, nodes.thead):
             self.body.append('\\textbf{')
             self.context.append('}')
-        else:
-            self.context.append('')
+        if self.remember_multirow.get(self.table.col + 1, 0) > 1:
+            self.remember_multirow[self.table.col + 1] -= 1
+            self.context.append(' & ')
     def depart_entry(self, node):
         self.body.append(self.context.pop()) # header
 

sphinx/writers/manpage.py

     def visit_admonition(self, node, name=None):
         if name:
             self.body.append('.IP %s\n' %
-                             admonitionlabels.get(name, name))
+                             self.deunicode(admonitionlabels.get(name, name)))
 
     def visit_productionlist(self, node):
         self.ensure_eol()

sphinx/writers/texinfo.py

 """
 
 import re
+import string
+import textwrap
 from os import path
-import textwrap
 
 from docutils import nodes, writers
 
         s = s.replace('@', '@@')
         s = s.replace('{', '@{')
         s = s.replace('}', '@}')
+        # prevent `` and '' quote conversion
+        s = s.replace('``', "`@w{`}")
+        s = s.replace("''", "'@w{'}")
         # prevent "--" from being converted to an "em dash"
         # s = s.replace('-', '@w{-}')
         return s
         s = ' '.join(s.split()).strip()
         return s
 
+    def ensure_eol(self):
+        """Ensure the last line in body is terminated by new line."""
+        if self.body and self.body[-1][-1:] != '\n':
+            self.body.append('\n')
+
     def format_menu_entry(self, name, node_name, desc):
         if name == node_name:
             s = '* %s:: ' % (name,)
     def visit_block_quote(self, node):
         self.body.append('\n@quotation\n')
     def depart_block_quote(self, node):
-        self.body.append('\n@end quotation\n\n')
+        self.ensure_eol()
+        self.body.append('@end quotation\n')
 
     def visit_literal_block(self, node):
         self.body.append('\n@example\n')
         bullet = node.get('bullet', '*')
         self.body.append('\n\n@itemize %s\n' % bullet)
     def depart_bullet_list(self, node):
-        self.body.append('\n@end itemize\n\n')
+        self.ensure_eol()
+        self.body.append('@end itemize\n')
 
     def visit_enumerated_list(self, node):
         # doesn't support Roman numerals
         start = node.get('start', starters.get(enum, ''))
         self.body.append('\n\n@enumerate %s\n' % start)
     def depart_enumerated_list(self, node):
-        self.body.append('\n@end enumerate\n\n')
+        self.ensure_eol()
+        self.body.append('@end enumerate\n')
 
     def visit_list_item(self, node):
-        self.body.append('\n@item\n')
+        self.body.append('\n@item ')
     def depart_list_item(self, node):
         pass
 
     def visit_option_list(self, node):
         self.body.append('\n\n@table @option\n')
     def depart_option_list(self, node):
-        self.body.append('\n@end table\n\n')
+        self.ensure_eol()
+        self.body.append('@end table\n')
 
     def visit_option_list_item(self, node):
         pass
     def visit_definition_list(self, node):
         self.body.append('\n\n@table @asis\n')
     def depart_definition_list(self, node):
-        self.body.append('\n@end table\n\n')
+        self.ensure_eol()
+        self.body.append('@end table\n')
 
     def visit_definition_list_item(self, node):
         self.at_item_x = '@item'
     def visit_field_list(self, node):
         self.body.append('\n\n@itemize @w\n')
     def depart_field_list(self, node):
-        self.body.append('\n@end itemize\n\n')
+        self.ensure_eol()
+        self.body.append('@end itemize\n')
 
     def visit_field(self, node):
         if not isinstance(node.parent, nodes.field_list):
         if not name:
             name = self.escape(node[0].astext())
         self.body.append('\n@cartouche\n'
-                         '@quotation %s\n' % name)
+                         '@quotation %s ' % name)
     def depart_admonition(self, node):
-        self.body.append('\n@end quotation\n'
-                         '@end cartouche\n\n')
+        self.ensure_eol()
+        self.body.append('@end quotation\n'
+                         '@end cartouche\n')
 
     def _make_visit_admonition(typ):
         def visit(self, node):
-            self.body.append('\n@cartouche\n'
-                             '@quotation %s\n' % self.escape(_(typ)))
+            self.visit_admonition(node, self.escape(_(typ)))
         return visit
 
     visit_attention = _make_visit_admonition('Attention')
         width = self.tex_image_length(attrs.get('width', ''))
         height = self.tex_image_length(attrs.get('height', ''))
         alt = self.escape_arg(attrs.get('alt', ''))
-        self.body.append('\n\n@image{%s,%s,%s,%s,%s}\n\n' %
+        self.body.append('\n@image{%s,%s,%s,%s,%s}\n' %
                          (name, width, height, alt, ext[1:]))
     def depart_image(self, node):
         pass
         pass
 
     def visit_comment(self, node):
+        self.body.append('\n')
         for line in node.astext().splitlines():
-            self.body.append('\n@c %s\n' % line)
+            self.body.append('@c %s\n' % line)
         raise nodes.SkipNode
 
     def visit_problematic(self, node):
         self.body.append('}')
 
     def visit_index(self, node):
+        # terminate the line but don't prevent paragraph breaks
+        if isinstance(node.parent, nodes.paragraph):
+            self.ensure_eol()
+        else:
+            self.body.append('\n')
         for entry in node['entries']:
             typ, text, tid, text2 = entry
             text = self.escape_menu(text)
     def visit_refcount(self, node):
         self.body.append('\n')
     def depart_refcount(self, node):
-        self.body.append('\n\n')
+        self.body.append('\n')
 
     def visit_versionmodified(self, node):
         intro = versionlabels[node['type']] % node['version']
             intro += ': '
         else:
             intro += '.'
-        self.body.append('\n\n%s' % self.escape(intro))
+        self.body.append('\n%s' % self.escape(intro))
     def depart_versionmodified(self, node):
-        self.body.append('\n\n')
+        self.body.append('\n')
 
     def visit_start_of_file(self, node):
         # add a document target
 
     ## Desc
 
-    desc_map = {
-        'cfunction':    'C Function',
-        'classmethod':  'Class Method',
-        'cmacro':       'C Macro',
-        'cmdoption':    'Command Option',
-        'cmember':      'C Member',
-        'confval':      'Configuration Value',
-        'ctype':        'C Type',
-        'cvar':         'C Variable',
-        'describe':     'Description',
-        'envvar':       'Environment Variable',
-        'staticmethod': 'Static Method',
-        'var':          'Variable',
-        }
-
     def visit_desc(self, node):
         self.at_deffnx = '@deffn'
     def depart_desc(self, node):
-        self.body.append('\n@end deffn\n\n')
+        self.ensure_eol()
+        self.body.append('@end deffn\n')
+
     def visit_desc_signature(self, node):
-        self.desctype = node.parent['desctype'].strip()
-        if self.desctype != 'describe':
+        objtype = node.parent['objtype']
+        if objtype != 'describe':
             for id in node.get('ids'):
                 self.add_anchor(id, node)
-        typ = _(self.desc_map.get(self.desctype,
-                                  self.desctype.capitalize()))
-        self.body.append('\n%s {%s} ' % (self.at_deffnx, self.escape_arg(typ)))
+        # use the full name of the objtype for the category
+        try:
+            domain = self.builder.env.domains[node.parent['domain']]
+            primary = self.builder.config.primary_domain
+            name = domain.get_type_name(domain.object_types[objtype],
+                                        primary == domain.name)
+        except KeyError:
+            name = objtype
+        category = self.escape_arg(string.capwords(name))
+        self.body.append('\n%s {%s} ' % (self.at_deffnx, category))
         self.at_deffnx = '@deffnx'
     def depart_desc_signature(self, node):
         self.body.append("\n")
             self.body.append(', ')
         else:
             self.first_param = 0
-        self.body.append(self.escape(node.astext()))
+        text = self.escape(node.astext())
+        # replace no-break spaces with normal ones
+        text = text.replace(u' ', '@w{ }')
+        self.body.append(text)
         raise nodes.SkipNode
 
     def visit_desc_optional(self, node):
         raise nodes.SkipNode
 
     def visit_desc_content(self, node):
-        self.body.append("\n")
+        pass
     def depart_desc_content(self, node):
         pass
 

tests/root/autosummary.txt

    :toctree: generated
 
    sphinx.application.TemplateBridge
+
+.. currentmodule:: sphinx.application
+
+.. autoclass:: TemplateBridge
+
+   Basic test
+
+   .. autosummary::
+
+      render                    -- some ignored stuff goes here
+      render_string             More ignored stuff
+
+   Test with tildes
+
+   .. autosummary::
+
+      ~TemplateBridge.render
+      ~TemplateBridge.render_string
+
+   Methods:
+
+  .. automethod:: render
+
+  .. automethod:: render_string

tests/root/markup.txt

    | 2  | Empty cells:   |    |
    +----+----------------+----+
 
+Table with multicol:
+
+   +----+---------------------+
+   | 1  | test!               |
+   +----+---------+------+----+
+   | 2  | col     | col  | c  |
+   | y  +---------+------+----+
+   | x  | test                |
+   +----+---------------------+
+
 
 Figures
 -------

tests/test_autosummary.py

     :: (a, b[, aaa, bbb, ccc, ...])
     (a, b, c=(), d=<foo>) :: (a, b[, c, d])
     (a, b, c='foobar()', d=123) :: (a, b[, c, d])
+    (a, b[, c]) :: (a, b[, c])
+    (a, b[, cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]) :: (a, b[, ...)
+    (a, b='c=d, e=f, g=h', c=3) :: (a[, b, c])
+    (a, b='c=d, \\'e=f,\\' g=h', c=3) :: (a[, b, c])
+    (a, b='c=d, ', e='\\\\' g=h, c=3) :: (a[, b, e, c])
+    (a, b={'c=d, ': 3, '\\\\': 3}) :: (a[, b])
+    (a=1, b=2, c=3) :: ([a, b, c])
+    (a=1, b=<SomeClass: a, b, c>, c=3) :: ([a, b, c])
     """
 
     TEST = [map(lambda x: x.strip(), x.split("::")) for x in TEST.split("\n")

tests/test_intersphinx.py

     rn = missing_reference(app, app.env, node, contnode)
     assert rn is None
     assert contnode[0].astext() == 'py3k:unknown'
+
+
+@with_app(confoverrides={'extensions': 'sphinx.ext.intersphinx'})
+@with_tempdir
+def test_load_mappings_warnings(tempdir, app):
+    """
+    load_mappings issues a warning if new-style mapping
+    identifiers are not alphanumeric
+    """
+    inv_file = tempdir / 'inventory'
+    write_file(inv_file, inventory_v2)
+    app.config.intersphinx_mapping = {
+        'http://docs.python.org/': inv_file,
+        'py3k': ('http://docs.python.org/py3k/', inv_file),
+        'repoze.workflow': ('http://docs.repoze.org/workflow/', inv_file),
+        'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/', inv_file)
+    }
+
+    app.config.intersphinx_cache_limit = 0
+    # load the inventory and check if it's done correctly
+    load_mappings(app)
+    assert len(app._warning.content) == 2
+
+