Commits

Georg Brandl committed 8ca644b Merge

merge with 1.0

Comments (0)

Files changed (15)

 Release 1.0.6 (in development)
 ==============================
 
+* #574: Add special code for better support of Japanese documents
+  in the LaTeX builder.
+
+* Regression of #77: If there is only one parameter given with
+  ``:param:`` markup, the bullet list is now suppressed again.
+
+* #556: Fix missing paragraph breaks in LaTeX output in certain
+  situations.
+
+* #567: Emit the ``autodoc-process-docstring`` event even for objects
+  without a docstring so that it can add content.
+
+* #565: In the LaTeX builder, not only literal blocks require different
+  table handling, but also quite a few other list-like block elements.
+
+* #515: Fix tracebacks in the viewcode extension for Python objects
+  that do not have a valid signature.
+
 * Fix strange reportings of line numbers for warnings generated from
   autodoc-included docstrings, due to different behavior depending
   on docutils version.

doc/markup/misc.rst

 
    :fieldname: Field content
 
-A field list at the very top of a file is parsed by docutils as the "docinfo",
+A field list near the top of a file is parsed by docutils as the "docinfo"
 which is normally used to record the author, date of publication and other
-metadata.  *In Sphinx*, the docinfo is used as metadata, too, but not displayed
-in the output.
+metadata.  *In Sphinx*, a field list preceding any other markup is moved from
+the docinfo to the Sphinx environment as document metadata and is not displayed
+in the output; a field list appearing after the document title will be part of
+the docinfo as normal and will be displayed in the output.
 
 At the moment, these metadata fields are recognized:
 
 
 .. warning::
 
-   Tables that contain literal blocks cannot be set with ``tabulary``.  They are
-   therefore set with the standard LaTeX ``tabular`` environment.  Also, the
-   verbatim environment used for literal blocks only works in ``p{width}``
-   columns, which means that by default, Sphinx generates such column specs for
-   such tables.  Use the :rst:dir:`tabularcolumns` directive to get finer control
-   over such tables.
+   Tables that contain list-like elements such as object descriptions,
+   blockquotes or any kind of lists cannot be set out of the box with
+   ``tabulary``.  They are therefore set with the standard LaTeX ``tabular``
+   environment if you don't give a ``tabularcolumns`` directive.  If you do, the
+   table will be set with ``tabulary``, but you must use the ``p{width}``
+   construct for the columns that contain these elements.
+
+   Literal blocks do not work with ``tabulary`` at all, so tables containing a
+   literal block are always set with ``tabular``.  Also, the verbatim
+   environment used for literal blocks only works in ``p{width}`` columns, which
+   means that by default, Sphinx generates such column specs for such tables.
+   Use the :rst:dir:`tabularcolumns` directive to get finer control over such
+   tables.

sphinx/builders/html.py

         # outfilename's path is in general different from self.outdir
         ensuredir(path.dirname(outfilename))
         try:
-            f = codecs.open(outfilename, 'w', encoding)
+            f = codecs.open(outfilename, 'w', encoding, 'xmlcharrefreplace')
             try:
                 f.write(output)
             finally:

sphinx/domains/c.py

                 self._parse_type(param, arg)
             else:
                 self._parse_type(param, ctype)
-                param += nodes.emphasis(' '+argname, ' '+argname)
+                # separate by non-breaking space in the output
+                param += nodes.emphasis(' '+argname, u'\xa0'+argname)
             paramlist += param
         signode += paramlist
         if const:

sphinx/domains/cpp.py

         return self.type.get_id()
 
     def __unicode__(self):
-        return (self.type is not None and u'%s %s' % (self.type, self.name)
-                or unicode(self.name)) + (self.default is not None and
-                                          u'=%s' % self.default or u'')
+        return (u'%s %s' % (self.type or u'', self.name or u'')).strip() + \
+               (self.default is not None and u'=%s' % self.default or u'')
 
 
 class NamedDefExpr(DefExpr):

sphinx/domains/python.py

         TypedField('parameter', label=l_('Parameters'),
                    names=('param', 'parameter', 'arg', 'argument',
                           'keyword', 'kwarg', 'kwparam'),
-                   typerolename='obj', typenames=('paramtype', 'type')),
+                   typerolename='obj', typenames=('paramtype', 'type'),
+                   can_collapse=True),
         TypedField('variable', label=l_('Variables'), rolename='obj',
                    names=('var', 'ivar', 'cvar'),
-                   typerolename='obj', typenames=('vartype',)),
+                   typerolename='obj', typenames=('vartype',),
+                   can_collapse=True),
         GroupedField('exceptions', label=l_('Raises'), rolename='exc',
                      names=('raises', 'raise', 'exception', 'except'),
                      can_collapse=True),

sphinx/environment.py

                 self.clear_doc(docname)
 
             # read all new and changed files
-            to_read = added | changed
-            for docname in sorted(to_read):
+            for docname in sorted(added | changed):
                 yield docname
                 self.read_doc(docname, app=app)
 

sphinx/ext/autodoc.py

         if not no_docstring:
             encoding = self.analyzer and self.analyzer.encoding
             docstrings = self.get_doc(encoding)
