mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'master' into refactor_literalinclude
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
language: python
|
||||
sudo: false
|
||||
dist: trusty
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
python:
|
||||
- "pypy"
|
||||
- "pypy-5.4.1"
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
@@ -26,7 +27,7 @@ matrix:
|
||||
env: DOCUTILS=0.12
|
||||
- python: nightly
|
||||
env: DOCUTILS=0.12
|
||||
- python: pypy
|
||||
- python: "pypy-5.4.1"
|
||||
env: DOCUTILS=0.12
|
||||
addons:
|
||||
apt:
|
||||
|
||||
2
CHANGES
2
CHANGES
@@ -13,6 +13,7 @@ Incompatible changes
|
||||
* LaTeX positioned long tables horizontally centered, and short ones
|
||||
flushed left (no text flow around table.) The position now defaults to center in
|
||||
both cases, and it will obey Docutils 0.13 ``:align:`` option (refs #3415, #3377)
|
||||
* option directive also allows all punctuations for the option name (refs: #3366)
|
||||
|
||||
Features removed
|
||||
----------------
|
||||
@@ -108,6 +109,7 @@ Bugs fixed
|
||||
* #3394: When ``'pointsize'`` is not ``10pt``, Japanese ``'manual'`` document
|
||||
gets wrong PDF page dimensions
|
||||
* #3399: quickstart: conf.py was not overwritten by template
|
||||
* #3366: option directive does not allow punctuations
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
36
EXAMPLES
36
EXAMPLES
@@ -12,58 +12,43 @@ interesting examples.
|
||||
Documentation using the alabaster theme
|
||||
---------------------------------------
|
||||
|
||||
* CodePy: https://documen.tician.de/codepy/
|
||||
* MeshPy: https://documen.tician.de/meshpy/
|
||||
* PyCuda: https://documen.tician.de/pycuda/
|
||||
* PyLangAcq: http://pylangacq.org/
|
||||
|
||||
Documentation using the classic theme
|
||||
-------------------------------------
|
||||
|
||||
* APSW: http://apidoc.apsw.googlecode.com/hg/index.html
|
||||
* ASE: https://wiki.fysik.dtu.dk/ase/
|
||||
* APSW: https://rogerbinns.github.io/apsw/
|
||||
* Calibre: http://manual.calibre-ebook.com/
|
||||
* CodePy: https://documen.tician.de/codepy/
|
||||
* Cython: http://docs.cython.org/
|
||||
* Cormoran: http://cormoran.nhopkg.org/docs/
|
||||
* Director: http://pythonhosted.org/director/
|
||||
* Dirigible: http://www.projectdirigible.com/
|
||||
* F2py: http://f2py.sourceforge.net/docs/
|
||||
* GeoDjango: https://docs.djangoproject.com/en/dev/ref/contrib/gis/
|
||||
* Genomedata:
|
||||
http://noble.gs.washington.edu/proj/genomedata/doc/1.2.2/genomedata.html
|
||||
* gevent: http://www.gevent.org/
|
||||
* Google Wave API:
|
||||
http://wave-robot-python-client.googlecode.com/svn/trunk/pydocs/index.html
|
||||
* GSL Shell: http://www.nongnu.org/gsl-shell/
|
||||
* Heapkeeper: http://heapkeeper.org/
|
||||
* Hands-on Python Tutorial:
|
||||
http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/
|
||||
* Hedge: https://documen.tician.de/hedge/
|
||||
* Leo: http://leoeditor.com/
|
||||
* Lino: http://www.lino-framework.org/
|
||||
* MeshPy: https://documen.tician.de/meshpy/
|
||||
* mpmath: http://mpmath.googlecode.com/svn/trunk/doc/build/index.html
|
||||
* mpmath: http://mpmath.org/doc/current/
|
||||
* OpenEXR: http://excamera.com/articles/26/doc/index.html
|
||||
* OpenGDA: http://www.opengda.org/gdadoc/html/
|
||||
* openWNS: http://docs.openwns.org/
|
||||
* Paste: http://pythonpaste.org/script/
|
||||
* Paver: http://paver.github.io/paver/
|
||||
* Pioneers and Prominent Men of Utah: http://pioneers.rstebbing.com/
|
||||
* PyCantonese: http://pycantonese.org/
|
||||
* Pyccuracy: https://github.com/heynemann/pyccuracy/wiki/
|
||||
* PyCuda: https://documen.tician.de/pycuda/
|
||||
* Pyevolve: http://pyevolve.sourceforge.net/
|
||||
* Pylo: https://documen.tician.de/pylo/
|
||||
* PyMQI: http://pythonhosted.org/pymqi/
|
||||
* PyPubSub: http://pubsub.sourceforge.net/
|
||||
* pySPACE: http://pyspace.github.io/pyspace/
|
||||
* Python: https://docs.python.org/3/
|
||||
* python-apt: http://apt.alioth.debian.org/python-apt-doc/
|
||||
* PyUblas: https://documen.tician.de/pyublas/
|
||||
* Quex: http://quex.sourceforge.net/doc/html/main.html
|
||||
* Ring programming language: http://ring-lang.sourceforge.net/doc/index.html
|
||||
* Scapy: http://www.secdev.org/projects/scapy/doc/
|
||||
* Seaborn: https://stanford.edu/~mwaskom/software/seaborn/
|
||||
* Segway: http://noble.gs.washington.edu/proj/segway/doc/1.1.0/segway.html
|
||||
* SimPy: http://simpy.readthedocs.org/en/latest/
|
||||
* SymPy: http://docs.sympy.org/
|
||||
* WTForms: http://wtforms.simplecodes.com/docs/
|
||||
* z3c: http://www.ibiblio.org/paulcarduner/z3ctutorial/
|
||||
@@ -129,6 +114,7 @@ Documentation using the sphinxdoc theme
|
||||
Documentation using another builtin theme
|
||||
-----------------------------------------
|
||||
|
||||
* ASE: https://wiki.fysik.dtu.dk/ase/ (sphinx_rtd_theme)
|
||||
* C/C++ Development with Eclipse: http://eclipsebook.in/ (agogo)
|
||||
* ESWP3 (http://eswp3.org) (sphinx_rtd_theme)
|
||||
* Jinja: http://jinja.pocoo.org/ (scrolls)
|
||||
@@ -137,13 +123,17 @@ Documentation using another builtin theme
|
||||
* Linguistica: http://linguistica-uchicago.github.io/lxa5/ (sphinx_rtd_theme)
|
||||
* MoinMoin: https://moin-20.readthedocs.io/en/latest/ (sphinx_rtd_theme)
|
||||
* MPipe: http://vmlaker.github.io/mpipe/ (sphinx13)
|
||||
* Paver: http://paver.readthedocs.io/en/latest/
|
||||
* pip: https://pip.pypa.io/en/latest/ (sphinx_rtd_theme)
|
||||
* Pyramid web framework:
|
||||
http://docs.pylonsproject.org/projects/pyramid/en/latest/ (pyramid)
|
||||
* Programmieren mit PyGTK und Glade (German):
|
||||
http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo)
|
||||
* PyPubSub: http://pypubsub.readthedocs.io/ (bizstyle)
|
||||
* Pyramid web framework:
|
||||
http://docs.pylonsproject.org/projects/pyramid/en/latest/ (pyramid)
|
||||
* Quex: http://quex.sourceforge.net/doc/html/main.html
|
||||
* Satchmo: http://docs.satchmoproject.com/en/latest/ (sphinx_rtd_theme)
|
||||
* Setuptools: http://pythonhosted.org/setuptools/ (nature)
|
||||
* SimPy: http://simpy.readthedocs.org/en/latest/
|
||||
* Spring Python: http://docs.spring.io/spring-python/1.2.x/sphinx/html/ (nature)
|
||||
* sqlparse: http://python-sqlparse.googlecode.com/svn/docs/api/index.html
|
||||
(agogo)
|
||||
@@ -169,6 +159,7 @@ Documentation using a custom theme/integrated in a site
|
||||
* Flask-OpenID: http://pythonhosted.org/Flask-OpenID/
|
||||
* Gameduino: http://excamera.com/sphinx/gameduino/
|
||||
* GeoServer: http://docs.geoserver.org/
|
||||
* gevent: http://www.gevent.org/
|
||||
* GHC - Glasgow Haskell Compiler: http://downloads.haskell.org/~ghc/master/users-guide/
|
||||
* Glashammer: http://glashammer.org/
|
||||
* Istihza (Turkish Python documentation project): http://belgeler.istihza.com/py2/
|
||||
@@ -194,6 +185,7 @@ Documentation using a custom theme/integrated in a site
|
||||
* QGIS: http://qgis.org/en/docs/index.html
|
||||
* qooxdoo: http://manual.qooxdoo.org/current/
|
||||
* Roundup: http://www.roundup-tracker.org/
|
||||
* Seaborn: https://stanford.edu/~mwaskom/software/seaborn/
|
||||
* Selenium: http://docs.seleniumhq.org/docs/
|
||||
* Self: http://www.selflanguage.org/
|
||||
* Substance D: http://docs.pylonsproject.org/projects/substanced/en/latest/
|
||||
|
||||
2
Makefile
2
Makefile
@@ -38,7 +38,7 @@ DONT_CHECK = -i .ropeproject \
|
||||
all: clean-pyc clean-backupfiles style-check type-check test
|
||||
|
||||
style-check:
|
||||
@$(PYTHON) utils/check_sources.py $(DONT_CHECK) .
|
||||
@PYTHONWARNINGS=all $(PYTHON) utils/check_sources.py $(DONT_CHECK) .
|
||||
|
||||
type-check:
|
||||
mypy sphinx/
|
||||
|
||||
@@ -184,9 +184,17 @@ Includes
|
||||
string option, only lines that precede the first lines containing that string
|
||||
are included.
|
||||
|
||||
With lines selected this way it is still possible to use ``lines``, the
|
||||
numbers being now interpreted relative to the already selected lines.
|
||||
|
||||
When lines have been selected in any of the ways described above, the
|
||||
line numbers in ``emphasize-lines`` refer to the selection, with the
|
||||
line count starting at ``1``.
|
||||
|
||||
When specifying particular parts of a file to display, it can be useful to
|
||||
display exactly which lines are being presented.
|
||||
This can be done using the ``lineno-match`` option.
|
||||
display the original line numbers. This can be done using the
|
||||
``lineno-match`` option, which is however allowed only when the selection
|
||||
consists of contiguous lines.
|
||||
|
||||
You can prepend and/or append a line to the included code, using the
|
||||
``prepend`` and ``append`` option, respectively. This is useful e.g. for
|
||||
|
||||
@@ -226,7 +226,7 @@ as long as the text::
|
||||
|
||||
Normally, there are no heading levels assigned to certain characters as the
|
||||
structure is determined from the succession of headings. However, this
|
||||
convention is used in `Python's Style Guide for documentating
|
||||
convention is used in `Python's Style Guide for documenting
|
||||
<https://docs.python.org/devguide/documenting.html#style-guide>`_ which you may
|
||||
follow:
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import codecs
|
||||
import zipfile
|
||||
from os import path
|
||||
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
||||
from datetime import datetime
|
||||
from collections import namedtuple
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
@@ -28,10 +28,12 @@ except ImportError:
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx import package_dir
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import status_iterator
|
||||
from sphinx.util.osutil import ensuredir, copyfile, make_filename, EEXIST
|
||||
from sphinx.util.osutil import ensuredir, copyfile, make_filename
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
from sphinx.util.smartypants import sphinx_smarty_pants as ssp
|
||||
|
||||
if False:
|
||||
@@ -43,101 +45,14 @@ if False:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# (Fragment) templates from which the metainfo files content.opf, toc.ncx,
|
||||
# mimetype, and META-INF/container.xml are created.
|
||||
# (Fragment) templates from which the metainfo files content.opf and
|
||||
# toc.ncx are created.
|
||||
# This template section also defines strings that are embedded in the html
|
||||
# output but that may be customized by (re-)setting module attributes,
|
||||
# e.g. from conf.py.
|
||||
|
||||
MIMETYPE_TEMPLATE = 'application/epub+zip' # no EOL!
|
||||
|
||||
CONTAINER_TEMPLATE = u'''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<container version="1.0"
|
||||
xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="content.opf"
|
||||
media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
||||
'''
|
||||
|
||||
TOC_TEMPLATE = u'''\
|
||||
<?xml version="1.0"?>
|
||||
<ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
|
||||
<head>
|
||||
<meta name="dtb:uid" content="%(uid)s"/>
|
||||
<meta name="dtb:depth" content="%(level)d"/>
|
||||
<meta name="dtb:totalPageCount" content="0"/>
|
||||
<meta name="dtb:maxPageNumber" content="0"/>
|
||||
</head>
|
||||
<docTitle>
|
||||
<text>%(title)s</text>
|
||||
</docTitle>
|
||||
<navMap>
|
||||
%(navpoints)s
|
||||
</navMap>
|
||||
</ncx>
|
||||
'''
|
||||
|
||||
NAVPOINT_TEMPLATE = u'''\
|
||||
%(indent)s <navPoint id="%(navpoint)s" playOrder="%(playorder)d">
|
||||
%(indent)s <navLabel>
|
||||
%(indent)s <text>%(text)s</text>
|
||||
%(indent)s </navLabel>
|
||||
%(indent)s <content src="%(refuri)s" />
|
||||
%(indent)s </navPoint>'''
|
||||
|
||||
NAVPOINT_INDENT = ' '
|
||||
NODE_NAVPOINT_TEMPLATE = 'navPoint%d'
|
||||
|
||||
CONTENT_TEMPLATE = u'''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package xmlns="http://www.idpf.org/2007/opf" version="2.0"
|
||||
unique-identifier="%(uid)s">
|
||||
<metadata xmlns:opf="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:language>%(lang)s</dc:language>
|
||||
<dc:title>%(title)s</dc:title>
|
||||
<dc:creator opf:role="aut">%(author)s</dc:creator>
|
||||
<dc:publisher>%(publisher)s</dc:publisher>
|
||||
<dc:rights>%(copyright)s</dc:rights>
|
||||
<dc:identifier id="%(uid)s" opf:scheme="%(scheme)s">%(id)s</dc:identifier>
|
||||
<dc:date>%(date)s</dc:date>
|
||||
</metadata>
|
||||
<manifest>
|
||||
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
|
||||
%(files)s
|
||||
</manifest>
|
||||
<spine toc="ncx">
|
||||
%(spine)s
|
||||
</spine>
|
||||
<guide>
|
||||
%(guide)s
|
||||
</guide>
|
||||
</package>
|
||||
'''
|
||||
|
||||
COVER_TEMPLATE = u'''\
|
||||
<meta name="cover" content="%(cover)s"/>
|
||||
'''
|
||||
|
||||
COVERPAGE_NAME = u'epub-cover.xhtml'
|
||||
|
||||
FILE_TEMPLATE = u'''\
|
||||
<item id="%(id)s"
|
||||
href="%(href)s"
|
||||
media-type="%(media_type)s" />'''
|
||||
|
||||
SPINE_TEMPLATE = u'''\
|
||||
<itemref idref="%(idref)s" />'''
|
||||
|
||||
NO_LINEAR_SPINE_TEMPLATE = u'''\
|
||||
<itemref idref="%(idref)s" linear="no" />'''
|
||||
|
||||
GUIDE_TEMPLATE = u'''\
|
||||
<reference type="%(type)s" title="%(title)s" href="%(uri)s" />'''
|
||||
|
||||
TOCTREE_TEMPLATE = u'toctree-l%d'
|
||||
|
||||
DOCTYPE = u'''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
@@ -178,6 +93,12 @@ VECTOR_GRAPHICS_EXTENSIONS = ('.svg',)
|
||||
REFURI_RE = re.compile("([^#:]*#)(.*)")
|
||||
|
||||
|
||||
ManifestItem = namedtuple('ManifestItem', ['href', 'id', 'media_type'])
|
||||
Spine = namedtuple('Spine', ['idref', 'linear'])
|
||||
Guide = namedtuple('Guide', ['type', 'title', 'uri'])
|
||||
NavPoint = namedtuple('NavPoint', ['navpoint', 'playorder', 'text', 'refuri', 'children'])
|
||||
|
||||
|
||||
# The epub publisher
|
||||
|
||||
class EpubBuilder(StandaloneHTMLBuilder):
|
||||
@@ -190,6 +111,8 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
name = 'epub2'
|
||||
|
||||
template_dir = path.join(package_dir, 'templates', 'epub2')
|
||||
|
||||
# don't copy the reST source
|
||||
copysource = False
|
||||
supported_image_types = ['image/svg+xml', 'image/png', 'image/gif',
|
||||
@@ -208,19 +131,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
# don't generate search index or include search page
|
||||
search = False
|
||||
|
||||
mimetype_template = MIMETYPE_TEMPLATE
|
||||
container_template = CONTAINER_TEMPLATE
|
||||
toc_template = TOC_TEMPLATE
|
||||
navpoint_template = NAVPOINT_TEMPLATE
|
||||
navpoint_indent = NAVPOINT_INDENT
|
||||
node_navpoint_template = NODE_NAVPOINT_TEMPLATE
|
||||
content_template = CONTENT_TEMPLATE
|
||||
cover_template = COVER_TEMPLATE
|
||||
coverpage_name = COVERPAGE_NAME
|
||||
file_template = FILE_TEMPLATE
|
||||
spine_template = SPINE_TEMPLATE
|
||||
no_linear_spine_template = NO_LINEAR_SPINE_TEMPLATE
|
||||
guide_template = GUIDE_TEMPLATE
|
||||
toctree_template = TOCTREE_TEMPLATE
|
||||
doctype = DOCTYPE
|
||||
link_target_template = LINK_TARGET_TEMPLATE
|
||||
@@ -237,6 +148,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
self.link_suffix = '.xhtml'
|
||||
self.playorder = 0
|
||||
self.tocid = 0
|
||||
self.id_cache = {} # type: Dict[unicode, unicode]
|
||||
self.use_index = self.get_builder_config('use_index', 'epub')
|
||||
|
||||
def get_theme_config(self):
|
||||
@@ -244,14 +156,14 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
return self.config.epub_theme, self.config.epub_theme_options
|
||||
|
||||
# generic support functions
|
||||
def make_id(self, name, id_cache={}):
|
||||
# type: (unicode, Dict[unicode, unicode]) -> unicode
|
||||
def make_id(self, name):
|
||||
# type: (unicode) -> unicode
|
||||
# id_cache is intentionally mutable
|
||||
"""Return a unique id for name."""
|
||||
id = id_cache.get(name)
|
||||
id = self.id_cache.get(name)
|
||||
if not id:
|
||||
id = 'epub-%d' % self.env.new_serialno('epub')
|
||||
id_cache[name] = id
|
||||
self.id_cache[name] = id
|
||||
return id
|
||||
|
||||
def esc(self, name):
|
||||
@@ -552,24 +464,19 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
# type: (unicode, unicode) -> None
|
||||
"""Write the metainfo file mimetype."""
|
||||
logger.info('writing %s file...', outname)
|
||||
with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore
|
||||
f.write(self.mimetype_template)
|
||||
copy_asset_file(path.join(self.template_dir, 'mimetype'),
|
||||
path.join(outdir, outname))
|
||||
|
||||
def build_container(self, outdir, outname):
|
||||
# type: (unicode, unicode) -> None
|
||||
"""Write the metainfo file META-INF/cointainer.xml."""
|
||||
"""Write the metainfo file META-INF/container.xml."""
|
||||
logger.info('writing %s file...', outname)
|
||||
fn = path.join(outdir, outname)
|
||||
try:
|
||||
os.mkdir(path.dirname(fn))
|
||||
except OSError as err:
|
||||
if err.errno != EEXIST:
|
||||
raise
|
||||
with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore
|
||||
f.write(self.container_template) # type: ignore
|
||||
filename = path.join(outdir, outname)
|
||||
ensuredir(path.dirname(filename))
|
||||
copy_asset_file(path.join(self.template_dir, 'container.xml'), filename)
|
||||
|
||||
def content_metadata(self, files, spine, guide):
|
||||
# type: (List[unicode], List[unicode], List[unicode]) -> Dict[unicode, Any]
|
||||
def content_metadata(self):
|
||||
# type: () -> Dict[unicode, Any]
|
||||
"""Create a dictionary with all metadata for the content.opf
|
||||
file properly escaped.
|
||||
"""
|
||||
@@ -583,9 +490,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
metadata['scheme'] = self.esc(self.config.epub_scheme)
|
||||
metadata['id'] = self.esc(self.config.epub_identifier)
|
||||
metadata['date'] = self.esc(datetime.utcnow().strftime("%Y-%m-%d"))
|
||||
metadata['files'] = files
|
||||
metadata['spine'] = spine
|
||||
metadata['guide'] = guide
|
||||
metadata['manifest_items'] = []
|
||||
metadata['spines'] = []
|
||||
metadata['guides'] = []
|
||||
return metadata
|
||||
|
||||
def build_content(self, outdir, outname):
|
||||
@@ -594,12 +501,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
a file list and the spine (the reading order).
|
||||
"""
|
||||
logger.info('writing %s file...', outname)
|
||||
metadata = self.content_metadata()
|
||||
|
||||
# files
|
||||
if not outdir.endswith(os.sep):
|
||||
outdir += os.sep
|
||||
olen = len(outdir)
|
||||
projectfiles = [] # type: List[unicode]
|
||||
self.files = [] # type: List[unicode]
|
||||
self.ignored_files = ['.buildinfo', 'mimetype', 'content.opf',
|
||||
'toc.ncx', 'META-INF/container.xml',
|
||||
@@ -609,7 +516,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
if not self.use_index:
|
||||
self.ignored_files.append('genindex' + self.out_suffix)
|
||||
for root, dirs, files in os.walk(outdir):
|
||||
for fn in files:
|
||||
for fn in sorted(files):
|
||||
filename = path.join(root, fn)[olen:]
|
||||
if filename in self.ignored_files:
|
||||
continue
|
||||
@@ -622,70 +529,57 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
type='epub', subtype='unknown_project_files')
|
||||
continue
|
||||
filename = filename.replace(os.sep, '/')
|
||||
projectfiles.append(self.file_template % {
|
||||
'href': self.esc(filename),
|
||||
'id': self.esc(self.make_id(filename)),
|
||||
'media_type': self.esc(self.media_types[ext])
|
||||
})
|
||||
item = ManifestItem(self.esc(filename),
|
||||
self.esc(self.make_id(filename)),
|
||||
self.esc(self.media_types[ext]))
|
||||
metadata['manifest_items'].append(item)
|
||||
self.files.append(filename)
|
||||
|
||||
# spine
|
||||
spine = []
|
||||
spinefiles = set()
|
||||
for item in self.refnodes:
|
||||
if '#' in item['refuri']:
|
||||
for refnode in self.refnodes:
|
||||
if '#' in refnode['refuri']:
|
||||
continue
|
||||
if item['refuri'] in self.ignored_files:
|
||||
if refnode['refuri'] in self.ignored_files:
|
||||
continue
|
||||
spine.append(self.spine_template % {
|
||||
'idref': self.esc(self.make_id(item['refuri']))
|
||||
})
|
||||
spinefiles.add(item['refuri'])
|
||||
spine = Spine(self.esc(self.make_id(refnode['refuri'])), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add(refnode['refuri'])
|
||||
for info in self.domain_indices:
|
||||
spine.append(self.spine_template % {
|
||||
'idref': self.esc(self.make_id(info[0] + self.out_suffix))
|
||||
})
|
||||
spine = Spine(self.esc(self.make_id(info[0] + self.out_suffix)), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add(info[0] + self.out_suffix)
|
||||
if self.use_index:
|
||||
spine.append(self.spine_template % {
|
||||
'idref': self.esc(self.make_id('genindex' + self.out_suffix))
|
||||
})
|
||||
spine = Spine(self.esc(self.make_id('genindex' + self.out_suffix)), True)
|
||||
metadata['spines'].append(spine)
|
||||
spinefiles.add('genindex' + self.out_suffix)
|
||||
# add auto generated files
|
||||
for name in self.files:
|
||||
if name not in spinefiles and name.endswith(self.out_suffix):
|
||||
spine.append(self.no_linear_spine_template % {
|
||||
'idref': self.esc(self.make_id(name))
|
||||
})
|
||||
spine = Spine(self.esc(self.make_id(name)), False)
|
||||
metadata['spines'].append(spine)
|
||||
|
||||
# add the optional cover
|
||||
content_tmpl = self.content_template
|
||||
html_tmpl = None
|
||||
if self.config.epub_cover:
|
||||
image, html_tmpl = self.config.epub_cover
|
||||
image = image.replace(os.sep, '/')
|
||||
mpos = content_tmpl.rfind('</metadata>')
|
||||
cpos = content_tmpl.rfind('\n', 0, mpos) + 1
|
||||
content_tmpl = content_tmpl[:cpos] + \
|
||||
COVER_TEMPLATE % {'cover': self.esc(self.make_id(image))} + \
|
||||
content_tmpl[cpos:]
|
||||
metadata['cover'] = self.esc(self.make_id(image))
|
||||
if html_tmpl:
|
||||
spine.insert(0, self.spine_template % {
|
||||
'idref': self.esc(self.make_id(self.coverpage_name))})
|
||||
spine = Spine(self.esc(self.make_id(self.coverpage_name)), True)
|
||||
metadata['spines'].insert(0, spine)
|
||||
if self.coverpage_name not in self.files:
|
||||
ext = path.splitext(self.coverpage_name)[-1]
|
||||
self.files.append(self.coverpage_name)
|
||||
projectfiles.append(self.file_template % {
|
||||
'href': self.esc(self.coverpage_name),
|
||||
'id': self.esc(self.make_id(self.coverpage_name)),
|
||||
'media_type': self.esc(self.media_types[ext])
|
||||
})
|
||||
item = ManifestItem(self.esc(filename),
|
||||
self.esc(self.make_id(filename)),
|
||||
self.esc(self.media_types[ext]))
|
||||
metadata['manifest_items'].append(item)
|
||||
ctx = {'image': self.esc(image), 'title': self.config.project}
|
||||
self.handle_page(
|
||||
path.splitext(self.coverpage_name)[0], ctx, html_tmpl)
|
||||
spinefiles.add(self.coverpage_name)
|
||||
|
||||
guide = []
|
||||
auto_add_cover = True
|
||||
auto_add_toc = True
|
||||
if self.config.epub_guide:
|
||||
@@ -697,64 +591,43 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
auto_add_cover = False
|
||||
if type == 'toc':
|
||||
auto_add_toc = False
|
||||
guide.append(self.guide_template % {
|
||||
'type': self.esc(type),
|
||||
'title': self.esc(title),
|
||||
'uri': self.esc(uri)
|
||||
})
|
||||
metadata['guides'].append(Guide(self.esc(type),
|
||||
self.esc(title),
|
||||
self.esc(uri)))
|
||||
if auto_add_cover and html_tmpl:
|
||||
guide.append(self.guide_template % {
|
||||
'type': 'cover',
|
||||
'title': self.guide_titles['cover'],
|
||||
'uri': self.esc(self.coverpage_name)
|
||||
})
|
||||
metadata['guides'].append(Guide('cover',
|
||||
self.guide_titles['cover'],
|
||||
self.esc(self.coverpage_name)))
|
||||
if auto_add_toc and self.refnodes:
|
||||
guide.append(self.guide_template % {
|
||||
'type': 'toc',
|
||||
'title': self.guide_titles['toc'],
|
||||
'uri': self.esc(self.refnodes[0]['refuri'])
|
||||
})
|
||||
projectfiles = '\n'.join(projectfiles) # type: ignore
|
||||
spine = '\n'.join(spine) # type: ignore
|
||||
guide = '\n'.join(guide) # type: ignore
|
||||
metadata['guides'].append(Guide('toc',
|
||||
self.guide_titles['toc'],
|
||||
self.esc(self.refnodes[0]['refuri'])))
|
||||
|
||||
# write the project file
|
||||
with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore
|
||||
f.write(content_tmpl % # type: ignore
|
||||
self.content_metadata(projectfiles, spine, guide))
|
||||
copy_asset_file(path.join(self.template_dir, 'content.opf_t'),
|
||||
path.join(outdir, outname),
|
||||
metadata)
|
||||
|
||||
def new_navpoint(self, node, level, incr=True):
|
||||
# type: (nodes.Node, int, bool) -> unicode
|
||||
# type: (nodes.Node, int, bool) -> NavPoint
|
||||
"""Create a new entry in the toc from the node at given level."""
|
||||
# XXX Modifies the node
|
||||
if incr:
|
||||
self.playorder += 1
|
||||
self.tocid += 1
|
||||
node['indent'] = self.navpoint_indent * level
|
||||
node['navpoint'] = self.esc(self.node_navpoint_template % self.tocid)
|
||||
node['playorder'] = self.playorder
|
||||
return self.navpoint_template % node
|
||||
|
||||
def insert_subnav(self, node, subnav):
|
||||
# type: (nodes.Node, unicode) -> unicode
|
||||
"""Insert nested navpoints for given node.
|
||||
|
||||
The node and subnav are already rendered to text.
|
||||
"""
|
||||
nlist = node.rsplit('\n', 1)
|
||||
nlist.insert(-1, subnav)
|
||||
return '\n'.join(nlist)
|
||||
return NavPoint(self.esc('navPoint%d' % self.tocid), self.playorder,
|
||||
node['text'], node['refuri'], [])
|
||||
|
||||
def build_navpoints(self, nodes):
|
||||
# type: (nodes.Node) -> unicode
|
||||
# type: (nodes.Node) -> List[NavPoint]
|
||||
"""Create the toc navigation structure.
|
||||
|
||||
Subelements of a node are nested inside the navpoint. For nested nodes
|
||||
the parent node is reinserted in the subnav.
|
||||
"""
|
||||
navstack = []
|
||||
navlist = []
|
||||
level = 1
|
||||
navstack = [] # type: List[NavPoint]
|
||||
navstack.append(NavPoint('dummy', '', '', '', []))
|
||||
level = 0
|
||||
lastnode = None
|
||||
for node in nodes:
|
||||
if not node['text']:
|
||||
@@ -765,32 +638,33 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
if node['level'] > self.config.epub_tocdepth:
|
||||
continue
|
||||
if node['level'] == level:
|
||||
navlist.append(self.new_navpoint(node, level))
|
||||
navpoint = self.new_navpoint(node, level)
|
||||
navstack.pop()
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
elif node['level'] == level + 1:
|
||||
navstack.append(navlist)
|
||||
navlist = []
|
||||
level += 1
|
||||
if lastnode and self.config.epub_tocdup:
|
||||
# Insert starting point in subtoc with same playOrder
|
||||
navlist.append(self.new_navpoint(lastnode, level, False))
|
||||
navlist.append(self.new_navpoint(node, level))
|
||||
navstack[-1].children.append(self.new_navpoint(lastnode, level, False))
|
||||
navpoint = self.new_navpoint(node, level)
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
elif node['level'] < level:
|
||||
while node['level'] < len(navstack):
|
||||
navstack.pop()
|
||||
level = node['level']
|
||||
navpoint = self.new_navpoint(node, level)
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
else:
|
||||
while node['level'] < level:
|
||||
subnav = '\n'.join(navlist)
|
||||
navlist = navstack.pop()
|
||||
navlist[-1] = self.insert_subnav(navlist[-1], subnav)
|
||||
level -= 1
|
||||
navlist.append(self.new_navpoint(node, level))
|
||||
raise
|
||||
lastnode = node
|
||||
while level != 1:
|
||||
subnav = '\n'.join(navlist)
|
||||
navlist = navstack.pop()
|
||||
navlist[-1] = self.insert_subnav(navlist[-1], subnav)
|
||||
level -= 1
|
||||
return '\n'.join(navlist)
|
||||
|
||||
return navstack[0].children
|
||||
|
||||
def toc_metadata(self, level, navpoints):
|
||||
# type: (int, List[unicode]) -> Dict[unicode, Any]
|
||||
# type: (int, List[NavPoint]) -> Dict[unicode, Any]
|
||||
"""Create a dictionary with all metadata for the toc.ncx file
|
||||
properly escaped.
|
||||
"""
|
||||
@@ -818,8 +692,9 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
navpoints = self.build_navpoints(refnodes)
|
||||
level = max(item['level'] for item in self.refnodes)
|
||||
level = min(level, self.config.epub_tocdepth)
|
||||
with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore
|
||||
f.write(self.toc_template % self.toc_metadata(level, navpoints)) # type: ignore
|
||||
copy_asset_file(path.join(self.template_dir, 'toc.ncx_t'),
|
||||
path.join(outdir, outname),
|
||||
self.toc_metadata(level, navpoints))
|
||||
|
||||
def build_epub(self, outdir, outname):
|
||||
# type: (unicode, unicode) -> None
|
||||
@@ -829,16 +704,13 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
entry.
|
||||
"""
|
||||
logger.info('writing %s file...', outname)
|
||||
projectfiles = ['META-INF/container.xml', 'content.opf', 'toc.ncx'] # type: List[unicode] # NOQA
|
||||
projectfiles.extend(self.files)
|
||||
epub = zipfile.ZipFile(path.join(outdir, outname), 'w', # type: ignore
|
||||
zipfile.ZIP_DEFLATED)
|
||||
epub.write(path.join(outdir, 'mimetype'), 'mimetype', # type: ignore
|
||||
zipfile.ZIP_STORED)
|
||||
for file in projectfiles:
|
||||
fp = path.join(outdir, file)
|
||||
epub.write(fp, file, zipfile.ZIP_DEFLATED) # type: ignore
|
||||
epub.close()
|
||||
epub_filename = path.join(outdir, outname)
|
||||
with ZipFile(epub_filename, 'w', ZIP_DEFLATED) as epub: # type: ignore
|
||||
epub.write(path.join(outdir, 'mimetype'), 'mimetype', ZIP_STORED) # type: ignore
|
||||
for filename in [u'META-INF/container.xml', u'content.opf', u'toc.ncx']:
|
||||
epub.write(path.join(outdir, filename), filename, ZIP_DEFLATED) # type: ignore
|
||||
for filename in self.files:
|
||||
epub.write(path.join(outdir, filename), filename, ZIP_DEFLATED) # type: ignore
|
||||
|
||||
|
||||
def setup(app):
|
||||
|
||||
@@ -10,13 +10,15 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import codecs
|
||||
from os import path
|
||||
from datetime import datetime
|
||||
from collections import namedtuple
|
||||
|
||||
from sphinx.config import string_classes
|
||||
from sphinx import package_dir
|
||||
from sphinx.config import string_classes, ENUM
|
||||
from sphinx.builders.epub import EpubBuilder
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@@ -27,79 +29,24 @@ if False:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# (Fragment) templates from which the metainfo files content.opf, toc.ncx,
|
||||
# mimetype, and META-INF/container.xml are created.
|
||||
# This template section also defines strings that are embedded in the html
|
||||
# output but that may be customized by (re-)setting module attributes,
|
||||
# e.g. from conf.py.
|
||||
NavPoint = namedtuple('NavPoint', ['text', 'refuri', 'children'])
|
||||
|
||||
NAVIGATION_DOC_TEMPLATE = u'''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"\
|
||||
xmlns:epub="http://www.idpf.org/2007/ops" lang="%(lang)s" xml:lang="%(lang)s">
|
||||
<head>
|
||||
<title>%(toc_locale)s</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav epub:type="toc">
|
||||
<h1>%(toc_locale)s</h1>
|
||||
<ol>
|
||||
%(navlist)s
|
||||
</ol>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
NAVLIST_TEMPLATE = u'''%(indent)s <li><a href="%(refuri)s">%(text)s</a></li>'''
|
||||
NAVLIST_TEMPLATE_HAS_CHILD = u'''%(indent)s <li><a href="%(refuri)s">%(text)s</a>'''
|
||||
NAVLIST_TEMPLATE_BEGIN_BLOCK = u'''%(indent)s <ol>'''
|
||||
NAVLIST_TEMPLATE_END_BLOCK = u'''%(indent)s </ol>
|
||||
%(indent)s </li>'''
|
||||
NAVLIST_INDENT = ' '
|
||||
|
||||
PACKAGE_DOC_TEMPLATE = u'''\
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="%(lang)s"
|
||||
unique-identifier="%(uid)s"
|
||||
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
|
||||
<metadata xmlns:opf="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:language>%(lang)s</dc:language>
|
||||
<dc:title>%(title)s</dc:title>
|
||||
<dc:description>%(description)s</dc:description>
|
||||
<dc:creator>%(author)s</dc:creator>
|
||||
<dc:contributor>%(contributor)s</dc:contributor>
|
||||
<dc:publisher>%(publisher)s</dc:publisher>
|
||||
<dc:rights>%(copyright)s</dc:rights>
|
||||
<dc:identifier id="%(uid)s">%(id)s</dc:identifier>
|
||||
<dc:date>%(date)s</dc:date>
|
||||
<meta property="dcterms:modified">%(date)s</meta>
|
||||
<meta property="ibooks:version">%(version)s</meta>
|
||||
<meta property="ibooks:specified-fonts">true</meta>
|
||||
<meta property="ibooks:binding">true</meta>
|
||||
<meta property="ibooks:scroll-axis">%(ibook_scroll_axis)s</meta>
|
||||
</metadata>
|
||||
<manifest>
|
||||
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
|
||||
<item id="nav" href="nav.xhtml"\
|
||||
media-type="application/xhtml+xml" properties="nav"/>
|
||||
%(files)s
|
||||
</manifest>
|
||||
<spine toc="ncx" page-progression-direction="%(page_progression_direction)s">
|
||||
%(spine)s
|
||||
</spine>
|
||||
<guide>
|
||||
%(guide)s
|
||||
</guide>
|
||||
</package>
|
||||
'''
|
||||
# writing modes
|
||||
PAGE_PROGRESSION_DIRECTIONS = {
|
||||
'horizontal': 'ltr',
|
||||
'vertical': 'rtl',
|
||||
}
|
||||
IBOOK_SCROLL_AXIS = {
|
||||
'horizontal': 'vertical',
|
||||
'vertical': 'horizontal',
|
||||
}
|
||||
THEME_WRITING_MODES = {
|
||||
'vertical': 'vertical-rl',
|
||||
'horizontal': 'horizontal-tb',
|
||||
}
|
||||
|
||||
DOCTYPE = u'''<!DOCTYPE html>'''
|
||||
|
||||
# The epub3 publisher
|
||||
|
||||
|
||||
class Epub3Builder(EpubBuilder):
|
||||
"""
|
||||
@@ -111,13 +58,7 @@ class Epub3Builder(EpubBuilder):
|
||||
"""
|
||||
name = 'epub'
|
||||
|
||||
navigation_doc_template = NAVIGATION_DOC_TEMPLATE
|
||||
navlist_template = NAVLIST_TEMPLATE
|
||||
navlist_template_has_child = NAVLIST_TEMPLATE_HAS_CHILD
|
||||
navlist_template_begin_block = NAVLIST_TEMPLATE_BEGIN_BLOCK
|
||||
navlist_template_end_block = NAVLIST_TEMPLATE_END_BLOCK
|
||||
navlist_indent = NAVLIST_INDENT
|
||||
content_template = PACKAGE_DOC_TEMPLATE
|
||||
template_dir = path.join(package_dir, 'templates', 'epub3')
|
||||
doctype = DOCTYPE
|
||||
|
||||
# Finish by building the epub file
|
||||
@@ -132,77 +73,31 @@ class Epub3Builder(EpubBuilder):
|
||||
self.build_toc(self.outdir, 'toc.ncx')
|
||||
self.build_epub(self.outdir, self.config.epub_basename + '.epub')
|
||||
|
||||
def content_metadata(self, files, spine, guide):
|
||||
# type: (List[unicode], List[unicode], List[unicode]) -> Dict
|
||||
def content_metadata(self):
|
||||
# type: () -> Dict
|
||||
"""Create a dictionary with all metadata for the content.opf
|
||||
file properly escaped.
|
||||
"""
|
||||
metadata = super(Epub3Builder, self).content_metadata(
|
||||
files, spine, guide)
|
||||
writing_mode = self.config.epub_writing_mode
|
||||
|
||||
metadata = super(Epub3Builder, self).content_metadata()
|
||||
metadata['description'] = self.esc(self.config.epub_description)
|
||||
metadata['contributor'] = self.esc(self.config.epub_contributor)
|
||||
metadata['page_progression_direction'] = self._page_progression_direction()
|
||||
metadata['ibook_scroll_axis'] = self._ibook_scroll_axis()
|
||||
metadata['page_progression_direction'] = PAGE_PROGRESSION_DIRECTIONS.get(writing_mode)
|
||||
metadata['ibook_scroll_axis'] = IBOOK_SCROLL_AXIS.get(writing_mode)
|
||||
metadata['date'] = self.esc(datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))
|
||||
metadata['version'] = self.esc(self.config.version)
|
||||
return metadata
|
||||
|
||||
def _page_progression_direction(self):
|
||||
# type: () -> unicode
|
||||
if self.config.epub_writing_mode == 'horizontal':
|
||||
page_progression_direction = 'ltr'
|
||||
elif self.config.epub_writing_mode == 'vertical':
|
||||
page_progression_direction = 'rtl'
|
||||
else:
|
||||
page_progression_direction = 'default'
|
||||
return page_progression_direction
|
||||
|
||||
def _ibook_scroll_axis(self):
|
||||
# type: () -> unicode
|
||||
if self.config.epub_writing_mode == 'horizontal':
|
||||
scroll_axis = 'vertical'
|
||||
elif self.config.epub_writing_mode == 'vertical':
|
||||
scroll_axis = 'horizontal'
|
||||
else:
|
||||
scroll_axis = 'default'
|
||||
return scroll_axis
|
||||
|
||||
def _css_writing_mode(self):
|
||||
# type: () -> unicode
|
||||
if self.config.epub_writing_mode == 'vertical':
|
||||
editing_mode = 'vertical-rl'
|
||||
else:
|
||||
editing_mode = 'horizontal-tb'
|
||||
return editing_mode
|
||||
|
||||
def prepare_writing(self, docnames):
|
||||
# type: (Iterable[unicode]) -> None
|
||||
super(Epub3Builder, self).prepare_writing(docnames)
|
||||
self.globalcontext['theme_writing_mode'] = self._css_writing_mode()
|
||||
|
||||
def new_navlist(self, node, level, has_child):
|
||||
# type: (nodes.Node, int, bool) -> unicode
|
||||
"""Create a new entry in the toc from the node at given level."""
|
||||
# XXX Modifies the node
|
||||
self.tocid += 1
|
||||
node['indent'] = self.navlist_indent * level
|
||||
if has_child:
|
||||
return self.navlist_template_has_child % node
|
||||
else:
|
||||
return self.navlist_template % node
|
||||
|
||||
def begin_navlist_block(self, level):
|
||||
# type: (int) -> unicode
|
||||
return self.navlist_template_begin_block % {
|
||||
"indent": self.navlist_indent * level
|
||||
}
|
||||
|
||||
def end_navlist_block(self, level):
|
||||
# type: (int) -> unicode
|
||||
return self.navlist_template_end_block % {"indent": self.navlist_indent * level}
|
||||
writing_mode = self.config.epub_writing_mode
|
||||
self.globalcontext['theme_writing_mode'] = THEME_WRITING_MODES.get(writing_mode)
|
||||
|
||||
def build_navlist(self, navnodes):
|
||||
# type: (List[nodes.Node]) -> unicode
|
||||
# type: (List[nodes.Node]) -> List[NavPoint]
|
||||
"""Create the toc navigation structure.
|
||||
|
||||
This method is almost same as build_navpoints method in epub.py.
|
||||
@@ -212,9 +107,9 @@ class Epub3Builder(EpubBuilder):
|
||||
The difference from build_navpoints method is templates which are used
|
||||
when generating navigation documents.
|
||||
"""
|
||||
navlist = []
|
||||
level = 1
|
||||
usenodes = []
|
||||
navstack = [] # type: List[NavPoint]
|
||||
navstack.append(NavPoint('', '', []))
|
||||
level = 0
|
||||
for node in navnodes:
|
||||
if not node['text']:
|
||||
continue
|
||||
@@ -223,31 +118,33 @@ class Epub3Builder(EpubBuilder):
|
||||
continue
|
||||
if node['level'] > self.config.epub_tocdepth:
|
||||
continue
|
||||
usenodes.append(node)
|
||||
for i, node in enumerate(usenodes):
|
||||
curlevel = node['level']
|
||||
if curlevel == level + 1:
|
||||
navlist.append(self.begin_navlist_block(level))
|
||||
while curlevel < level:
|
||||
level -= 1
|
||||
navlist.append(self.end_navlist_block(level))
|
||||
level = curlevel
|
||||
if i != len(usenodes) - 1 and usenodes[i + 1]['level'] > level:
|
||||
has_child = True
|
||||
|
||||
navpoint = NavPoint(node['text'], node['refuri'], [])
|
||||
if node['level'] == level:
|
||||
navstack.pop()
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
elif node['level'] == level + 1:
|
||||
level += 1
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
elif node['level'] < level:
|
||||
while node['level'] < len(navstack):
|
||||
navstack.pop()
|
||||
level = node['level']
|
||||
navstack[-1].children.append(navpoint)
|
||||
navstack.append(navpoint)
|
||||
else:
|
||||
has_child = False
|
||||
navlist.append(self.new_navlist(node, level, has_child))
|
||||
while level != 1:
|
||||
level -= 1
|
||||
navlist.append(self.end_navlist_block(level))
|
||||
return '\n'.join(navlist)
|
||||
raise
|
||||
|
||||
return navstack[0].children
|
||||
|
||||
def navigation_doc_metadata(self, navlist):
|
||||
# type: (unicode) -> Dict
|
||||
# type: (List[NavPoint]) -> Dict
|
||||
"""Create a dictionary with all metadata for the nav.xhtml file
|
||||
properly escaped.
|
||||
"""
|
||||
metadata = {}
|
||||
metadata = {} # type: Dict
|
||||
metadata['lang'] = self.esc(self.config.epub_language)
|
||||
metadata['toc_locale'] = self.esc(self.guide_titles['toc'])
|
||||
metadata['navlist'] = navlist
|
||||
@@ -268,9 +165,9 @@ class Epub3Builder(EpubBuilder):
|
||||
# 'includehidden'
|
||||
refnodes = self.refnodes
|
||||
navlist = self.build_navlist(refnodes)
|
||||
with codecs.open(path.join(outdir, outname), 'w', 'utf-8') as f: # type: ignore
|
||||
f.write(self.navigation_doc_template % # type: ignore
|
||||
self.navigation_doc_metadata(navlist))
|
||||
copy_asset_file(path.join(self.template_dir, 'nav.xhtml_t'),
|
||||
path.join(outdir, outname),
|
||||
self.navigation_doc_metadata(navlist))
|
||||
|
||||
# Add nav.xhtml to epub file
|
||||
if outname not in self.files:
|
||||
@@ -284,7 +181,8 @@ def setup(app):
|
||||
|
||||
app.add_config_value('epub_description', '', 'epub3', string_classes)
|
||||
app.add_config_value('epub_contributor', 'unknown', 'epub3', string_classes)
|
||||
app.add_config_value('epub_writing_mode', 'horizontal', 'epub3', string_classes)
|
||||
app.add_config_value('epub_writing_mode', 'horizontal', 'epub3',
|
||||
ENUM('horizontal', 'vertical'))
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
|
||||
@@ -1032,7 +1032,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
return tree
|
||||
|
||||
def assemble_toc_secnumbers(self):
|
||||
# type: () -> Dict[unicode, Dict[Tuple[unicode, unicode], Tuple[int, ...]]]
|
||||
# type: () -> Dict[unicode, Dict[unicode, Tuple[int, ...]]]
|
||||
# Assemble toc_secnumbers to resolve section numbers on SingleHTML.
|
||||
# Merge all secnumbers to single secnumber.
|
||||
#
|
||||
@@ -1042,15 +1042,16 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
#
|
||||
# There are related codes in inline_all_toctres() and
|
||||
# HTMLTranslter#add_secnumber().
|
||||
new_secnumbers = {}
|
||||
new_secnumbers = {} # type: Dict[unicode, Tuple[int, ...]]
|
||||
for docname, secnums in iteritems(self.env.toc_secnumbers):
|
||||
for id, secnum in iteritems(secnums):
|
||||
new_secnumbers[(docname, id)] = secnum
|
||||
alias = "%s/%s" % (docname, id)
|
||||
new_secnumbers[alias] = secnum
|
||||
|
||||
return {self.config.master_doc: new_secnumbers}
|
||||
|
||||
def assemble_toc_fignumbers(self):
|
||||
# type: () -> Dict[unicode, Dict[Tuple[unicode, unicode], Dict[unicode, Tuple[int, ...]]]] # NOQA
|
||||
# type: () -> Dict[unicode, Dict[unicode, Dict[unicode, Tuple[int, ...]]]] # NOQA
|
||||
# Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
|
||||
# Merge all fignumbers to single fignumber.
|
||||
#
|
||||
@@ -1060,13 +1061,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
#
|
||||
# There are related codes in inline_all_toctres() and
|
||||
# HTMLTranslter#add_fignumber().
|
||||
new_fignumbers = {} # type: Dict[Tuple[unicode, unicode], Dict[unicode, Tuple[int, ...]]] # NOQA
|
||||
new_fignumbers = {} # type: Dict[unicode, Dict[unicode, Tuple[int, ...]]]
|
||||
# {u'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, u'bar': {'figure': {'id1': (3,)}}}
|
||||
for docname, fignumlist in iteritems(self.env.toc_fignumbers):
|
||||
for figtype, fignums in iteritems(fignumlist):
|
||||
new_fignumbers.setdefault((docname, figtype), {})
|
||||
alias = "%s/%s" % (docname, figtype)
|
||||
new_fignumbers.setdefault(alias, {})
|
||||
for id, fignum in iteritems(fignums):
|
||||
new_fignumbers[(docname, figtype)][id] = fignum
|
||||
new_fignumbers[alias][id] = fignum
|
||||
|
||||
return {self.config.master_doc: new_fignumbers}
|
||||
|
||||
|
||||
@@ -171,7 +171,8 @@ class Config(object):
|
||||
if getenv('SOURCE_DATE_EPOCH') is not None:
|
||||
for k in ('copyright', 'epub_copyright'):
|
||||
if k in config:
|
||||
config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'), config[k])
|
||||
config[k] = copyright_year_re.sub(r'\g<1>%s' % format_date('%Y'),
|
||||
config[k])
|
||||
|
||||
def check_types(self):
|
||||
# type: () -> None
|
||||
|
||||
@@ -214,9 +214,9 @@ class LiteralIncludeReader(object):
|
||||
lines = self.show_diff()
|
||||
else:
|
||||
filters = [self.pyobject_filter,
|
||||
self.lines_filter,
|
||||
self.start_filter,
|
||||
self.end_filter,
|
||||
self.lines_filter,
|
||||
self.prepend_filter,
|
||||
self.append_filter]
|
||||
lines = self.read_file(self.filename)
|
||||
|
||||
@@ -116,7 +116,7 @@ class PyXrefMixin(object):
|
||||
def make_xrefs(self, rolename, domain, target, innernode=nodes.emphasis,
|
||||
contnode=None):
|
||||
# type: (unicode, unicode, unicode, nodes.Node, nodes.Node) -> List[nodes.Node]
|
||||
delims = '(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
|
||||
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
|
||||
delims_re = re.compile(delims)
|
||||
sub_targets = re.split(delims, target)
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# RE for option descriptions
|
||||
option_desc_re = re.compile(r'((?:/|--|-|\+)?[-\.?@#_a-zA-Z0-9]+)(=?\s*.*)')
|
||||
option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
|
||||
# RE for grammar tokens
|
||||
token_re = re.compile('`(\w+)`', re.U)
|
||||
token_re = re.compile(r'`(\w+)`', re.U)
|
||||
|
||||
|
||||
class GenericObject(ObjectDescription):
|
||||
|
||||
@@ -357,7 +357,7 @@ class Autosummary(Directive):
|
||||
*items* is a list produced by :meth:`get_items`.
|
||||
"""
|
||||
table_spec = addnodes.tabular_col_spec()
|
||||
table_spec['spec'] = 'p{0.5\linewidth}p{0.5\linewidth}'
|
||||
table_spec['spec'] = r'p{0.5\linewidth}p{0.5\linewidth}'
|
||||
|
||||
table = autosummary_table('')
|
||||
real_table = nodes.table('', classes=['longtable'])
|
||||
@@ -388,7 +388,7 @@ class Autosummary(Directive):
|
||||
for name, sig, summary, real_name in items:
|
||||
qualifier = 'obj'
|
||||
if 'nosignatures' not in self.options:
|
||||
col1 = ':%s:`%s <%s>`\ %s' % (qualifier, name, real_name, rst.escape(sig)) # type: unicode # NOQA
|
||||
col1 = ':%s:`%s <%s>`\\ %s' % (qualifier, name, real_name, rst.escape(sig)) # type: unicode # NOQA
|
||||
else:
|
||||
col1 = ':%s:`%s <%s>`' % (qualifier, name, real_name)
|
||||
col2 = summary
|
||||
|
||||
@@ -52,7 +52,7 @@ number2name.update(token.tok_name)
|
||||
|
||||
_eq = nodes.Leaf(token.EQUAL, '=')
|
||||
|
||||
emptyline_re = re.compile('^\s*(#.*)?$')
|
||||
emptyline_re = re.compile(r'^\s*(#.*)?$')
|
||||
|
||||
|
||||
class AttrDocVisitor(nodes.NodeVisitor):
|
||||
|
||||
@@ -293,7 +293,7 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner,
|
||||
return [retnode], []
|
||||
|
||||
|
||||
_abbr_re = re.compile('\((.*)\)$', re.S)
|
||||
_abbr_re = re.compile(r'\((.*)\)$', re.S)
|
||||
|
||||
|
||||
def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
|
||||
6
sphinx/templates/epub2/container.xml
Normal file
6
sphinx/templates/epub2/container.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
||||
37
sphinx/templates/epub2/content.opf_t
Normal file
37
sphinx/templates/epub2/content.opf_t
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package xmlns="http://www.idpf.org/2007/opf" version="2.0"
|
||||
unique-identifier="%(uid)s">
|
||||
<metadata xmlns:opf="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:language>{{ lang }}</dc:language>
|
||||
<dc:title>{{ title }}</dc:title>
|
||||
<dc:creator opf:role="aut">{{ author }}</dc:creator>
|
||||
<dc:publisher>{{ publisher }}</dc:publisher>
|
||||
<dc:rights>{{ copyright }}</dc:rights>
|
||||
<dc:identifier id="{{ uid }}" opf:scheme="{{ scheme }}">{{ id }}</dc:identifier>
|
||||
<dc:date>{{ date }}</dc:date>
|
||||
{%- if cover %}
|
||||
<meta name="cover" content="{{ cover }}"/>
|
||||
{%- endif %}
|
||||
</metadata>
|
||||
<manifest>
|
||||
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
|
||||
{%- for item in manifest_items %}
|
||||
<item id="{{ item.id }}" href="{{ item.href }}" media-type="{{ item.media_type }}" />
|
||||
{%- endfor %}
|
||||
</manifest>
|
||||
<spine toc="ncx">
|
||||
{%- for spine in spines %}
|
||||
{%- if spine.linear %}
|
||||
<itemref idref="{{ spine.idref }}" />
|
||||
{%- else %}
|
||||
<itemref idref="{{ spine.idref }}" linear="no" />'''
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</spine>
|
||||
<guide>
|
||||
{%- for guide in guides %}
|
||||
<reference type="{{ guide.type }}" title="{{ guide.title }}" href="{{ guide.uri }}" />'''
|
||||
{%- endfor %}
|
||||
</guide>
|
||||
</package>
|
||||
1
sphinx/templates/epub2/mimetype
Normal file
1
sphinx/templates/epub2/mimetype
Normal file
@@ -0,0 +1 @@
|
||||
application/epub+zip
|
||||
15
sphinx/templates/epub2/toc.ncx_t
Normal file
15
sphinx/templates/epub2/toc.ncx_t
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0"?>
|
||||
<ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
|
||||
<head>
|
||||
<meta name="dtb:uid" content="{{ uid }}"/>
|
||||
<meta name="dtb:depth" content="{{ level }}"/>
|
||||
<meta name="dtb:totalPageCount" content="0"/>
|
||||
<meta name="dtb:maxPageNumber" content="0"/>
|
||||
</head>
|
||||
<docTitle>
|
||||
<text>{{ title }}</text>
|
||||
</docTitle>
|
||||
<navMap>
|
||||
{{ navpoints }}
|
||||
</navMap>
|
||||
</ncx>
|
||||
6
sphinx/templates/epub3/container.xml
Normal file
6
sphinx/templates/epub3/container.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
||||
46
sphinx/templates/epub3/content.opf_t
Normal file
46
sphinx/templates/epub3/content.opf_t
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package xmlns="http://www.idpf.org/2007/opf" version="3.0" xml:lang="{{ lang }}"
|
||||
unique-identifier="{{ uid }}"
|
||||
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/">
|
||||
<metadata xmlns:opf="http://www.idpf.org/2007/opf"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<dc:language>{{ lang }}</dc:language>
|
||||
<dc:title>{{ title }}</dc:title>
|
||||
<dc:description>{{ description }}</dc:description>
|
||||
<dc:creator>{{ author }}</dc:creator>
|
||||
<dc:contributor>{{ contributor }}</dc:contributor>
|
||||
<dc:publisher>{{ publisher }}</dc:publisher>
|
||||
<dc:rights>{{ copyright }}</dc:rights>
|
||||
<dc:identifier id="{{ uid }}">{{ id }}</dc:identifier>
|
||||
<dc:date>{{ date }}</dc:date>
|
||||
<meta property="dcterms:modified">{{ date }}</meta>
|
||||
<meta property="ibooks:version">{{ version }}</meta>
|
||||
<meta property="ibooks:specified-fonts">true</meta>
|
||||
<meta property="ibooks:binding">true</meta>
|
||||
<meta property="ibooks:scroll-axis">{{ ibook_scroll_axis }}</meta>
|
||||
{%- if cover %}
|
||||
<meta name="cover" content="{{ cover }}"/>
|
||||
{%- endif %}
|
||||
</metadata>
|
||||
<manifest>
|
||||
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />
|
||||
<item id="nav" href="nav.xhtml" media-type="application/xhtml+xml" properties="nav"/>
|
||||
{%- for item in manifest_items %}
|
||||
<item id="{{ item.id }}" href="{{ item.href }}" media-type="{{ item.media_type }}" />
|
||||
{%- endfor %}
|
||||
</manifest>
|
||||
<spine toc="ncx" page-progression-direction="{{ page_progression_direction }}">
|
||||
{%- for spine in spines %}
|
||||
{%- if spine.linear %}
|
||||
<itemref idref="{{ spine.idref }}" />
|
||||
{%- else %}
|
||||
<itemref idref="{{ spine.idref }}" linear="no" />'''
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
</spine>
|
||||
<guide>
|
||||
{%- for guide in guides %}
|
||||
<reference type="{{ guide.type }}" title="{{ guide.title }}" href="{{ guide.uri }}" />'''
|
||||
{%- endfor %}
|
||||
</guide>
|
||||
</package>
|
||||
1
sphinx/templates/epub3/mimetype
Normal file
1
sphinx/templates/epub3/mimetype
Normal file
@@ -0,0 +1 @@
|
||||
application/epub+zip
|
||||
26
sphinx/templates/epub3/nav.xhtml_t
Normal file
26
sphinx/templates/epub3/nav.xhtml_t
Normal file
@@ -0,0 +1,26 @@
|
||||
{%- macro toctree(navlist) -%}
|
||||
<ol>
|
||||
{%- for nav in navlist %}
|
||||
<li>
|
||||
<a href="{{ nav.refuri }}">{{ nav.text }}</a>
|
||||
{%- if nav.children %}
|
||||
{{ toctree(nav.children)|indent(4, true) }}
|
||||
{%- endif %}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ol>
|
||||
{%- endmacro -%}
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:epub="http://www.idpf.org/2007/ops" lang="{{ lang }}" xml:lang="{{ lang }}">
|
||||
<head>
|
||||
<title>{{ toc_locale }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav epub:type="toc">
|
||||
<h1>{{ toc_locale }}</h1>
|
||||
{{ toctree(navlist)|indent(6, true) }}
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
24
sphinx/templates/epub3/toc.ncx_t
Normal file
24
sphinx/templates/epub3/toc.ncx_t
Normal file
@@ -0,0 +1,24 @@
|
||||
{%- macro navPoints(navlist) %}
|
||||
{%- for nav in navlist %}
|
||||
<navPoint id="{{ nav.navpoint }}" playOrder="{{ nav.playorder }}">
|
||||
<navLabel>
|
||||
<text>{{ nav.text }}</text>
|
||||
</navLabel>
|
||||
<content src="{{ nav.refuri }}" />{{ navPoints(nav.children)|indent(2, true) }}
|
||||
</navPoint>
|
||||
{%- endfor %}
|
||||
{%- endmacro -%}
|
||||
<?xml version="1.0"?>
|
||||
<ncx version="2005-1" xmlns="http://www.daisy.org/z3986/2005/ncx/">
|
||||
<head>
|
||||
<meta name="dtb:uid" content="{{ uid }}"/>
|
||||
<meta name="dtb:depth" content="{{ level }}"/>
|
||||
<meta name="dtb:totalPageCount" content="0"/>
|
||||
<meta name="dtb:maxPageNumber" content="0"/>
|
||||
</head>
|
||||
<docTitle>
|
||||
<text>{{ title }}</text>
|
||||
</docTitle>
|
||||
<navMap>{{ navPoints(navpoints)|indent(4, true) }}
|
||||
</navMap>
|
||||
</ncx>
|
||||
@@ -561,7 +561,7 @@ def encode_uri(uri):
|
||||
|
||||
def split_docinfo(text):
|
||||
# type: (unicode) -> Sequence[unicode]
|
||||
docinfo_re = re.compile('\A((?:\s*:\w+:.*?\n(?:[ \t]+.*?\n)*)+)', re.M)
|
||||
docinfo_re = re.compile('\\A((?:\\s*:\\w+:.*?\n(?:[ \\t]+.*?\n)*)+)', re.M)
|
||||
result = docinfo_re.split(text, 1) # type: ignore
|
||||
if len(result) == 1:
|
||||
return '', result[0]
|
||||
|
||||
@@ -21,7 +21,8 @@ from docutils.parsers.rst import directives, roles
|
||||
from sphinx.util import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
report_re = re.compile('^(.+?:\d+): \((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\d+)?\) (.+?)\n?$')
|
||||
report_re = re.compile('^(.+?:\\d+): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(\\d+)?\\) '
|
||||
'(.+?)\n?$')
|
||||
|
||||
|
||||
if False:
|
||||
|
||||
@@ -65,8 +65,8 @@ def apply_source_workaround(node):
|
||||
if isinstance(node, nodes.term):
|
||||
# strip classifier from rawsource of term
|
||||
for classifier in reversed(node.parent.traverse(nodes.classifier)):
|
||||
node.rawsource = re.sub(
|
||||
'\s*:\s*%s' % re.escape(classifier.astext()), '', node.rawsource)
|
||||
node.rawsource = re.sub(r'\s*:\s*%s' % re.escape(classifier.astext()),
|
||||
'', node.rawsource)
|
||||
|
||||
# workaround: recommonmark-0.2.0 doesn't set rawsource attribute
|
||||
if not node.rawsource:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import re
|
||||
|
||||
symbols_re = re.compile('([!-/:-@\[-`{-~])')
|
||||
symbols_re = re.compile(r'([!-/:-@\[-`{-~])')
|
||||
|
||||
|
||||
def escape(text):
|
||||
|
||||
@@ -310,11 +310,9 @@ class HTMLTranslator(BaseTranslator):
|
||||
elif isinstance(node.parent, nodes.section):
|
||||
if self.builder.name == 'singlehtml':
|
||||
docname = self.docnames[-1]
|
||||
anchorname = '#' + node.parent['ids'][0]
|
||||
if (docname, anchorname) not in self.builder.secnumbers:
|
||||
anchorname = (docname, '') # try first heading which has no anchor
|
||||
else:
|
||||
anchorname = (docname, anchorname)
|
||||
anchorname = "%s/#%s" % (docname, node.parent['ids'][0])
|
||||
if anchorname not in self.builder.secnumbers:
|
||||
anchorname = "%s/" % docname # try first heading which has no anchor
|
||||
else:
|
||||
anchorname = '#' + node.parent['ids'][0]
|
||||
if anchorname not in self.builder.secnumbers:
|
||||
@@ -329,7 +327,7 @@ class HTMLTranslator(BaseTranslator):
|
||||
def append_fignumber(figtype, figure_id):
|
||||
# type: (unicode, unicode) -> None
|
||||
if self.builder.name == 'singlehtml':
|
||||
key = (self.docnames[-1], figtype)
|
||||
key = "%s/%s" % (self.docnames[-1], figtype)
|
||||
else:
|
||||
key = figtype
|
||||
|
||||
|
||||
@@ -371,7 +371,7 @@ class Table(object):
|
||||
|
||||
This is what LaTeX calls the 'preamble argument' of the used table environment.
|
||||
|
||||
.. note:: the ``\X`` column type specifier is defined in ``sphinx.sty``.
|
||||
.. note:: the ``\\X`` column type specifier is defined in ``sphinx.sty``.
|
||||
"""
|
||||
if self.colspec:
|
||||
return self.colspec
|
||||
@@ -456,13 +456,13 @@ class TableCell(object):
|
||||
def escape_abbr(text):
|
||||
# type: (unicode) -> unicode
|
||||
"""Adjust spacing after abbreviations."""
|
||||
return re.sub('\.(?=\s|$)', '.\\@', text)
|
||||
return re.sub(r'\.(?=\s|$)', r'.\@', text)
|
||||
|
||||
|
||||
def rstdim_to_latexdim(width_str):
|
||||
# type: (unicode) -> unicode
|
||||
"""Convert `width_str` with rst length to LaTeX length."""
|
||||
match = re.match('^(\d*\.?\d*)\s*(\S*)$', width_str)
|
||||
match = re.match(r'^(\d*\.?\d*)\s*(\S*)$', width_str)
|
||||
if not match:
|
||||
raise ValueError
|
||||
res = width_str
|
||||
@@ -2244,7 +2244,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
|
||||
def visit_line(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.body.append('\item[] ')
|
||||
self.body.append(r'\item[] ')
|
||||
|
||||
def depart_line(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
|
||||
@@ -129,7 +129,7 @@ class ManualPageTranslator(BaseTranslator):
|
||||
tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\""
|
||||
" \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
|
||||
".SH NAME\n"
|
||||
"%(title)s \- %(subtitle)s\n")
|
||||
"%(title)s \\- %(subtitle)s\n")
|
||||
return tmpl % self._docinfo
|
||||
|
||||
def visit_start_of_file(self, node):
|
||||
|
||||
@@ -469,7 +469,7 @@ class TexinfoTranslator(nodes.NodeVisitor):
|
||||
|
||||
def tex_image_length(self, width_str):
|
||||
# type: (unicode) -> unicode
|
||||
match = re.match('(\d*\.?\d*)\s*(\S*)', width_str)
|
||||
match = re.match(r'(\d*\.?\d*)\s*(\S*)', width_str)
|
||||
if not match:
|
||||
# fallback
|
||||
return width_str
|
||||
|
||||
@@ -52,15 +52,15 @@
|
||||
import re
|
||||
|
||||
xpath_tokenizer = re.compile(
|
||||
"("
|
||||
"'[^']*'|\"[^\"]*\"|"
|
||||
"::|"
|
||||
"//?|"
|
||||
"\.\.|"
|
||||
"\(\)|"
|
||||
"[/.*:\[\]\(\)@=])|"
|
||||
"((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|"
|
||||
"\s+"
|
||||
r"("
|
||||
r"'[^']*'|\"[^\"]*\"|"
|
||||
r"::|"
|
||||
r"//?|"
|
||||
r"\.\.|"
|
||||
r"\(\)|"
|
||||
r"[/.*:\[\]\(\)@=])|"
|
||||
r"((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|"
|
||||
r"\s+"
|
||||
).findall
|
||||
|
||||
def prepare_tag(next, token):
|
||||
|
||||
@@ -976,7 +976,7 @@ def _serialize_text(write, elem, encoding):
|
||||
# invalid.
|
||||
|
||||
def register_namespace(prefix, uri):
|
||||
if re.match("ns\d+$", prefix):
|
||||
if re.match(r"ns\d+$", prefix):
|
||||
raise ValueError("Prefix format reserved for internal use")
|
||||
for k, v in _namespace_map.items():
|
||||
if k == uri or v == prefix:
|
||||
|
||||
@@ -172,13 +172,15 @@ Others
|
||||
|
||||
.. option:: +p
|
||||
|
||||
.. option:: --ObjC++
|
||||
|
||||
.. option:: --plugin.option
|
||||
|
||||
.. option:: create-auth-token
|
||||
|
||||
.. option:: arg
|
||||
|
||||
Link to :option:`perl +p`, :option:`--plugin.option`, :option:`create-auth-token` and :option:`arg`
|
||||
Link to :option:`perl +p`, :option:`--ObjC++`, :option:`--plugin.option`, :option:`create-auth-token` and :option:`arg`
|
||||
|
||||
.. program:: hg
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Literal Includes with Line Numbers Matching
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
:language: python
|
||||
:pyobject: Bar
|
||||
:pyobject: Foo
|
||||
:lineno-match:
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
@@ -16,6 +16,12 @@ Literal Includes with Line Numbers Matching
|
||||
:start-after: pass
|
||||
:lineno-match:
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
:language: python
|
||||
:start-after: Literally
|
||||
:lines: 1,2
|
||||
:lineno-match:
|
||||
|
||||
.. literalinclude:: literal.inc
|
||||
:language: python
|
||||
:start-at: class Bar:
|
||||
|
||||
@@ -24,6 +24,7 @@ sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir)))
|
||||
# filter warnings of test dependencies
|
||||
warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv
|
||||
warnings.filterwarnings('ignore', category=ImportWarning, module='backports')
|
||||
warnings.filterwarnings('ignore', category=ImportWarning, module='pytest_cov')
|
||||
warnings.filterwarnings('ignore', category=PendingDeprecationWarning, module=r'_pytest\..*')
|
||||
|
||||
# check dependencies before testing
|
||||
|
||||
247
tests/test_build_epub.py
Normal file
247
tests/test_build_epub.py
Normal file
@@ -0,0 +1,247 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_build_html
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Test the HTML builder and check output against XPath.
|
||||
|
||||
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class EPUBElementTree(object):
|
||||
"""Test helper for content.opf and tox.ncx"""
|
||||
namespaces = {
|
||||
'idpf': 'http://www.idpf.org/2007/opf',
|
||||
'dc': 'http://purl.org/dc/elements/1.1/',
|
||||
'ibooks': 'http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/',
|
||||
'ncx': 'http://www.daisy.org/z3986/2005/ncx/',
|
||||
'xhtml': 'http://www.w3.org/1999/xhtml',
|
||||
'epub': 'http://www.idpf.org/2007/ops'
|
||||
}
|
||||
|
||||
def __init__(self, tree):
|
||||
self.tree = tree
|
||||
|
||||
@classmethod
|
||||
def fromstring(cls, string):
|
||||
return cls(ElementTree.fromstring(string))
|
||||
|
||||
def find(self, match):
|
||||
ret = self.tree.find(match, namespaces=self.namespaces)
|
||||
if ret is not None:
|
||||
return self.__class__(ret)
|
||||
else:
|
||||
return ret
|
||||
|
||||
def findall(self, match):
|
||||
ret = self.tree.findall(match, namespaces=self.namespaces)
|
||||
return [self.__class__(e) for e in ret]
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.tree, name)
|
||||
|
||||
def __iter__(self):
|
||||
for child in self.tree:
|
||||
yield self.__class__(child)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='basic')
|
||||
def test_build_epub(app):
|
||||
app.build()
|
||||
assert (app.outdir / 'mimetype').text() == 'application/epub+zip'
|
||||
assert (app.outdir / 'META-INF' / 'container.xml').exists()
|
||||
|
||||
# toc.ncx
|
||||
toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').text())
|
||||
assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation'
|
||||
|
||||
# toc.ncx / head
|
||||
meta = list(toc.find("./ncx:head"))
|
||||
assert meta[0].attrib == {'name': 'dtb:uid', 'content': 'unknown'}
|
||||
assert meta[1].attrib == {'name': 'dtb:depth', 'content': '1'}
|
||||
assert meta[2].attrib == {'name': 'dtb:totalPageCount', 'content': '0'}
|
||||
assert meta[3].attrib == {'name': 'dtb:maxPageNumber', 'content': '0'}
|
||||
|
||||
# toc.ncx / navMap
|
||||
navpoints = toc.findall("./ncx:navMap/ncx:navPoint")
|
||||
assert len(navpoints) == 1
|
||||
assert navpoints[0].attrib == {'id': 'navPoint1', 'playOrder': '1'}
|
||||
assert navpoints[0].find("./ncx:content").attrib == {'src': 'index.xhtml'}
|
||||
|
||||
navlabel = navpoints[0].find("./ncx:navLabel/ncx:text")
|
||||
assert navlabel.text == 'The basic Sphinx documentation for testing'
|
||||
|
||||
# content.opf
|
||||
opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').text())
|
||||
|
||||
# content.opf / metadata
|
||||
metadata = opf.find("./idpf:metadata")
|
||||
assert metadata.find("./dc:language").text == 'en'
|
||||
assert metadata.find("./dc:title").text == 'Python documentation'
|
||||
assert metadata.find("./dc:description").text is None
|
||||
assert metadata.find("./dc:creator").text == 'unknown'
|
||||
assert metadata.find("./dc:contributor").text == 'unknown'
|
||||
assert metadata.find("./dc:publisher").text == 'unknown'
|
||||
assert metadata.find("./dc:rights").text is None
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:version']").text is None
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:specified-fonts']").text == 'true'
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:binding']").text == 'true'
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical'
|
||||
|
||||
# content.opf / manifest
|
||||
manifest = opf.find("./idpf:manifest")
|
||||
items = list(manifest)
|
||||
assert items[0].attrib == {'id': 'ncx',
|
||||
'href': 'toc.ncx',
|
||||
'media-type': 'application/x-dtbncx+xml'}
|
||||
assert items[1].attrib == {'id': 'nav',
|
||||
'href': 'nav.xhtml',
|
||||
'media-type': 'application/xhtml+xml',
|
||||
'properties': 'nav'}
|
||||
assert items[2].attrib == {'id': 'epub-0',
|
||||
'href': 'genindex.xhtml',
|
||||
'media-type': 'application/xhtml+xml'}
|
||||
assert items[3].attrib == {'id': 'epub-1',
|
||||
'href': 'index.xhtml',
|
||||
'media-type': 'application/xhtml+xml'}
|
||||
|
||||
for i, item in enumerate(items[2:]):
|
||||
# items are named as epub-NN
|
||||
assert item.get('id') == 'epub-%d' % i
|
||||
|
||||
# content.opf / spine
|
||||
spine = opf.find("./idpf:spine")
|
||||
itemrefs = list(spine)
|
||||
assert spine.get('toc') == 'ncx'
|
||||
assert spine.get('page-progression-direction') == 'ltr'
|
||||
assert itemrefs[0].get('idref') == 'epub-1'
|
||||
assert itemrefs[1].get('idref') == 'epub-0'
|
||||
|
||||
# content.opf / guide
|
||||
reference = opf.find("./idpf:guide/idpf:reference")
|
||||
assert reference.get('type') == 'toc'
|
||||
assert reference.get('title') == 'Table of Contents'
|
||||
assert reference.get('href') == 'index.xhtml'
|
||||
|
||||
# nav.xhtml
|
||||
nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').text())
|
||||
assert nav.attrib == {'lang': 'en',
|
||||
'{http://www.w3.org/XML/1998/namespace}lang': 'en'}
|
||||
assert nav.find("./xhtml:head/xhtml:title").text == 'Table of Contents'
|
||||
|
||||
# nav.xhtml / nav
|
||||
navlist = nav.find("./xhtml:body/xhtml:nav")
|
||||
toc = navlist.findall("./xhtml:ol/xhtml:li")
|
||||
assert navlist.find("./xhtml:h1").text == 'Table of Contents'
|
||||
assert len(toc) == 1
|
||||
assert toc[0].find("./xhtml:a").get("href") == 'index.xhtml'
|
||||
assert toc[0].find("./xhtml:a").text == 'The basic Sphinx documentation for testing'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='footnotes',
|
||||
confoverrides={'epub_cover': ('_images/rimg.png', None)})
|
||||
def test_epub_cover(app):
|
||||
app.build()
|
||||
|
||||
# content.opf / metadata
|
||||
opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').text())
|
||||
cover_image = opf.find("./idpf:manifest/idpf:item[@href='%s']" % app.config.epub_cover[0])
|
||||
cover = opf.find("./idpf:metadata/idpf:meta[@name='cover']")
|
||||
assert cover
|
||||
assert cover.get('content') == cover_image.get('id')
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='toctree')
|
||||
def test_nested_toc(app):
|
||||
app.build()
|
||||
|
||||
# toc.ncx
|
||||
toc = EPUBElementTree.fromstring((app.outdir / 'toc.ncx').text())
|
||||
assert toc.find("./ncx:docTitle/ncx:text").text == 'Python documentation'
|
||||
|
||||
# toc.ncx / navPoint
|
||||
def navinfo(elem):
|
||||
label = elem.find("./ncx:navLabel/ncx:text")
|
||||
content = elem.find("./ncx:content")
|
||||
return (elem.get('id'), elem.get('playOrder'),
|
||||
content.get('src'), label.text)
|
||||
|
||||
navpoints = toc.findall("./ncx:navMap/ncx:navPoint")
|
||||
assert len(navpoints) == 4
|
||||
assert navinfo(navpoints[0]) == ('navPoint1', '1', 'index.xhtml',
|
||||
"Welcome to Sphinx Tests's documentation!")
|
||||
assert navpoints[0].findall("./ncx:navPoint") == []
|
||||
|
||||
# toc.ncx / nested navPoints
|
||||
assert navinfo(navpoints[1]) == ('navPoint2', '2', 'foo.xhtml', 'foo')
|
||||
navchildren = navpoints[1].findall("./ncx:navPoint")
|
||||
assert len(navchildren) == 4
|
||||
assert navinfo(navchildren[0]) == ('navPoint3', '2', 'foo.xhtml', 'foo')
|
||||
assert navinfo(navchildren[1]) == ('navPoint4', '3', 'quux.xhtml', 'quux')
|
||||
assert navinfo(navchildren[2]) == ('navPoint5', '4', 'foo.xhtml#foo-1', 'foo.1')
|
||||
assert navinfo(navchildren[3]) == ('navPoint8', '6', 'foo.xhtml#foo-2', 'foo.2')
|
||||
|
||||
# nav.xhtml / nav
|
||||
def navinfo(elem):
|
||||
anchor = elem.find("./xhtml:a")
|
||||
return (anchor.get('href'), anchor.text)
|
||||
|
||||
nav = EPUBElementTree.fromstring((app.outdir / 'nav.xhtml').text())
|
||||
toc = nav.findall("./xhtml:body/xhtml:nav/xhtml:ol/xhtml:li")
|
||||
assert len(toc) == 4
|
||||
assert navinfo(toc[0]) == ('index.xhtml',
|
||||
"Welcome to Sphinx Tests's documentation!")
|
||||
assert toc[0].findall("./xhtml:ol") == []
|
||||
|
||||
# nav.xhtml / nested toc
|
||||
assert navinfo(toc[1]) == ('foo.xhtml', 'foo')
|
||||
tocchildren = toc[1].findall("./xhtml:ol/xhtml:li")
|
||||
assert len(tocchildren) == 3
|
||||
assert navinfo(tocchildren[0]) == ('quux.xhtml', 'quux')
|
||||
assert navinfo(tocchildren[1]) == ('foo.xhtml#foo-1', 'foo.1')
|
||||
assert navinfo(tocchildren[2]) == ('foo.xhtml#foo-2', 'foo.2')
|
||||
|
||||
grandchild = tocchildren[1].findall("./xhtml:ol/xhtml:li")
|
||||
assert len(grandchild) == 1
|
||||
assert navinfo(grandchild[0]) == ('foo.xhtml#foo-1-1', 'foo.1-1')
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='basic')
|
||||
def test_epub_writing_mode(app):
|
||||
# horizontal (default)
|
||||
app.build()
|
||||
|
||||
# horizontal / page-progression-direction
|
||||
opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').text())
|
||||
assert opf.find("./idpf:spine").get('page-progression-direction') == 'ltr'
|
||||
|
||||
# horizontal / ibooks:scroll-axis
|
||||
metadata = opf.find("./idpf:metadata")
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'vertical'
|
||||
|
||||
# horizontal / writing-mode (CSS)
|
||||
css = (app.outdir / '_static' / 'epub.css').text()
|
||||
assert 'writing-mode: horizontal-tb;' in css
|
||||
|
||||
# vertical
|
||||
app.config.epub_writing_mode = 'vertical'
|
||||
(app.outdir / 'index.xhtml').unlink() # forcely rebuild
|
||||
app.build()
|
||||
|
||||
# vertical / page-progression-direction
|
||||
opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').text())
|
||||
assert opf.find("./idpf:spine").get('page-progression-direction') == 'rtl'
|
||||
|
||||
# vertical / ibooks:scroll-axis
|
||||
metadata = opf.find("./idpf:metadata")
|
||||
assert metadata.find("./idpf:meta[@property='ibooks:scroll-axis']").text == 'horizontal'
|
||||
|
||||
# vertical / writing-mode (CSS)
|
||||
css = (app.outdir / '_static' / 'epub.css').text()
|
||||
assert 'writing-mode: vertical-rl;' in css
|
||||
@@ -346,7 +346,9 @@ def test_static_output(app):
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||||
'perl'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||||
'\+p'),
|
||||
'\\+p'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-objc']/code/span",
|
||||
'--ObjC\\+\\+'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span",
|
||||
'--plugin.option'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
|
||||
|
||||
@@ -482,9 +482,9 @@ def test_footnote(app, status, warning):
|
||||
assert '\\caption{Table caption \\sphinxfootnotemark[4]' in result
|
||||
assert 'name \\sphinxfootnotemark[5]' in result
|
||||
assert ('\\end{threeparttable}\n\\par\n\\endgroup\n%\n'
|
||||
'\\begin{footnotetext}[4]\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
|
||||
'footnotes in table caption\n%\n\\end{footnotetext}%\n'
|
||||
'\\begin{footnotetext}[5]\sphinxAtStartFootnote\n'
|
||||
'\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n'
|
||||
'footnotes in table\n%\n\\end{footnotetext}') in result
|
||||
|
||||
|
||||
@@ -507,18 +507,18 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
|
||||
'%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n'
|
||||
'Footnote in section\n%\n\\end{footnotetext}') in result
|
||||
assert ('\\caption{This is the figure caption with a footnote to '
|
||||
'\\sphinxfootnotemark[6].}\label{\\detokenize{index:id27}}\end{figure}\n'
|
||||
'\\sphinxfootnotemark[6].}\\label{\\detokenize{index:id27}}\\end{figure}\n'
|
||||
'%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n'
|
||||
'Footnote in caption\n%\n\\end{footnotetext}')in result
|
||||
assert ('\\caption{footnote \\sphinxfootnotemark[7] '
|
||||
'in caption of normal table}\\label{\\detokenize{index:id28}}') in result
|
||||
assert ('\\caption{footnote \\sphinxfootnotemark[8] '
|
||||
'in caption \sphinxfootnotemark[9] of longtable}') in result
|
||||
assert ('\end{longtable}\n%\n\\begin{footnotetext}[8]'
|
||||
'\sphinxAtStartFootnote\n'
|
||||
'in caption \\sphinxfootnotemark[9] of longtable}') in result
|
||||
assert ('\\end{longtable}\n%\n\\begin{footnotetext}[8]'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Foot note in longtable\n%\n\\end{footnotetext}' in result)
|
||||
assert ('This is a reference to the code-block in the footnote:\n'
|
||||
'{\hyperref[\\detokenize{index:codeblockinfootnote}]'
|
||||
'{\\hyperref[\\detokenize{index:codeblockinfootnote}]'
|
||||
'{\\sphinxcrossref{\\DUrole{std,std-ref}{I am in a footnote}}}}') in result
|
||||
assert ('&\nThis is one more footnote with some code in it '
|
||||
'\\sphinxfootnotemark[10].\n\\\\') in result
|
||||
@@ -855,7 +855,7 @@ def test_latex_table_tabulars(app, status, warning):
|
||||
# table having :align: option (tabular)
|
||||
table = tables['table having :align: option (tabular)']
|
||||
assert ('\\begingroup\n\\raggedright\n'
|
||||
'\\begin{tabular}{|\X{30}{100}|\X{70}{100}|}\n' in table)
|
||||
'\\begin{tabular}{|\\X{30}{100}|\\X{70}{100}|}\n' in table)
|
||||
assert ('\\hline\n\\end{tabular}\n\\par\n\\endgroup' in table)
|
||||
|
||||
# table with tabularcolumn
|
||||
|
||||
@@ -263,7 +263,7 @@ def test_code_block_caption_latex(app, status, warning):
|
||||
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
|
||||
caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstyleemphasis{test} rb}'
|
||||
label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id1}}}'
|
||||
link = '\hyperref[\\detokenize{caption:name-test-rb}]' \
|
||||
link = '\\hyperref[\\detokenize{caption:name-test-rb}]' \
|
||||
'{Listing \\ref{\\detokenize{caption:name-test-rb}}}'
|
||||
assert caption in latex
|
||||
assert label in latex
|
||||
@@ -393,9 +393,8 @@ def test_literal_include_lineno_match(app, status, warning):
|
||||
html = (app.outdir / 'lineno_match.html').text(encoding='utf-8')
|
||||
pyobject = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
' 9\n'
|
||||
'10\n'
|
||||
'11</pre></div></td>')
|
||||
'6\n'
|
||||
'7</pre></div></td>')
|
||||
|
||||
assert pyobject in html
|
||||
|
||||
@@ -419,6 +418,12 @@ def test_literal_include_lineno_match(app, status, warning):
|
||||
'14</pre></div></td>')
|
||||
assert start_after in html
|
||||
|
||||
start_after_with_lines = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
'2\n'
|
||||
'3</pre></div></td>')
|
||||
assert start_after_with_lines in html
|
||||
|
||||
start_at_end_at = (
|
||||
'<td class="linenos"><div class="linenodiv"><pre>'
|
||||
' 9\n'
|
||||
@@ -459,7 +464,7 @@ def test_literalinclude_caption_latex(app, status, warning):
|
||||
latex = (app.outdir / 'Python.tex').text(encoding='utf-8')
|
||||
caption = '\\sphinxSetupCaptionForVerbatim{caption \\sphinxstylestrong{test} py}'
|
||||
label = '\\def\\sphinxLiteralBlockLabel{\\label{\\detokenize{caption:id2}}}'
|
||||
link = '\hyperref[\\detokenize{caption:name-test-py}]' \
|
||||
link = '\\hyperref[\\detokenize{caption:name-test-py}]' \
|
||||
'{Listing \\ref{\\detokenize{caption:name-test-py}}}'
|
||||
assert caption in latex
|
||||
assert label in latex
|
||||
|
||||
@@ -520,12 +520,12 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war
|
||||
('', 'MyEnum')
|
||||
]
|
||||
parenPatterns = [
|
||||
('ref function without parens ', 'paren_1\(\)'),
|
||||
('ref function with parens ', 'paren_2\(\)'),
|
||||
('ref function without parens ', r'paren_1\(\)'),
|
||||
('ref function with parens ', r'paren_2\(\)'),
|
||||
('ref function without parens, explicit title ', 'paren_3_title'),
|
||||
('ref function with parens, explicit title ', 'paren_4_title'),
|
||||
('ref op call without parens ', 'paren_5::operator\(\)\(\)'),
|
||||
('ref op call with parens ', 'paren_6::operator\(\)\(\)'),
|
||||
('ref op call without parens ', r'paren_5::operator\(\)\(\)'),
|
||||
('ref op call with parens ', r'paren_6::operator\(\)\(\)'),
|
||||
('ref op call without parens, explicit title ', 'paren_7_title'),
|
||||
('ref op call with parens, explicit title ', 'paren_8_title')
|
||||
]
|
||||
@@ -566,8 +566,8 @@ def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, wa
|
||||
('ref function with parens ', 'paren_2'),
|
||||
('ref function without parens, explicit title ', 'paren_3_title'),
|
||||
('ref function with parens, explicit title ', 'paren_4_title'),
|
||||
('ref op call without parens ', 'paren_5::operator\(\)'),
|
||||
('ref op call with parens ', 'paren_6::operator\(\)'),
|
||||
('ref op call without parens ', r'paren_5::operator\(\)'),
|
||||
('ref op call with parens ', r'paren_6::operator\(\)'),
|
||||
('ref op call without parens, explicit title ', 'paren_7_title'),
|
||||
('ref op call with parens, explicit title ', 'paren_8_title')
|
||||
]
|
||||
|
||||
@@ -20,8 +20,8 @@ def test_graphviz_html(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = ('<div class="figure" .*?>\s*<img .*?/>\s*<p class="caption">'
|
||||
'<span class="caption-text">caption of graph</span>.*</p>\s*</div>')
|
||||
html = (r'<div class="figure" .*?>\s*<img .*?/>\s*<p class="caption">'
|
||||
r'<span class="caption-text">caption of graph</span>.*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = 'Hello <img .*?/>\n graphviz world'
|
||||
@@ -30,8 +30,8 @@ def test_graphviz_html(app, status, warning):
|
||||
html = '<img src=".*?" alt="digraph {\n bar -> baz\n}" />'
|
||||
assert re.search(html, content, re.M)
|
||||
|
||||
html = ('<div class="figure align-right" .*?>\s*<img .*?/>\s*<p class="caption">'
|
||||
'<span class="caption-text">on right</span>.*</p>\s*</div>')
|
||||
html = (r'<div class="figure align-right" .*?>\s*<img .*?/>\s*<p class="caption">'
|
||||
r'<span class="caption-text">on right</span>.*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
@@ -41,16 +41,16 @@ def test_graphviz_latex(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'SphinxTests.tex').text()
|
||||
macro = ('\\\\begin{figure}\[htbp\]\n\\\\centering\n\\\\capstart\n\n'
|
||||
'\\\\includegraphics{graphviz-\w+.pdf}\n'
|
||||
macro = ('\\\\begin{figure}\\[htbp\\]\n\\\\centering\n\\\\capstart\n\n'
|
||||
'\\\\includegraphics{graphviz-\\w+.pdf}\n'
|
||||
'\\\\caption{caption of graph}\\\\label{.*}\\\\end{figure}')
|
||||
assert re.search(macro, content, re.S)
|
||||
|
||||
macro = 'Hello \\\\includegraphics{graphviz-\w+.pdf} graphviz world'
|
||||
macro = 'Hello \\\\includegraphics{graphviz-\\w+.pdf} graphviz world'
|
||||
assert re.search(macro, content, re.S)
|
||||
|
||||
macro = ('\\\\begin{wrapfigure}{r}{0pt}\n\\\\centering\n'
|
||||
'\\\\includegraphics{graphviz-\w+.pdf}\n'
|
||||
'\\\\includegraphics{graphviz-\\w+.pdf}\n'
|
||||
'\\\\caption{on right}\\\\label{.*}\\\\end{wrapfigure}')
|
||||
assert re.search(macro, content, re.S)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ def test_inheritance_diagram_html(app, status, warning):
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
pattern = ('<div class="figure" id="id1">\n'
|
||||
'<img src="_images/inheritance-\w+.png" alt="Inheritance diagram of test.Foo" '
|
||||
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
|
||||
'class="inheritance"/>\n<p class="caption"><span class="caption-text">'
|
||||
'Test Foo!</span><a class="headerlink" href="#id1" '
|
||||
'title="Permalink to this image">\xb6</a></p>')
|
||||
|
||||
@@ -45,8 +45,8 @@ def test_imgmath_png(app, status, warning):
|
||||
raise SkipTest('dvipng command "dvipng" is not available')
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = ('<div class="math">\s*<p>\s*<img src="_images/math/\w+.png"'
|
||||
'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
|
||||
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.png"'
|
||||
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ def test_imgmath_svg(app, status, warning):
|
||||
raise SkipTest('dvisvgm command "dvisvgm" is not available')
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = ('<div class="math">\s*<p>\s*<img src="_images/math/\w+.svg"'
|
||||
'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
|
||||
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.svg"'
|
||||
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
|
||||
@@ -12,5 +12,5 @@ from sphinx.util.rst import escape
|
||||
|
||||
|
||||
def test_escape():
|
||||
assert escape(':ref:`id`') == '\:ref\:\`id\`'
|
||||
assert escape('footnote [#]_') == 'footnote \[\#\]\_'
|
||||
assert escape(':ref:`id`') == r'\:ref\:\`id\`'
|
||||
assert escape('footnote [#]_') == r'footnote \[\#\]\_'
|
||||
|
||||
@@ -29,9 +29,9 @@ def bump_version(path, version_info):
|
||||
|
||||
with open(path, 'r+') as f:
|
||||
body = f.read()
|
||||
body = re.sub("(?<=__version__ = ')[^']+", version, body)
|
||||
body = re.sub("(?<=__released__ = ')[^']+", release, body)
|
||||
body = re.sub("(?<=version_info = )\(.*\)", str(version_info), body)
|
||||
body = re.sub(r"(?<=__version__ = ')[^']+", version, body)
|
||||
body = re.sub(r"(?<=__released__ = ')[^']+", release, body)
|
||||
body = re.sub(r"(?<=version_info = )\(.*\)", str(version_info), body)
|
||||
|
||||
f.seek(0)
|
||||
f.truncate(0)
|
||||
@@ -39,23 +39,23 @@ def bump_version(path, version_info):
|
||||
|
||||
|
||||
def parse_version(version):
|
||||
matched = re.search('^(\d+)\.(\d+)$', version)
|
||||
matched = re.search(r'^(\d+)\.(\d+)$', version)
|
||||
if matched:
|
||||
major, minor = matched.groups()
|
||||
return (int(major), int(minor), 0, 'final', 0)
|
||||
|
||||
matched = re.search('^(\d+)\.(\d+)\.(\d+)$', version)
|
||||
matched = re.search(r'^(\d+)\.(\d+)\.(\d+)$', version)
|
||||
if matched:
|
||||
major, minor, rev = matched.groups()
|
||||
return (int(major), int(minor), int(rev), 'final', 0)
|
||||
|
||||
matched = re.search('^(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
||||
matched = re.search(r'^(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
||||
if matched:
|
||||
major, minor, typ, relver = matched.groups()
|
||||
release = RELEASE_TYPE.get(typ, typ)
|
||||
return (int(major), int(minor), 0, release, int(relver))
|
||||
|
||||
matched = re.search('^(\d+)\.(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
||||
matched = re.search(r'^(\d+)\.(\d+)\.(\d+)\s*(a|b|alpha|beta)(\d+)$', version)
|
||||
if matched:
|
||||
major, minor, rev, typ, relver = matched.groups()
|
||||
release = RELEASE_TYPE.get(typ, typ)
|
||||
@@ -90,7 +90,7 @@ class Changes(object):
|
||||
def fetch_version(self):
|
||||
with open(self.path) as f:
|
||||
version = f.readline().strip()
|
||||
matched = re.search('^Release (.*) \((.*)\)$', version)
|
||||
matched = re.search(r'^Release (.*) \((.*)\)$', version)
|
||||
if matched is None:
|
||||
raise RuntimeError('Unknown CHANGES format: %s' % version)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user