summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES11
-rw-r--r--doc/config.rst10
-rw-r--r--doc/extdev/appapi.rst12
-rw-r--r--sphinx/application.py5
-rw-r--r--sphinx/builders/__init__.py4
-rw-r--r--sphinx/builders/epub.py6
-rw-r--r--sphinx/builders/gettext.py19
-rw-r--r--sphinx/builders/html.py10
-rw-r--r--sphinx/builders/latex.py1
-rw-r--r--sphinx/builders/qthelp.py2
-rw-r--r--sphinx/builders/websupport.py7
-rw-r--r--sphinx/config.py7
-rw-r--r--sphinx/environment.py91
-rw-r--r--sphinx/ext/graphviz.py10
-rw-r--r--sphinx/ext/pngmath.py2
-rw-r--r--sphinx/themes/basic/layout.html2
-rw-r--r--sphinx/themes/bizstyle/static/bizstyle.css_t8
-rw-r--r--sphinx/transforms.py58
-rw-r--r--sphinx/writers/html.py16
-rw-r--r--sphinx/writers/latex.py10
-rw-r--r--tests/roots/test-intl/conf.py1
-rw-r--r--tests/roots/test-numfig/bar.rst58
-rw-r--r--tests/roots/test-numfig/baz.rst16
-rw-r--r--tests/roots/test-numfig/conf.py3
-rw-r--r--tests/roots/test-numfig/foo.rst71
-rw-r--r--tests/roots/test-numfig/index.rst36
-rw-r--r--tests/roots/test-numfig/rimg.pngbin0 -> 218 bytes
-rw-r--r--tests/test_build_gettext.py32
-rw-r--r--tests/test_build_html.py399
-rw-r--r--tests/test_build_latex.py10
30 files changed, 864 insertions, 53 deletions
diff --git a/CHANGES b/CHANGES
index 258a29ad..f55a5a09 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,8 @@ Incompatible changes
templates directory.
* Custom domains should implement the new `Domain.resolve_any_xref`
method to make the `any` role work properly.
+* gettext builder: disable extracting/apply 'index' node by default. Please set
+ 'index' to :confval:`gettext_enables` to enable extracting index entries.
Features added
--------------
@@ -44,6 +46,8 @@ Features added
directive is now supported.
* PR#214: Added stemming support for 14 languages, so that the built-in document
search can now handle these. Thanks to Shibukawa Yoshiki.
+* PR#296: numfig feature: Assign numbers to figures, tables and code-blocks.
+ Thanks to Takeshi Komiya.
* PR#202: Allow "." and "~" prefixed references in ``:param:`` doc fields
for Python.
* PR#184: Add `autodoc_mock_imports`, allowing to mock imports of
@@ -83,6 +87,10 @@ Features added
``+``.
* PR#291: The caption of :rst:dir:`code-block` is recognised as a title of ref
target. Thanks to Takeshi Komiya.
+* PR#298: Add new API: :meth:`~sphinx.application.Sphinx.add_latex_package`.
+ Thanks to Takeshi Komiya.
+* #1344: add :confval:`gettext_enables` to enable extracting 'index' to gettext
+ catalog output / applying translation catalog to generated documentation.
Bugs fixed
----------
@@ -147,6 +155,9 @@ Bugs fixed
to avoid separation of caption and body. Thanks to Takeshi Komiya.
* PR#295, #1520: ``make.bat latexpdf`` mechanism to ``cd`` back to the current
directory. Thanks to Peter Suter.
+* PR#297, #1571: Add imgpath property to all builders. It make easier to
+ develop builder extensions. Thanks to Takeshi Komiya.
+* #1584: Point to master doc in HTML "top" link.
Documentation
-------------
diff --git a/doc/config.rst b/doc/config.rst
index a11254ea..f181e5c5 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -447,6 +447,16 @@ documentation on :ref:`intl` for details.
.. versionadded:: 1.3
+.. confval:: gettext_enables
+
+ To specify names to enable gettext extracting and translation applying for
+ i18n. You can specify below names:
+
+ :index: index terms
+
+ The default is ``[]``.
+
+ .. versionadded:: 1.3
.. _html-options:
diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst
index 4fed158c..d0d49286 100644
--- a/doc/extdev/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -288,6 +288,18 @@ package.
.. versionadded:: 1.0
+.. method:: Sphinx.add_latex_package(packagename, options=None)
+
+ Add *packagename* to the list of packages that LaTeX source code will include.
+ If you provide *options*, it will be taken to `\usepackage` declaration.
+
+ .. code-block:: python
+
+ app.add_latex_package('mypackage') # => \usepackage{mypackage}
+ app.add_latex_package('mypackage', 'foo,bar') # => \usepackage[foo,bar]{mypackage}
+
+ .. versionadded:: 1.3
+
.. method:: Sphinx.add_lexer(alias, lexer)
Use *lexer*, which must be an instance of a Pygments lexer class, to
diff --git a/sphinx/application.py b/sphinx/application.py
index 13a2d272..630bff36 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -694,6 +694,11 @@ class Sphinx(object):
StandaloneHTMLBuilder.css_files.append(
posixpath.join('_static', filename))
+ def add_latex_package(self, packagename, options=None):
+ self.debug('[app] adding latex package: %r', packagename)
+ from sphinx.builders.latex import LaTeXBuilder
+ LaTeXBuilder.usepackages.append((packagename, options))
+
def add_lexer(self, alias, lexer):
self.debug('[app] adding lexer: %r', (alias, lexer))
from sphinx.highlighting import lexers
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index d52a7983..7d1bd920 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -70,6 +70,10 @@ class Builder(object):
# images that need to be copied over (source -> dest)
self.images = {}
+ # basename of images directory
+ self.imagedir = ""
+ # relative path to image directory from current docname (used at writing docs)
+ self.imgpath = ""
# these get set later
self.parallel_ok = False
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index 5f9f6643..848dfaa4 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -404,7 +404,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
The method tries to read and write the files with the PIL,
converting the format and resizing the image if necessary/possible.
"""
- ensuredir(path.join(self.outdir, '_images'))
+ ensuredir(path.join(self.outdir, self.imagedir))
for src in self.app.status_iterator(self.images, 'copying images... ',
brown, len(self.images)):
dest = self.images[src]
@@ -416,7 +416,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
(path.join(self.srcdir, src), ))
try:
copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, '_images', dest))
+ path.join(self.outdir, self.imagedir, dest))
except (IOError, OSError) as err:
self.warn('cannot copy image file %r: %s' %
(path.join(self.srcdir, src), err))
@@ -432,7 +432,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
nh = (height * nw) / width
img = img.resize((nw, nh), Image.BICUBIC)
try:
- img.save(path.join(self.outdir, '_images', dest))
+ img.save(path.join(self.outdir, self.imagedir, dest))
except (IOError, OSError) as err:
self.warn('cannot write image file %r: %s' %
(path.join(self.srcdir, src), err))
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index 082059fd..01fa06a6 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -108,15 +108,16 @@ class I18nBuilder(Builder):
for node, msg in extract_messages(doctree):
catalog.add(msg, node)
- # Extract translatable messages from index entries.
- for node, entries in traverse_translatable_index(doctree):
- for typ, msg, tid, main in entries:
- for m in split_index_msg(typ, msg):
- if typ == 'pair' and m in pairindextypes.values():
- # avoid built-in translated message was incorporated
- # in 'sphinx.util.nodes.process_index_entry'
- continue
- catalog.add(m, node)
+ if 'index' in self.env.config.gettext_enables:
+ # Extract translatable messages from index entries.
+ for node, entries in traverse_translatable_index(doctree):
+ for typ, msg, tid, main in entries:
+ for m in split_index_msg(typ, msg):
+ if typ == 'pair' and m in pairindextypes.values():
+ # avoid built-in translated message was incorporated
+ # in 'sphinx.util.nodes.process_index_entry'
+ continue
+ catalog.add(m, node)
# determine tzoffset once to remain unaffected by DST change during build
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index c2c30893..d33d2eef 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -95,6 +95,8 @@ class StandaloneHTMLBuilder(Builder):
# a hash of all config values that, if changed, cause a full rebuild
self.config_hash = ''
self.tags_hash = ''
+ # basename of images directory
+ self.imagedir = '_images'
# section numbers for headings in the currently visited document
self.secnumbers = {}
# currently written docname
@@ -424,6 +426,7 @@ class StandaloneHTMLBuilder(Builder):
doctree.settings = self.docsettings
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
+ self.fignumbers = self.env.toc_fignumbers.get(docname, {})
self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads')
self.current_docname = docname
@@ -436,7 +439,7 @@ class StandaloneHTMLBuilder(Builder):
self.handle_page(docname, ctx, event_arg=doctree)
def write_doc_serialized(self, docname, doctree):
- self.imgpath = relative_uri(self.get_target_uri(docname), '_images')
+ self.imgpath = relative_uri(self.get_target_uri(docname), self.imagedir)
self.post_process_images(doctree)
title = self.env.longtitles.get(docname)
title = title and self.render_partial(title)['title'] or ''
@@ -534,13 +537,13 @@ class StandaloneHTMLBuilder(Builder):
def copy_image_files(self):
# copy image files
if self.images:
- ensuredir(path.join(self.outdir, '_images'))
+ ensuredir(path.join(self.outdir, self.imagedir))
for src in self.app.status_iterator(self.images, 'copying images... ',
brown, len(self.images)):
dest = self.images[src]
try:
copyfile(path.join(self.srcdir, src),
- path.join(self.outdir, '_images', dest))
+ path.join(self.outdir, self.imagedir, dest))
except Exception as err:
self.warn('cannot copy image file %r: %s' %
(path.join(self.srcdir, src), err))
@@ -1028,6 +1031,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
def init(self):
self.config_hash = ''
self.tags_hash = ''
+ self.imagedir = '_images'
self.theme = None # no theme necessary
self.templates = None # no template bridge necessary
self.init_translator_class()
diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py
index bf7991cf..5683ade2 100644
--- a/sphinx/builders/latex.py
+++ b/sphinx/builders/latex.py
@@ -37,6 +37,7 @@ class LaTeXBuilder(Builder):
format = 'latex'
supported_image_types = ['application/pdf', 'image/png',
'image/gif', 'image/jpeg']
+ usepackages = []
def init(self):
self.docnames = []
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index c0fff2a6..8e2558e7 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -157,7 +157,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
olen = len(outdir)
projectfiles = []
staticdir = path.join(outdir, '_static')
- imagesdir = path.join(outdir, '_images')
+ imagesdir = path.join(outdir, self.imagedir)
for root, dirs, files in os.walk(outdir):
resourcedir = root.startswith(staticdir) or \
root.startswith(imagesdir)
diff --git a/sphinx/builders/websupport.py b/sphinx/builders/websupport.py
index 7b0e6f72..619ef6fe 100644
--- a/sphinx/builders/websupport.py
+++ b/sphinx/builders/websupport.py
@@ -58,7 +58,8 @@ class WebSupportBuilder(PickleHTMLBuilder):
doctree.settings = self.docsettings
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
- self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images')
+ self.fignumbers = self.env.toc_fignumbers.get(docname, {})
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads')
self.current_docname = docname
self.docwriter.write(doctree, destination)
@@ -70,7 +71,7 @@ class WebSupportBuilder(PickleHTMLBuilder):
self.handle_page(docname, ctx, event_arg=doctree)
def write_doc_serialized(self, docname, doctree):
- self.imgpath = '/' + posixpath.join(self.virtual_staticdir, '_images')
+ self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
self.post_process_images(doctree)
title = self.env.longtitles.get(docname)
title = title and self.render_partial(title)['title'] or ''
@@ -148,7 +149,7 @@ class WebSupportBuilder(PickleHTMLBuilder):
PickleHTMLBuilder.handle_finish(self)
# move static stuff over to separate directory
- directories = ['_images', '_static']
+ directories = [self.imagedir, '_static']
for directory in directories:
src = path.join(self.outdir, directory)
dst = path.join(self.staticdir, directory)
diff --git a/sphinx/config.py b/sphinx/config.py
index 40536ae5..8593a190 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -70,6 +70,12 @@ class Config(object):
needs_extensions = ({}, None),
nitpicky = (False, 'env'),
nitpick_ignore = ([], 'html'),
+ numfig = (False, 'env'),
+ numfig_secnum_depth = (1, 'env'),
+ numfig_prefix = ({'figure': l_('Fig.'),
+ 'table': l_('Table '),
+ 'code-block': l_('List ')},
+ 'env'),
# HTML options
html_theme = ('default', 'html'),
@@ -205,6 +211,7 @@ class Config(object):
gettext_location = (True, 'gettext'),
gettext_uuid = (True, 'gettext'),
gettext_auto_build = (True, 'env'),
+ gettext_enables = ([], 'env'),
# XML options
xml_pretty = (True, 'env'),
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 1ce284f6..d9be0be5 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -48,7 +48,7 @@ from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
- HandleCodeBlocks, SortIds, CitationReferences, Locale, \
+ HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \
RemoveTranslatableInline, SphinxContentsFilter
@@ -98,7 +98,7 @@ class SphinxStandaloneReader(standalone.Reader):
Add our own transforms.
"""
transforms = [Locale, CitationReferences, DefaultSubstitutions,
- MoveModuleTargets, HandleCodeBlocks, SortIds,
+ MoveModuleTargets, HandleCodeBlocks, AutoNumbering, SortIds,
RemoveTranslatableInline]
def get_transforms(self):
@@ -234,6 +234,8 @@ class BuildEnvironment:
# used to determine when to show the TOC
# in a sidebar (don't show if it's only one item)
self.toc_secnumbers = {} # docname -> dict of sectionid -> number
+ self.toc_fignumbers = {} # docname -> dict of figtype ->
+ # dict of figureid -> number
self.toctree_includes = {} # docname -> list of toctree includefiles
self.files_to_rebuild = {} # docname -> set of files
@@ -635,8 +637,8 @@ class BuildEnvironment:
self._warnfunc(*warning)
def check_dependents(self, already):
- to_rewrite = self.assign_section_numbers()
- for docname in to_rewrite:
+ to_rewrite = self.assign_section_numbers() + self.assign_figure_numbers()
+ for docname in set(to_rewrite):
if docname not in already:
yield docname
@@ -1693,6 +1695,87 @@ class BuildEnvironment:
return rewrite_needed
+ def assign_figure_numbers(self):
+ """Assign a figure number to each figure under a numbered toctree."""
+
+ rewrite_needed = []
+
+ assigned = set()
+ old_fignumbers = getattr(self, 'toc_fignumbers', {}) # compatible with old envs
+ self.toc_fignumbers = {}
+ fignum_counter = {}
+
+ def has_child(node, cls):
+ return any(isinstance(child, cls) for child in node)
+
+ def get_section_number(docname, section):
+ anchorname = '#' + section['ids'][0]
+ secnumbers = self.toc_secnumbers.get(docname, {})
+ if anchorname in secnumbers:
+ secnum = secnumbers.get(anchorname)
+ else:
+ secnum = secnumbers.get('')
+
+ return secnum or tuple()
+
+ def get_next_fignumber(figtype, secnum):
+ counter = fignum_counter.setdefault(figtype, {})
+
+ secnum = secnum[:self.config.numfig_secnum_depth]
+ counter[secnum] = counter.get(secnum, 0) + 1
+ return secnum + (counter[secnum],)
+
+ def register_fignumber(docname, secnum, figtype, figure_id):
+ self.toc_fignumbers.setdefault(docname, {})
+ fignumbers = self.toc_fignumbers[docname].setdefault(figtype, {})
+ fignumbers[figure_id] = get_next_fignumber(figtype, secnum)
+
+ def _walk_doctree(docname, doctree, secnum):
+ for subnode in doctree.children:
+ if isinstance(subnode, nodes.section):
+ next_secnum = get_section_number(docname, subnode)
+ if next_secnum:
+ _walk_doctree(docname, subnode, next_secnum)
+ else:
+ _walk_doctree(docname, subnode, secnum)
+ continue
+ elif isinstance(subnode, addnodes.toctree):
+ for title, subdocname in subnode['entries']:
+ if url_re.match(subdocname) or subdocname == 'self':
+ # don't mess with those
+ continue
+
+ _walk_doc(subdocname, secnum)
+
+ continue
+
+ if isinstance(subnode, nodes.figure):
+ figure_id = subnode['ids'][0]
+ register_fignumber(docname, secnum, 'figure', figure_id)
+ elif isinstance(subnode, nodes.table):
+ table_id = subnode['ids'][0]
+ register_fignumber(docname, secnum, 'table', table_id)
+ elif isinstance(subnode, nodes.container):
+ if has_child(subnode, nodes.literal_block):
+ code_block_id = subnode['ids'][0]
+ register_fignumber(docname, secnum, 'code-block', code_block_id)
+
+ _walk_doctree(docname, subnode, secnum)
+
+ def _walk_doc(docname, secnum):
+ if docname not in assigned:
+ assigned.add(docname)
+ doctree = self.get_doctree(docname)
+ _walk_doctree(docname, doctree, secnum)
+
+ if self.config.numfig:
+ _walk_doc(self.config.master_doc, tuple())
+ for docname, fignums in iteritems(self.toc_fignumbers):
+ if fignums != old_fignumbers.get(docname):
+ rewrite_needed.append(docname)
+
+ return rewrite_needed
+
def create_index(self, builder, group_entries=True,
_fixre=re.compile(r'(.*) ([(][^()]*[)])')):
"""Create the real index from the collected index entries."""
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 56831c64..71e7ba65 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -146,14 +146,8 @@ def render_dot(self, code, options, format, prefix='graphviz'):
).encode('utf-8')
fname = '%s-%s.%s' % (prefix, sha1(hashkey).hexdigest(), format)
- if hasattr(self.builder, 'imgpath'):
- # HTML
- relfn = posixpath.join(self.builder.imgpath, fname)
- outfn = path.join(self.builder.outdir, '_images', fname)
- else:
- # LaTeX
- relfn = fname
- outfn = path.join(self.builder.outdir, fname)
+ relfn = posixpath.join(self.builder.imgpath, fname)
+ outfn = path.join(self.builder.outdir, self.builder.imagedir, fname)
if path.isfile(outfn):
return relfn, outfn
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index 27d7978f..81a6f9d5 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -86,7 +86,7 @@ def render_math(self, math):
shasum = "%s.png" % sha1(latex.encode('utf-8')).hexdigest()
relfn = posixpath.join(self.builder.imgpath, 'math', shasum)
- outfn = path.join(self.builder.outdir, '_images', 'math', shasum)
+ outfn = path.join(self.builder.outdir, self.builder.imagedir, 'math', shasum)
if path.isfile(outfn):
depth = read_png_depth(outfn)
return relfn, depth
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index f33b7cbe..a7bbbb9a 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -139,7 +139,7 @@
{%- if hasdoc('copyright') %}
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
{%- endif %}
- <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
+ <link rel="top" title="{{ docstitle|e }}" href="{{ pathto(master_doc) }}" />
{%- if parents %}
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
{%- endif %}
diff --git a/sphinx/themes/bizstyle/static/bizstyle.css_t b/sphinx/themes/bizstyle/static/bizstyle.css_t
index 80b6dfe3..2b3964c2 100644
--- a/sphinx/themes/bizstyle/static/bizstyle.css_t
+++ b/sphinx/themes/bizstyle/static/bizstyle.css_t
@@ -244,13 +244,13 @@ cite, code, tt {
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;
}
@@ -259,12 +259,12 @@ hr {
margin: 2em;
}
-a tt {
+a code {
border: 0;
color: #CA7900;
}
-a tt:hover {
+a code:hover {
color: #2491CF;
}
diff --git a/sphinx/transforms.py b/sphinx/transforms.py
index 42abea58..a62fa99a 100644
--- a/sphinx/transforms.py
+++ b/sphinx/transforms.py
@@ -101,6 +101,31 @@ class HandleCodeBlocks(Transform):
# del node.parent[parindex+1]
+class AutoNumbering(Transform):
+ """
+ Register IDs of tables, figures and literal_blocks to assign numbers.
+ """
+ default_priority = 210
+
+ def apply(self):
+ def has_child(node, cls):
+ return any(isinstance(child, cls) for child in node)
+
+ for node in self.document.traverse(nodes.Element):
+ if isinstance(node, nodes.figure):
+ if has_child(node, nodes.caption):
+ self.document.note_implicit_target(node)
+ elif isinstance(node, nodes.image):
+ if has_child(node.parent, nodes.caption):
+ self.document.note_implicit_target(node.parent)
+ elif isinstance(node, nodes.table):
+ if has_child(node, nodes.title):
+ self.document.note_implicit_target(node)
+ elif isinstance(node, nodes.literal_block):
+ if has_child(node.parent, nodes.caption):
+ self.document.note_implicit_target(node.parent)
+
+
class SortIds(Transform):
"""
Sort secion IDs so that the "id[0-9]+" one comes last.
@@ -437,22 +462,23 @@ class Locale(Transform):
node.children = patch.children
node['translated'] = True
- # Extract and translate messages for index entries.
- for node, entries in traverse_translatable_index(self.document):
- new_entries = []
- for type, msg, tid, main in entries:
- msg_parts = split_index_msg(type, msg)
- msgstr_parts = []
- for part in msg_parts:
- msgstr = catalog.gettext(part)
- if not msgstr:
- msgstr = part
- msgstr_parts.append(msgstr)
-
- new_entries.append((type, ';'.join(msgstr_parts), tid, main))
-
- node['raw_entries'] = entries
- node['entries'] = new_entries
+ if 'index' in env.config.gettext_enables:
+ # Extract and translate messages for index entries.
+ for node, entries in traverse_translatable_index(self.document):
+ new_entries = []
+ for type, msg, tid, main in entries:
+ msg_parts = split_index_msg(type, msg)
+ msgstr_parts = []
+ for part in msg_parts:
+ msgstr = catalog.gettext(part)
+ if not msgstr:
+ msgstr = part
+ msgstr_parts.append(msgstr)
+
+ new_entries.append((type, ';'.join(msgstr_parts), tid, main))
+
+ node['raw_entries'] = entries
+ node['entries'] = new_entries
class RemoveTranslatableInline(Transform):
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 56657ee7..82c228bd 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -250,6 +250,20 @@ class HTMLTranslator(BaseTranslator):
self.body.append('.'.join(map(str, numbers)) +
self.secnumber_suffix)
+ def add_fignumber(self, node):
+ def append_fignumber(figtype, figure_id):
+ if figure_id in self.builder.fignumbers.get(figtype, {}):
+ prefix = self.builder.config.numfig_prefix.get(figtype, '')
+ numbers = self.builder.fignumbers[figtype][figure_id]
+ self.body.append(prefix + '.'.join(map(str, numbers)) + " ")
+
+ if isinstance(node.parent, nodes.figure):
+ append_fignumber('figure', node.parent['ids'][0])
+ elif isinstance(node.parent, nodes.table):
+ append_fignumber('table', node.parent['ids'][0])
+ elif isinstance(node.parent, nodes.container):
+ append_fignumber('code-block', node.parent['ids'][0])
+
# overwritten to avoid emitting empty <ul></ul>
def visit_bullet_list(self, node):
if len(node) == 1 and node[0].tagname == 'toctree':
@@ -260,6 +274,7 @@ class HTMLTranslator(BaseTranslator):
def visit_title(self, node):
BaseTranslator.visit_title(self, node)
self.add_secnumber(node)
+ self.add_fignumber(node)
# overwritten
def visit_literal_block(self, node):
@@ -291,6 +306,7 @@ class HTMLTranslator(BaseTranslator):
self.body.append(self.starttag(node, 'div', '', CLASS='code-block-caption'))
else:
BaseTranslator.visit_caption(self, node)
+ self.add_fignumber(node)
def depart_caption(self, node):
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index abc7ed75..ea79761f 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -42,6 +42,7 @@ HEADER = r'''%% Generated by Sphinx.
%(longtable)s
\usepackage{sphinx}
\usepackage{multirow}
+%(usepackages)s
%(preamble)s
\title{%(title)s}
@@ -153,6 +154,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'fontpkg': '\\usepackage{times}',
'fncychap': '\\usepackage[Bjarne]{fncychap}',
'longtable': '\\usepackage{longtable}',
+ 'usepackages': '',
'preamble': '',
'title': '',
'date': '',
@@ -234,6 +236,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['fncychap'] = ''
else:
self.elements['classoptions'] += ',english'
+ if getattr(builder, 'usepackages', None):
+ def declare_package(packagename, options=None):
+ if options:
+ return '\\usepackage[%s]{%s}' % (options, packagename)
+ else:
+ return '\\usepackage{%s}' % (packagename,)
+ usepackages = (declare_package(*p) for p in builder.usepackages)
+ self.elements['usepackages'] += "\n".join(usepackages)
# allow the user to override them all
self.elements.update(builder.config.latex_elements)
if self.elements['extraclassoptions']:
diff --git a/tests/roots/test-intl/conf.py b/tests/roots/test-intl/conf.py
index 4c37f771..1b20244c 100644
--- a/tests/roots/test-intl/conf.py
+++ b/tests/roots/test-intl/conf.py
@@ -6,3 +6,4 @@ keep_warnings = True
templates_path = ['_templates']
html_additional_pages = {'index': 'index.html'}
release = version = '2013.120'
+gettext_enables = ['index']
diff --git a/tests/roots/test-numfig/bar.rst b/tests/roots/test-numfig/bar.rst
new file mode 100644
index 00000000..f86e7475
--- /dev/null
+++ b/tests/roots/test-numfig/bar.rst
@@ -0,0 +1,58 @@
+===
+Bar
+===
+
+Bar A
+=====
+
+.. figure:: rimg.png
+
+ should be Fig.2.1
+
+.. csv-table:: should be Table 2.1
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 2.1
+
+ print('hello world')
+
+.. toctree::
+
+ baz
+
+.. figure:: rimg.png
+
+ should be Fig.2.3
+
+.. csv-table:: should be Table 2.3
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 2.3
+
+ print('hello world')
+
+Bar B
+=====
+
+Bar B1
+------
+
+.. figure:: rimg.png
+
+ should be Fig.2.4
+
+.. csv-table:: should be Table 2.4
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 2.4
+
+ print('hello world')
diff --git a/tests/roots/test-numfig/baz.rst b/tests/roots/test-numfig/baz.rst
new file mode 100644
index 00000000..9e2ccfeb
--- /dev/null
+++ b/tests/roots/test-numfig/baz.rst
@@ -0,0 +1,16 @@
+Baz A
+-----
+
+.. figure:: rimg.png
+
+ should be Fig.2.2
+
+.. csv-table:: should be Table 2.2
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 2.2
+
+ print('hello world')
diff --git a/tests/roots/test-numfig/conf.py b/tests/roots/test-numfig/conf.py
new file mode 100644
index 00000000..f81c30bc
--- /dev/null
+++ b/tests/roots/test-numfig/conf.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
diff --git a/tests/roots/test-numfig/foo.rst b/tests/roots/test-numfig/foo.rst
new file mode 100644
index 00000000..ef713574
--- /dev/null
+++ b/tests/roots/test-numfig/foo.rst
@@ -0,0 +1,71 @@
+===
+Foo
+===
+
+.. figure:: rimg.png
+
+ should be Fig.1.1
+
+.. csv-table:: should be Table 1.1
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 1.1
+
+ print('hello world')
+
+Foo A
+=====
+
+.. figure:: rimg.png
+
+ should be Fig.1.2
+
+.. figure:: rimg.png
+
+ should be Fig.1.3
+
+.. csv-table:: should be Table 1.2
+ :header-rows: 0
+
+ hello,world
+
+.. csv-table:: should be Table 1.3
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 1.2
+
+ print('hello world')
+
+.. code-block:: python
+ :caption: should be List 1.3
+
+ print('hello world')
+
+Foo A1
+------
+
+Foo B
+=====
+
+Foo B1
+------
+
+.. figure:: rimg.png
+
+ should be Fig.1.4
+
+.. csv-table:: should be Table 1.4
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 1.4
+
+ print('hello world')
diff --git a/tests/roots/test-numfig/index.rst b/tests/roots/test-numfig/index.rst
new file mode 100644
index 00000000..564018e3
--- /dev/null
+++ b/tests/roots/test-numfig/index.rst
@@ -0,0 +1,36 @@
+test-tocdepth
+=============
+
+.. toctree::
+ :numbered:
+
+ foo
+ bar
+
+.. figure:: rimg.png
+
+ should be Fig.1
+
+.. figure:: rimg.png
+
+ should be Fig.2
+
+.. csv-table:: should be Table 1
+ :header-rows: 0
+
+ hello,world
+
+.. csv-table:: should be Table 2
+ :header-rows: 0
+
+ hello,world
+
+.. code-block:: python
+ :caption: should be List 1
+
+ print('hello world')
+
+.. code-block:: python
+ :caption: should be List 2
+
+ print('hello world')
diff --git a/tests/roots/test-numfig/rimg.png b/tests/roots/test-numfig/rimg.png
new file mode 100644
index 00000000..1081dc14
--- /dev/null
+++ b/tests/roots/test-numfig/rimg.png
Binary files differ
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index d7189443..1de1306e 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -113,6 +113,38 @@ def test_gettext_index_entries(app, status, warning):
"Exception",
"Statement",
"Builtin",
+ ]
+ for expect in expected_msgids:
+ assert expect in msgids
+ msgids.remove(expect)
+
+ # unexpected msgid existent
+ assert msgids == []
+
+
+@with_app('gettext', testroot='intl',
+ confoverrides={'gettext_compact': False, 'gettext_enables': []})
+def test_gettext_disable_index_entries(app, status, warning):
+ # regression test for #976
+ app.builder.build(['index_entries'])
+
+ _msgid_getter = re.compile(r'msgid "(.*)"').search
+
+ def msgid_getter(msgid):
+ m = _msgid_getter(msgid)
+ if m:
+ return m.groups()[0]
+ return None
+
+ pot = (app.outdir / 'index_entries.pot').text(encoding='utf-8')
+ msgids = [_f for _f in map(msgid_getter, pot.splitlines()) if _f]
+
+ expected_msgids = [
+ "i18n with index entries",
+ "index target section",
+ "this is :index:`Newsletter` target paragraph.",
+ "various index entries",
+ "That's all.",
]
for expect in expected_msgids:
assert expect in msgids
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 52604468..ae518062 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -455,3 +455,402 @@ def test_tocdepth_singlehtml(app, status, warning):
for xpath, check, be_found in paths:
yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@gen_with_app(buildername='html', testroot='numfig')
+def test_numfig(app, status, warning):
+ # issue #1251
+ app.builder.build_all()
+
+ expects = {
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.2$', True),
+ (".//table/caption", '^should be Table 1$', True),
+ (".//table/caption", '^should be Table 2$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 1$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 2$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.1.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.1.2$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.1.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.1.4$', True),
+ (".//table/caption", '^should be Table 1.1$', True),
+ (".//table/caption", '^should be Table 1.2$', True),
+ (".//table/caption", '^should be Table 1.3$', True),
+ (".//table/caption", '^should be Table 1.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 1.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 1.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 1.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 1.4$', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.2.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.2.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.2.4$', True),
+ (".//table/caption", '^should be Table 2.1$', True),
+ (".//table/caption", '^should be Table 2.3$', True),
+ (".//table/caption", '^should be Table 2.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 2.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 2.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 2.4$', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^should be Fig.2.2$', True),
+ (".//table/caption", '^should be Table 2.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^should be List 2.2$', True),
+ ],
+ }
+
+ for fname, paths in iteritems(expects):
+ parser = NslessParser()
+ parser.entity.update(html_entities.entitydefs)
+ fp = open(os.path.join(app.outdir, fname), 'rb')
+ try:
+ etree = ET.parse(fp, parser)
+ finally:
+ fp.close()
+
+ for xpath, check, be_found in paths:
+ yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@gen_with_app(buildername='html', testroot='numfig',
+ confoverrides={'numfig': True})
+def test_numfig_without_numbered_toctree(app, status, warning):
+ # remove :numbered: option
+ index = (app.srcdir / 'index.rst').text()
+ index = re.sub(':numbered:.*', '', index, re.MULTILINE)
+ (app.srcdir / 'index.rst').write_text(index, encoding='utf-8')
+ app.builder.build_all()
+
+ expects = {
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.9 should be Fig.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.10 should be Fig.2$', True),
+ (".//table/caption", '^Table 9 should be Table 1$', True),
+ (".//table/caption", '^Table 10 should be Table 2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 9 should be List 1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 10 should be List 2$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1 should be Fig.1.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2 should be Fig.1.2$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.3 should be Fig.1.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.4 should be Fig.1.4$', True),
+ (".//table/caption", '^Table 1 should be Table 1.1$', True),
+ (".//table/caption", '^Table 2 should be Table 1.2$', True),
+ (".//table/caption", '^Table 3 should be Table 1.3$', True),
+ (".//table/caption", '^Table 4 should be Table 1.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1 should be List 1.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2 should be List 1.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 3 should be List 1.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 4 should be List 1.4$', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.5 should be Fig.2.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.7 should be Fig.2.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.8 should be Fig.2.4$', True),
+ (".//table/caption", '^Table 5 should be Table 2.1$', True),
+ (".//table/caption", '^Table 7 should be Table 2.3$', True),
+ (".//table/caption", '^Table 8 should be Table 2.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 5 should be List 2.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 7 should be List 2.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 8 should be List 2.4$', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.6 should be Fig.2.2$', True),
+ (".//table/caption", '^Table 6 should be Table 2.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 6 should be List 2.2$', True),
+ ],
+ }
+
+ for fname, paths in iteritems(expects):
+ parser = NslessParser()
+ parser.entity.update(html_entities.entitydefs)
+ fp = open(os.path.join(app.outdir, fname), 'rb')
+ try:
+ etree = ET.parse(fp, parser)
+ finally:
+ fp.close()
+
+ for xpath, check, be_found in paths:
+ yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@gen_with_app(buildername='html', testroot='numfig',
+ confoverrides={'numfig': True})
+def test_numfig_with_numbered_toctree(app, status, warning):
+ app.builder.build_all()
+
+ expects = {
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1 should be Fig.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2 should be Fig.2$', True),
+ (".//table/caption", '^Table 1 should be Table 1$', True),
+ (".//table/caption", '^Table 2 should be Table 2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1 should be List 1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2 should be List 2$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.1 should be Fig.1.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.2 should be Fig.1.2$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.3 should be Fig.1.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.4 should be Fig.1.4$', True),
+ (".//table/caption", '^Table 1.1 should be Table 1.1$', True),
+ (".//table/caption", '^Table 1.2 should be Table 1.2$', True),
+ (".//table/caption", '^Table 1.3 should be Table 1.3$', True),
+ (".//table/caption", '^Table 1.4 should be Table 1.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.1 should be List 1.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.2 should be List 1.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.3 should be List 1.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.4 should be List 1.4$', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.1 should be Fig.2.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.3 should be Fig.2.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.4 should be Fig.2.4$', True),
+ (".//table/caption", '^Table 2.1 should be Table 2.1$', True),
+ (".//table/caption", '^Table 2.3 should be Table 2.3$', True),
+ (".//table/caption", '^Table 2.4 should be Table 2.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.1 should be List 2.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.3 should be List 2.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.4 should be List 2.4$', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.2 should be Fig.2.2$', True),
+ (".//table/caption", '^Table 2.2 should be Table 2.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.2 should be List 2.2$', True),
+ ],
+ }
+
+ for fname, paths in iteritems(expects):
+ parser = NslessParser()
+ parser.entity.update(html_entities.entitydefs)
+ fp = open(os.path.join(app.outdir, fname), 'rb')
+ try:
+ etree = ET.parse(fp, parser)
+ finally:
+ fp.close()
+
+ for xpath, check, be_found in paths:
+ yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@gen_with_app(buildername='html', testroot='numfig',
+ confoverrides={'numfig': True, 'numfig_prefix': {'figure': 'Figure:', 'table': 'Tab_', 'code-block': 'Code-'}})
+def test_numfig_with_prefix(app, status, warning):
+ app.builder.build_all()
+
+ expects = {
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:1 should be Fig.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:2 should be Fig.2$', True),
+ (".//table/caption", '^Tab_1 should be Table 1$', True),
+ (".//table/caption", '^Tab_2 should be Table 2$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-1 should be List 1$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-2 should be List 2$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:1.1 should be Fig.1.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:1.2 should be Fig.1.2$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:1.3 should be Fig.1.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:1.4 should be Fig.1.4$', True),
+ (".//table/caption", '^Tab_1.1 should be Table 1.1$', True),
+ (".//table/caption", '^Tab_1.2 should be Table 1.2$', True),
+ (".//table/caption", '^Tab_1.3 should be Table 1.3$', True),
+ (".//table/caption", '^Tab_1.4 should be Table 1.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-1.1 should be List 1.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-1.2 should be List 1.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-1.3 should be List 1.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-1.4 should be List 1.4$', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:2.1 should be Fig.2.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:2.3 should be Fig.2.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:2.4 should be Fig.2.4$', True),
+ (".//table/caption", '^Tab_2.1 should be Table 2.1$', True),
+ (".//table/caption", '^Tab_2.3 should be Table 2.3$', True),
+ (".//table/caption", '^Tab_2.4 should be Table 2.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-2.1 should be List 2.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-2.3 should be List 2.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-2.4 should be List 2.4$', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Figure:2.2 should be Fig.2.2$', True),
+ (".//table/caption", '^Tab_2.2 should be Table 2.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^Code-2.2 should be List 2.2$', True),
+ ],
+ }
+
+ for fname, paths in iteritems(expects):
+ parser = NslessParser()
+ parser.entity.update(html_entities.entitydefs)
+ fp = open(os.path.join(app.outdir, fname), 'rb')
+ try:
+ etree = ET.parse(fp, parser)
+ finally:
+ fp.close()
+
+ for xpath, check, be_found in paths:
+ yield check_xpath, etree, fname, xpath, check, be_found
+
+
+@gen_with_app(buildername='html', testroot='numfig',
+ confoverrides={'numfig': True, 'numfig_secnum_depth': 2})
+def test_numfig_with_secnum_depth(app, status, warning):
+ app.builder.build_all()
+
+ expects = {
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1 should be Fig.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2 should be Fig.2$', True),
+ (".//table/caption", '^Table 1 should be Table 1$', True),
+ (".//table/caption", '^Table 2 should be Table 2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1 should be List 1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2 should be List 2$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.1 should be Fig.1.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.1.1 should be Fig.1.2$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.1.2 should be Fig.1.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.1.2.1 should be Fig.1.4$', True),
+ (".//table/caption", '^Table 1.1 should be Table 1.1$', True),
+ (".//table/caption", '^Table 1.1.1 should be Table 1.2$', True),
+ (".//table/caption", '^Table 1.1.2 should be Table 1.3$', True),
+ (".//table/caption", '^Table 1.2.1 should be Table 1.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.1 should be List 1.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.1.1 should be List 1.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.1.2 should be List 1.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 1.2.1 should be List 1.4$', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.1.1 should be Fig.2.1$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.1.3 should be Fig.2.3$', True),
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.2.1 should be Fig.2.4$', True),
+ (".//table/caption", '^Table 2.1.1 should be Table 2.1$', True),
+ (".//table/caption", '^Table 2.1.3 should be Table 2.3$', True),
+ (".//table/caption", '^Table 2.2.1 should be Table 2.4$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.1.1 should be List 2.1$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.1.3 should be List 2.3$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.2.1 should be List 2.4$', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']",
+ '^Fig.2.1.2 should be Fig.2.2$', True),
+ (".//table/caption", '^Table 2.1.2 should be Table 2.2$', True),
+ (".//div[@class='code-block-caption']",
+ '^List 2.1.2 should be List 2.2$', True),
+ ],
+ }
+
+ for fname, paths in iteritems(expects):
+ parser = NslessParser()
+ parser.entity.update(html_entities.entitydefs)
+ fp = open(os.path.join(app.outdir, fname), 'rb')
+ try:
+ etree = ET.parse(fp, parser)
+ finally:
+ fp.close()
+
+ for xpath, check, be_found in paths:
+ yield check_xpath, etree, fname, xpath, check, be_found
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 9e4c11d5..374b54a2 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -91,3 +91,13 @@ def test_latex(app, status, warning):
assert False, 'latex exited with return code %s' % p.returncode
finally:
os.chdir(cwd)
+
+
+@with_app(buildername='latex')
+def test_latex_add_latex_package(app, status, warning):
+ app.add_latex_package('foo')
+ app.add_latex_package('bar', 'baz')
+ app.builder.build_all()
+ result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8')
+ assert '\\usepackage{foo}' in result
+ assert '\\usepackage[baz]{bar}' in result