+            if not docstrings:
+                # append at least a dummy docstring, so that the event
+                # autodoc-process-docstring is fired and can add some
+                # content if desired
+                docstrings.append([])
             for i, line in enumerate(self.process_doc(docstrings)):
                 self.add_line(line, sourcename, i)
 
         content = self.env.config.autoclass_content
 
         docstrings = []
-        docstring = self.get_attr(self.object, '__doc__', None)
-        if docstring:
-            docstrings.append(docstring)
+        attrdocstring = self.get_attr(self.object, '__doc__', None)
+        if attrdocstring:
+            docstrings.append(attrdocstring)
 
         # for classes, what the "docstring" is can be controlled via a
         # config value; the default is only the class docstring

sphinx/ext/viewcode.py

             modname = signode.get('module')
             if not modname:
                 continue
-            fullname = signode['fullname']
+            fullname = signode.get('fullname')
             if not has_tag(modname, fullname, env.docname):
                 continue
             if fullname in names:

sphinx/texinputs/Makefile

 all-dvi: $(ALLDVI)
 all-ps: all-dvi
 	for f in *.dvi; do dvips $$f; done
+all-pdf-ja: $(wildcard *.tex)
+	ebb $(wildcard *.pdf *.png *.gif *.jpeg)
+	platex -kanji=utf8 $(LATEXOPTS) '$<'
+	platex -kanji=utf8 $(LATEXOPTS) '$<'
+	platex -kanji=utf8 $(LATEXOPTS) '$<'
+	-mendex -U -f -d '$(basename $<).dic' -s python.ist '$(basename $<).idx'
+	platex -kanji=utf8 $(LATEXOPTS) '$<'
+	platex -kanji=utf8 $(LATEXOPTS) '$<'
+	dvipdfmx '$(basename $<).dvi'
 
 zip: all-$(FMT)
 	mkdir $(ARCHIVEPREFIX)docs-$(FMT)

sphinx/util/docfields.py

     is_typed = True
 
     def __init__(self, name, names=(), typenames=(), label=None,
-                 rolename=None, typerolename=None):
-        GroupedField.__init__(self, name, names, label, rolename, False)
+                 rolename=None, typerolename=None, can_collapse=False):
+        GroupedField.__init__(self, name, names, label, rolename, can_collapse)
         self.typenames = typenames
         self.typerolename = typerolename
 
     def make_field(self, types, domain, items):
-        fieldname = nodes.field_name('', self.label)
-        listnode = self.list_type()
-        for fieldarg, content in items:
+        def handle_item(fieldarg, content):
             par = nodes.paragraph()
             par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong)
             if fieldarg in types:
                 par += nodes.Text(')')
             par += nodes.Text(' -- ')
             par += content
-            listnode += nodes.list_item('', par)
-        fieldbody = nodes.field_body('', listnode)
+            return par
+
+        fieldname = nodes.field_name('', self.label)
+        if len(items) == 1 and self.can_collapse:
+            fieldarg, content = items[0]
+            bodynode = handle_item(fieldarg, content)
+        else:
+            bodynode = self.list_type()
+            for fieldarg, content in items:
+                bodynode += nodes.list_item('', handle_item(fieldarg, content))
+        fieldbody = nodes.field_body('', bodynode)
         return nodes.field('', fieldname, fieldbody)
 
 

sphinx/writers/latex.py

         self.colspec = None
         self.rowcount = 0
         self.had_head = False
+        self.has_problematic = False
         self.has_verbatim = False
         self.caption = None
         self.longtable = False
             lang = babel.get_language()
             if lang:
                 self.elements['classoptions'] += ',' + babel.get_language()
+            elif builder.config.language == 'ja':
+                self.elements['classoptions'] += ',english,dvipdfm'
+                # not elements of babel, but this should be above sphinx.sty.
+                # because pTeX (Japanese TeX) cannot handle this count.
+                self.elements['babel'] += r'\newcount\pdfoutput\pdfoutput=0'
+                # to make the pdf with correct encoded hyperref bookmarks
+                self.elements['preamble'] += r'\AtBeginDvi{\special{pdf:tounicode EUC-UCS2}}'
             else:
                 self.builder.warn('no Babel option known for language %r' %
                                   builder.config.language)
 
     def visit_desc(self, node):
         self.body.append('\n\n\\begin{fulllineitems}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_desc(self, node):
         self.body.append('\n\\end{fulllineitems}\n\n')
 
                              u'\\capstart\\caption{%s}\n' % self.table.caption)
         if self.table.longtable:
             self.body.append('\n\\begin{longtable}')
+            endmacro = '\\end{longtable}\n\n'
         elif self.table.has_verbatim:
             self.body.append('\n\\begin{tabular}')
+            endmacro = '\\end{tabular}\n\n'
+        elif self.table.has_problematic and not self.table.colspec:
+            # if the user has given us tabularcolumns, accept them and use
+            # tabulary nevertheless
+            self.body.append('\n\\begin{tabular}')
+            endmacro = '\\end{tabular}\n\n'
         else:
             self.body.append('\n\\begin{tabulary}{\\linewidth}')
+            endmacro = '\\end{tabulary}\n\n'
         if self.table.colspec:
             self.body.append(self.table.colspec)
         else:
-            if self.table.has_verbatim:
+            if self.table.has_problematic:
                 colwidth = 0.95 / self.table.colcount
                 colspec = ('p{%.3f\\linewidth}|' % colwidth) * \
                           self.table.colcount
         else:
             self.body.append('\\hline\n')
         self.body.extend(self.tablebody)
-        if self.table.longtable:
-            self.body.append('\\end{longtable}\n\n')
-        elif self.table.has_verbatim:
-            self.body.append('\\end{tabular}\n\n')
-        else:
-            self.body.append('\\end{tabulary}\n\n')
+        self.body.append(endmacro)
         if not self.table.longtable and self.table.caption is not None:
             self.body.append('\\end{threeparttable}\n\n')
         self.table = None
     def visit_bullet_list(self, node):
         if not self.compact_list:
             self.body.append('\\begin{itemize}\n' )
+        if self.table:
+            self.table.has_problematic = True
     def depart_bullet_list(self, node):
         if not self.compact_list:
             self.body.append('\\end{itemize}\n' )
         self.body.append('\\begin{enumerate}\n' )
         if 'start' in node:
             self.body.append('\\setcounter{enumi}{%d}\n' % (node['start'] - 1))
+        if self.table:
+            self.table.has_problematic = True
     def depart_enumerated_list(self, node):
         self.body.append('\\end{enumerate}\n' )
 
 
     def visit_definition_list(self, node):
         self.body.append('\\begin{description}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_definition_list(self, node):
         self.body.append('\\end{description}\n')
 
 
     def visit_field_list(self, node):
         self.body.append('\\begin{quote}\\begin{description}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_field_list(self, node):
         self.body.append('\\end{description}\\end{quote}\n')
 
     def visit_paragraph(self, node):
         self.body.append('\n')
     def depart_paragraph(self, node):
-        self.body.append('\n')
+        self.body.append('\n\n')
 
     def visit_centered(self, node):
         self.body.append('\n\\begin{center}')
+        if self.table:
+            self.table.has_problematic = True
     def depart_centered(self, node):
         self.body.append('\n\\end{center}')
 
         self.compact_list += 1
         self.body.append('\\begin{itemize}\\setlength{\\itemsep}{0pt}'
                          '\\setlength{\\parskip}{0pt}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_hlist(self, node):
         self.compact_list -= 1
         self.body.append('\\end{itemize}\n')
         if self.table:
             hlcode = hlcode.replace('\\begin{Verbatim}',
                                     '\\begin{OriginalVerbatim}')
+            self.table.has_problematic = True
             self.table.has_verbatim = True
         # get consistent trailer
         hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
                              '\\begin{DUlineblock}{\\DUlineblockindent}\n')
         else:
             self.body.append('\n\\begin{DUlineblock}{0em}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_line_block(self, node):
         self.body.append('\\end{DUlineblock}\n')
 
                 done = 1
         if not done:
             self.body.append('\\begin{quote}\n')
+            if self.table:
+                self.table.has_problematic = True
     def depart_block_quote(self, node):
         done = 0
         if len(node.children) == 1:
 
     def visit_option_list(self, node):
         self.body.append('\\begin{optionlist}{3cm}\n')
+        if self.table:
+            self.table.has_problematic = True
     def depart_option_list(self, node):
         self.body.append('\\end{optionlist}\n')
 

tests/root/autodoc_fodder.py

+
+class MarkupError(object):
+    """
+    .. note:: This is a docstring with a
+    small markup error which should have
+    correct location information.
+    """

tests/root/objects.txt

 
 .. class:: TimeInt
 
+   Has only one parameter (triggers special behavior...)
+
    :param moo: |test|
    :type moo: |test|
 
 
 .. class:: Time(hour, minute, isdst)
 
-   :param hour: The year.
-   :type hour: TimeInt
+   :param year: The year.
+   :type year: TimeInt
    :param TimeInt minute: The minute.
    :param isdst: whether it's DST
    :type isdst: * some complex
    :ivar int hour: like *hour*
    :ivar minute: like *minute*
    :vartype minute: int
+   :param hour: Some parameter
+   :type hour: DuplicateType
    :param hour: Duplicate param.  Should not lead to crashes.
-   :type hour: Duplicate type.
+   :type hour: DuplicateType
 
 
 C items

tests/test_build_html.py

         # custom sidebar
         (".//h4", 'Custom sidebar'),
         # docfields
-        (".//td[@class='field-body']/ul/li/strong", '^moo$'),
-        (".//td[@class='field-body']/ul/li/strong",
+        (".//td[@class='field-body']/strong", '^moo$'),
+        (".//td[@class='field-body']/strong",
              tail_check(r'\(Moo\) .* Moo')),
+        (".//td[@class='field-body']/ul/li/strong", '^hour$'),
+        (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
+        (".//td[@class='field-body']/ul/li/em",
+             tail_check(r'.* Some parameter')),
     ],
     'contents.html': [
         (".//meta[@name='hc'][@content='hcval']", ''),