summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore3
-rw-r--r--CHANGES70
-rw-r--r--CHANGES.DasIch36
-rw-r--r--EXAMPLES7
-rw-r--r--MANIFEST.in2
-rw-r--r--Makefile52
-rw-r--r--README12
-rw-r--r--custom_fixers/__init__.py0
-rw-r--r--custom_fixers/fix_alt_unicode.py12
-rw-r--r--distribute_setup.py485
-rw-r--r--doc/Makefile4
-rw-r--r--doc/_templates/indexsidebar.html2
-rw-r--r--doc/builders.rst8
-rw-r--r--doc/conf.py8
-rw-r--r--doc/config.rst36
-rw-r--r--doc/domains.rst30
-rw-r--r--doc/ext/appapi.rst4
-rw-r--r--doc/ext/autodoc.rst40
-rw-r--r--doc/ext/inheritance.rst2
-rw-r--r--doc/ext/intersphinx.rst22
-rw-r--r--doc/ext/math.rst8
-rw-r--r--doc/faq.rst10
-rw-r--r--doc/intro.rst12
-rw-r--r--doc/markup/inline.rst8
-rw-r--r--doc/markup/para.rst16
-rw-r--r--doc/markup/toctree.rst2
-rw-r--r--doc/templating.rst12
-rw-r--r--ez_setup.py276
-rw-r--r--setup.py9
-rw-r--r--sphinx/__init__.py24
-rw-r--r--sphinx/application.py7
-rw-r--r--sphinx/builders/epub.py145
-rw-r--r--sphinx/builders/html.py91
-rw-r--r--sphinx/builders/htmlhelp.py3
-rw-r--r--sphinx/builders/linkcheck.py19
-rw-r--r--sphinx/builders/qthelp.py14
-rw-r--r--sphinx/config.py39
-rw-r--r--sphinx/directives/__init__.py7
-rw-r--r--sphinx/directives/code.py4
-rw-r--r--sphinx/directives/other.py7
-rw-r--r--sphinx/domains/cpp.py4
-rw-r--r--sphinx/domains/javascript.py2
-rw-r--r--sphinx/domains/python.py94
-rw-r--r--sphinx/domains/rst.py14
-rw-r--r--sphinx/environment.py31
-rw-r--r--sphinx/ext/autodoc.py94
-rw-r--r--sphinx/ext/coverage.py5
-rw-r--r--sphinx/ext/doctest.py6
-rw-r--r--sphinx/ext/graphviz.py1
-rw-r--r--sphinx/ext/inheritance_diagram.py2
-rw-r--r--sphinx/ext/intersphinx.py44
-rw-r--r--sphinx/ext/oldcmarkup.py15
-rw-r--r--sphinx/ext/viewcode.py6
-rw-r--r--sphinx/highlighting.py6
-rw-r--r--sphinx/locale/__init__.py10
-rw-r--r--sphinx/locale/bn/LC_MESSAGES/sphinx.js1
-rw-r--r--sphinx/locale/bn/LC_MESSAGES/sphinx.mobin0 -> 12509 bytes
-rw-r--r--sphinx/locale/bn/LC_MESSAGES/sphinx.po698
-rw-r--r--sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js2
-rw-r--r--sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mobin8836 -> 10252 bytes
-rw-r--r--sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po222
-rw-r--r--sphinx/pycode/__init__.py6
-rw-r--r--sphinx/pycode/nodes.py2
-rw-r--r--sphinx/pycode/pgen2/literals.py2
-rw-r--r--sphinx/pycode/pgen2/tokenize.py4
-rw-r--r--sphinx/quickstart.py81
-rw-r--r--sphinx/roles.py21
-rw-r--r--sphinx/texinputs/sphinx.sty1
-rw-r--r--sphinx/themes/basic/layout.html2
-rw-r--r--sphinx/util/__init__.py57
-rw-r--r--sphinx/util/docfields.py16
-rw-r--r--sphinx/util/jsonimpl.py48
-rw-r--r--sphinx/util/nodes.py4
-rw-r--r--sphinx/util/osutil.py12
-rw-r--r--sphinx/util/pycompat.py59
-rw-r--r--sphinx/versioning.py148
-rw-r--r--sphinx/writers/latex.py72
-rw-r--r--sphinx/writers/text.py2
-rw-r--r--tests/etree13/ElementTree.py14
-rw-r--r--tests/path.py1023
-rw-r--r--tests/root/conf.py2
-rw-r--r--tests/root/contents.txt1
-rw-r--r--tests/root/doctest.txt38
-rw-r--r--tests/root/literal.inc2
-rw-r--r--tests/root/markup.txt30
-rw-r--r--tests/root/objects.txt11
-rw-r--r--tests/root/versioning/added.txt20
-rw-r--r--tests/root/versioning/deleted.txt12
-rw-r--r--tests/root/versioning/deleted_end.txt11
-rw-r--r--tests/root/versioning/index.txt11
-rw-r--r--tests/root/versioning/insert.txt18
-rw-r--r--tests/root/versioning/modified.txt17
-rw-r--r--tests/root/versioning/original.txt15
-rwxr-xr-xtests/run.py16
-rw-r--r--tests/test_application.py2
-rw-r--r--tests/test_autosummary.py4
-rw-r--r--tests/test_build.py4
-rw-r--r--tests/test_build_html.py338
-rw-r--r--tests/test_build_latex.py9
-rw-r--r--tests/test_config.py17
-rw-r--r--tests/test_coverage.py2
-rw-r--r--tests/test_env.py10
-rw-r--r--tests/test_intersphinx.py84
-rw-r--r--tests/test_markup.py3
-rw-r--r--tests/test_quickstart.py31
-rw-r--r--tests/test_search.py3
-rw-r--r--tests/test_versioning.py104
-rw-r--r--tests/util.py18
-rw-r--r--tox.ini17
-rwxr-xr-xutils/check_sources.py115
-rw-r--r--utils/convert.py43
-rwxr-xr-xutils/reindent.py80
112 files changed, 3443 insertions, 2024 deletions
diff --git a/.hgignore b/.hgignore
index 612c9331..b68cccf9 100644
--- a/.hgignore
+++ b/.hgignore
@@ -2,6 +2,7 @@
.*\.egg
.*\.so
.dir-locals.el
+^\.tox
\.DS_Store$
^build/
^dist/
@@ -14,3 +15,5 @@
^env/
\.DS_Store$
~$
+^utils/.*3\.py$
+^distribute-
diff --git a/CHANGES b/CHANGES
index 4be9eed8..a30d490e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,64 @@
-Release 1.0 (in development)
+Release 1.1 (in development)
============================
+* Added Python 3.x support.
+
+
+Release 1.0.2 (in development)
+==============================
+
+* Allow breaking long signatures, continuing with backlash-escaped
+ newlines.
+
+* Fix unwanted styling of C domain references (because of a namespace
+ clash with Pygments styles).
+
+* Allow references to PEPs and RFCs with explicit anchors.
+
+* #471: Fix LaTeX references to figures.
+
+* #482: When doing a non-exact search, match only the given type
+ of object.
+
+* #481: Apply non-exact search for Python reference targets with
+ ``.name`` for modules too.
+
+* #484: Fix crash when duplicating a parameter in an info field list.
+
+* #487: Fix setting the default role to one provided by the
+ ``oldcmarkup`` extension.
+
+* #488: Fix crash when json-py is installed, which provides a
+ ``json`` module but is incompatible to simplejson.
+
+* #480: Fix handling of target naming in intersphinx.
+
+* #486: Fix removal of ``!`` for all cross-reference roles.
+
+
+Release 1.0.1 (Jul 27, 2010)
+============================
+
+* #470: Fix generated target names for reST domain objects; they
+ are not in the same namespace.
+
+* #266: Add Bengali language.
+
+* #473: Fix a bug in parsing JavaScript object names.
+
+* #474: Fix building with SingleHTMLBuilder when there is no toctree.
+
+* Fix display names for objects linked to by intersphinx with
+ explicit targets.
+
+* Fix building with the JSON builder.
+
+* Fix hyperrefs in object descriptions for LaTeX.
+
+
+Release 1.0 (Jul 23, 2010)
+==========================
+
Incompatible changes
--------------------
@@ -15,9 +73,10 @@ Incompatible changes
- JavaScript
- reStructuredText
-* The old markup for defining and linking to C directives will not work
- anymore without activating the :mod:`~sphinx.ext.oldcmarkup`
- extension.
+* The old markup for defining and linking to C directives is now
+ deprecated. It will not work anymore in future versions without
+ activating the :mod:`~sphinx.ext.oldcmarkup` extension; in Sphinx
+ 1.0, it is activated by default.
* Removed support for old dependency versions; requirements are now:
@@ -80,6 +139,9 @@ Features added
- Added :confval:`html_show_copyright` config value.
- Added :confval:`latex_show_pagerefs` and :confval:`latex_show_urls`
config values.
+ - The behavior of :confval:`html_file_suffix` changed slightly: the
+ empty string now means "no suffix" instead of "default suffix", use
+ ``None`` for "default suffix".
* New builders:
diff --git a/CHANGES.DasIch b/CHANGES.DasIch
new file mode 100644
index 00000000..3f716726
--- /dev/null
+++ b/CHANGES.DasIch
@@ -0,0 +1,36 @@
+Changes
+=======
+
+This file contains changes made by Daniel Neuhäuser, during the Google Summer
+of Code 2010, to port Sphinx to Python 3.x. Changes are ordered descending by
+date.
+
+May 16: - Added utils/convert.py which converts entire directories of python
+ files with 2to3 and names the converted files foo3.py.
+ - Modified the Makefile so that in case Python 3 is used the scripts in
+ utils get converted with utils/convert.py and are used instead of the
+ Python 2 scripts.
+
+May 10: Fixed a couple of tests and made several small changes.
+
+May 9: - Removed ez_setup.py which does not work with Python 3.x. and replaced
+ it with distribute_setup.py
+ - Use distribute (at least on 3.x) in order to run 2to3 automatically.
+ - Reverted some of the changes made in revision bac40c7c924c which
+ caused errors.
+ - Modified tests/run.py to test against the build created by
+ setup.py build in order to run the test suite with 3.x
+ - Several small changes to fix 3.x compatibilty.
+
+May 1: - Removed deprecated tuple parameter unpacking.
+ - Removed a pre-2.3 workaround for booleans because this creates a
+ deprecation warning for 3.x, in which you can't assign values to
+ booleans.
+ - Moved :func:`open()` calls out of the try-blocks, which fixes revision
+ c577c25bd44b.
+
+April 30: Made :cls:`sphinx.domains.cpp.DefExpr` unhashable as described by the
+ documentation because classes in 3.x don't inherit ``__hash__`` if
+ they implement ``__eq__``.
+
+April 29: Removed several deprecated function/method calls.
diff --git a/EXAMPLES b/EXAMPLES
index c618b8bf..778b235e 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -12,7 +12,7 @@ interesting examples.
Documentation using the default theme
-------------------------------------
-* APSW: http://apsw.googlecode.com/svn/publish/index.html
+* APSW: http://apidoc.apsw.googlecode.com/hg/index.html
* ASE: https://wiki.fysik.dtu.dk/ase/
* boostmpi: http://documen.tician.de/boostmpi/
* Calibre: http://calibre.kovidgoyal.net/user_manual/
@@ -38,6 +38,7 @@ Documentation using the default theme
* PyCuda: http://documen.tician.de/pycuda/
* Pyevolve: http://pyevolve.sourceforge.net/
* Pylo: http://documen.tician.de/pylo/
+* PyMQI: http://packages.python.org/pymqi/
* PyPubSub: http://pubsub.sourceforge.net/
* pyrticle: http://documen.tician.de/pyrticle/
* Python: http://docs.python.org/
@@ -96,6 +97,7 @@ Documentation using the sphinxdoc theme
* Satchmo: http://www.satchmoproject.com/docs/svn/
* Sphinx: http://sphinx.pocoo.org/
* Sqlkit: http://sqlkit.argolinux.org/
+* Tau: http://www.tango-controls.org/static/tau/latest/doc/html/index.html
* Total Open Station: http://tops.berlios.de/
* WebFaction: http://docs.webfaction.com/
@@ -109,6 +111,8 @@ Documentation using another builtin theme
* pip: http://pip.openplans.org/ (nature)
* Programmieren mit PyGTK und Glade (German):
http://www.florian-diesch.de/doc/python-und-glade/online/ (agogo)
+* Spring Python: http://springpython.webfactional.com/current/sphinx/index.html
+ (nature)
* sqlparse: http://python-sqlparse.googlecode.com/svn/docs/api/index.html
(agogo)
* libLAS: http://liblas.org/ (nature)
@@ -139,6 +143,7 @@ Documentation using a custom theme/integrated in a site
* Self: http://selflanguage.org/
* SQLAlchemy: http://www.sqlalchemy.org/docs/
* tinyTiM: http://tinytim.sourceforge.net/docs/2.0/
+* tipfy: http://www.tipfy.org/docs/
* Werkzeug: http://werkzeug.pocoo.org/documentation/dev/
* WFront: http://discorporate.us/projects/WFront/
diff --git a/MANIFEST.in b/MANIFEST.in
index 25cbc334..5e3104a8 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -7,7 +7,7 @@ include TODO
include babel.cfg
include Makefile
-include ez_setup.py
+include setup_distribute.py
include sphinx-autogen.py
include sphinx-build.py
include sphinx-quickstart.py
diff --git a/Makefile b/Makefile
index 90f7c3d4..09aa3c96 100644
--- a/Makefile
+++ b/Makefile
@@ -1,35 +1,63 @@
PYTHON ?= python
-export PYTHONPATH = $(shell echo "$$PYTHONPATH"):./sphinx
+.PHONY: all check clean clean-pyc clean-patchfiles clean-backupfiles \
+ clean-generated pylint reindent test covertest build convert-utils
-.PHONY: all check clean clean-pyc clean-patchfiles pylint reindent test
+DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
+ -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
+ -i .ropeproject -i doc/_build -i tests/path.py \
+ -i tests/coverage.py -i env -i utils/convert.py \
+ -i utils/reindent3.py -i utils/check_sources3.py -i .tox
-all: clean-pyc check test
+all: clean-pyc clean-backupfiles check test
+ifeq ($(PYTHON), python3)
+check: convert-utils
+ @$(PYTHON) utils/check_sources3.py $(DONT_CHECK) .
+else
check:
- @$(PYTHON) utils/check_sources.py -i build -i dist -i sphinx/style/jquery.js \
- -i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py -i .ropeproject \
- -i doc/_build -i ez_setup.py -i tests/path.py -i tests/coverage.py -i env .
+ @$(PYTHON) utils/check_sources.py $(DONT_CHECK) .
+endif
-clean: clean-pyc clean-patchfiles
+clean: clean-pyc clean-patchfiles clean-backupfiles clean-generated
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
- find . -name '*~' -exec rm -f {} +
clean-patchfiles:
find . -name '*.orig' -exec rm -f {} +
find . -name '*.rej' -exec rm -f {} +
+clean-backupfiles:
+ find . -name '*~' -exec rm -f {} +
+ find . -name '*.bak' -exec rm -f {} +
+
+clean-generated:
+ rm -f utils/*3.py*
+
pylint:
@pylint --rcfile utils/pylintrc sphinx
+ifeq ($(PYTHON), python3)
+reindent: convert-utils
+ @$(PYTHON) utils/reindent3.py -r -n .
+else
reindent:
- @$(PYTHON) utils/reindent.py -r -B .
+ @$(PYTHON) utils/reindent.py -r -n .
+endif
-test:
+test: build
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
-covertest:
- @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage --cover-package=sphinx $(TEST)
+covertest: build
+ @cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \
+ --cover-package=sphinx $(TEST)
+
+build:
+ @$(PYTHON) setup.py build
+
+ifeq ($(PYTHON), python3)
+convert-utils:
+ @python3 utils/convert.py -i utils/convert.py utils/
+endif
diff --git a/README b/README
index bb2dea9d..e31d6b93 100644
--- a/README
+++ b/README
@@ -26,6 +26,18 @@ Then, direct your browser to ``_build/html/index.html``.
Or read them online at <http://sphinx.pocoo.org/>.
+Testing
+=======
+
+To run the tests with the interpreter available as ``python``, use::
+
+ make test
+
+If you want to use a different interpreter, e.g. ``python3``, use::
+
+ PYTHON=python3 make test
+
+
Contributing
============
diff --git a/custom_fixers/__init__.py b/custom_fixers/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/custom_fixers/__init__.py
diff --git a/custom_fixers/fix_alt_unicode.py b/custom_fixers/fix_alt_unicode.py
new file mode 100644
index 00000000..55175e90
--- /dev/null
+++ b/custom_fixers/fix_alt_unicode.py
@@ -0,0 +1,12 @@
+from lib2to3.fixer_base import BaseFix
+from lib2to3.fixer_util import Name
+
+class FixAltUnicode(BaseFix):
+ PATTERN = """
+ func=funcdef< 'def' name='__unicode__'
+ parameters< '(' NAME ')' > any+ >
+ """
+
+ def transform(self, node, results):
+ name = results['name']
+ name.replace(Name('__str__', prefix=name.prefix))
diff --git a/distribute_setup.py b/distribute_setup.py
new file mode 100644
index 00000000..37117b34
--- /dev/null
+++ b/distribute_setup.py
@@ -0,0 +1,485 @@
+#!python
+"""Bootstrap distribute installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from distribute_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import os
+import sys
+import time
+import fnmatch
+import tempfile
+import tarfile
+from distutils import log
+
+try:
+ from site import USER_SITE
+except ImportError:
+ USER_SITE = None
+
+try:
+ import subprocess
+
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ return subprocess.call(args) == 0
+
+except ImportError:
+ # will be used for python 2.3
+ def _python_cmd(*args):
+ args = (sys.executable,) + args
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
+
+DEFAULT_VERSION = "0.6.13"
+DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
+SETUPTOOLS_FAKED_VERSION = "0.6c11"
+
+SETUPTOOLS_PKG_INFO = """\
+Metadata-Version: 1.0
+Name: setuptools
+Version: %s
+Summary: xxxx
+Home-page: xxx
+Author: xxx
+Author-email: xxx
+License: xxx
+Description: xxx
+""" % SETUPTOOLS_FAKED_VERSION
+
+
+def _install(tarball):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # installing
+ log.warn('Installing Distribute')
+ if not _python_cmd('setup.py', 'install'):
+ log.warn('Something went wrong during the installation.')
+ log.warn('See the error message above.')
+ finally:
+ os.chdir(old_wd)
+
+
+def _build_egg(egg, tarball, to_dir):
+ # extracting the tarball
+ tmpdir = tempfile.mkdtemp()
+ log.warn('Extracting in %s', tmpdir)
+ old_wd = os.getcwd()
+ try:
+ os.chdir(tmpdir)
+ tar = tarfile.open(tarball)
+ _extractall(tar)
+ tar.close()
+
+ # going in the directory
+ subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
+ os.chdir(subdir)
+ log.warn('Now working in %s', subdir)
+
+ # building an egg
+ log.warn('Building a Distribute egg in %s', to_dir)
+ _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
+
+ finally:
+ os.chdir(old_wd)
+ # returning the result
+ log.warn(egg)
+ if not os.path.exists(egg):
+ raise IOError('Could not build the egg.')
+
+
+def _do_download(version, download_base, to_dir, download_delay):
+ egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
+ % (version, sys.version_info[0], sys.version_info[1]))
+ if not os.path.exists(egg):
+ tarball = download_setuptools(version, download_base,
+ to_dir, download_delay)
+ _build_egg(egg, tarball, to_dir)
+ sys.path.insert(0, egg)
+ import setuptools
+ setuptools.bootstrap_install_from = egg
+
+
+def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, download_delay=15, no_fake=True):
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ was_imported = 'pkg_resources' in sys.modules or \
+ 'setuptools' in sys.modules
+ try:
+ try:
+ import pkg_resources
+ if not hasattr(pkg_resources, '_distribute'):
+ if not no_fake:
+ _fake_setuptools()
+ raise ImportError
+ except ImportError:
+ return _do_download(version, download_base, to_dir, download_delay)
+ try:
+ pkg_resources.require("distribute>="+version)
+ return
+ except pkg_resources.VersionConflict:
+ e = sys.exc_info()[1]
+ if was_imported:
+ sys.stderr.write(
+ "The required version of distribute (>=%s) is not available,\n"
+ "and can't be installed while this script is running. Please\n"
+ "install a more recent version first, using\n"
+ "'easy_install -U distribute'."
+ "\n\n(Currently using %r)\n" % (version, e.args[0]))
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ except pkg_resources.DistributionNotFound:
+ return _do_download(version, download_base, to_dir,
+ download_delay)
+ finally:
+ if not no_fake:
+ _create_fake_setuptools_pkg_info(to_dir)
+
+def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
+ to_dir=os.curdir, delay=15):
+ """Download distribute from a specified location and return its filename
+
+ `version` should be a valid distribute version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download
+ attempt.
+ """
+ # making sure we use the absolute path
+ to_dir = os.path.abspath(to_dir)
+ try:
+ from urllib.request import urlopen
+ except ImportError:
+ from urllib2 import urlopen
+ tgz_name = "distribute-%s.tar.gz" % version
+ url = download_base + tgz_name
+ saveto = os.path.join(to_dir, tgz_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ log.warn("Downloading %s", url)
+ src = urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = src.read()
+ dst = open(saveto, "wb")
+ dst.write(data)
+ finally:
+ if src:
+ src.close()
+ if dst:
+ dst.close()
+ return os.path.realpath(saveto)
+
+def _no_sandbox(function):
+ def __no_sandbox(*args, **kw):
+ try:
+ from setuptools.sandbox import DirectorySandbox
+ if not hasattr(DirectorySandbox, '_old'):
+ def violation(*args):
+ pass
+ DirectorySandbox._old = DirectorySandbox._violation
+ DirectorySandbox._violation = violation
+ patched = True
+ else:
+ patched = False
+ except ImportError:
+ patched = False
+
+ try:
+ return function(*args, **kw)
+ finally:
+ if patched:
+ DirectorySandbox._violation = DirectorySandbox._old
+ del DirectorySandbox._old
+
+ return __no_sandbox
+
+def _patch_file(path, content):
+ """Will backup the file then patch it"""
+ existing_content = open(path).read()
+ if existing_content == content:
+ # already patched
+ log.warn('Already patched.')
+ return False
+ log.warn('Patching...')
+ _rename_path(path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+ return True
+
+_patch_file = _no_sandbox(_patch_file)
+
+def _same_content(path, content):
+ return open(path).read() == content
+
+def _rename_path(path):
+ new_name = path + '.OLD.%s' % time.time()
+ log.warn('Renaming %s into %s', path, new_name)
+ os.rename(path, new_name)
+ return new_name
+
+def _remove_flat_installation(placeholder):
+ if not os.path.isdir(placeholder):
+ log.warn('Unkown installation at %s', placeholder)
+ return False
+ found = False
+ for file in os.listdir(placeholder):
+ if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
+ found = True
+ break
+ if not found:
+ log.warn('Could not locate setuptools*.egg-info')
+ return
+
+ log.warn('Removing elements out of the way...')
+ pkg_info = os.path.join(placeholder, file)
+ if os.path.isdir(pkg_info):
+ patched = _patch_egg_dir(pkg_info)
+ else:
+ patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
+
+ if not patched:
+ log.warn('%s already patched.', pkg_info)
+ return False
+ # now let's move the files out of the way
+ for element in ('setuptools', 'pkg_resources.py', 'site.py'):
+ element = os.path.join(placeholder, element)
+ if os.path.exists(element):
+ _rename_path(element)
+ else:
+ log.warn('Could not find the %s element of the '
+ 'Setuptools distribution', element)
+ return True
+
+_remove_flat_installation = _no_sandbox(_remove_flat_installation)
+
+def _after_install(dist):
+ log.warn('After install bootstrap.')
+ placeholder = dist.get_command_obj('install').install_purelib
+ _create_fake_setuptools_pkg_info(placeholder)
+
+def _create_fake_setuptools_pkg_info(placeholder):
+ if not placeholder or not os.path.exists(placeholder):
+ log.warn('Could not find the install location')
+ return
+ pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
+ setuptools_file = 'setuptools-%s-py%s.egg-info' % \
+ (SETUPTOOLS_FAKED_VERSION, pyver)
+ pkg_info = os.path.join(placeholder, setuptools_file)
+ if os.path.exists(pkg_info):
+ log.warn('%s already exists', pkg_info)
+ return
+
+ log.warn('Creating %s', pkg_info)
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+
+ pth_file = os.path.join(placeholder, 'setuptools.pth')
+ log.warn('Creating %s', pth_file)
+ f = open(pth_file, 'w')
+ try:
+ f.write(os.path.join(os.curdir, setuptools_file))
+ finally:
+ f.close()
+
+_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
+
+def _patch_egg_dir(path):
+ # let's check if it's already patched
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ if os.path.exists(pkg_info):
+ if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
+ log.warn('%s already patched.', pkg_info)
+ return False
+ _rename_path(path)
+ os.mkdir(path)
+ os.mkdir(os.path.join(path, 'EGG-INFO'))
+ pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+ f = open(pkg_info, 'w')
+ try:
+ f.write(SETUPTOOLS_PKG_INFO)
+ finally:
+ f.close()
+ return True
+
+_patch_egg_dir = _no_sandbox(_patch_egg_dir)
+
+def _before_install():
+ log.warn('Before install bootstrap.')
+ _fake_setuptools()
+
+
+def _under_prefix(location):
+ if 'install' not in sys.argv:
+ return True
+ args = sys.argv[sys.argv.index('install')+1:]
+ for index, arg in enumerate(args):
+ for option in ('--root', '--prefix'):
+ if arg.startswith('%s=' % option):
+ top_dir = arg.split('root=')[-1]
+ return location.startswith(top_dir)
+ elif arg == option:
+ if len(args) > index:
+ top_dir = args[index+1]
+ return location.startswith(top_dir)
+ if arg == '--user' and USER_SITE is not None:
+ return location.startswith(USER_SITE)
+ return True
+
+
+def _fake_setuptools():
+ log.warn('Scanning installed packages')
+ try:
+ import pkg_resources
+ except ImportError:
+ # we're cool
+ log.warn('Setuptools or Distribute does not seem to be installed.')
+ return
+ ws = pkg_resources.working_set
+ try:
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
+ replacement=False))
+ except TypeError:
+ # old distribute API
+ setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
+
+ if setuptools_dist is None:
+ log.warn('No setuptools distribution found')
+ return
+ # detecting if it was already faked
+ setuptools_location = setuptools_dist.location
+ log.warn('Setuptools installation detected at %s', setuptools_location)
+
+ # if --root or --preix was provided, and if
+ # setuptools is not located in them, we don't patch it
+ if not _under_prefix(setuptools_location):
+ log.warn('Not patching, --root or --prefix is installing Distribute'
+ ' in another location')
+ return
+
+ # let's see if its an egg
+ if not setuptools_location.endswith('.egg'):
+ log.warn('Non-egg installation')
+ res = _remove_flat_installation(setuptools_location)
+ if not res:
+ return
+ else:
+ log.warn('Egg installation')
+ pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
+ if (os.path.exists(pkg_info) and
+ _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
+ log.warn('Already patched.')
+ return
+ log.warn('Patching...')
+ # let's create a fake egg replacing setuptools one
+ res = _patch_egg_dir(setuptools_location)
+ if not res:
+ return
+ log.warn('Patched done.')
+ _relaunch()
+
+
+def _relaunch():
+ log.warn('Relaunching...')
+ # we have to relaunch the process
+ # pip marker to avoid a relaunch bug
+ if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
+ sys.argv[0] = 'setup.py'
+ args = [sys.executable] + sys.argv
+ sys.exit(subprocess.call(args))
+
+
+def _extractall(self, path=".", members=None):
+ """Extract all members from the archive to the current working
+ directory and set owner, modification time and permissions on
+ directories afterwards. `path' specifies a different directory
+ to extract to. `members' is optional and must be a subset of the
+ list returned by getmembers().
+ """
+ import copy
+ import operator
+ from tarfile import ExtractError
+ directories = []
+
+ if members is None:
+ members = self
+
+ for tarinfo in members:
+ if tarinfo.isdir():
+ # Extract directories with a safe mode.
+ directories.append(tarinfo)
+ tarinfo = copy.copy(tarinfo)
+ tarinfo.mode = 448 # decimal for oct 0700
+ self.extract(tarinfo, path)
+
+ # Reverse sort directories.
+ if sys.version_info < (2, 4):
+ def sorter(dir1, dir2):
+ return cmp(dir1.name, dir2.name)
+ directories.sort(sorter)
+ directories.reverse()
+ else:
+ directories.sort(key=operator.attrgetter('name'), reverse=True)
+
+ # Set correct owner, mtime and filemode on directories.
+ for tarinfo in directories:
+ dirpath = os.path.join(path, tarinfo.name)
+ try:
+ self.chown(tarinfo, dirpath)
+ self.utime(tarinfo, dirpath)
+ self.chmod(tarinfo, dirpath)
+ except ExtractError:
+ e = sys.exc_info()[1]
+ if self.errorlevel > 1:
+ raise
+ else:
+ self._dbg(1, "tarfile: %s" % e)
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ tarball = download_setuptools()
+ _install(tarball)
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/doc/Makefile b/doc/Makefile
index ff3cb22b..a6662cda 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -16,8 +16,8 @@ ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \
help:
@echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files called index.html in directories"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files called index.html in directories"
@echo " singlehtml to make one big HTML file"
@echo " text to make text files"
@echo " man to make manual pages"
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index a268ec6e..85b8fba9 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -23,6 +23,6 @@ are also available.</p>
<input type="text" name="email" value="your@email"/>
<input type="submit" name="sub" value="Subscribe" />
</form>
-<p>or come to the <tt>#python-docs</tt> channel on FreeNode.</p>
+<p>or come to the <tt>#pocoo</tt> channel on FreeNode.</p>
<p>You can also open an issue at the
<a href="http://www.bitbucket.org/birkenfeld/sphinx/issues/">tracker</a>.</p>
diff --git a/doc/builders.rst b/doc/builders.rst
index 5aefac44..4f95b614 100644
--- a/doc/builders.rst
+++ b/doc/builders.rst
@@ -267,11 +267,11 @@ All serialization builders outputs one file per source file and a few special
files. They also copy the reST source files in the directory ``_sources``
under the output directory.
-The :class:`PickleHTMLBuilder` is a builtin subclass that implements the pickle
+The :class:`.PickleHTMLBuilder` is a builtin subclass that implements the pickle
serialization interface.
The files per source file have the extensions of
-:attr:`~SerializingHTMLBuilder.out_suffix`, and are arranged in directories
+:attr:`~.SerializingHTMLBuilder.out_suffix`, and are arranged in directories
just as the source files are. They unserialize to a dictionary (or dictionary
like structure) with these keys:
@@ -302,7 +302,7 @@ like structure) with these keys:
The special files are located in the root output directory. They are:
-:attr:`SerializingHTMLBuilder.globalcontext_filename`
+:attr:`.SerializingHTMLBuilder.globalcontext_filename`
A pickled dict with these keys:
``project``, ``copyright``, ``release``, ``version``
@@ -321,7 +321,7 @@ The special files are located in the root output directory. They are:
``titles``
A dictionary of all documents' titles, as HTML strings.
-:attr:`SerializingHTMLBuilder.searchindex_filename`
+:attr:`.SerializingHTMLBuilder.searchindex_filename`
An index that can be used for searching the documentation. It is a pickled
list with these entries:
diff --git a/doc/conf.py b/doc/conf.py
index b268a13f..b3a1cda7 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -34,9 +34,9 @@ epub_author = 'Georg Brandl'
epub_publisher = 'http://sphinx.pocoo.org/'
epub_scheme = 'url'
epub_identifier = epub_publisher
-epub_pre_files = [('index', 'Welcome')]
+epub_pre_files = [('index.html', 'Welcome')]
epub_exclude_files = ['_static/opensearch.xml', '_static/doctools.js',
- '_static/jquery.js', '_static/searchtools.js',
+ '_static/jquery.js', '_static/searchtools.js', '_static/underscore.js',
'_static/basic.css', 'search.html']
latex_documents = [('contents', 'sphinx.tex', 'Sphinx Documentation',
@@ -64,6 +64,10 @@ man_pages = [
'template generator', '', 1),
]
+# We're not using intersphinx right now, but if we did, this would be part of
+# the mapping:
+intersphinx_mapping = {'python': ('http://docs.python.org/dev', None)}
+
# -- Extension interface -------------------------------------------------------
diff --git a/doc/config.rst b/doc/config.rst
index d0fe9cdd..e0fbeb46 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -283,6 +283,7 @@ Project information
Currently supported languages are:
+ * ``bn`` -- Bengali
* ``ca`` -- Catalan
* ``cs`` -- Czech
* ``da`` -- Danish
@@ -345,12 +346,12 @@ Project information
A boolean that decides whether module names are prepended to all
:term:`object` names (for object types where a "module" of some kind is
- defined), e.g. for :rst:dir:`function` directives. Default is ``True``.
+ defined), e.g. for :rst:dir:`py:function` directives. Default is ``True``.
.. confval:: show_authors
- A boolean that decides whether :rst:dir:`moduleauthor` and :rst:dir:`sectionauthor`
- directives produce any output in the built files.
+ A boolean that decides whether :rst:dir:`codeauthor` and
+ :rst:dir:`sectionauthor` directives produce any output in the built files.
.. confval:: modindex_common_prefix
@@ -387,6 +388,8 @@ Options for HTML output
These options influence HTML as well as HTML Help output, and other builders
that use Sphinx' HTMLWriter class.
+.. XXX document html_context
+
.. confval:: html_theme
The "theme" that the HTML output should use. See the :doc:`section about
@@ -552,19 +555,6 @@ that use Sphinx' HTMLWriter class.
This will render the template ``customdownload.html`` as the page
``download.html``.
- .. note::
-
- Earlier versions of Sphinx had a value called :confval:`html_index` which
- was a clumsy way of controlling the content of the "index" document. If
- you used this feature, migrate it by adding an ``'index'`` key to this
- setting, with your custom template as the value, and in your custom
- template, use ::
-
- {% extend "defindex.html" %}
- {% block tables %}
- ... old template content ...
- {% endblock %}
-
.. confval:: html_domain_indices
If true, generate domain-specific indices in addition to the general index.
@@ -626,8 +616,8 @@ that use Sphinx' HTMLWriter class.
.. confval:: html_file_suffix
- If nonempty, this is the file name suffix for generated HTML files. The
- default is ``".html"``.
+ This is the file name suffix for generated HTML files. The default is
+ ``".html"``.
.. versionadded:: 0.4
@@ -768,8 +758,8 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
.. confval:: epub_post_files
Additional files that should be inserted after the text generated by Sphinx.
- It is a list of tuples containing the file name and the title. The default
- value is ``[]``.
+ It is a list of tuples containing the file name and the title. This option
+ can be used to add an appendix. The default value is ``[]``.
.. confval:: epub_exclude_files
@@ -782,6 +772,12 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
be an integer greater than zero. The default value is 3. Note: A deeply
nested table of contents may be difficult to navigate.
+.. confval:: epub_tocdup
+
+ This flag determines if a toc entry is inserted again at the beginning of
+ it's nested toc listing. This allows easier navitation to the top of
+ a chapter, but can be confusing because it mixes entries of differnet
+ depth in one list. The default value is ``True``.
.. _latex-options:
diff --git a/doc/domains.rst b/doc/domains.rst
index c64930a2..8cd7a0c7 100644
--- a/doc/domains.rst
+++ b/doc/domains.rst
@@ -52,10 +52,19 @@ flag ``:noindex:``. An example using a Python domain directive::
.. py:function:: spam(eggs)
ham(eggs)
- :noindex:
Spam or ham the foo.
+This describes the two Python functions ``spam`` and ``ham``. (Note that when
+signatures become too long, you can break them if you add a backslash to lines
+that are continued in the next line. Example::
+
+ .. py:function:: filterwarnings(action, message='', category=Warning, \
+ module='', lineno=0, append=False)
+ :noindex:
+
+(This example also shows how to use the ``:noindex:`` flag.)
+
The domains also provide roles that link back to these object descriptions. For
example, to link to one of the functions described in the example above, you
could say ::
@@ -138,11 +147,12 @@ declarations:
.. rst:directive:: .. py:currentmodule:: name
This directive tells Sphinx that the classes, functions etc. documented from
- here are in the given module (like :rst:dir:`py:module`), but it will not create
- index entries, an entry in the Global Module Index, or a link target for
- :rst:role:`mod`. This is helpful in situations where documentation for things in
- a module is spread over multiple files or sections -- one location has the
- :rst:dir:`py:module` directive, the others only :rst:dir:`py:currentmodule`.
+ here are in the given module (like :rst:dir:`py:module`), but it will not
+ create index entries, an entry in the Global Module Index, or a link target
+ for :rst:role:`py:mod`. This is helpful in situations where documentation
+ for things in a module is spread over multiple files or sections -- one
+ location has the :rst:dir:`py:module` directive, the others only
+ :rst:dir:`py:currentmodule`.
The following directives are provided for module and class contents:
@@ -363,6 +373,9 @@ dot, this order is reversed. For example, in the documentation of Python's
:mod:`codecs` module, ``:py:func:`open``` always refers to the built-in
function, while ``:py:func:`.open``` refers to :func:`codecs.open`.
+A similar heuristic is used to determine whether the name is an attribute of the
+currently documented class.
+
Also, if the name is prefixed with a dot, and no exact match is found, the
target is taken as a suffix and all object names with that suffix are
searched. For example, ``:py:meth:`.TarFile.close``` references the
@@ -370,8 +383,9 @@ searched. For example, ``:py:meth:`.TarFile.close``` references the
``tarfile``. Since this can get ambiguous, if there is more than one possible
match, you will get a warning from Sphinx.
-A similar heuristic is used to determine whether the name is an attribute of the
-currently documented class.
+Note that you can combine the ``~`` and ``.`` prefixes:
+``:py:meth:`~.TarFile.close``` will reference the ``tarfile.TarFile.close()``
+method, but the visible link caption will only be ``close()``.
.. _c-domain:
diff --git a/doc/ext/appapi.rst b/doc/ext/appapi.rst
index 402dd72f..2717c6a8 100644
--- a/doc/ext/appapi.rst
+++ b/doc/ext/appapi.rst
@@ -210,7 +210,7 @@ the following public API:
standard Sphinx roles (see :ref:`xref-syntax`).
This method is also available under the deprecated alias
- :meth:`add_description_unit`.
+ ``add_description_unit``.
.. method:: Sphinx.add_crossref_type(directivename, rolename, indextemplate='', ref_nodeclass=None, objname='')
@@ -272,6 +272,8 @@ the following public API:
This allows to auto-document new types of objects. See the source of the
autodoc module for examples on how to subclass :class:`Documenter`.
+ .. XXX add real docs for Documenter and subclassing
+
.. versionadded:: 0.6
.. method:: Sphinx.add_autodoc_attrgetter(type, getter)
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index bd725cfa..81333101 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -27,20 +27,21 @@ two locations for documentation, while at the same time avoiding
auto-generated-looking pure API documentation.
:mod:`autodoc` provides several directives that are versions of the usual
-:rst:dir:`module`, :rst:dir:`class` and so forth. On parsing time, they import the
-corresponding module and extract the docstring of the given objects, inserting
-them into the page source under a suitable :rst:dir:`module`, :rst:dir:`class` etc.
-directive.
+:rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they
+import the corresponding module and extract the docstring of the given objects,
+inserting them into the page source under a suitable :rst:dir:`py:module`,
+:rst:dir:`py:class` etc. directive.
.. note::
- Just as :rst:dir:`class` respects the current :rst:dir:`module`, :rst:dir:`autoclass`
- will also do so, and likewise with :rst:dir:`method` and :rst:dir:`class`.
+ Just as :rst:dir:`py:class` respects the current :rst:dir:`py:module`,
+ :rst:dir:`autoclass` will also do so. Likewise, :rst:dir:`automethod` will
+ respect the current :rst:dir:`py:class`.
.. rst:directive:: automodule
- autoclass
- autoexception
+ autoclass
+ autoexception
Document a module, class or exception. All three directives will by default
only insert the docstring of the object itself::
@@ -127,23 +128,24 @@ directive.
.. versionadded:: 0.4
- * The :rst:dir:`automodule`, :rst:dir:`autoclass` and :rst:dir:`autoexception` directives
- also support a flag option called ``show-inheritance``. When given, a list
- of base classes will be inserted just below the class signature (when used
- with :rst:dir:`automodule`, this will be inserted for every class that is
- documented in the module).
+ * The :rst:dir:`automodule`, :rst:dir:`autoclass` and
+ :rst:dir:`autoexception` directives also support a flag option called
+ ``show-inheritance``. When given, a list of base classes will be inserted
+ just below the class signature (when used with :rst:dir:`automodule`, this
+ will be inserted for every class that is documented in the module).
.. versionadded:: 0.4
* All autodoc directives support the ``noindex`` flag option that has the
- same effect as for standard :rst:dir:`function` etc. directives: no index
- entries are generated for the documented object (and all autodocumented
- members).
+ same effect as for standard :rst:dir:`py:function` etc. directives: no
+ index entries are generated for the documented object (and all
+ autodocumented members).
.. versionadded:: 0.4
* :rst:dir:`automodule` also recognizes the ``synopsis``, ``platform`` and
- ``deprecated`` options that the standard :rst:dir:`module` directive supports.
+ ``deprecated`` options that the standard :rst:dir:`py:module` directive
+ supports.
.. versionadded:: 0.5
@@ -213,8 +215,8 @@ There are also new config values that you can set:
``"class"``
Only the class' docstring is inserted. This is the default. You can
- still document ``__init__`` as a separate method using :rst:dir:`automethod`
- or the ``members`` option to :rst:dir:`autoclass`.
+ still document ``__init__`` as a separate method using
+ :rst:dir:`automethod` or the ``members`` option to :rst:dir:`autoclass`.
``"both"``
Both the class' and the ``__init__`` method's docstring are concatenated
and inserted.
diff --git a/doc/ext/inheritance.rst b/doc/ext/inheritance.rst
index 76388a94..cdd01791 100644
--- a/doc/ext/inheritance.rst
+++ b/doc/ext/inheritance.rst
@@ -17,7 +17,7 @@ It adds this directive:
This directive has one or more arguments, each giving a module or class
name. Class names can be unqualified; in that case they are taken to exist
- in the currently described module (see :rst:dir:`module`).
+ in the currently described module (see :rst:dir:`py:module`).
For each given class, and each class in each given module, the base classes
are determined. Then, from all classes and their base classes, a graph is
diff --git a/doc/ext/intersphinx.rst b/doc/ext/intersphinx.rst
index 29b4057f..7997472a 100644
--- a/doc/ext/intersphinx.rst
+++ b/doc/ext/intersphinx.rst
@@ -9,7 +9,21 @@
.. versionadded:: 0.5
This extension can generate automatic links to the documentation of objects in
-other projects. This works as follows:
+other projects.
+
+Usage is simple: whenever Sphinx encounters a cross-reference that has no
+matching target in the current documentation set, it looks for targets in the
+documentation sets configured in :confval:`intersphinx_mapping`. A reference
+like ``:py:class:`zipfile.ZipFile``` can then link to the Python documentation
+for the ZipFile class, without you having to specify where it is located
+exactly.
+
+When using the "new" format (see below), you can even force lookup in a foreign
+set by prefixing the link target appropriately. A link like ``:ref:`comparison
+manual <python:comparisons>``` will then link to the label "comparisons" in the
+doc set "python", if it exists.
+
+Behind the scenes, this works as follows:
* Each Sphinx HTML build creates a file named :file:`objects.inv` that contains
a mapping from object names to URIs relative to the HTML set's root.
@@ -70,7 +84,7 @@ linking:
To add links to modules and objects in the Python standard library
documentation, use::
- intersphinx_mapping = {'python': ('http://docs.python.org/', None)}
+ intersphinx_mapping = {'python': ('http://docs.python.org/3.2', None)}
This will download the corresponding :file:`objects.inv` file from the
Internet and generate links to the pages under the given URI. The downloaded
@@ -80,12 +94,12 @@ linking:
A second example, showing the meaning of a non-``None`` value of the second
tuple item::
- intersphinx_mapping = {'python': ('http://docs.python.org/',
+ intersphinx_mapping = {'python': ('http://docs.python.org/3.2',
'python-inv.txt')}
This will read the inventory from :file:`python-inv.txt` in the source
directory, but still generate links to the pages under
- ``http://docs.python.org/``. It is up to you to update the inventory file as
+ ``http://docs.python.org/3.2``. It is up to you to update the inventory file as
new objects are added to the Python documentation.
.. confval:: intersphinx_cache_limit
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index b9f6ab12..f2896c39 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -17,15 +17,15 @@ if possible, reuse that support too.
.. note::
- :mod:`sphinx.ext.mathbase` is not meant to be added to the
- :confval:`extensions` config value, instead, use either
- :mod:`sphinx.ext.pngmath` or :mod:`sphinx.ext.jsmath` as described below.
+ :mod:`.mathbase` is not meant to be added to the :confval:`extensions` config
+ value, instead, use either :mod:`sphinx.ext.pngmath` or
+ :mod:`sphinx.ext.jsmath` as described below.
The input language for mathematics is LaTeX markup. This is the de-facto
standard for plain-text math notation and has the added advantage that no
further translation is necessary when building LaTeX output.
-:mod:`mathbase` defines these new markup elements:
+:mod:`.mathbase` defines these new markup elements:
.. rst:role:: math
diff --git a/doc/faq.rst b/doc/faq.rst
index 613283c5..5869e3af 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -134,6 +134,16 @@ some notes:
and Bookworm_. For bookworm you can download the source from
http://code.google.com/p/threepress/ and run your own local server.
+* Large floating divs are not displayed properly.
+ If they cover more than one page, the div is only shown on the first page.
+ In that case you can copy the :file:`epub.css` from the
+ ``sphinx/themes/epub/static/`` directory to your local ``_static/``
+ directory and remove the float settings.
+
+* Files that are inserted outside of the ``toctree`` directive must be manually
+ included. This sometimes applies to appendixes, e.g. the glossary or
+ the indices. You can add them with the :confval:`epub_post_files` option.
+
.. _Epubcheck: http://code.google.com/p/epubcheck/
.. _Calibre: http://calibre-ebook.com/
.. _FBreader: http://www.fbreader.org/
diff --git a/doc/intro.rst b/doc/intro.rst
index 33f97a3f..c85fbbad 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -45,13 +45,15 @@ See the :ref:`pertinent section in the FAQ list <usingwith>`.
Prerequisites
-------------
-Sphinx needs at least **Python 2.4** to run. If you like to have source code
-highlighting support, you must also install the Pygments_ library, which you can
-do via setuptools' easy_install. Sphinx should work with docutils version 0.4
-or some (not broken) SVN trunk snapshot.
+Sphinx needs at least **Python 2.4** or **Python 3.1** to run, as well as the
+docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.5
+or some (not broken) SVN trunk snapshot. If you like to have source code
+highlighting support, you must also install the Pygments_ library.
.. _reStructuredText: http://docutils.sf.net/rst.html
-.. _Pygments: http://pygments.org
+.. _docutils: http://docutils.sf.net/
+.. _Jinja2: http://jinja.pocoo.org/2/
+.. _Pygments: http://pygments.org/
Usage
diff --git a/doc/markup/inline.rst b/doc/markup/inline.rst
index bb1ed68e..4b704228 100644
--- a/doc/markup/inline.rst
+++ b/doc/markup/inline.rst
@@ -260,7 +260,7 @@ in a different style:
.. rst:role:: samp
A piece of literal text, such as code. Within the contents, you can use
- curly braces to indicate a "variable" part, as in :rst:dir:`file`. For
+ curly braces to indicate a "variable" part, as in :rst:role:`file`. For
example, in ``:samp:`print 1+{variable}```, the part ``variable`` would be
emphasized.
@@ -274,13 +274,15 @@ The following roles generate external links:
A reference to a Python Enhancement Proposal. This generates appropriate
index entries. The text "PEP *number*\ " is generated; in the HTML output,
- this text is a hyperlink to an online copy of the specified PEP.
+ this text is a hyperlink to an online copy of the specified PEP. You can
+ link to a specific section by saying ``:pep:`number#anchor```.
.. rst:role:: rfc
A reference to an Internet Request for Comments. This generates appropriate
index entries. The text "RFC *number*\ " is generated; in the HTML output,
- this text is a hyperlink to an online copy of the specified RFC.
+ this text is a hyperlink to an online copy of the specified RFC. You can
+ link to a specific section by saying ``:rfc:`number#anchor```.
Note that there are no special roles for including hyperlinks as you can use
diff --git a/doc/markup/para.rst b/doc/markup/para.rst
index be06f636..ecc6b4a6 100644
--- a/doc/markup/para.rst
+++ b/doc/markup/para.rst
@@ -42,15 +42,25 @@ units as well as normal text:
Example::
.. versionadded:: 2.5
- The `spam` parameter.
+ The *spam* parameter.
Note that there must be no blank line between the directive head and the
explanation; this is to make these blocks visually continuous in the markup.
.. rst:directive:: .. versionchanged:: version
- Similar to :rst:dir:`versionadded`, but describes when and what changed in the named
- feature in some way (new parameters, changed side effects, etc.).
+ Similar to :rst:dir:`versionadded`, but describes when and what changed in
+ the named feature in some way (new parameters, changed side effects, etc.).
+
+.. rst:directive:: .. deprecated:: vesion
+
+ Similar to :rst:dir:`versionchanged`, but describes when the feature was
+ deprecated. An explanation can also be given, for example to inform the
+ reader what should be used instead. Example::
+
+ .. deprecated:: 3.1
+ Use :func:`spam` instead.
+
--------------
diff --git a/doc/markup/toctree.rst b/doc/markup/toctree.rst
index 474427d7..2c0a418a 100644
--- a/doc/markup/toctree.rst
+++ b/doc/markup/toctree.rst
@@ -151,7 +151,7 @@ The special document names (and pages generated for them) are:
:ref:`object descriptions <basic-domain-markup>`, and from :rst:dir:`index`
directives.
- The module index contains one entry per :rst:dir:`module` directive.
+ The Python module index contains one entry per :rst:dir:`py:module` directive.
The search page contains a form that uses the generated JSON search index and
JavaScript to full-text search the generated documents for search words; it
diff --git a/doc/templating.rst b/doc/templating.rst
index 6880663d..193a90bd 100644
--- a/doc/templating.rst
+++ b/doc/templating.rst
@@ -21,10 +21,10 @@ No. You have several other options:
configuration value accordingly.
* You can :ref:`write a custom builder <writing-builders>` that derives from
- :class:`~sphinx.builders.StandaloneHTMLBuilder` and calls your template engine
- of choice.
+ :class:`~sphinx.builders.html.StandaloneHTMLBuilder` and calls your template
+ engine of choice.
-* You can use the :class:`~sphinx.builders.PickleHTMLBuilder` that produces
+* You can use the :class:`~sphinx.builders.html.PickleHTMLBuilder` that produces
pickle files with the page contents, and postprocess them using a custom tool,
or use them in your Web application.
@@ -261,9 +261,9 @@ in the future.
.. data:: file_suffix
- The value of the builder's :attr:`out_suffix` attribute, i.e. the file name
- extension that the output files will get. For a standard HTML builder, this
- is usually ``.html``.
+ The value of the builder's :attr:`~.SerializingHTMLBuilder.out_suffix`
+ attribute, i.e. the file name extension that the output files will get. For
+ a standard HTML builder, this is usually ``.html``.
.. data:: has_source
diff --git a/ez_setup.py b/ez_setup.py
deleted file mode 100644
index d24e845e..00000000
--- a/ez_setup.py
+++ /dev/null
@@ -1,276 +0,0 @@
-#!python
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
- from ez_setup import use_setuptools
- use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import sys
-DEFAULT_VERSION = "0.6c9"
-DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
-
-md5_data = {
- 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
- 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
- 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
- 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
- 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
- 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
- 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
- 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
- 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
- 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
- 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
- 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
- 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
- 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
- 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
- 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
- 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
- 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
- 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
- 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
- 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
- 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
- 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
- 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
- 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
- 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
- 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
- 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
- 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
- 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
- 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
- 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
- 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
- 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
-}
-
-import sys, os
-try: from hashlib import md5
-except ImportError: from md5 import md5
-
-def _validate_md5(egg_name, data):
- if egg_name in md5_data:
- digest = md5(data).hexdigest()
- if digest != md5_data[egg_name]:
- print >>sys.stderr, (
- "md5 validation of %s failed! (Possible download problem?)"
- % egg_name
- )
- sys.exit(2)
- return data
-
-def use_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- download_delay=15
-):
- """Automatically find/download setuptools and make it available on sys.path
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end with
- a '/'). `to_dir` is the directory where setuptools will be downloaded, if
- it is not already available. If `download_delay` is specified, it should
- be the number of seconds that will be paused before initiating a download,
- should one be required. If an older version of setuptools is installed,
- this routine will print a message to ``sys.stderr`` and raise SystemExit in
- an attempt to abort the calling script.
- """
- was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
- def do_download():
- egg = download_setuptools(version, download_base, to_dir, download_delay)
- sys.path.insert(0, egg)
- import setuptools; setuptools.bootstrap_install_from = egg
- try:
- import pkg_resources
- except ImportError:
- return do_download()
- try:
- pkg_resources.require("setuptools>="+version); return
- except pkg_resources.VersionConflict, e:
- if was_imported:
- print >>sys.stderr, (
- "The required version of setuptools (>=%s) is not available, and\n"
- "can't be installed while this script is running. Please install\n"
- " a more recent version first, using 'easy_install -U setuptools'."
- "\n\n(Currently using %r)"
- ) % (version, e.args[0])
- sys.exit(2)
- else:
- del pkg_resources, sys.modules['pkg_resources'] # reload ok
- return do_download()
- except pkg_resources.DistributionNotFound:
- return do_download()
-
-def download_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- delay = 15
-):
- """Download setuptools from a specified location and return its filename
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end
- with a '/'). `to_dir` is the directory where the egg will be downloaded.
- `delay` is the number of seconds to pause before an actual download attempt.
- """
- import urllib2, shutil
- egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
- url = download_base + egg_name
- saveto = os.path.join(to_dir, egg_name)
- src = dst = None
- if not os.path.exists(saveto): # Avoid repeated downloads
- try:
- from distutils import log
- if delay:
- log.warn("""
----------------------------------------------------------------------------
-This script requires setuptools version %s to run (even to display
-help). I will attempt to download it for you (from
-%s), but
-you may need to enable firewall access for this script first.
-I will start the download in %d seconds.
-
-(Note: if this machine does not have network access, please obtain the file
-
- %s
-
-and place it in this directory before rerunning this script.)
----------------------------------------------------------------------------""",
- version, download_base, delay, url
- ); from time import sleep; sleep(delay)
- log.warn("Downloading %s", url)
- src = urllib2.urlopen(url)
- # Read/write all in one block, so we don't create a corrupt file
- # if the download is interrupted.
- data = _validate_md5(egg_name, src.read())
- dst = open(saveto,"wb"); dst.write(data)
- finally:
- if src: src.close()
- if dst: dst.close()
- return os.path.realpath(saveto)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-def main(argv, version=DEFAULT_VERSION):
- """Install or upgrade setuptools and EasyInstall"""
- try:
- import setuptools
- except ImportError:
- egg = None
- try:
- egg = download_setuptools(version, delay=0)
- sys.path.insert(0,egg)
- from setuptools.command.easy_install import main
- return main(list(argv)+[egg]) # we're done here
- finally:
- if egg and os.path.exists(egg):
- os.unlink(egg)
- else:
- if setuptools.__version__ == '0.0.1':
- print >>sys.stderr, (
- "You have an obsolete version of setuptools installed. Please\n"
- "remove it from your system entirely before rerunning this script."
- )
- sys.exit(2)
-
- req = "setuptools>="+version
- import pkg_resources
- try:
- pkg_resources.require(req)
- except pkg_resources.VersionConflict:
- try:
- from setuptools.command.easy_install import main
- except ImportError:
- from easy_install import main
- main(list(argv)+[download_setuptools(delay=0)])
- sys.exit(0) # try to force an exit
- else:
- if argv:
- from setuptools.command.easy_install import main
- main(argv)
- else:
- print "Setuptools version",version,"or greater has been installed."
- print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-
-def update_md5(filenames):
- """Update our built-in md5 registry"""
-
- import re
-
- for name in filenames:
- base = os.path.basename(name)
- f = open(name,'rb')
- md5_data[base] = md5(f.read()).hexdigest()
- f.close()
-
- data = [" %r: %r,\n" % it for it in md5_data.items()]
- data.sort()
- repl = "".join(data)
-
- import inspect
- srcfile = inspect.getsourcefile(sys.modules[__name__])
- f = open(srcfile, 'rb'); src = f.read(); f.close()
-
- match = re.search("\nmd5_data = {\n([^}]+)}", src)
- if not match:
- print >>sys.stderr, "Internal error!"
- sys.exit(2)
-
- src = src[:match.start(1)] + repl + src[match.end(1):]
- f = open(srcfile,'w')
- f.write(src)
- f.close()
-
-
-if __name__=='__main__':
- if len(sys.argv)>2 and sys.argv[1]=='--md5update':
- update_md5(sys.argv[2:])
- else:
- main(sys.argv[1:])
-
-
-
-
-
-
diff --git a/setup.py b/setup.py
index de805b7e..fe4066b8 100644
--- a/setup.py
+++ b/setup.py
@@ -2,8 +2,8 @@
try:
from setuptools import setup, find_packages
except ImportError:
- import ez_setup
- ez_setup.use_setuptools()
+ import distribute_setup
+ distribute_setup.use_setuptools()
from setuptools import setup, find_packages
import os
@@ -47,7 +47,7 @@ A development egg can be found `here
requires = ['Pygments>=0.8', 'Jinja2>=2.2', 'docutils>=0.5']
if sys.version_info < (2, 4):
- print 'ERROR: Sphinx requires at least Python 2.4 to run.'
+ print('ERROR: Sphinx requires at least Python 2.4 to run.')
sys.exit(1)
if sys.version_info < (2, 5):
@@ -178,6 +178,7 @@ setup(
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
'Topic :: Documentation',
'Topic :: Text Processing',
'Topic :: Utilities',
@@ -197,4 +198,6 @@ setup(
},
install_requires=requires,
cmdclass=cmdclass,
+ use_2to3=True,
+ use_2to3_fixers=['custom_fixers'],
)
diff --git a/sphinx/__init__.py b/sphinx/__init__.py
index 37b6ecef..1ea2e7bf 100644
--- a/sphinx/__init__.py
+++ b/sphinx/__init__.py
@@ -9,11 +9,14 @@
:license: BSD, see LICENSE for details.
"""
+# Keep this file executable as-is in Python 3!
+# (Otherwise getting the version out of it from setup.py is impossible.)
+
import sys
from os import path
-__version__ = '1.0b2+'
-__released__ = '1.0b2' # used when Sphinx builds its own docs
+__version__ = '1.1pre'
+__released__ = '1.1 (hg)' # used when Sphinx builds its own docs
package_dir = path.abspath(path.dirname(__file__))
@@ -35,13 +38,14 @@ if '+' in __version__ or 'pre' in __version__:
def main(argv=sys.argv):
if sys.version_info[:3] < (2, 4, 0):
- print >>sys.stderr, \
- 'Error: Sphinx requires at least Python 2.4 to run.'
+ sys.stderr.write('Error: Sphinx requires at least '
+ 'Python 2.4 to run.\n')
return 1
try:
from sphinx import cmdline
- except ImportError, err:
+ except ImportError:
+ err = sys.exc_info()[1]
errstr = str(err)
if errstr.lower().startswith('no module named'):
whichmod = errstr[16:]
@@ -54,14 +58,14 @@ def main(argv=sys.argv):
whichmod = 'roman module (which is distributed with Docutils)'
hint = ('This can happen if you upgraded docutils using\n'
'easy_install without uninstalling the old version'
- 'first.')
+ 'first.\n')
else:
whichmod += ' module'
- print >>sys.stderr, ('Error: The %s cannot be found. '
- 'Did you install Sphinx and its dependencies '
- 'correctly?' % whichmod)
+ sys.stderr.write('Error: The %s cannot be found. '
+ 'Did you install Sphinx and its dependencies '
+ 'correctly?\n' % whichmod)
if hint:
- print >> sys.stderr, hint
+ sys.stderr.write(hint)
return 1
raise
return cmdline.main(argv)
diff --git a/sphinx/application.py b/sphinx/application.py
index 3ffd86c2..b3d2aebc 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -37,9 +37,6 @@ from sphinx.util.osutil import ENOENT
from sphinx.util.console import bold
-# Directive is either new-style or old-style
-clstypes = (type, types.ClassType)
-
# List of all known core events. Maps name to arguments description.
events = {
'builder-inited': '',
@@ -109,7 +106,9 @@ class Sphinx(object):
if self.confdir is None:
self.confdir = self.srcdir
- # load all extension modules
+ # backwards compatibility: activate old C markup
+ self.setup_extension('sphinx.ext.oldcmarkup')
+ # load all user-given extension modules
for extension in self.config.extensions:
self.setup_extension(extension)
# the config file itself can be an extension
diff --git a/sphinx/builders/epub.py b/sphinx/builders/epub.py
index 9767391e..aea07d4d 100644
--- a/sphinx/builders/epub.py
+++ b/sphinx/builders/epub.py
@@ -11,15 +11,17 @@
"""
import os
+import re
import codecs
-from os import path
import zipfile
+from os import path
from docutils import nodes
-from docutils.transforms import Transform
+from sphinx import addnodes
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.util.osutil import EEXIST
+from sphinx.util.smartypants import sphinx_smarty_pants as ssp
# (Fragment) templates from which the metainfo files content.opf, toc.ncx,
@@ -119,29 +121,10 @@ _media_types = {
'.ttf': 'application/x-font-ttf',
}
-
-# The transform to show link targets
-
-class VisibleLinksTransform(Transform):
- """
- Add the link target of referances to the text, unless it is already
- present in the description.
- """
-
- # This transform must run after the references transforms
- default_priority = 680
-
- def apply(self):
- for ref in self.document.traverse(nodes.reference):
- uri = ref.get('refuri', '')
- if ( uri.startswith('http:') or uri.startswith('https:') or \
- uri.startswith('ftp:') ) and uri not in ref.astext():
- uri = _link_target_template % {'uri': uri}
- if uri:
- idx = ref.parent.index(ref) + 1
- link = nodes.inline(uri, uri)
- link['classes'].append(_css_link_target_class)
- ref.parent.insert(idx, link)
+# Regular expression to match colons only in local fragment identifiers.
+# If the URI contains a colon before the #,
+# it is an external link that should not change.
+_refuri_re = re.compile("([^#:]*#)(.*)")
# The epub publisher
@@ -170,7 +153,6 @@ class EpubBuilder(StandaloneHTMLBuilder):
# the output files for epub must be .html only
self.out_suffix = '.html'
self.playorder = 0
- self.app.add_transform(VisibleLinksTransform)
def get_theme_config(self):
return self.config.epub_theme, {}
@@ -194,17 +176,20 @@ class EpubBuilder(StandaloneHTMLBuilder):
"""Collect section titles, their depth in the toc and the refuri."""
# XXX: is there a better way than checking the attribute
# toctree-l[1-8] on the parent node?
- if isinstance(doctree, nodes.reference):
+ if isinstance(doctree, nodes.reference) and doctree.has_key('refuri'):
+ refuri = doctree['refuri']
+ if refuri.startswith('http://') or refuri.startswith('https://') \
+ or refuri.startswith('irc:') or refuri.startswith('mailto:'):
+ return result
classes = doctree.parent.attributes['classes']
- level = 1
- for l in range(8, 0, -1): # or range(1, 8)?
- if (_toctree_template % l) in classes:
- level = l
- result.append({
- 'level': level,
- 'refuri': self.esc(doctree['refuri']),
- 'text': self.esc(doctree.astext())
- })
+ for level in range(8, 0, -1): # or range(1, 8)?
+ if (_toctree_template % level) in classes:
+ result.append({
+ 'level': level,
+ 'refuri': self.esc(refuri),
+ 'text': ssp(self.esc(doctree.astext()))
+ })
+ break
else:
for elem in doctree.children:
result = self.get_refnodes(elem, result)
@@ -220,21 +205,97 @@ class EpubBuilder(StandaloneHTMLBuilder):
self.refnodes.insert(0, {
'level': 1,
'refuri': self.esc(self.config.master_doc + '.html'),
- 'text': self.esc(self.env.titles[self.config.master_doc].astext())
+ 'text': ssp(self.esc(
+ self.env.titles[self.config.master_doc].astext()))
})
for file, text in reversed(self.config.epub_pre_files):
self.refnodes.insert(0, {
'level': 1,
- 'refuri': self.esc(file + '.html'),
- 'text': self.esc(text)
+ 'refuri': self.esc(file),
+ 'text': ssp(self.esc(text))
})
for file, text in self.config.epub_post_files:
self.refnodes.append({
'level': 1,
- 'refuri': self.esc(file + '.html'),
- 'text': self.esc(text)
+ 'refuri': self.esc(file),
+ 'text': ssp(self.esc(text))
})
+ def fix_fragment(self, match):
+ """Return a href attribute with colons replaced by hyphens.
+ """
+ return match.group(1) + match.group(2).replace(':', '-')
+
+ def fix_ids(self, tree):
+ """Replace colons with hyphens in href and id attributes.
+ Some readers crash because they interpret the part as a
+ transport protocol specification.
+ """
+ for node in tree.traverse(nodes.reference):
+ if 'refuri' in node:
+ m = _refuri_re.match(node['refuri'])
+ if m:
+ node['refuri'] = self.fix_fragment(m)
+ if 'refid' in node:
+ node['refid'] = node['refid'].replace(':', '-')
+ for node in tree.traverse(addnodes.desc_signature):
+ ids = node.attributes['ids']
+ newids = []
+ for id in ids:
+ newids.append(id.replace(':', '-'))
+ node.attributes['ids'] = newids
+
+ def add_visible_links(self, tree):
+ """Append visible link targets after external links.
+ """
+ for node in tree.traverse(nodes.reference):
+ uri = node.get('refuri', '')
+ if (uri.startswith('http:') or uri.startswith('https:') or
+ uri.startswith('ftp:')) and uri not in node.astext():
+ uri = _link_target_template % {'uri': uri}
+ if uri:
+ idx = node.parent.index(node) + 1
+ link = nodes.inline(uri, uri)
+ link['classes'].append(_css_link_target_class)
+ node.parent.insert(idx, link)
+
+ def write_doc(self, docname, doctree):
+ """Write one document file.
+ This method is overwritten in order to fix fragment identifiers
+ and to add visible external links.
+ """
+ self.fix_ids(doctree)
+ self.add_visible_links(doctree)
+ return StandaloneHTMLBuilder.write_doc(self, docname, doctree)
+
+ def fix_genindex(self, tree):
+ """Fix href attributes for genindex pages.
+ """
+ # XXX: modifies tree inline
+ # Logic modeled from themes/basic/genindex.html
+ for key, columns in tree:
+ for entryname, (links, subitems) in columns:
+ for (i, link) in enumerate(links):
+ m = _refuri_re.match(link)
+ if m:
+ links[i] = self.fix_fragment(m)
+ for subentryname, subentrylinks in subitems:
+ for (i, link) in enumerate(subentrylinks):
+ m = _refuri_re.match(link)
+ if m:
+ subentrylinks[i] = self.fix_fragment(m)
+
+ def handle_page(self, pagename, addctx, templatename='page.html',
+ outfilename=None, event_arg=None):
+ """Create a rendered page.
+ This method is overwritten for genindex pages in order to fix
+ href link attributes.
+ """
+ if pagename.startswith('genindex'):
+ self.fix_genindex(addctx['genindexentries'])
+ StandaloneHTMLBuilder.handle_page(self, pagename, addctx, templatename,
+ outfilename, event_arg)
+
# Finish by building the epub file
def handle_finish(self):
@@ -380,7 +441,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
navstack.append(navlist)
navlist = []
level += 1
- if lastnode:
+ 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))
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 6b3eada7..5a7d49cd 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -30,12 +30,12 @@ from docutils.frontend import OptionParser
from docutils.readers.doctree import Reader as DoctreeReader
from sphinx import package_dir, __version__
-from sphinx.util import copy_static_entry
+from sphinx.util import jsonimpl, copy_static_entry
from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \
movefile, ustrftime, copyfile
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.matching import patmatch, compile_matchers
-from sphinx.util.pycompat import any
+from sphinx.util.pycompat import any, b
from sphinx.errors import SphinxError
from sphinx.locale import _
from sphinx.search import js_index
@@ -47,14 +47,6 @@ from sphinx.util.console import bold, darkgreen, brown
from sphinx.writers.html import HTMLWriter, HTMLTranslator, \
SmartyPantsHTMLTranslator
-try:
- import json
-except ImportError:
- try:
- import simplejson as json
- except ImportError:
- json = None
-
#: the filename for the inventory of objects
INVENTORY_FILENAME = 'objects.inv'
#: the filename for the "last build" file (for serializing builders)
@@ -71,6 +63,7 @@ class StandaloneHTMLBuilder(Builder):
out_suffix = '.html'
link_suffix = '.html' # defaults to matching out_suffix
indexer_format = js_index
+ indexer_dumps_unicode = True
supported_image_types = ['image/svg+xml', 'image/png',
'image/gif', 'image/jpeg']
searchindex_filename = 'searchindex.js'
@@ -99,7 +92,7 @@ class StandaloneHTMLBuilder(Builder):
self.init_templates()
self.init_highlighter()
self.init_translator_class()
- if self.config.html_file_suffix:
+ if self.config.html_file_suffix is not None:
self.out_suffix = self.config.html_file_suffix
if self.config.html_link_suffix is not None:
@@ -154,8 +147,9 @@ class StandaloneHTMLBuilder(Builder):
cfgdict = dict((name, self.config[name])
for (name, desc) in self.config.values.iteritems()
if desc[1] == 'html')
- self.config_hash = md5(str(cfgdict)).hexdigest()
- self.tags_hash = md5(str(sorted(self.tags))).hexdigest()
+ self.config_hash = md5(unicode(cfgdict).encode('utf-8')).hexdigest()
+ self.tags_hash = md5(unicode(sorted(self.tags)).encode('utf-8')) \
+ .hexdigest()
old_config_hash = old_tags_hash = ''
try:
fp = open(path.join(self.outdir, '.buildinfo'))
@@ -207,7 +201,7 @@ class StandaloneHTMLBuilder(Builder):
"""Utility: Render a lone doctree node."""
if node is None:
return {'fragment': ''}
- doc = new_document('<partial node>')
+ doc = new_document(b('<partial node>'))
doc.append(node)
if self._publisher is None:
@@ -735,10 +729,12 @@ class StandaloneHTMLBuilder(Builder):
self.info(bold('dumping object inventory... '), nonl=True)
f = open(path.join(self.outdir, INVENTORY_FILENAME), 'wb')
try:
- f.write('# Sphinx inventory version 2\n')
- f.write('# Project: %s\n' % self.config.project.encode('utf-8'))
- f.write('# Version: %s\n' % self.config.version.encode('utf-8'))
- f.write('# The remainder of this file is compressed using zlib.\n')
+ f.write((u'# Sphinx inventory version 2\n'
+ u'# Project: %s\n'
+ u'# Version: %s\n'
+ u'# The remainder of this file is compressed using zlib.\n'
+ % (self.config.project, self.config.version)
+ ).encode('utf-8'))
compressor = zlib.compressobj(9)
for domainname, domain in self.env.domains.iteritems():
for name, dispname, type, docname, anchor, prio in \
@@ -750,11 +746,9 @@ class StandaloneHTMLBuilder(Builder):
if dispname == name:
dispname = u'-'
f.write(compressor.compress(
- '%s %s:%s %s %s %s\n' % (name.encode('utf-8'),
- domainname.encode('utf-8'),
- type.encode('utf-8'), prio,
- uri.encode('utf-8'),
- dispname.encode('utf-8'))))
+ (u'%s %s:%s %s %s %s\n' % (name, domainname, type,
+ prio, uri, dispname)
+ ).encode('utf-8')))
f.write(compressor.flush())
finally:
f.close()
@@ -766,7 +760,10 @@ class StandaloneHTMLBuilder(Builder):
searchindexfn = path.join(self.outdir, self.searchindex_filename)
# first write to a temporary file, so that if dumping fails,
# the existing index won't be overwritten
- f = open(searchindexfn + '.tmp', 'wb')
+ if self.indexer_dumps_unicode:
+ f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8')
+ else:
+ f = open(searchindexfn + '.tmp', 'wb')
try:
self.indexer.dump(f, self.indexer_format)
finally:
@@ -855,8 +852,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def get_doc_context(self, docname, body, metatags):
# no relation links...
toc = self.env.get_toctree_for(self.config.master_doc, self, False)
- self.fix_refuris(toc)
- toc = self.render_partial(toc)['fragment']
+ # if there is no toctree, toc is None
+ if toc:
+ self.fix_refuris(toc)
+ toc = self.render_partial(toc)['fragment']
+ display_toc = True
+ else:
+ toc = ''
+ display_toc = False
return dict(
parents = [],
prev = None,
@@ -869,7 +872,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
rellinks = [],
sourcename = '',
toc = toc,
- display_toc = True,
+ display_toc = display_toc,
)
def write(self, *ignored):
@@ -917,6 +920,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
#: implements a `dump`, `load`, `dumps` and `loads` functions
#: (pickle, simplejson etc.)
implementation = None
+ implementation_dumps_unicode = False
#: the filename for the global context file
globalcontext_filename = None
@@ -939,6 +943,17 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
return docname[:-5] # up to sep
return docname + SEP
+ def dump_context(self, context, filename):
+ if self.implementation_dumps_unicode:
+ f = codecs.open(filename, 'w', encoding='utf-8')
+ else:
+ f = open(filename, 'wb')
+ try:
+ # XXX: the third argument is pickle-specific!
+ self.implementation.dump(context, f, 2)
+ finally:
+ f.close()
+
def handle_page(self, pagename, ctx, templatename='page.html',
outfilename=None, event_arg=None):
ctx['current_page_name'] = pagename
@@ -952,11 +967,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
ctx, event_arg)
ensuredir(path.dirname(outfilename))
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(ctx, f, 2)
- finally:
- f.close()
+ self.dump_context(ctx, outfilename)
# if there is a source file, copy the source file for the
# "show source" link
@@ -969,11 +980,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
def handle_finish(self):
# dump the global context
outfilename = path.join(self.outdir, self.globalcontext_filename)
- f = open(outfilename, 'wb')
- try:
- self.implementation.dump(self.globalcontext, f, 2)
- finally:
- f.close()
+ self.dump_context(self.globalcontext, outfilename)
# super here to dump the search index
StandaloneHTMLBuilder.handle_finish(self)
@@ -993,7 +1000,9 @@ class PickleHTMLBuilder(SerializingHTMLBuilder):
A Builder that dumps the generated HTML into pickle files.
"""
implementation = pickle
+ implementation_dumps_unicode = False
indexer_format = pickle
+ indexer_dumps_unicode = False
name = 'pickle'
out_suffix = '.fpickle'
globalcontext_filename = 'globalcontext.pickle'
@@ -1007,15 +1016,17 @@ class JSONHTMLBuilder(SerializingHTMLBuilder):
"""
A builder that dumps the generated HTML into JSON files.
"""
- implementation = json
- indexer_format = json
+ implementation = jsonimpl
+ implementation_dumps_unicode = True
+ indexer_format = jsonimpl
+ indexer_dumps_unicode = True
name = 'json'
out_suffix = '.fjson'
globalcontext_filename = 'globalcontext.json'
searchindex_filename = 'searchindex.json'
def init(self):
- if json is None:
+ if jsonimpl.json is None:
raise SphinxError(
'The module simplejson (or json in Python >= 2.6) '
'is not available. The JSONHTMLBuilder builder will not work.')
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 538f4c84..e3a58e72 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -258,7 +258,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
def write_index(title, refs, subitems):
def write_param(name, value):
item = ' <param name="%s" value="%s">\n' % (name, value)
- f.write(item.encode('ascii', 'xmlcharrefreplace'))
+ f.write(item.encode('ascii', 'xmlcharrefreplace')
+ .decode('ascii'))
title = cgi.escape(title)
f.write('<LI> <OBJECT type="text/sitemap">\n')
write_param('Keyword', title)
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index c9bc363d..4a0bab63 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -16,7 +16,7 @@ from urllib2 import build_opener, HTTPError
from docutils import nodes
from sphinx.builders import Builder
-from sphinx.util.console import purple, red, darkgreen
+from sphinx.util.console import purple, red, darkgreen, darkgray
# create an opener that will simulate a browser user-agent
opener = build_opener()
@@ -71,9 +71,12 @@ class CheckExternalLinksBuilder(Builder):
break
lineno = node.line
+ if len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
+ return
+
+ if lineno:
+ self.info('(line %3d) ' % lineno, nonl=1)
if uri[0:5] == 'http:' or uri[0:6] == 'https:':
- if lineno:
- self.info('(line %3d) ' % lineno, nonl=1)
self.info(uri, nonl=1)
if uri in self.broken:
@@ -98,15 +101,9 @@ class CheckExternalLinksBuilder(Builder):
self.write_entry('redirected', docname,
lineno, uri + ' to ' + s)
self.redirected[uri] = (r, s)
- elif len(uri) == 0 or uri[0:7] == 'mailto:' or uri[0:4] == 'ftp:':
- return
else:
- self.warn(uri + ' - ' + red('malformed!'))
- self.write_entry('malformed', docname, lineno, uri)
- if self.app.quiet:
- self.warn('malformed link: %s' % uri,
- '%s:%s' % (self.env.doc2path(docname), lineno))
- self.app.statuscode = 1
+ self.info(uri + ' - ' + darkgray('local'))
+ self.write_entry('local', docname, lineno, uri)
if self.broken:
self.app.statuscode = 1
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index ffc52334..e86f1921 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -130,8 +130,16 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
for indexname, indexcls, content, collapse in self.domain_indices:
item = section_template % {'title': indexcls.localname,
'ref': '%s.html' % indexname}
- sections.append(' '*4*4 + item)
- sections = '\n'.join(sections)
+ sections.append((' ' * 4 * 4 + item).encode('utf-8'))
+ # sections may be unicode strings or byte strings, we have to make sure
+ # they are all byte strings before joining them
+ new_sections = []
+ for section in sections:
+ if isinstance(section, unicode):
+ new_sections.append(section.encode('utf-8'))
+ else:
+ new_sections.append(section)
+ sections = u'\n'.encode('utf-8').join(new_sections)
# keywords
keywords = []
@@ -230,7 +238,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
link = node['refuri']
title = escape(node.astext()).replace('"','&quot;')
item = section_template % {'title': title, 'ref': link}
- item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace')
+ item = u' ' * 4 * indentlevel + item
parts.append(item.encode('ascii', 'xmlcharrefreplace'))
elif isinstance(node, nodes.bullet_list):
for subnode in node:
diff --git a/sphinx/config.py b/sphinx/config.py
index e1075ff6..6c27f85f 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -11,13 +11,18 @@
import os
import re
+import sys
from os import path
from sphinx.errors import ConfigError
from sphinx.util.osutil import make_filename
+from sphinx.util.pycompat import bytes, b, convert_with_2to3
-nonascii_re = re.compile(r'[\x80-\xff]')
+nonascii_re = re.compile(b(r'[\x80-\xff]'))
+CONFIG_SYNTAX_ERROR = "There is a syntax error in your configuration file: %s"
+if sys.version_info >= (3, 0):
+ CONFIG_SYNTAX_ERROR += "\nDid you change the syntax from 2.x to 3.x?"
class Config(object):
"""Configuration file abstraction."""
@@ -124,6 +129,7 @@ class Config(object):
epub_post_files = ([], 'env'),
epub_exclude_files = ([], 'env'),
epub_tocdepth = (3, 'env'),
+ epub_tocdup = (True, 'env'),
# LaTeX options
latex_documents = ([], None),
@@ -162,12 +168,30 @@ class Config(object):
config['tags'] = tags
olddir = os.getcwd()
try:
+ # we promise to have the config dir as current dir while the
+ # config file is executed
+ os.chdir(dirname)
+ # get config source
+ f = open(config_file, 'rb')
try:
- os.chdir(dirname)
- execfile(config['__file__'], config)
+ source = f.read()
+ finally:
+ f.close()
+ try:
+ # compile to a code object, handle syntax errors
+ try:
+ code = compile(source, config_file, 'exec')
+ except SyntaxError:
+ if convert_with_2to3:
+ # maybe the file uses 2.x syntax; try to refactor to
+ # 3.x syntax using 2to3
+ source = convert_with_2to3(config_file)
+ code = compile(source, config_file, 'exec')
+ else:
+ raise
+ exec code in config
except SyntaxError, err:
- raise ConfigError('There is a syntax error in your '
- 'configuration file: ' + str(err))
+ raise ConfigError(CONFIG_SYNTAX_ERROR % err)
finally:
os.chdir(olddir)
@@ -181,10 +205,11 @@ class Config(object):
# check all string values for non-ASCII characters in bytestrings,
# since that can result in UnicodeErrors all over the place
for name, value in self._raw_config.iteritems():
- if isinstance(value, str) and nonascii_re.search(value):
+ if isinstance(value, bytes) and nonascii_re.search(value):
warn('the config value %r is set to a string with non-ASCII '
'characters; this can lead to Unicode errors occurring. '
- 'Please use Unicode strings, e.g. u"Content".' % name)
+ 'Please use Unicode strings, e.g. %r.' % (name, u'Content')
+ )
def init_values(self):
config = self._raw_config
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index 6c03b8e5..48c44178 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -32,6 +32,7 @@ except AttributeError:
# RE to strip backslash escapes
+nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(?=[^\\])')
@@ -57,10 +58,12 @@ class ObjectDescription(Directive):
"""
Retrieve the signatures to document from the directive arguments. By
default, signatures are given as arguments, one per line.
+
+ Backslash-escaping of newlines is supported.
"""
+ lines = nl_escape_re.sub('', self.arguments[0]).split('\n')
# remove backslashes to support (dummy) escapes; helps Vim highlighting
- return [strip_backslash_re.sub('', sig.strip())
- for sig in self.arguments[0].split('\n')]
+ return [strip_backslash_re.sub('', line.strip()) for line in lines]
def handle_signature(self, sig, signode):
"""
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index aff4395f..1808cdab 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -102,7 +102,7 @@ class LiteralInclude(Directive):
rel_fn = filename[1:]
else:
docdir = path.dirname(env.doc2path(env.docname, base=None))
- rel_fn = path.normpath(path.join(docdir, filename))
+ rel_fn = path.join(docdir, filename)
try:
fn = path.join(env.srcdir, rel_fn)
except UnicodeDecodeError:
@@ -119,7 +119,7 @@ class LiteralInclude(Directive):
encoding = self.options.get('encoding', env.config.source_encoding)
codec_info = codecs.lookup(encoding)
try:
- f = codecs.StreamReaderWriter(open(fn, 'U'),
+ f = codecs.StreamReaderWriter(open(fn, 'rb'),
codec_info[2], codec_info[3], 'strict')
lines = f.readlines()
f.close()
diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py
index 5ce1ce1a..332c4084 100644
--- a/sphinx/directives/other.py
+++ b/sphinx/directives/other.py
@@ -215,12 +215,7 @@ class VersionChange(Directive):
else:
ret = [node]
env = self.state.document.settings.env
- env.versionchanges.setdefault(node['version'], []).append(
- (node['type'], env.temp_data['docname'], self.lineno,
- # XXX: python domain specific
- env.temp_data.get('py:module'),
- env.temp_data.get('object'),
- node.astext()))
+ env.note_versionchange(node['type'], node['version'], node, self.lineno)
return ret
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 4dac8925..8df89459 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -110,7 +110,7 @@ class DefinitionError(Exception):
return self.description
def __str__(self):
- return unicode(self.encode('utf-8'))
+ return unicode(self).encode('utf-8')
class DefExpr(object):
@@ -132,6 +132,8 @@ class DefExpr(object):
def __ne__(self, other):
return not self.__eq__(other)
+ __hash__ = None
+
def clone(self):
"""Close a definition expression node"""
return deepcopy(self)
diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py
index 890af8e1..582e2adc 100644
--- a/sphinx/domains/javascript.py
+++ b/sphinx/domains/javascript.py
@@ -56,7 +56,7 @@ class JSObject(ObjectDescription):
else:
# just a function or constructor
objectname = ''
- fullname = ''
+ fullname = name
signode['object'] = objectname
signode['fullname'] = fullname
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index fc808699..cd87bfbd 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -356,6 +356,9 @@ class PyModule(Directive):
env.domaindata['py']['modules'][modname] = \
(env.docname, self.options.get('synopsis', ''),
self.options.get('platform', ''), 'deprecated' in self.options)
+ # make a duplicate entry in 'objects' to facilitate searching for the
+ # module in PythonDomain.find_obj()
+ env.domaindata['py']['objects'][modname] = (env.docname, 'module')
targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
self.state.document.note_explicit_target(targetnode)
ret = [targetnode]
@@ -544,7 +547,7 @@ class PythonDomain(Domain):
if fn == docname:
del self.data['modules'][modname]
- def find_obj(self, env, modname, classname, name, type, searchorder=0):
+ def find_obj(self, env, modname, classname, name, type, searchmode=0):
"""
Find a Python object for "name", perhaps using the given module and/or
classname. Returns a list of (name, object entry) tuples.
@@ -560,22 +563,31 @@ class PythonDomain(Domain):
matches = []
newname = None
- if searchorder == 1:
- if modname and classname and \
- modname + '.' + classname + '.' + name in objects:
- newname = modname + '.' + classname + '.' + name
- elif modname and modname + '.' + name in objects:
- newname = modname + '.' + name
- elif name in objects:
- newname = name
- else:
- # "fuzzy" searching mode
- searchname = '.' + name
- matches = [(name, objects[name]) for name in objects
- if name.endswith(searchname)]
+ if searchmode == 1:
+ objtypes = self.objtypes_for_role(type)
+ if modname and classname:
+ fullname = modname + '.' + classname + '.' + name
+ if fullname in objects and objects[fullname][1] in objtypes:
+ newname = fullname
+ if not newname:
+ if modname and modname + '.' + name in objects and \
+ objects[modname + '.' + name][1] in objtypes:
+ newname = modname + '.' + name
+ elif name in objects and objects[name][1] in objtypes:
+ newname = name
+ else:
+ # "fuzzy" searching mode
+ searchname = '.' + name
+ matches = [(name, objects[name]) for name in objects
+ if name.endswith(searchname)
+ and objects[name][1] in objtypes]
else:
+ # NOTE: searching for exact match, object type is not considered
if name in objects:
newname = name
+ elif type == 'mod':
+ # only exact matches allowed for modules
+ return []
elif classname and classname + '.' + name in objects:
newname = classname + '.' + name
elif modname and modname + '.' + name in objects:
@@ -597,33 +609,35 @@ class PythonDomain(Domain):
def resolve_xref(self, env, fromdocname, builder,
type, target, node, contnode):
- if (type == 'mod' or
- type == 'obj' and target in self.data['modules']):
- docname, synopsis, platform, deprecated = \
- self.data['modules'].get(target, ('','','', ''))
- if not docname:
- return None
- else:
- title = '%s%s%s' % ((platform and '(%s) ' % platform),
- synopsis,
- (deprecated and ' (deprecated)' or ''))
- return make_refnode(builder, fromdocname, docname,
- 'module-' + target, contnode, title)
+ modname = node.get('py:module')
+ clsname = node.get('py:class')
+ searchmode = node.hasattr('refspecific') and 1 or 0
+ matches = self.find_obj(env, modname, clsname, target,
+ type, searchmode)
+ if not matches:
+ return None
+ elif len(matches) > 1:
+ env.warn(fromdocname,
+ 'more than one target found for cross-reference '
+ '%r: %s' % (target,
+ ', '.join(match[0] for match in matches)),
+ node.line)
+ name, obj = matches[0]
+
+ if obj[1] == 'module':
+ # get additional info for modules
+ docname, synopsis, platform, deprecated = self.data['modules'][name]
+ assert docname == obj[0]
+ title = name
+ if synopsis:
+ title += ': ' + synopsis
+ if deprecated:
+ title += _(' (deprecated)')
+ if platform:
+ title += ' (' + platform + ')'
+ return make_refnode(builder, fromdocname, docname,
+ 'module-' + name, contnode, title)
else:
- modname = node.get('py:module')
- clsname = node.get('py:class')
- searchorder = node.hasattr('refspecific') and 1 or 0
- matches = self.find_obj(env, modname, clsname, target,
- type, searchorder)
- if not matches:
- return None
- elif len(matches) > 1:
- env.warn(fromdocname,
- 'more than one target found for cross-reference '
- '%r: %s' % (target,
- ', '.join(match[0] for match in matches)),
- node.line)
- name, obj = matches[0]
return make_refnode(builder, fromdocname, obj[0], name,
contnode, name)
diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py
index 04092eab..d3ffc6bd 100644
--- a/sphinx/domains/rst.py
+++ b/sphinx/domains/rst.py
@@ -28,9 +28,10 @@ class ReSTMarkup(ObjectDescription):
"""
def add_target_and_index(self, name, sig, signode):
- if name not in self.state.document.ids:
- signode['names'].append(name)
- signode['ids'].append(name)
+ targetname = self.objtype + '-' + name
+ if targetname not in self.state.document.ids:
+ signode['names'].append(targetname)
+ signode['ids'].append(targetname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
@@ -47,7 +48,7 @@ class ReSTMarkup(ObjectDescription):
indextext = self.get_index_text(self.objtype, name)
if indextext:
self.indexnode['entries'].append(('single', indextext,
- name, name))
+ targetname, targetname))
def get_index_text(self, objectname, name):
if self.objtype == 'directive':
@@ -129,8 +130,9 @@ class ReSTDomain(Domain):
if (objtype, target) in objects:
return make_refnode(builder, fromdocname,
objects[objtype, target],
- target, contnode, target)
+ objtype + '-' + target,
+ contnode, target + ' ' + objtype)
def get_objects(self):
for (typ, name), docname in self.data['objects'].iteritems():
- yield name, name, typ, docname, name, 1
+ yield name, name, typ, docname, typ + '-' + name, 1
diff --git a/sphinx/environment.py b/sphinx/environment.py
index b03e4625..6339675b 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -11,6 +11,7 @@
import re
import os
+import sys
import time
import types
import codecs
@@ -40,10 +41,11 @@ from sphinx.util import url_re, get_matching_docs, docname_join, \
from sphinx.util.nodes import clean_astext, make_refnode, extract_messages
from sphinx.util.osutil import movefile, SEP, ustrftime
from sphinx.util.matching import compile_matchers
-from sphinx.util.pycompat import all
+from sphinx.util.pycompat import all, class_types
from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _, init as init_locale
+fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
orig_role_function = roles.role
orig_directive_function = directives.directive
@@ -64,7 +66,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
-ENV_VERSION = 36
+ENV_VERSION = 38
default_substitutions = set([
@@ -81,7 +83,7 @@ class WarningStream(object):
self.warnfunc = warnfunc
def write(self, text):
if text.strip():
- self.warnfunc(text, None, '')
+ self.warnfunc(text.strip(), None, '')
class NoUri(Exception):
@@ -289,7 +291,7 @@ class BuildEnvironment:
if key.startswith('_') or \
isinstance(val, types.ModuleType) or \
isinstance(val, types.FunctionType) or \
- isinstance(val, (type, types.ClassType)):
+ isinstance(val, class_types):
del self.config[key]
try:
pickle.dump(self, picklefile, pickle.HIGHEST_PROTOCOL)
@@ -421,14 +423,14 @@ class BuildEnvironment:
If base is a path string, return absolute path under that.
If suffix is not None, add it instead of config.source_suffix.
"""
+ docname = docname.replace(SEP, path.sep)
suffix = suffix or self.config.source_suffix
if base is True:
- return path.join(self.srcdir,
- docname.replace(SEP, path.sep)) + suffix
+ return path.join(self.srcdir, docname) + suffix
elif base is None:
- return docname.replace(SEP, path.sep) + suffix
+ return docname + suffix
else:
- return path.join(base, docname.replace(SEP, path.sep)) + suffix
+ return path.join(base, docname) + suffix
def find_files(self, config):
"""
@@ -666,6 +668,8 @@ class BuildEnvironment:
class SphinxSourceClass(FileInput):
def decode(self_, data):
+ if isinstance(data, unicode):
+ return data
return data.decode(self_.encoding, 'sphinx')
def read(self_):
@@ -687,7 +691,7 @@ class BuildEnvironment:
destination_class=NullOutput)
pub.set_components(None, 'restructuredtext', None)
pub.process_programmatic_settings(None, self.settings, None)
- pub.set_source(None, src_path)
+ pub.set_source(None, src_path.encode(fs_encoding))
pub.set_destination(None, None)
try:
pub.publish()
@@ -771,6 +775,12 @@ class BuildEnvironment:
def note_dependency(self, filename):
self.dependencies.setdefault(self.docname, set()).add(filename)
+ def note_versionchange(self, type, version, node, lineno):
+ self.versionchanges.setdefault(version, []).append(
+ (type, self.temp_data['docname'], lineno,
+ self.temp_data.get('py:module'),
+ self.temp_data.get('object'), node.astext()))
+
# post-processing of read doctrees
def filter_messages(self, doctree):
@@ -1521,8 +1531,9 @@ class BuildEnvironment:
i += 1
# group the entries by letter
- def keyfunc2((k, v), letters=string.ascii_uppercase + '_'):
+ def keyfunc2(item, letters=string.ascii_uppercase + '_'):
# hack: mutating the subitems dicts to a list in the keyfunc
+ k, v = item
v[1] = sorted((si, se) for (si, (se, void)) in v[1].iteritems())
# now calculate the key
letter = k[0].upper()
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index adf08bcd..eef181de 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -14,7 +14,7 @@
import re
import sys
import inspect
-from types import FunctionType, BuiltinFunctionType, MethodType, ClassType
+from types import FunctionType, BuiltinFunctionType, MethodType
from docutils import nodes
from docutils.utils import assemble_option_dict
@@ -27,15 +27,10 @@ from sphinx.application import ExtensionError
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive
from sphinx.util.inspect import isdescriptor, safe_getmembers, safe_getattr
+from sphinx.util.pycompat import base_exception, class_types
from sphinx.util.docstrings import prepare_docstring
-try:
- base_exception = BaseException
-except NameError:
- base_exception = Exception
-
-
#: extended signature RE: with explicit module name separated by ::
py_ext_sig_re = re.compile(
r'''^ ([\w.]+::)? # explicit module name
@@ -256,6 +251,9 @@ class Documenter(object):
self.retann = None
# the object to document (set after import_object succeeds)
self.object = None
+ self.object_name = None
+ # the parent/owner of the object to document
+ self.parent = None
# the module analyzer to get at attribute docs, or None
self.analyzer = None
@@ -321,9 +319,13 @@ class Documenter(object):
"""
try:
__import__(self.modname)
+ parent = None
obj = self.module = sys.modules[self.modname]
for part in self.objpath:
+ parent = obj
obj = self.get_attr(obj, part)
+ self.object_name = part
+ self.parent = parent
self.object = obj
return True
# this used to only catch SyntaxError, ImportError and AttributeError,
@@ -416,9 +418,11 @@ class Documenter(object):
def get_doc(self, encoding=None):
"""Decode and return lines of the docstring(s) for the object."""
docstring = self.get_attr(self.object, '__doc__', None)
- if docstring:
- # make sure we have Unicode docstrings, then sanitize and split
- # into lines
+ # make sure we have Unicode docstrings, then sanitize and split
+ # into lines
+ if isinstance(docstring, unicode):
+ return [prepare_docstring(docstring)]
+ elif docstring:
return [prepare_docstring(force_decode(docstring, encoding))]
return []
@@ -438,8 +442,11 @@ class Documenter(object):
# set sourcename and add content from attribute documentation
if self.analyzer:
# prevent encoding errors when the file name is non-ASCII
- filename = unicode(self.analyzer.srcname,
- sys.getfilesystemencoding(), 'replace')
+ if not isinstance(self.analyzer.srcname, unicode):
+ filename = unicode(self.analyzer.srcname,
+ sys.getfilesystemencoding(), 'replace')
+ else:
+ filename = self.analyzer.srcname
sourcename = u'%s:docstring of %s' % (filename, self.fullname)
attr_docs = self.analyzer.find_attr_docs()
@@ -866,7 +873,7 @@ class ClassDocumenter(ModuleLevelDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
- return isinstance(member, (type, ClassType))
+ return isinstance(member, class_types)
def import_object(self):
ret = ModuleLevelDocumenter.import_object(self)
@@ -939,9 +946,12 @@ class ClassDocumenter(ModuleLevelDocumenter):
docstrings = [initdocstring]
else:
docstrings.append(initdocstring)
-
- return [prepare_docstring(force_decode(docstring, encoding))
- for docstring in docstrings]
+ doc = []
+ for docstring in docstrings:
+ if not isinstance(docstring, unicode):
+ docstring = force_decode(docstring, encoding)
+ doc.append(prepare_docstring(docstring))
+ return doc
def add_content(self, more_content, no_docstring=False):
if self.doc_as_attr:
@@ -972,7 +982,7 @@ class ExceptionDocumenter(ClassDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
- return isinstance(member, (type, ClassType)) and \
+ return isinstance(member, class_types) and \
issubclass(member, base_exception)
@@ -1004,24 +1014,38 @@ class MethodDocumenter(ClassLevelDocumenter):
return inspect.isroutine(member) and \
not isinstance(parent, ModuleDocumenter)
- def import_object(self):
- ret = ClassLevelDocumenter.import_object(self)
- if isinstance(self.object, classmethod) or \
- (isinstance(self.object, MethodType) and
- self.object.im_self is not None):
- self.directivetype = 'classmethod'
- # document class and static members before ordinary ones
- self.member_order = self.member_order - 1
- elif isinstance(self.object, FunctionType) or \
- (isinstance(self.object, BuiltinFunctionType) and
- hasattr(self.object, '__self__') and
- self.object.__self__ is not None):
- self.directivetype = 'staticmethod'
- # document class and static members before ordinary ones
- self.member_order = self.member_order - 1
- else:
- self.directivetype = 'method'
- return ret
+ if sys.version_info >= (3, 0):
+ def import_object(self):
+ ret = ClassLevelDocumenter.import_object(self)
+ obj_from_parent = self.parent.__dict__.get(self.object_name)
+ if isinstance(obj_from_parent, classmethod):
+ self.directivetype = 'classmethod'
+ self.member_order = self.member_order - 1
+ elif isinstance(obj_from_parent, staticmethod):
+ self.directivetype = 'staticmethod'
+ self.member_order = self.member_order - 1
+ else:
+ self.directivetype = 'method'
+ return ret
+ else:
+ def import_object(self):
+ ret = ClassLevelDocumenter.import_object(self)
+ if isinstance(self.object, classmethod) or \
+ (isinstance(self.object, MethodType) and
+ self.object.im_self is not None):
+ self.directivetype = 'classmethod'
+ # document class and static members before ordinary ones
+ self.member_order = self.member_order - 1
+ elif isinstance(self.object, FunctionType) or \
+ (isinstance(self.object, BuiltinFunctionType) and
+ hasattr(self.object, '__self__') and
+ self.object.__self__ is not None):
+ self.directivetype = 'staticmethod'
+ # document class and static members before ordinary ones
+ self.member_order = self.member_order - 1
+ else:
+ self.directivetype = 'method'
+ return ret
def format_args(self):
if inspect.isbuiltin(self.object) or \
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 4924d30b..f41820e2 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -173,8 +173,11 @@ class CoverageBuilder(Builder):
attrs = []
+ for attr_name in dir(obj):
+ attr = getattr(obj, attr_name)
for attr_name, attr in inspect.getmembers(
- obj, inspect.ismethod):
+ obj, lambda x: inspect.ismethod(x) or \
+ inspect.isfunction(x)):
if attr_name[0] == '_':
# starts with an underscore, ignore it
continue
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 9d681f90..62fbfdff 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -149,14 +149,14 @@ class TestCode(object):
class SphinxDocTestRunner(doctest.DocTestRunner):
def summarize(self, out, verbose=None):
- io = StringIO.StringIO()
+ string_io = StringIO.StringIO()
old_stdout = sys.stdout
- sys.stdout = io
+ sys.stdout = string_io
try:
res = doctest.DocTestRunner.summarize(self, verbose)
finally:
sys.stdout = old_stdout
- out(io.getvalue())
+ out(string_io.getvalue())
return res
def _DocTestRunner__patched_linecache_getlines(self, filename,
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 106de7a6..257ff1b6 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -93,6 +93,7 @@ def render_dot(self, code, options, format, prefix='graphviz'):
Render graphviz code into a PNG or PDF output file.
"""
hashkey = code.encode('utf-8') + str(options) + \
+ str(self.builder.config.graphviz_dot) + \
str(self.builder.config.graphviz_dot_args)
fname = '%s-%s.%s' % (prefix, sha(hashkey).hexdigest(), format)
if hasattr(self.builder, 'imgpath'):
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index b930d8ca..a12bad25 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-"""
+r"""
sphinx.ext.inheritance_diagram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 4fb3805f..251f5c55 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -26,6 +26,7 @@
import time
import zlib
+import codecs
import urllib2
import posixpath
from os import path
@@ -33,19 +34,26 @@ from os import path
from docutils import nodes
from sphinx.builders.html import INVENTORY_FILENAME
+from sphinx.util.pycompat import b
+
handlers = [urllib2.ProxyHandler(), urllib2.HTTPRedirectHandler(),
urllib2.HTTPHandler()]
-if hasattr(urllib2, 'HTTPSHandler'):
+try:
handlers.append(urllib2.HTTPSHandler)
+except NameError:
+ pass
urllib2.install_opener(urllib2.build_opener(*handlers))
+UTF8StreamReader = codecs.lookup('utf-8')[2]
+
def read_inventory_v1(f, uri, join):
+ f = UTF8StreamReader(f)
invdata = {}
line = f.next()
- projname = line.rstrip()[11:].decode('utf-8')
+ projname = line.rstrip()[11:]
line = f.next()
version = line.rstrip()[11:]
for line in f:
@@ -68,25 +76,25 @@ def read_inventory_v2(f, uri, join, bufsize=16*1024):
projname = line.rstrip()[11:].decode('utf-8')
line = f.readline()
version = line.rstrip()[11:].decode('utf-8')
- line = f.readline()
+ line = f.readline().decode('utf-8')
if 'zlib' not in line:
raise ValueError
def read_chunks():
decompressor = zlib.decompressobj()
- for chunk in iter(lambda: f.read(bufsize), ''):
+ for chunk in iter(lambda: f.read(bufsize), b('')):
yield decompressor.decompress(chunk)
yield decompressor.flush()
def split_lines(iter):
- buf = ''
+ buf = b('')
for chunk in iter:
buf += chunk
- lineend = buf.find('\n')
+ lineend = buf.find(b('\n'))
while lineend != -1:
yield buf[:lineend].decode('utf-8')
buf = buf[lineend+1:]
- lineend = buf.find('\n')
+ lineend = buf.find(b('\n'))
assert not buf
for line in split_lines(read_chunks()):
@@ -109,13 +117,13 @@ def fetch_inventory(app, uri, inv):
if inv.find('://') != -1:
f = urllib2.urlopen(inv)
else:
- f = open(path.join(app.srcdir, inv))
+ f = open(path.join(app.srcdir, inv), 'rb')
except Exception, err:
app.warn('intersphinx inventory %r not fetchable due to '
'%s: %s' % (inv, err.__class__, err))
return
try:
- line = f.readline().rstrip()
+ line = f.readline().rstrip().decode('utf-8')
try:
if line == '# Sphinx inventory version 1':
invdata = read_inventory_v1(f, uri, join)
@@ -191,10 +199,12 @@ def missing_reference(app, env, node, contnode):
return
objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
to_try = [(env.intersphinx_inventory, target)]
+ in_set = None
if ':' in target:
# first part may be the foreign doc set name
setname, newtarget = target.split(':', 1)
if setname in env.intersphinx_named_inventory:
+ in_set = setname
to_try.append((env.intersphinx_named_inventory[setname], newtarget))
for inventory, target in to_try:
for objtype in objtypes:
@@ -203,11 +213,25 @@ def missing_reference(app, env, node, contnode):
proj, version, uri, dispname = inventory[objtype][target]
newnode = nodes.reference('', '', internal=False, refuri=uri,
reftitle='(in %s v%s)' % (proj, version))
- if dispname == '-':
+ if node.get('refexplicit'):
+ # use whatever title was given
newnode.append(contnode)
+ elif dispname == '-':
+ # use whatever title was given, but strip prefix
+ title = contnode.astext()
+ if in_set and title.startswith(in_set+':'):
+ newnode.append(contnode.__class__(title[len(in_set)+1:],
+ title[len(in_set)+1:]))
+ else:
+ newnode.append(contnode)
else:
+ # else use the given display name (used for :ref:)
newnode.append(contnode.__class__(dispname, dispname))
return newnode
+ # at least get rid of the ':' in the target if no explicit title given
+ if in_set is not None and not node.get('refexplicit', True):
+ if len(contnode) and isinstance(contnode[0], nodes.Text):
+ contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)
def setup(app):
diff --git a/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py
index 62f5ee28..571d82a5 100644
--- a/sphinx/ext/oldcmarkup.py
+++ b/sphinx/ext/oldcmarkup.py
@@ -13,6 +13,10 @@ from docutils.parsers.rst import directives
from sphinx.util.compat import Directive
+_warned_oldcmarkup = False
+WARNING_MSG = 'using old C markup; please migrate to new-style markup ' \
+ '(e.g. c:function instead of cfunction), see ' \
+ 'http://sphinx.pocoo.org/domains.html'
class OldCDirective(Directive):
has_content = True
@@ -26,6 +30,10 @@ class OldCDirective(Directive):
def run(self):
env = self.state.document.settings.env
+ if not env.app._oldcmarkup_warned:
+ print 'XXXYYY'
+ env.warn(env.docname, WARNING_MSG, self.lineno)
+ env.app._oldcmarkup_warned = True
newname = 'c:' + self.name[1:]
newdir = env.lookup_domain_element('directive', newname)[0]
return newdir(newname, self.arguments, self.options,
@@ -35,12 +43,18 @@ class OldCDirective(Directive):
def old_crole(typ, rawtext, text, lineno, inliner, options={}, content=[]):
env = inliner.document.settings.env
+ if not typ:
+ typ = env.config.default_role
+ if not env.app._oldcmarkup_warned:
+ env.warn(env.docname, WARNING_MSG)
+ env.app._oldcmarkup_warned = True
newtyp = 'c:' + typ[1:]
newrole = env.lookup_domain_element('role', newtyp)[0]
return newrole(newtyp, rawtext, text, lineno, inliner, options, content)
def setup(app):
+ app._oldcmarkup_warned = False
app.add_directive('cfunction', OldCDirective)
app.add_directive('cmember', OldCDirective)
app.add_directive('cmacro', OldCDirective)
@@ -50,3 +64,4 @@ def setup(app):
app.add_role('cfunc', old_crole)
app.add_role('cmacro', old_crole)
app.add_role('ctype', old_crole)
+ app.add_role('cmember', old_crole)
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index 80b887c0..b9bb9d77 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -31,7 +31,11 @@ def doctree_read(app, doctree):
env._viewcode_modules[modname] = False
return
analyzer.find_tags()
- entry = analyzer.code.decode(analyzer.encoding), analyzer.tags, {}
+ if not isinstance(analyzer.code, unicode):
+ code = analyzer.code.decode(analyzer.encoding)
+ else:
+ code = analyzer.code
+ entry = code, analyzer.tags, {}
env._viewcode_modules[modname] = entry
elif entry is False:
return
diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py
index f5ea859c..6d710919 100644
--- a/sphinx/highlighting.py
+++ b/sphinx/highlighting.py
@@ -156,7 +156,7 @@ class PygmentsBridge(object):
if sys.version_info >= (2, 5):
src = 'from __future__ import with_statement\n' + src
- if isinstance(src, unicode):
+ if sys.version_info < (3, 0) and isinstance(src, unicode):
# Non-ASCII chars will only occur in string literals
# and comments. If we wanted to give them to the parser
# correctly, we'd have to find out the correct source
@@ -175,7 +175,7 @@ class PygmentsBridge(object):
return True
def highlight_block(self, source, lang, linenos=False, warn=None):
- if isinstance(source, str):
+ if not isinstance(source, unicode):
source = source.decode()
if not pygments:
return self.unhighlighted(source)
@@ -240,7 +240,7 @@ class PygmentsBridge(object):
# no HTML styles needed
return ''
if self.dest == 'html':
- return self.fmter[0].get_style_defs()
+ return self.fmter[0].get_style_defs('.highlight')
else:
styledefs = self.fmter[0].get_style_defs()
# workaround for Pygments < 0.12
diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py
index 02583ff8..48116991 100644
--- a/sphinx/locale/__init__.py
+++ b/sphinx/locale/__init__.py
@@ -8,6 +8,8 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+
+import sys
import gettext
import UserString
@@ -178,8 +180,12 @@ pairindextypes = {
translators = {}
-def _(message):
- return translators['sphinx'].ugettext(message)
+if sys.version_info >= (3, 0):
+ def _(message):
+ return translators['sphinx'].gettext(message)
+else:
+ def _(message):
+ return translators['sphinx'].ugettext(message)
def init(locale_dirs, language, catalog='sphinx'):
"""
diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.js b/sphinx/locale/bn/LC_MESSAGES/sphinx.js
new file mode 100644
index 00000000..277cd3d0
--- /dev/null
+++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.js
@@ -0,0 +1 @@
+Documentation.addTranslations({"locale": "bn", "plural_expr": "(n != 1)", "messages": {"Search Results": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ab\u09b2\u09be\u09ab\u09b2", "Preparing search...": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09aa\u09cd\u09b0\u09b8\u09cd\u09a4\u09c1\u09a4\u09bf \u099a\u09b2\u099b\u09c7...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "\u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7 \u0995\u09c7\u09be\u09a8 \u09ab\u09b2\u09be\u09ab\u09b2 \u09aa\u09be\u0993\u09df\u09be \u09af\u09be\u09df\u09a8\u09bf\u0964 \u0986\u09aa\u09a8\u09be\u09b0 \u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09b6\u09ac\u09cd\u09a6\u0997\u09c1\u09b2\u09c7\u09be\u09b0 \u09b8\u09a0\u09bf\u0995 \u09ac\u09be\u09a8\u09be\u09a8 \u0993 \u09ac\u09bf\u09ad\u09be\u0997 \u09a8\u09bf\u09b0\u09cd\u09ac\u09be\u099a\u09a8 \u09a8\u09bf\u09b6\u09cd\u099a\u09bf\u09a4 \u0995\u09b0\u09c1\u09a8\u0964", "Search finished, found %s page(s) matching the search query.": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u09b6\u09c7\u09b7 \u09b9\u09df\u09c7\u099b\u09c7, \u09ab\u09b2\u09be\u09ab\u09b2\u09c7 %s-\u099f\u09bf \u09aa\u09be\u09a4\u09be \u09aa\u09be\u0993\u09df\u09be \u0997\u09c7\u099b\u09c7\u0964", ", in ": ", -", "Permalink to this headline": "\u098f\u0987 \u09b6\u09bf\u09b0\u09c7\u09be\u09a8\u09be\u09ae\u09c7\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Searching": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8 \u099a\u09b2\u099b\u09c7", "Permalink to this definition": "\u098f\u0987 \u09b8\u0982\u099c\u09cd\u099e\u09be\u09b0 \u09aa\u09be\u09b0\u09cd\u09ae\u09be\u09b2\u09bf\u0999\u09cd\u0995", "Hide Search Matches": "\u0985\u09a8\u09c1\u09b8\u09a8\u09cd\u09a7\u09be\u09a8\u09c7\u09b0 \u09ae\u09cd\u09af\u09be\u099a\u0997\u09c1\u09b2\u09c7\u09be \u09b2\u09c1\u0995\u09be\u09a8"}}); \ No newline at end of file
diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.mo b/sphinx/locale/bn/LC_MESSAGES/sphinx.mo
new file mode 100644
index 00000000..9b60397e
--- /dev/null
+++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/bn/LC_MESSAGES/sphinx.po b/sphinx/locale/bn/LC_MESSAGES/sphinx.po
new file mode 100644
index 00000000..d5141c79
--- /dev/null
+++ b/sphinx/locale/bn/LC_MESSAGES/sphinx.po
@@ -0,0 +1,698 @@
+# Translations template for Sphinx.
+# Copyright (C) 2009 ORGANIZATION
+# This file is distributed under the same license as the Sphinx project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 1.0pre/[?1034h2e1ab15e035e\n"
+"Report-Msgid-Bugs-To: nasim.haque@gmail.com\n"
+"POT-Creation-Date: 2009-11-08 16:28+0100\n"
+"PO-Revision-Date: 2009-11-10 13:42+0100\n"
+"Last-Translator: Nasimul Haque <nasim.haque@gmail.com>\n"
+"Language-Team: Nasimul Haque <nasim.haque@gmail.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: sphinx/environment.py:130
+#: sphinx/writers/latex.py:184
+#, python-format
+msgid "%B %d, %Y"
+msgstr "%B %d, %Y"
+
+#: sphinx/environment.py:348
+#: sphinx/themes/basic/genindex-single.html:2
+#: sphinx/themes/basic/genindex-split.html:2
+#: sphinx/themes/basic/genindex-split.html:5
+#: sphinx/themes/basic/genindex.html:2
+#: sphinx/themes/basic/genindex.html:5
+#: sphinx/themes/basic/genindex.html:48
+#: sphinx/themes/basic/layout.html:134
+#: sphinx/writers/latex.py:190
+msgid "Index"
+msgstr "ইনডেক্স"
+
+#: sphinx/environment.py:349
+#: sphinx/writers/latex.py:189
+msgid "Module Index"
+msgstr "মডিউল ইনডেক্স"
+
+#: sphinx/environment.py:350
+#: sphinx/themes/basic/defindex.html:16
+msgid "Search Page"
+msgstr "অনুসন্ধান পাতা"
+
+#: sphinx/roles.py:167
+#, python-format
+msgid "Python Enhancement Proposals!PEP %s"
+msgstr "পাইথন উন্নয়ন পরামর্শ!PEP %s"
+
+#: sphinx/builders/changes.py:70
+msgid "Builtins"
+msgstr "বিল্টইন সমূহ"
+
+#: sphinx/builders/changes.py:72
+msgid "Module level"
+msgstr "মডিউল লেভেল"
+
+#: sphinx/builders/html.py:224
+#, python-format
+msgid "%b %d, %Y"
+msgstr "%b %d, %Y"
+
+#: sphinx/builders/html.py:243
+#: sphinx/themes/basic/defindex.html:21
+msgid "General Index"
+msgstr "সাধারণ ইনডেক্স"
+
+#: sphinx/builders/html.py:243
+msgid "index"
+msgstr "ইনডেক্স"
+
+#: sphinx/builders/html.py:247
+#: sphinx/builders/htmlhelp.py:220
+#: sphinx/builders/qthelp.py:133
+#: sphinx/themes/basic/defindex.html:19
+#: sphinx/themes/basic/modindex.html:2
+#: sphinx/themes/basic/modindex.html:13
+#: sphinx/themes/scrolls/modindex.html:2
+#: sphinx/themes/scrolls/modindex.html:13
+msgid "Global Module Index"
+msgstr "গ্লোবাল মডিউল ইনডেক্স"
+
+#: sphinx/builders/html.py:248
+msgid "modules"
+msgstr "মডিউল সমূহ"
+
+#: sphinx/builders/html.py:304
+msgid "next"
+msgstr "পরবর্তী"
+
+#: sphinx/builders/html.py:313
+msgid "previous"
+msgstr "পূর্ববর্তী"
+
+#: sphinx/builders/latex.py:162
+msgid " (in "
+msgstr "(-"
+
+#: sphinx/directives/__init__.py:78
+#: sphinx/directives/__init__.py:79
+#: sphinx/directives/__init__.py:80
+#: sphinx/directives/__init__.py:81
+msgid "Raises"
+msgstr "রেইজেস"
+
+#: sphinx/directives/__init__.py:82
+#: sphinx/directives/__init__.py:83
+#: sphinx/directives/__init__.py:84
+msgid "Variable"
+msgstr "ভ্যারিয়েবল"
+
+#: sphinx/directives/__init__.py:85
+#: sphinx/directives/__init__.py:86
+#: sphinx/directives/__init__.py:92
+#: sphinx/directives/__init__.py:93
+msgid "Returns"
+msgstr "রিটার্নস"
+
+#: sphinx/directives/__init__.py:94
+msgid "Return type"
+msgstr "রিটার্ন টাইপ"
+
+#: sphinx/directives/__init__.py:169
+msgid "Parameter"
+msgstr "প্যারামিটার"
+
+#: sphinx/directives/__init__.py:173
+msgid "Parameters"
+msgstr "প্যারামিটার"
+
+#: sphinx/directives/other.py:127
+msgid "Section author: "
+msgstr "অনুচ্ছেদ লেখক:"
+
+#: sphinx/directives/other.py:129
+msgid "Module author: "
+msgstr "মডিউল লেখক:"
+
+#: sphinx/directives/other.py:131
+msgid "Author: "
+msgstr "লেখক:"
+
+#: sphinx/directives/other.py:233
+msgid "See also"
+msgstr "আরও দেখুন"
+
+#: sphinx/domains/c.py:124
+#, python-format
+msgid "%s (C function)"
+msgstr "%s (C ফাংশন)"
+
+#: sphinx/domains/c.py:126
+#, python-format
+msgid "%s (C member)"
+msgstr "%s (C মেম্বার)"
+
+#: sphinx/domains/c.py:128
+#, python-format
+msgid "%s (C macro)"
+msgstr "%s (C ম্যাক্রো)"
+
+#: sphinx/domains/c.py:130
+#, python-format
+msgid "%s (C type)"
+msgstr "%s (C টাইপ)"
+
+#: sphinx/domains/c.py:132
+#, python-format
+msgid "%s (C variable)"
+msgstr "%s (C ভ্যারিয়েবল)"
+
+#: sphinx/domains/c.py:162
+msgid "C function"
+msgstr "C ফাংশন"
+
+#: sphinx/domains/c.py:163
+msgid "C member"
+msgstr "C মেম্বার"
+
+#: sphinx/domains/c.py:164
+msgid "C macro"
+msgstr "C ম্যাক্রো"
+
+#: sphinx/domains/c.py:165
+msgid "C type"
+msgstr "C টাইপ"
+
+#: sphinx/domains/c.py:166
+msgid "C variable"
+msgstr "C ভ্যারিয়েবল"
+
+#: sphinx/domains/python.py:186
+#, python-format
+msgid "%s() (built-in function)"
+msgstr "%s() (বিল্ট-ইন ফাংশন)"
+
+#: sphinx/domains/python.py:187
+#: sphinx/domains/python.py:244
+#: sphinx/domains/python.py:256
+#: sphinx/domains/python.py:269
+#, python-format
+msgid "%s() (in module %s)"
+msgstr "%s() (%s মডিউলে)"
+
+#: sphinx/domains/python.py:190
+#, python-format
+msgid "%s (built-in variable)"
+msgstr "%s (বিল্ট-ইন ভ্যারিয়েবল)"
+
+#: sphinx/domains/python.py:191
+#: sphinx/domains/python.py:282
+#, python-format
+msgid "%s (in module %s)"
+msgstr "%s (%s মডিউলে)"
+
+#: sphinx/domains/python.py:207
+#, python-format
+msgid "%s (built-in class)"
+msgstr "%s (বিল্ট-ইন ক্লাস)"
+
+#: sphinx/domains/python.py:208
+#, python-format
+msgid "%s (class in %s)"
+msgstr "%s (%s ক্লাসে)"
+
+#: sphinx/domains/python.py:248
+#, python-format
+msgid "%s() (%s.%s method)"
+msgstr "%s (%s.%s মেথড)"
+
+#: sphinx/domains/python.py:250
+#, python-format
+msgid "%s() (%s method)"
+msgstr "%s() (%s মেথড)"
+
+#: sphinx/domains/python.py:260
+#, python-format
+msgid "%s() (%s.%s static method)"
+msgstr "%s (%s.%s স্ট্যাটিক মেথড)"
+
+#: sphinx/domains/python.py:263
+#, python-format
+msgid "%s() (%s static method)"
+msgstr "%s() (%s স্ট্যাটিক মেথড)"
+
+#: sphinx/domains/python.py:273
+#, python-format
+msgid "%s() (%s.%s class method)"
+msgstr "%s() (%s.%s ক্লাস মেথড)"
+
+#: sphinx/domains/python.py:276
+#, python-format
+msgid "%s() (%s class method)"
+msgstr "%s() (%s ক্লাস মেথড)"
+
+#: sphinx/domains/python.py:286
+#, python-format
+msgid "%s (%s.%s attribute)"
+msgstr "%s (%s.%s এ্যট্রিবিউট)"
+
+#: sphinx/domains/python.py:288
+#, python-format
+msgid "%s (%s attribute)"
+msgstr "%s (%s এ্যট্রিবিউট)"
+
+#: sphinx/domains/python.py:334
+msgid "Platforms: "
+msgstr "প্লাটফরম:"
+
+#: sphinx/domains/python.py:340
+#, python-format
+msgid "%s (module)"
+msgstr "%s (মডিউল)"
+
+#: sphinx/domains/python.py:396
+msgid "function"
+msgstr "ফাংশন"
+
+#: sphinx/domains/python.py:397
+msgid "data"
+msgstr "ডাটা"
+
+#: sphinx/domains/python.py:398
+msgid "class"
+msgstr "ক্লাস"
+
+#: sphinx/domains/python.py:399
+#: sphinx/locale/__init__.py:161
+msgid "exception"
+msgstr "এক্সেপশন"
+
+#: sphinx/domains/python.py:400
+msgid "method"
+msgstr "মেথড"
+
+#: sphinx/domains/python.py:401
+msgid "attribute"
+msgstr "এ্যট্রিবিউট"
+
+#: sphinx/domains/python.py:402
+#: sphinx/locale/__init__.py:157
+msgid "module"
+msgstr "মডিউল"
+
+#: sphinx/domains/std.py:67
+#: sphinx/domains/std.py:83
+#, python-format
+msgid "environment variable; %s"
+msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল; %s"
+
+#: sphinx/domains/std.py:156
+#, python-format
+msgid "%scommand line option; %s"
+msgstr "%sকমান্ড লাইন অপশন; %s"
+
+#: sphinx/domains/std.py:324
+msgid "glossary term"
+msgstr "শব্দকোষ"
+
+#: sphinx/domains/std.py:325
+msgid "grammar token"
+msgstr "ব্যকরণ টোকেন"
+
+#: sphinx/domains/std.py:326
+msgid "environment variable"
+msgstr "এনভায়রনমেন্ট ভ্যারিয়েবল"
+
+#: sphinx/domains/std.py:327
+msgid "program option"
+msgstr "প্রোগ্রাম অপশন"
+
+#: sphinx/ext/autodoc.py:892
+#, python-format
+msgid " Bases: %s"
+msgstr "বেস: %s"
+
+#: sphinx/ext/autodoc.py:925
+#, python-format
+msgid "alias of :class:`%s`"
+msgstr ":class:`%s` এর উপনাম"
+
+#: sphinx/ext/todo.py:40
+msgid "Todo"
+msgstr "অসমাপ্ত কাজ"
+
+#: sphinx/ext/todo.py:98
+#, python-format
+msgid "(The original entry is located in %s, line %d and can be found "
+msgstr "(%s, %d লাইনে মূল অন্তর্ভুক্তিটি রয়েছে, যা পাওয়া যাবে"
+
+#: sphinx/ext/todo.py:104
+msgid "here"
+msgstr "এখানে"
+
+#: sphinx/locale/__init__.py:138
+msgid "Attention"
+msgstr "দৃষ্টি আকর্ষণ"
+
+#: sphinx/locale/__init__.py:139
+msgid "Caution"
+msgstr "সতর্কীকরণ"
+
+#: sphinx/locale/__init__.py:140
+msgid "Danger"
+msgstr "বিপজ্জনক"
+
+#: sphinx/locale/__init__.py:141
+msgid "Error"
+msgstr "ভুল (এরর)"
+
+#: sphinx/locale/__init__.py:142
+msgid "Hint"
+msgstr "আভাস"
+
+#: sphinx/locale/__init__.py:143
+msgid "Important"
+msgstr "গুরুত্বপূর্ণ"
+
+#: sphinx/locale/__init__.py:144
+msgid "Note"
+msgstr "নোট"
+
+#: sphinx/locale/__init__.py:145
+msgid "See Also"
+msgstr "আরও দেখুন"
+
+#: sphinx/locale/__init__.py:146
+msgid "Tip"
+msgstr "পরামর্শ"
+
+#: sphinx/locale/__init__.py:147
+msgid "Warning"
+msgstr "সতর্কতা"
+
+#: sphinx/locale/__init__.py:151
+#, python-format
+msgid "New in version %s"
+msgstr "%s ভার্সনে নতুন"
+
+#: sphinx/locale/__init__.py:152
+#, python-format
+msgid "Changed in version %s"
+msgstr "%s ভার্সনে পরিবর্তিত"
+
+#: sphinx/locale/__init__.py:153
+#, python-format
+msgid "Deprecated since version %s"
+msgstr "%s ভার্সন থেকে ডেপ্রিকেটেড"
+
+#: sphinx/locale/__init__.py:158
+msgid "keyword"
+msgstr "কিওয়ার্ড"
+
+#: sphinx/locale/__init__.py:159
+msgid "operator"
+msgstr "অপারেটর"
+
+#: sphinx/locale/__init__.py:160
+msgid "object"
+msgstr "অবজেক্ট"
+
+#: sphinx/locale/__init__.py:162
+msgid "statement"
+msgstr "স্ট্যাটমেন্ট"
+
+#: sphinx/locale/__init__.py:163
+msgid "built-in function"
+msgstr "বিল্ট-ইন ফাংশন"
+
+#: sphinx/themes/basic/defindex.html:2
+msgid "Overview"
+msgstr "ভুমিকা"
+
+#: sphinx/themes/basic/defindex.html:11
+msgid "Indices and tables:"
+msgstr "ইনডেক্স ও টেবিল সমূহ:"
+
+#: sphinx/themes/basic/defindex.html:14
+msgid "Complete Table of Contents"
+msgstr "পূর্ণাঙ্গ সূচীপত্র"
+
+#: sphinx/themes/basic/defindex.html:15
+msgid "lists all sections and subsections"
+msgstr "সকল অনুচ্ছেদ সমূহের তালিকা"
+
+#: sphinx/themes/basic/defindex.html:17
+msgid "search this documentation"
+msgstr "এই সহায়িকাতে অনুসন্ধা করুন"
+
+#: sphinx/themes/basic/defindex.html:20
+msgid "quick access to all modules"
+msgstr "সকল মডিউলে দ্রুত প্রবেশ"
+
+#: sphinx/themes/basic/defindex.html:22
+msgid "all functions, classes, terms"
+msgstr "সকল ফাংশন, ক্লাস, টার্ম"
+
+#: sphinx/themes/basic/genindex-single.html:5
+#, python-format
+msgid "Index &ndash; %(key)s"
+msgstr "ইনডেক্স &ndash; %(key)s"
+
+#: sphinx/themes/basic/genindex-single.html:44
+#: sphinx/themes/basic/genindex-split.html:14
+#: sphinx/themes/basic/genindex-split.html:27
+#: sphinx/themes/basic/genindex.html:54
+msgid "Full index on one page"
+msgstr "এক পাতায় সম্পূর্ণ ইনডেক্স"
+
+#: sphinx/themes/basic/genindex-split.html:7
+msgid "Index pages by letter"
+msgstr "বর্ণানুসারে ইনডেক্স পাতা"
+
+#: sphinx/themes/basic/genindex-split.html:15
+msgid "can be huge"
+msgstr "খুব বড় হতে পারে"
+
+#: sphinx/themes/basic/layout.html:10
+msgid "Navigation"
+msgstr "নেভিগেশন"
+
+#: sphinx/themes/basic/layout.html:42
+msgid "Table Of Contents"
+msgstr "সূচীপত্র"
+
+#: sphinx/themes/basic/layout.html:48
+msgid "Previous topic"
+msgstr "পূর্ববর্তী টপিক"
+
+#: sphinx/themes/basic/layout.html:50
+msgid "previous chapter"
+msgstr "পূর্ববর্তী অধ্যায়"
+
+#: sphinx/themes/basic/layout.html:53
+msgid "Next topic"
+msgstr "পরবর্তী টপিক"
+
+#: sphinx/themes/basic/layout.html:55
+msgid "next chapter"
+msgstr "পরবর্তী অধ্যায়"
+
+#: sphinx/themes/basic/layout.html:60
+msgid "This Page"
+msgstr "এই পাতা"
+
+#: sphinx/themes/basic/layout.html:63
+msgid "Show Source"
+msgstr "সোর্স দেখুন"
+
+#: sphinx/themes/basic/layout.html:73
+msgid "Quick search"
+msgstr "দ্রুত অনুসন্ধান"
+
+#: sphinx/themes/basic/layout.html:76
+msgid "Go"
+msgstr "যান"
+
+#: sphinx/themes/basic/layout.html:81
+msgid "Enter search terms or a module, class or function name."
+msgstr "অনুসন্ধানের জন্য টার্ম, মডিউল, ক্লাস অথবা ফাংশনের নাম দিন।"
+
+#: sphinx/themes/basic/layout.html:122
+#, python-format
+msgid "Search within %(docstitle)s"
+msgstr "%(docstitle)s এর মধ্যে খুঁজুন"
+
+#: sphinx/themes/basic/layout.html:131
+msgid "About these documents"
+msgstr "এই ডকুমেন্ট সম্পর্কে"
+
+#: sphinx/themes/basic/layout.html:137
+#: sphinx/themes/basic/search.html:2
+#: sphinx/themes/basic/search.html:5
+msgid "Search"
+msgstr "অনুসন্ধান"
+
+#: sphinx/themes/basic/layout.html:140
+msgid "Copyright"
+msgstr "কপিরাইট"
+
+#: sphinx/themes/basic/layout.html:187
+#: sphinx/themes/scrolls/layout.html:83
+#, python-format
+msgid "&copy; <a href=\"%(path)s\">Copyright</a> %(copyright)s."
+msgstr "&copy; <a href=\"%(path)s\">কপিরাইট</a> %(copyright)s."
+
+#: sphinx/themes/basic/layout.html:189
+#: sphinx/themes/scrolls/layout.html:85
+#, python-format
+msgid "&copy; Copyright %(copyright)s."
+msgstr "&copy; কপিরাইট %(copyright)s."
+
+#: sphinx/themes/basic/layout.html:193
+#: sphinx/themes/scrolls/layout.html:89
+#, python-format
+msgid "Last updated on %(last_updated)s."
+msgstr "%(last_updated)s সর্বশেষ পরিবর্তন করা হয়েছে।"
+
+#: sphinx/themes/basic/layout.html:196
+#: sphinx/themes/scrolls/layout.html:92
+#, python-format
+msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s."
+msgstr "<a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s দিয়ে তৈরী।"
+
+#: sphinx/themes/basic/modindex.html:36
+#: sphinx/themes/scrolls/modindex.html:37
+msgid "Deprecated"
+msgstr "ডেপ্রিকেটেড"
+
+#: sphinx/themes/basic/opensearch.xml:4
+#, python-format
+msgid "Search %(docstitle)s"
+msgstr "%(docstitle)s-এ খুঁজুন"
+
+#: sphinx/themes/basic/search.html:9
+msgid ""
+"Please activate JavaScript to enable the search\n"
+" functionality."
+msgstr ""
+"অনুসন্ধান করার জন্য অনুগ্রহপূর্বক জাভাস্ক্রিপ্ট \n"
+" সক্রিয় করুন।"
+
+#: sphinx/themes/basic/search.html:14
+msgid ""
+"From here you can search these documents. Enter your search\n"
+" words into the box below and click \"search\". Note that the search\n"
+" function will automatically search for all of the words. Pages\n"
+" containing fewer words won't appear in the result list."
+msgstr ""
+"এখান থেকে এই নথিগুলোতে আপনি অনুসন্ধান করতে পারবেন। \n"
+" আপনার কাঙ্ক্ষিত শব্দসমূহ নিচের বাক্সে লিখুন এবং \"অনুসন্ধান\" বাটনে ক্লিক করুন।\n"
+" উল্লেখ্য, সকল শব্দসমূহের উপস্থিতি নিয়ে অনুসন্ধান করা হবে। যেসব পাতায় সকল\n"
+" শব্দ নেই সেগুলো বাদ দেয়া হবে।"
+
+#: sphinx/themes/basic/search.html:21
+msgid "search"
+msgstr "খুঁজুন"
+
+#: sphinx/themes/basic/search.html:25
+#: sphinx/themes/basic/static/searchtools.js:473
+msgid "Search Results"
+msgstr "অনুসন্ধানের ফলাফল"
+
+#: sphinx/themes/basic/search.html:27
+msgid "Your search did not match any results."
+msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি।"
+
+#: sphinx/themes/basic/changes/frameset.html:5
+#: sphinx/themes/basic/changes/versionchanges.html:12
+#, python-format
+msgid "Changes in Version %(version)s &mdash; %(docstitle)s"
+msgstr "%(version)s &mdash; %(docstitle)s-এ পরিবর্তন সমূহ"
+
+#: sphinx/themes/basic/changes/rstsource.html:5
+#, python-format
+msgid "%(filename)s &mdash; %(docstitle)s"
+msgstr "%(filename)s &mdash; %(docstitle)s"
+
+#: sphinx/themes/basic/changes/versionchanges.html:17
+#, python-format
+msgid "Automatically generated list of changes in version %(version)s"
+msgstr "স্বয়ংক্রিয়ভাবে তৈরী %(version)s-এ পরিবর্তন সমূহের তালিকা।"
+
+#: sphinx/themes/basic/changes/versionchanges.html:18
+msgid "Library changes"
+msgstr "লাইব্রেরির পরিবর্তন"
+
+#: sphinx/themes/basic/changes/versionchanges.html:23
+msgid "C API changes"
+msgstr "C API পরিবর্তন"
+
+#: sphinx/themes/basic/changes/versionchanges.html:25
+msgid "Other changes"
+msgstr "অন্যান্য পরিবর্তন"
+
+#: sphinx/themes/basic/static/doctools.js:138
+#: sphinx/writers/html.py:462
+#: sphinx/writers/html.py:467
+msgid "Permalink to this headline"
+msgstr "এই শিরোনামের পার্মালিঙ্ক"
+
+#: sphinx/themes/basic/static/doctools.js:144
+#: sphinx/writers/html.py:80
+msgid "Permalink to this definition"
+msgstr "এই সংজ্ঞার পার্মালিঙ্ক"
+
+#: sphinx/themes/basic/static/doctools.js:173
+msgid "Hide Search Matches"
+msgstr "অনুসন্ধানের ম্যাচগুলো লুকান"
+
+#: sphinx/themes/basic/static/searchtools.js:274
+msgid "Searching"
+msgstr "অনুসন্ধান চলছে"
+
+#: sphinx/themes/basic/static/searchtools.js:279
+msgid "Preparing search..."
+msgstr "অনুসন্ধানের প্রস্তুতি চলছে..."
+
+#: sphinx/themes/basic/static/searchtools.js:352
+msgid ", in "
+msgstr ", -"
+
+#: sphinx/themes/basic/static/searchtools.js:475
+msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
+msgstr "আপনার অনুসন্ধানে কোন ফলাফল পাওয়া যায়নি। আপনার অনুসন্ধানের শব্দগুলোর সঠিক বানান ও বিভাগ নির্বাচন নিশ্চিত করুন।"
+
+#: sphinx/themes/basic/static/searchtools.js:477
+#, python-format
+msgid "Search finished, found %s page(s) matching the search query."
+msgstr "অনুসন্ধান শেষ হয়েছে, ফলাফলে %s-টি পাতা পাওয়া গেছে।"
+
+#: sphinx/writers/latex.py:187
+msgid "Release"
+msgstr "রিলিজ"
+
+#: sphinx/writers/latex.py:579
+msgid "Footnotes"
+msgstr "পাদটীকা"
+
+#: sphinx/writers/latex.py:647
+msgid "continued from previous page"
+msgstr "পূর্ববর্তী পাতা হতে চলমান"
+
+#: sphinx/writers/latex.py:652
+msgid "Continued on next page"
+msgstr "পরবর্তী পাতাতে চলমান"
+
+#: sphinx/writers/text.py:166
+#, python-format
+msgid "Platform: %s"
+msgstr "প্লাটফরম: %s"
+
+#: sphinx/writers/text.py:428
+msgid "[image]"
+msgstr "[ছবি]"
+
diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js
index dbfaab85..312d0fc7 100644
--- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js
+++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.js
@@ -1 +1 @@
-Documentation.addTranslations({"locale": "pt_BR", "plural_expr": "(n > 1)", "messages": {"Search Results": "Resultados da Pesquisa", "Preparing search...": "Preparando pesquisa...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Sua pesquisa n\u00e3o encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que voc\u00ea tenha selecionado o m\u00ednimo de categorias.", "Search finished, found %s page(s) matching the search query.": "Pesquisa finalizada, foram encontrada(s) %s p\u00e1gina(s) que conferem com o crit\u00e9rio de pesquisa.", ", in ": ", em ", "Expand sidebar": "", "Permalink to this headline": "Link permanente para este t\u00edtulo", "Searching": "Pesquisando", "Collapse sidebar": "", "Permalink to this definition": "Link permanente para esta defini\u00e7\u00e3o", "Hide Search Matches": "Esconder Resultados da Pesquisa"}}); \ No newline at end of file
+Documentation.addTranslations({"locale": "pt_BR", "plural_expr": "(n > 1)", "messages": {"Search Results": "Resultados da Pesquisa", "Preparing search...": "Preparando pesquisa...", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "Sua pesquisa n\u00e3o encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que voc\u00ea tenha selecionado o m\u00ednimo de categorias.", "Search finished, found %s page(s) matching the search query.": "Pesquisa finalizada, foram encontrada(s) %s p\u00e1gina(s) que conferem com o crit\u00e9rio de pesquisa.", ", in ": ", em ", "Expand sidebar": "Expandir painel lateral", "Permalink to this headline": "Link permanente para este t\u00edtulo", "Searching": "Pesquisando", "Collapse sidebar": "Recolher painel lateral", "Permalink to this definition": "Link permanente para esta defini\u00e7\u00e3o", "Hide Search Matches": "Esconder Resultados da Pesquisa"}}); \ No newline at end of file
diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo
index b644a114..67c1ce54 100644
--- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo
+++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo
Binary files differ
diff --git a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
index 44e2053a..7df9013e 100644
--- a/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
+++ b/sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: Sphinx 0.5\n"
"Report-Msgid-Bugs-To: roger.demetrescu@gmail.com\n"
"POT-Creation-Date: 2008-11-09 19:46+0100\n"
-"PO-Revision-Date: 2010-05-24 23:54+0200\n"
+"PO-Revision-Date: 2010-06-20 18:34-0300\n"
"Last-Translator: Roger Demetrescu <roger.demetrescu@gmail.com>\n"
"Language-Team: pt_BR <roger.demetrescu@gmail.com>\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
@@ -17,7 +17,8 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.4\n"
-#: sphinx/environment.py:106 sphinx/writers/latex.py:184
+#: sphinx/environment.py:106
+#: sphinx/writers/latex.py:184
#: sphinx/writers/manpage.py:67
#, python-format
msgid "%B %d, %Y"
@@ -41,7 +42,8 @@ msgstr "Módulo"
msgid "%b %d, %Y"
msgstr "%d/%m/%Y"
-#: sphinx/builders/html.py:285 sphinx/themes/basic/defindex.html:30
+#: sphinx/builders/html.py:285
+#: sphinx/themes/basic/defindex.html:30
msgid "General Index"
msgstr "Índice Geral"
@@ -70,9 +72,8 @@ msgid "Module author: "
msgstr "Autor do módulo: "
#: sphinx/directives/other.py:131
-#, fuzzy
msgid "Code author: "
-msgstr "Autor do módulo: "
+msgstr "Autor do código: "
#: sphinx/directives/other.py:133
msgid "Author: "
@@ -85,18 +86,21 @@ msgstr "Veja também"
#: sphinx/domains/__init__.py:253
#, python-format
msgid "%s %s"
-msgstr ""
+msgstr "%s %s"
-#: sphinx/domains/c.py:51 sphinx/domains/python.py:49
+#: sphinx/domains/c.py:51
+#: sphinx/domains/python.py:49
msgid "Parameters"
msgstr "Parâmetros"
-#: sphinx/domains/c.py:54 sphinx/domains/javascript.py:137
+#: sphinx/domains/c.py:54
+#: sphinx/domains/javascript.py:137
#: sphinx/domains/python.py:59
msgid "Returns"
msgstr "Retorna"
-#: sphinx/domains/c.py:56 sphinx/domains/python.py:61
+#: sphinx/domains/c.py:56
+#: sphinx/domains/python.py:61
msgid "Return type"
msgstr "Tipo de retorno"
@@ -125,12 +129,15 @@ msgstr "%s (tipo C)"
msgid "%s (C variable)"
msgstr "%s (variável C)"
-#: sphinx/domains/c.py:171 sphinx/domains/cpp.py:1031
-#: sphinx/domains/javascript.py:166 sphinx/domains/python.py:497
+#: sphinx/domains/c.py:171
+#: sphinx/domains/cpp.py:1031
+#: sphinx/domains/javascript.py:166
+#: sphinx/domains/python.py:497
msgid "function"
msgstr "função"
-#: sphinx/domains/c.py:172 sphinx/domains/cpp.py:1032
+#: sphinx/domains/c.py:172
+#: sphinx/domains/cpp.py:1032
msgid "member"
msgstr "membro"
@@ -138,14 +145,14 @@ msgstr "membro"
msgid "macro"
msgstr "macro"
-#: sphinx/domains/c.py:174 sphinx/domains/cpp.py:1033
+#: sphinx/domains/c.py:174
+#: sphinx/domains/cpp.py:1033
msgid "type"
msgstr "tipo"
#: sphinx/domains/c.py:175
-#, fuzzy
msgid "variable"
-msgstr "Variável"
+msgstr "variável"
#: sphinx/domains/cpp.py:876
#, python-format
@@ -167,16 +174,19 @@ msgstr "%s (membro C++)"
msgid "%s (C++ function)"
msgstr "%s (função C++)"
-#: sphinx/domains/cpp.py:1030 sphinx/domains/python.py:499
+#: sphinx/domains/cpp.py:1030
+#: sphinx/domains/python.py:499
msgid "class"
msgstr "classe"
-#: sphinx/domains/javascript.py:117 sphinx/domains/python.py:221
+#: sphinx/domains/javascript.py:117
+#: sphinx/domains/python.py:221
#, python-format
msgid "%s() (built-in function)"
msgstr "%s() (função interna)"
-#: sphinx/domains/javascript.py:118 sphinx/domains/python.py:285
+#: sphinx/domains/javascript.py:118
+#: sphinx/domains/python.py:285
#, python-format
msgid "%s() (%s method)"
msgstr "%s() (método %s)"
@@ -184,41 +194,44 @@ msgstr "%s() (método %s)"
#: sphinx/domains/javascript.py:120
#, python-format
msgid "%s (global variable or constant)"
-msgstr ""
+msgstr "%s (variável global ou constante)"
-#: sphinx/domains/javascript.py:122 sphinx/domains/python.py:323
+#: sphinx/domains/javascript.py:122
+#: sphinx/domains/python.py:323
#, python-format
msgid "%s (%s attribute)"
msgstr "%s (atributo %s)"
#: sphinx/domains/javascript.py:131
-#, fuzzy
msgid "Arguments"
msgstr "Parâmetros"
#: sphinx/domains/javascript.py:134
msgid "Throws"
-msgstr ""
+msgstr "Gera"
-#: sphinx/domains/javascript.py:167 sphinx/domains/python.py:498
+#: sphinx/domains/javascript.py:167
+#: sphinx/domains/python.py:498
msgid "data"
-msgstr ""
+msgstr "dado"
-#: sphinx/domains/javascript.py:168 sphinx/domains/python.py:504
+#: sphinx/domains/javascript.py:168
+#: sphinx/domains/python.py:504
msgid "attribute"
msgstr "atributo"
#: sphinx/domains/python.py:53
-#, fuzzy
msgid "Variables"
-msgstr "Variável"
+msgstr "Variáveis"
#: sphinx/domains/python.py:56
msgid "Raises"
msgstr "Levanta"
-#: sphinx/domains/python.py:222 sphinx/domains/python.py:279
-#: sphinx/domains/python.py:291 sphinx/domains/python.py:304
+#: sphinx/domains/python.py:222
+#: sphinx/domains/python.py:279
+#: sphinx/domains/python.py:291
+#: sphinx/domains/python.py:304
#, python-format
msgid "%s() (in module %s)"
msgstr "%s() (no módulo %s)"
@@ -228,7 +241,8 @@ msgstr "%s() (no módulo %s)"
msgid "%s (built-in variable)"
msgstr "%s (variável interna)"
-#: sphinx/domains/python.py:226 sphinx/domains/python.py:317
+#: sphinx/domains/python.py:226
+#: sphinx/domains/python.py:317
#, python-format
msgid "%s (in module %s)"
msgstr "%s (no módulo %s)"
@@ -259,14 +273,14 @@ msgid "%s() (%s static method)"
msgstr "%s() (método estático %s)"
#: sphinx/domains/python.py:308
-#, fuzzy, python-format
+#, python-format
msgid "%s() (%s.%s class method)"
-msgstr "%s() (método %s.%s)"
+msgstr "%s() (método de classe %s.%s)"
#: sphinx/domains/python.py:311
-#, fuzzy, python-format
+#, python-format
msgid "%s() (%s class method)"
-msgstr "%s() (método %s)"
+msgstr "%s() (método de classe %s)"
#: sphinx/domains/python.py:321
#, python-format
@@ -283,9 +297,8 @@ msgid "%s (module)"
msgstr "%s (módulo)"
#: sphinx/domains/python.py:429
-#, fuzzy
msgid "Python Module Index"
-msgstr "Índice do Módulo"
+msgstr "Índice de Módulos do Python"
#: sphinx/domains/python.py:430
msgid "modules"
@@ -295,47 +308,49 @@ msgstr "módulos"
msgid "Deprecated"
msgstr "Obsoleto"
-#: sphinx/domains/python.py:500 sphinx/locale/__init__.py:162
+#: sphinx/domains/python.py:500
+#: sphinx/locale/__init__.py:162
msgid "exception"
msgstr "exceção"
#: sphinx/domains/python.py:501
msgid "method"
-msgstr ""
+msgstr "método"
#: sphinx/domains/python.py:502
-#, fuzzy, python-format
+#, python-format
msgid "class method"
-msgstr "%s() (método %s)"
+msgstr "método de classe"
#: sphinx/domains/python.py:503
msgid "static method"
msgstr "método estático"
-#: sphinx/domains/python.py:505 sphinx/locale/__init__.py:158
+#: sphinx/domains/python.py:505
+#: sphinx/locale/__init__.py:158
msgid "module"
msgstr "módulo"
#: sphinx/domains/rst.py:53
#, python-format
msgid "%s (directive)"
-msgstr ""
+msgstr "%s (diretiva)"
#: sphinx/domains/rst.py:55
-#, fuzzy, python-format
+#, python-format
msgid "%s (role)"
-msgstr "%s (módulo)"
+msgstr "%s (papel)"
#: sphinx/domains/rst.py:103
msgid "directive"
-msgstr ""
+msgstr "diretiva"
#: sphinx/domains/rst.py:104
-#, fuzzy
msgid "role"
-msgstr "módulo"
+msgstr "papel"
-#: sphinx/domains/std.py:68 sphinx/domains/std.py:84
+#: sphinx/domains/std.py:68
+#: sphinx/domains/std.py:84
#, python-format
msgid "environment variable; %s"
msgstr "váriavel de ambiente; %s"
@@ -347,15 +362,15 @@ msgstr "%sopção de linha de comando; %s"
#: sphinx/domains/std.py:328
msgid "glossary term"
-msgstr ""
+msgstr "Termo de glossário"
#: sphinx/domains/std.py:329
msgid "grammar token"
-msgstr ""
+msgstr "token de gramática"
#: sphinx/domains/std.py:330
msgid "reference label"
-msgstr ""
+msgstr "rótulo de referência"
#: sphinx/domains/std.py:331
msgid "environment variable"
@@ -363,13 +378,16 @@ msgstr "váriavel de ambiente"
#: sphinx/domains/std.py:332
msgid "program option"
-msgstr ""
+msgstr "opção de programa"
-#: sphinx/domains/std.py:360 sphinx/themes/basic/genindex-single.html:11
+#: sphinx/domains/std.py:360
+#: sphinx/themes/basic/genindex-single.html:11
#: sphinx/themes/basic/genindex-split.html:11
#: sphinx/themes/basic/genindex-split.html:14
-#: sphinx/themes/basic/genindex.html:11 sphinx/themes/basic/genindex.html:14
-#: sphinx/themes/basic/genindex.html:50 sphinx/themes/basic/layout.html:125
+#: sphinx/themes/basic/genindex.html:11
+#: sphinx/themes/basic/genindex.html:14
+#: sphinx/themes/basic/genindex.html:50
+#: sphinx/themes/basic/layout.html:125
#: sphinx/writers/latex.py:173
msgid "Index"
msgstr "Índice"
@@ -378,19 +396,20 @@ msgstr "Índice"
msgid "Module Index"
msgstr "Índice do Módulo"
-#: sphinx/domains/std.py:362 sphinx/themes/basic/defindex.html:25
+#: sphinx/domains/std.py:362
+#: sphinx/themes/basic/defindex.html:25
msgid "Search Page"
msgstr "Página de Pesquisa"
#: sphinx/ext/autodoc.py:917
#, python-format
msgid " Bases: %s"
-msgstr ""
+msgstr " Bases: %s"
#: sphinx/ext/autodoc.py:950
#, python-format
msgid "alias of :class:`%s`"
-msgstr ""
+msgstr "apelido de :class:`%s`"
#: sphinx/ext/todo.py:41
msgid "Todo"
@@ -407,29 +426,28 @@ msgstr "entrada original"
#: sphinx/ext/viewcode.py:66
msgid "[source]"
-msgstr ""
+msgstr "[código fonte]"
#: sphinx/ext/viewcode.py:109
msgid "[docs]"
-msgstr ""
+msgstr "[documentos]"
#: sphinx/ext/viewcode.py:123
-#, fuzzy
msgid "Module code"
-msgstr "módulo"
+msgstr "Código do módulo"
#: sphinx/ext/viewcode.py:129
#, python-format
msgid "<h1>Source code for %s</h1>"
-msgstr ""
+msgstr "<h1>Código fonte de %s</h1>"
#: sphinx/ext/viewcode.py:156
msgid "Overview: module code"
-msgstr ""
+msgstr "Visão geral: código do módulo"
#: sphinx/ext/viewcode.py:157
msgid "<h1>All modules for which code is available</h1>"
-msgstr ""
+msgstr "<h1>Todos os módulos onde este código está disponível</h1>"
#: sphinx/locale/__init__.py:139
msgid "Attention"
@@ -506,26 +524,31 @@ msgstr "comando"
msgid "built-in function"
msgstr "função interna"
-#: sphinx/themes/agogo/layout.html:45 sphinx/themes/basic/globaltoc.html:10
+#: sphinx/themes/agogo/layout.html:45
+#: sphinx/themes/basic/globaltoc.html:10
#: sphinx/themes/basic/localtoc.html:11
msgid "Table Of Contents"
msgstr "Tabela de Conteúdo"
-#: sphinx/themes/agogo/layout.html:49 sphinx/themes/basic/layout.html:128
-#: sphinx/themes/basic/search.html:11 sphinx/themes/basic/search.html:14
+#: sphinx/themes/agogo/layout.html:49
+#: sphinx/themes/basic/layout.html:128
+#: sphinx/themes/basic/search.html:11
+#: sphinx/themes/basic/search.html:14
msgid "Search"
msgstr "Pesquisar"
-#: sphinx/themes/agogo/layout.html:52 sphinx/themes/basic/searchbox.html:15
+#: sphinx/themes/agogo/layout.html:52
+#: sphinx/themes/basic/searchbox.html:15
msgid "Go"
msgstr "Ir"
-#: sphinx/themes/agogo/layout.html:57 sphinx/themes/basic/searchbox.html:20
-#, fuzzy
+#: sphinx/themes/agogo/layout.html:57
+#: sphinx/themes/basic/searchbox.html:20
msgid "Enter search terms or a module, class or function name."
-msgstr "Informe o nome de um módulo, classe ou função."
+msgstr "Digite os termos da busca ou o nome de um módulo, classe ou função."
-#: sphinx/themes/agogo/layout.html:78 sphinx/themes/basic/sourcelink.html:14
+#: sphinx/themes/agogo/layout.html:78
+#: sphinx/themes/basic/sourcelink.html:14
msgid "Show Source"
msgstr "Exibir Fonte"
@@ -615,12 +638,8 @@ msgstr "Última atualização em %(last_updated)s."
#: sphinx/themes/basic/layout.html:189
#, python-format
-msgid ""
-"Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
-"%(sphinx_version)s."
-msgstr ""
-"Criado com <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> "
-"%(sphinx_version)s."
+msgid "Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s."
+msgstr "Criado com <a href=\"http://sphinx.pocoo.org/\">Sphinx</a> %(sphinx_version)s."
#: sphinx/themes/basic/opensearch.xml:4
#, python-format
@@ -647,10 +666,9 @@ msgstr "próximo capítulo"
msgid ""
"Please activate JavaScript to enable the search\n"
" functionality."
-msgstr ""
+msgstr "Por favor ative o JavaScript para habilitar a funcionalidade de pesquisa."
#: sphinx/themes/basic/search.html:23
-#, fuzzy
msgid ""
"From here you can search these documents. Enter your search\n"
" words into the box below and click \"search\". Note that the search\n"
@@ -658,11 +676,9 @@ msgid ""
" containing fewer words won't appear in the result list."
msgstr ""
"A partir daqui você pode pesquisar estes documentos. Preencha suas \n"
-" palavras de pesquisa na caixa abaixo e clique em \"pesquisar\". "
-"Observe que a função de pesquisa\n"
+" palavras de pesquisa na caixa abaixo e clique em \"pesquisar\". Observe que a função de pesquisa\n"
" irá pesquisar automaticamente por todas as palavras.\n"
-" Páginas contendo menos palavras não irão aparecer na lista de "
-"resultado."
+" Páginas contendo menos palavras não irão aparecer na lista de resultado."
#: sphinx/themes/basic/search.html:30
msgid "search"
@@ -713,12 +729,14 @@ msgstr "Alterações na API C"
msgid "Other changes"
msgstr "Outras alterações"
-#: sphinx/themes/basic/static/doctools.js:154 sphinx/writers/html.py:482
+#: sphinx/themes/basic/static/doctools.js:154
+#: sphinx/writers/html.py:482
#: sphinx/writers/html.py:487
msgid "Permalink to this headline"
msgstr "Link permanente para este título"
-#: sphinx/themes/basic/static/doctools.js:160 sphinx/writers/html.py:87
+#: sphinx/themes/basic/static/doctools.js:160
+#: sphinx/writers/html.py:87
msgid "Permalink to this definition"
msgstr "Link permanente para esta definição"
@@ -739,51 +757,45 @@ msgid ", in "
msgstr ", em "
#: sphinx/themes/basic/static/searchtools.js:491
-msgid ""
-"Your search did not match any documents. Please make sure that all words "
-"are spelled correctly and that you've selected enough categories."
-msgstr ""
-"Sua pesquisa não encontrou nenhum documento. Por favor assegure-se de que"
-" todas as palavras foram digitadas corretamente e de que você tenha "
-"selecionado o mínimo de categorias."
+msgid "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
+msgstr "Sua pesquisa não encontrou nenhum documento. Por favor assegure-se de que todas as palavras foram digitadas corretamente e de que você tenha selecionado o mínimo de categorias."
#: sphinx/themes/basic/static/searchtools.js:493
#, python-format
msgid "Search finished, found %s page(s) matching the search query."
-msgstr ""
-"Pesquisa finalizada, foram encontrada(s) %s página(s) que conferem com o "
-"critério de pesquisa."
+msgstr "Pesquisa finalizada, foram encontrada(s) %s página(s) que conferem com o critério de pesquisa."
#: sphinx/themes/default/static/sidebar.js:66
msgid "Expand sidebar"
-msgstr ""
+msgstr "Expandir painel lateral"
#: sphinx/themes/default/static/sidebar.js:79
#: sphinx/themes/default/static/sidebar.js:106
msgid "Collapse sidebar"
-msgstr ""
+msgstr "Recolher painel lateral"
#: sphinx/themes/haiku/layout.html:26
msgid "Contents"
-msgstr ""
+msgstr "Conteúdo"
#: sphinx/writers/latex.py:171
msgid "Release"
msgstr "Versão"
-#: sphinx/writers/latex.py:572 sphinx/writers/manpage.py:178
+#: sphinx/writers/latex.py:572
+#: sphinx/writers/manpage.py:178
msgid "Footnotes"
-msgstr ""
+msgstr "Notas de rodapé"
#: sphinx/writers/latex.py:641
msgid "continued from previous page"
-msgstr ""
+msgstr "continuação da página anterior"
#: sphinx/writers/latex.py:646
-#, fuzzy
msgid "Continued on next page"
-msgstr "Índice completo em uma página"
+msgstr "Continua na próxima página"
#: sphinx/writers/text.py:422
msgid "[image]"
msgstr "[imagem]"
+
diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py
index b8e2fded..ef92297c 100644
--- a/sphinx/pycode/__init__.py
+++ b/sphinx/pycode/__init__.py
@@ -18,6 +18,7 @@ from sphinx.errors import PycodeError
from sphinx.pycode import nodes
from sphinx.pycode.pgen2 import driver, token, tokenize, parse, literals
from sphinx.util import get_module_source
+from sphinx.util.pycompat import next
from sphinx.util.docstrings import prepare_docstring, prepare_commentdoc
@@ -98,7 +99,8 @@ class AttrDocVisitor(nodes.NodeVisitor):
if not pnode or pnode.type not in (token.INDENT, token.DEDENT):
break
prefix = pnode.get_prefix()
- prefix = prefix.decode(self.encoding)
+ if not isinstance(prefix, unicode):
+ prefix = prefix.decode(self.encoding)
docstring = prepare_commentdoc(prefix)
self.add_docstring(node, docstring)
@@ -278,7 +280,7 @@ class ModuleAnalyzer(object):
result[fullname] = (dtype, startline, endline)
expect_indent = False
if tok in ('def', 'class'):
- name = tokeniter.next()[1]
+ name = next(tokeniter)[1]
namespace.append(name)
fullname = '.'.join(namespace)
stack.append((tok, fullname, spos[0], indent))
diff --git a/sphinx/pycode/nodes.py b/sphinx/pycode/nodes.py
index e7184677..fc6eb93a 100644
--- a/sphinx/pycode/nodes.py
+++ b/sphinx/pycode/nodes.py
@@ -29,6 +29,8 @@ class BaseNode(object):
return NotImplemented
return not self._eq(other)
+ __hash__ = None
+
def get_prev_sibling(self):
"""Return previous child in parent's children, or None."""
if self.parent is None:
diff --git a/sphinx/pycode/pgen2/literals.py b/sphinx/pycode/pgen2/literals.py
index 31900291..d4893702 100644
--- a/sphinx/pycode/pgen2/literals.py
+++ b/sphinx/pycode/pgen2/literals.py
@@ -66,7 +66,7 @@ uni_escape_re = re.compile(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3}|"
def evalString(s, encoding=None):
regex = escape_re
repl = escape
- if encoding:
+ if encoding and not isinstance(s, unicode):
s = s.decode(encoding)
if s.startswith('u') or s.startswith('U'):
regex = uni_escape_re
diff --git a/sphinx/pycode/pgen2/tokenize.py b/sphinx/pycode/pgen2/tokenize.py
index 4489db89..7ad9f012 100644
--- a/sphinx/pycode/pgen2/tokenize.py
+++ b/sphinx/pycode/pgen2/tokenize.py
@@ -143,7 +143,9 @@ class TokenError(Exception): pass
class StopTokenizing(Exception): pass
-def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing
+def printtoken(type, token, scell, ecell, line): # for testing
+ srow, scol = scell
+ erow, ecol = ecell
print "%d,%d-%d,%d:\t%s\t%s" % \
(srow, scol, erow, ecol, tok_name[type], repr(token))
diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py
index e656d218..fdac4cbe 100644
--- a/sphinx/quickstart.py
+++ b/sphinx/quickstart.py
@@ -9,8 +9,9 @@
:license: BSD, see LICENSE for details.
"""
-import sys, os, time
+import sys, os, time, re
from os import path
+from codecs import open
TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
@@ -20,10 +21,23 @@ from sphinx.util.console import purple, bold, red, turquoise, \
nocolor, color_terminal
from sphinx.util import texescape
+# function to get input from terminal -- overridden by the test suite
+try:
+ # this raw_input is not converted by 2to3
+ term_input = raw_input
+except NameError:
+ term_input = input
+
PROMPT_PREFIX = '> '
-QUICKSTART_CONF = '''\
+if sys.version_info >= (3, 0):
+ # prevents that the file is checked for being written in Python 2.x syntax
+ QUICKSTART_CONF = '#!/usr/bin/env python3\n'
+else:
+ QUICKSTART_CONF = ''
+
+QUICKSTART_CONF += '''\
# -*- coding: utf-8 -*-
#
# %(project)s documentation build configuration file, created by
@@ -42,7 +56,7 @@ import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.append(os.path.abspath('.'))
+#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
@@ -186,8 +200,8 @@ html_static_path = ['%(dot)sstatic']
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = ''
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = '%(project_fn)sdoc'
@@ -279,6 +293,9 @@ epub_copyright = u'%(copyright_str)s'
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#epub_tocdup = True
'''
INTERSPHINX_CONFIG = '''
@@ -667,20 +684,22 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
prompt = purple(PROMPT_PREFIX + '%s [%s]: ' % (text, default))
else:
prompt = purple(PROMPT_PREFIX + text + ': ')
- x = raw_input(prompt)
+ x = term_input(prompt)
if default and not x:
x = default
- if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
- if TERM_ENCODING:
- x = x.decode(TERM_ENCODING)
- else:
- print turquoise('* Note: non-ASCII characters entered '
- 'and terminal encoding unknown -- assuming '
- 'UTF-8 or Latin-1.')
- try:
- x = x.decode('utf-8')
- except UnicodeDecodeError:
- x = x.decode('latin1')
+ if not isinstance(x, unicode):
+ # for Python 2.x, try to get a Unicode string out of it
+ if x.decode('ascii', 'replace').encode('ascii', 'replace') != x:
+ if TERM_ENCODING:
+ x = x.decode(TERM_ENCODING)
+ else:
+ print turquoise('* Note: non-ASCII characters entered '
+ 'and terminal encoding unknown -- assuming '
+ 'UTF-8 or Latin-1.')
+ try:
+ x = x.decode('utf-8')
+ except UnicodeDecodeError:
+ x = x.decode('latin1')
try:
x = validator(x)
except ValidationError, err:
@@ -690,6 +709,18 @@ def do_prompt(d, key, text, default=None, validator=nonempty):
d[key] = x
+if sys.version_info >= (3, 0):
+ # remove Unicode literal prefixes
+ _unicode_string_re = re.compile(r"[uU]('.*?')")
+ def _convert_python_source(source):
+ return _unicode_string_re.sub('\\1', source)
+
+ for f in ['QUICKSTART_CONF', 'EPUB_CONFIG', 'INTERSPHINX_CONFIG']:
+ globals()[f] = _convert_python_source(globals()[f])
+
+ del _unicode_string_re, _convert_python_source
+
+
def inner_main(args):
d = {}
texescape.init()
@@ -845,28 +876,28 @@ directly.'''
if d['ext_intersphinx']:
conf_text += INTERSPHINX_CONFIG
- f = open(path.join(srcdir, 'conf.py'), 'w')
- f.write(conf_text.encode('utf-8'))
+ f = open(path.join(srcdir, 'conf.py'), 'w', encoding='utf-8')
+ f.write(conf_text)
f.close()
masterfile = path.join(srcdir, d['master'] + d['suffix'])
- f = open(masterfile, 'w')
- f.write((MASTER_FILE % d).encode('utf-8'))
+ f = open(masterfile, 'w', encoding='utf-8')
+ f.write(MASTER_FILE % d)
f.close()
if d['makefile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
# use binary mode, to avoid writing \r\n on Windows
- f = open(path.join(d['path'], 'Makefile'), 'wb')
- f.write((MAKEFILE % d).encode('utf-8'))
+ f = open(path.join(d['path'], 'Makefile'), 'wb', encoding='utf-8')
+ f.write(MAKEFILE % d)
f.close()
if d['batchfile']:
d['rsrcdir'] = d['sep'] and 'source' or '.'
d['rbuilddir'] = d['sep'] and 'build' or d['dot'] + 'build'
- f = open(path.join(d['path'], 'make.bat'), 'w')
- f.write((BATCHFILE % d).encode('utf-8'))
+ f = open(path.join(d['path'], 'make.bat'), 'w', encoding='utf-8')
+ f.write(BATCHFILE % d)
f.close()
print
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 0164d757..0ea0ec48 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -105,9 +105,9 @@ class XRefRole(object):
classes = ['xref', domain, '%s-%s' % (domain, role)]
# if the first character is a bang, don't cross-reference at all
if text[0:1] == '!':
- text = utils.unescape(text)
+ text = utils.unescape(text)[1:]
if self.fix_parens:
- text, tgt = self._fix_parens(env, False, text[1:], "")
+ text, tgt = self._fix_parens(env, False, text, "")
innernode = self.innernodeclass(rawtext, text, classes=classes)
return self.result_nodes(inliner.document, env, innernode,
is_ref=False)
@@ -173,6 +173,10 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
indexnode['entries'] = [
('single', _('Python Enhancement Proposals!PEP %s') % text,
targetid, 'PEP %s' % text)]
+ anchor = ''
+ anchorindex = text.find('#')
+ if anchorindex > 0:
+ text, anchor = text[:anchorindex], text[anchorindex:]
try:
pepnum = int(text)
except ValueError:
@@ -182,12 +186,17 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.pep_base_url + 'pep-%04d' % pepnum
sn = nodes.strong('PEP '+text, 'PEP '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
+ rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
+ classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
elif typ == 'rfc':
indexnode['entries'] = [('single', 'RFC; RFC %s' % text,
targetid, 'RFC %s' % text)]
+ anchor = ''
+ anchorindex = text.find('#')
+ if anchorindex > 0:
+ text, anchor = text[:anchorindex], text[anchorindex:]
try:
rfcnum = int(text)
except ValueError:
@@ -197,7 +206,8 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
return [prb], [msg]
ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
sn = nodes.strong('RFC '+text, 'RFC '+text)
- rn = nodes.reference('', '', internal=False, refuri=ref, classes=[typ])
+ rn = nodes.reference('', '', internal=False, refuri=ref+anchor,
+ classes=[typ])
rn += sn
return [indexnode, targetnode, rn], []
@@ -205,8 +215,9 @@ def indexmarkup_role(typ, rawtext, etext, lineno, inliner,
_amp_re = re.compile(r'(?<!&)&(?![&\s])')
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
+ text = utils.unescape(text)
if typ == 'menuselection':
- text = utils.unescape(text).replace('-->', u'\N{TRIANGULAR BULLET}')
+ text = text.replace('-->', u'\N{TRIANGULAR BULLET}')
spans = _amp_re.split(text)
node = nodes.emphasis(rawtext=rawtext)
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index be8a6c15..b5381392 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -446,6 +446,7 @@
linkcolor=InnerLinkColor,filecolor=OuterLinkColor,
menucolor=OuterLinkColor,urlcolor=OuterLinkColor,
citecolor=InnerLinkColor]{hyperref}
+\RequirePackage[figure,table]{hypcap}
% From docutils.writers.latex2e
\providecommand{\DUspan}[2]{%
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index 1dac6693..e31e8544 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -14,7 +14,7 @@
{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
- (not sidebars == []) %}
+ (sidebars != []) %}
{%- set url_root = pathto('', 1) %}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index 8d1298cd..a434f3a8 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -18,6 +18,8 @@ import tempfile
import posixpath
import traceback
from os import path
+from codecs import open
+from collections import deque
import docutils
from docutils.utils import relative_path
@@ -140,8 +142,8 @@ def copy_static_entry(source, targetdir, builder, context={},
target = path.join(targetdir, path.basename(source))
if source.lower().endswith('_t') and builder.templates:
# templated!
- fsrc = open(source, 'rb')
- fdst = open(target[:-2], 'wb')
+ fsrc = open(source, 'r', encoding='utf-8')
+ fdst = open(target[:-2], 'w', encoding='utf-8')
fdst.write(builder.templates.render_string(fsrc.read(), context))
fsrc.close()
fdst.close()
@@ -162,17 +164,23 @@ def copy_static_entry(source, targetdir, builder, context={},
shutil.copytree(source, target)
+_DEBUG_HEADER = '''\
+# Sphinx version: %s
+# Docutils version: %s %s
+# Jinja2 version: %s
+'''
+
def save_traceback():
"""
Save the current exception's traceback in a temporary file.
"""
exc = traceback.format_exc()
fd, path = tempfile.mkstemp('.log', 'sphinx-err-')
- os.write(fd, '# Sphinx version: %s\n' % sphinx.__version__)
- os.write(fd, '# Docutils version: %s %s\n' % (docutils.__version__,
- docutils.__version_details__))
- os.write(fd, '# Jinja2 version: %s\n' % jinja2.__version__)
- os.write(fd, exc)
+ os.write(fd, (_DEBUG_HEADER %
+ (sphinx.__version__,
+ docutils.__version__, docutils.__version_details__,
+ jinja2.__version__)).encode('utf-8'))
+ os.write(fd, exc.encode('utf-8'))
os.close(fd)
return path
@@ -290,3 +298,38 @@ def format_exception_cut_frames(x=1):
res += tbres[-x:]
res += traceback.format_exception_only(typ, val)
return ''.join(res)
+
+class PeekableIterator(object):
+ """
+ An iterator which wraps any iterable and makes it possible to peek to see
+ what's the next item.
+ """
+ def __init__(self, iterable):
+ self.remaining = deque()
+ self._iterator = iter(iterable)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ """
+ Returns the next item from the iterator.
+ """
+ if self.remaining:
+ return self.remaining.popleft()
+ return self._iterator.next()
+
+ def push(self, item):
+ """
+ Pushes the `item` on the internal stack, it will be returned on the
+ next :meth:`next` call.
+ """
+ self.remaining.append(item)
+
+ def peek(self):
+ """
+ Returns the next item without changing the state of the iterator.
+ """
+ item = self.next()
+ self.push(item)
+ return item
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index c975b9b5..6ce6d82b 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -141,9 +141,16 @@ class TypedField(GroupedField):
par = nodes.paragraph()
par += self.make_xref(self.rolename, domain, fieldarg, nodes.strong)
if fieldarg in types:
- typename = u''.join(n.astext() for n in types[fieldarg])
par += nodes.Text(' (')
- par += self.make_xref(self.typerolename, domain, typename)
+ # NOTE: using .pop() here to prevent a single type node to be
+ # inserted twice into the doctree, which leads to
+ # inconsistencies later when references are resolved
+ fieldtype = types.pop(fieldarg)
+ if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
+ typename = u''.join(n.astext() for n in fieldtype)
+ par += self.make_xref(self.typerolename, domain, typename)
+ else:
+ par += fieldtype
par += nodes.Text(')')
par += nodes.Text(' -- ')
par += content
@@ -222,7 +229,10 @@ class DocFieldTransformer(object):
if is_typefield:
# filter out only inline nodes; others will result in invalid
# markup being written out
- content = filter(lambda n: isinstance(n, nodes.Inline), content)
+ content = filter(
+ lambda n: isinstance(n, nodes.Inline) or
+ isinstance(n, nodes.Text),
+ content)
if content:
types.setdefault(typename, {})[fieldarg] = content
continue
diff --git a/sphinx/util/jsonimpl.py b/sphinx/util/jsonimpl.py
new file mode 100644
index 00000000..fda85b5e
--- /dev/null
+++ b/sphinx/util/jsonimpl.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.util.jsonimpl
+ ~~~~~~~~~~~~~~~~~~~~
+
+ JSON serializer implementation wrapper.
+
+ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import UserString
+
+try:
+ import json
+ # json-py's json module has not JSONEncoder; this will raise AttributeError
+ # if json-py is imported instead of the built-in json module
+ JSONEncoder = json.JSONEncoder
+except (ImportError, AttributeError):
+ try:
+ import simplejson as json
+ JSONEncoder = json.JSONEncoder
+ except ImportError:
+ json = None
+ JSONEncoder = object
+
+
+class SphinxJSONEncoder(JSONEncoder):
+ """JSONEncoder subclass that forces translation proxies."""
+ def default(self, obj):
+ if isinstance(obj, UserString.UserString):
+ return unicode(obj)
+ return JSONEncoder.default(self, obj)
+
+
+def dump(obj, fp, *args, **kwds):
+ kwds['cls'] = SphinxJSONEncoder
+ return json.dump(obj, fp, *args, **kwds)
+
+def dumps(obj, *args, **kwds):
+ kwds['cls'] = SphinxJSONEncoder
+ return json.dumps(obj, *args, **kwds)
+
+def load(*args, **kwds):
+ return json.load(*args, **kwds)
+
+def loads(*args, **kwds):
+ return json.loads(*args, **kwds)
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 3728eac9..c2f96f07 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -10,11 +10,11 @@
"""
import re
-import types
from docutils import nodes
from sphinx import addnodes
+from sphinx.util.pycompat import class_types
# \x00 means the "<" was backslash-escaped
@@ -129,7 +129,7 @@ def _new_traverse(self, condition=None,
if include_self and descend and not siblings and not ascend:
if condition is None:
return self._all_traverse([])
- elif isinstance(condition, (types.ClassType, type)):
+ elif isinstance(condition, class_types):
return self._fast_traverse(condition, [])
return self._old_traverse(condition, include_self,
descend, siblings, ascend)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index beab38cb..9943b207 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -11,6 +11,7 @@
import os
import re
+import sys
import time
import errno
import shutil
@@ -124,7 +125,10 @@ no_fn_re = re.compile(r'[^a-zA-Z0-9_-]')
def make_filename(string):
return no_fn_re.sub('', string)
-
-def ustrftime(format, *args):
- # strftime for unicode strings
- return time.strftime(unicode(format).encode('utf-8'), *args).decode('utf-8')
+if sys.version_info < (3, 0):
+ def ustrftime(format, *args):
+ # strftime for unicode strings
+ return time.strftime(unicode(format).encode('utf-8'), *args) \
+ .decode('utf-8')
+else:
+ ustrftime = time.strftime
diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py
index bdd9507d..5f23bbe1 100644
--- a/sphinx/util/pycompat.py
+++ b/sphinx/util/pycompat.py
@@ -12,6 +12,65 @@
import sys
import codecs
import encodings
+import re
+
+try:
+ from types import ClassType
+ class_types = (type, ClassType)
+except ImportError:
+ # Python 3
+ class_types = (type,)
+
+
+# the ubiquitous "bytes" helper function
+if sys.version_info >= (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
+
+# Support for running 2to3 over config files
+
+if sys.version_info < (3, 0):
+ # no need to refactor on 2.x versions
+ convert_with_2to3 = None
+else:
+ def convert_with_2to3(filepath):
+ from lib2to3.refactor import RefactoringTool, get_fixers_from_package
+ from lib2to3.pgen2.parse import ParseError
+ fixers = get_fixers_from_package('lib2to3.fixes')
+ refactoring_tool = RefactoringTool(fixers)
+ source = refactoring_tool._read_python_source(filepath)[0]
+ try:
+ tree = refactoring_tool.refactor_string(source, 'conf.py')
+ except ParseError, err:
+ # do not propagate lib2to3 exceptions
+ lineno, offset = err.context[1]
+ # try to match ParseError details with SyntaxError details
+ raise SyntaxError(err.msg, (filepath, lineno, offset, err.value))
+ return unicode(tree)
+
+
+try:
+ base_exception = BaseException
+except NameError:
+ base_exception = Exception
+
+
+try:
+ next = next
+except NameError:
+ # this is on Python 2, where the method is called "next" (it is refactored
+ # to __next__ by 2to3, but in that case never executed)
+ def next(iterator):
+ return iterator.next()
+
+
+try:
+ bytes = bytes
+except NameError:
+ bytes = str
try:
diff --git a/sphinx/versioning.py b/sphinx/versioning.py
new file mode 100644
index 00000000..d0ea18a7
--- /dev/null
+++ b/sphinx/versioning.py
@@ -0,0 +1,148 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.versioning
+ ~~~~~~~~~~~~~~~~~
+
+ Implements the low-level algorithms Sphinx uses for the versioning of
+ doctrees.
+
+ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+from uuid import uuid4
+from itertools import product
+try:
+ from itertools import izip_longest as zip_longest
+except ImportError:
+ from itertools import zip_longest
+from difflib import SequenceMatcher
+
+from sphinx.util import PeekableIterator
+
+def add_uids(doctree, condition):
+ """
+ Adds a unique id to every node in the `doctree` which matches the condition
+ and yields it.
+
+ :param doctree:
+ A :class:`docutils.nodes.document` instance.
+
+ :param condition:
+ A callable which returns either ``True`` or ``False`` for a given node.
+ """
+ for node in doctree.traverse(condition):
+ node.uid = uuid4().hex
+ yield node
+
+def merge_node(old, new):
+ """
+ Merges the `old` node with the `new` one, if it's successful the `new` node
+ get's the unique identifier of the `new` one and ``True`` is returned. If
+ the merge is unsuccesful ``False`` is returned.
+ """
+ equals, changed, replaced = make_diff(old.rawsource,
+ new.rawsource)
+ if equals or changed:
+ new.uid = old.uid
+ return True
+ return False
+
+def merge_doctrees(old, new, condition):
+ """
+ Merges the `old` doctree with the `new` one while looking at nodes matching
+ the `condition`.
+
+ Each node which replaces another one or has been added to the `new` doctree
+ will be yielded.
+
+ :param condition:
+ A callable which returns either ``True`` or ``False`` for a given node.
+ """
+ old_iter = PeekableIterator(old.traverse(condition))
+ new_iter = PeekableIterator(new.traverse(condition))
+ old_nodes = []
+ new_nodes = []
+ for old_node, new_node in zip_longest(old_iter, new_iter):
+ if old_node is None:
+ new_nodes.append(new_node)
+ continue
+ if new_node is None:
+ old_nodes.append(old_node)
+ continue
+ if not merge_node(old_node, new_node):
+ if old_nodes:
+ for i, very_old_node in enumerate(old_nodes):
+ if merge_node(very_old_node, new_node):
+ del old_nodes[i]
+ # If the last identified node which has not matched the
+ # unidentified node matches the current one, we have to
+ # assume that the last unidentified one has been
+ # inserted.
+ #
+ # As the required time multiplies with each insert, we
+ # want to avoid that by checking if the next
+ # unidentified node matches the current identified one
+ # and if so we make a shift.
+ if i == len(old_nodes):
+ next_new_node = new_iter.next()
+ if not merge_node(old_node, next_new_node):
+ new_iter.push(next_new_node)
+ break
+ else:
+ old_nodes.append(old_node)
+ new_nodes.append(new_node)
+ for (i, new_node), (j, old_node) in product(enumerate(new_nodes),
+ enumerate(old_nodes)):
+ if merge_node(old_node, new_node):
+ del new_nodes[i]
+ del old_nodes[j]
+ for node in new_nodes:
+ node.uid = uuid4().hex
+ # Yielding the new nodes here makes it possible to use this generator
+ # like add_uids
+ yield node
+
+def make_diff(old, new):
+ """
+ Takes two strings `old` and `new` and returns a :class:`tuple` of boolean
+ values ``(equals, changed, replaced)``.
+
+ equals
+
+ ``True`` if the `old` string and the `new` one are equal.
+
+ changed
+
+ ``True`` if the `new` string is a changed version of the `old` one.
+
+ replaced
+
+ ``True`` if the `new` string and the `old` string are totally
+ different.
+
+ .. note:: This assumes the two strings are human readable text or at least
+ something very similar to that, otherwise it can not detect if
+ the string has been changed or replaced. In any case the
+ detection should not be considered reliable.
+ """
+ if old == new:
+ return True, False, False
+ if new in old or levenshtein_distance(old, new) / (len(old) / 100.0) < 70:
+ return False, True, False
+ return False, False, True
+
+def levenshtein_distance(a, b):
+ if len(a) < len(b):
+ a, b = b, a
+ if not a:
+ return len(b)
+ previous_row = xrange(len(b) + 1)
+ for i, column1 in enumerate(a):
+ current_row = [i + 1]
+ for j, column2 in enumerate(b):
+ insertions = previous_row[j + 1] + 1
+ deletions = current_row[j] + 1
+ substitutions = previous_row[j] + (column1 != column2)
+ current_row.append(min(insertions, deletions, substitutions))
+ previous_row = current_row
+ return previous_row[-1]
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index ea12f003..de0bbdf2 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -224,6 +224,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.top_sectionlevel = 1
self.next_section_ids = set()
+ self.next_figure_ids = set()
+ self.next_table_ids = set()
# flags
self.verbatim = None
self.in_title = 0
@@ -250,7 +252,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'\\label{%s}' % self.idescape(id)
def hyperlink(self, id):
- return '\\hyperref[%s]{' % (self.idescape(id))
+ return '{\\hyperref[%s]{' % (self.idescape(id))
def hyperpageref(self, id):
return '\\autopageref*{%s}' % (self.idescape(id))
@@ -314,7 +316,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# ... and all others are the appendices
self.body.append(u'\n\\appendix\n')
self.first_document = -1
- if node.has_key('docname'):
+ if 'docname' in node:
self.body.append(self.hypertarget(':doc'))
# "- 1" because the level is increased before the title is visited
self.sectionlevel = self.top_sectionlevel - 1
@@ -633,7 +635,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
if self.table.longtable and self.table.caption is not None:
self.body.append(u'\\caption{%s} \\\\\n' % self.table.caption)
-
+ if self.table.caption is not None:
+ for id in self.next_table_ids:
+ self.body.append(self.hypertarget(id, anchor=False))
+ self.next_table_ids.clear()
if self.table.longtable:
self.body.append('\\hline\n')
self.body.append('\\endfirsthead\n\n')
@@ -694,7 +699,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.table.rowcount += 1
def visit_entry(self, node):
- if node.has_key('morerows') or node.has_key('morecols'):
+ if 'morerows' in node or 'morecols' in node:
raise UnsupportedError('%s:%s: column or row spanning cells are '
'not yet implemented.' %
(self.curfilestack[-1], node.line or ''))
@@ -751,7 +756,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_term(self, node):
ctx = '}] \\leavevmode'
- if node.has_key('ids') and node['ids']:
+ if node.get('ids'):
ctx += self.hypertarget(node['ids'][0])
self.body.append('\\item[{')
self.context.append(ctx)
@@ -833,20 +838,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
post = []
include_graphics_options = []
is_inline = self.is_inline(node)
- if attrs.has_key('scale'):
+ if 'scale' in attrs:
# Could also be done with ``scale`` option to
# ``\includegraphics``; doing it this way for consistency.
pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
post.append('}')
- if attrs.has_key('width'):
+ if 'width' in attrs:
w = self.latex_image_length(attrs['width'])
if w:
include_graphics_options.append('width=%s' % w)
- if attrs.has_key('height'):
+ if 'height' in attrs:
h = self.latex_image_length(attrs['height'])
if h:
include_graphics_options.append('height=%s' % h)
- if attrs.has_key('align'):
+ if 'align' in attrs:
align_prepost = {
# By default latex aligns the top of an image.
(1, 'top'): ('', ''),
@@ -887,13 +892,17 @@ class LaTeXTranslator(nodes.NodeVisitor):
pass
def visit_figure(self, node):
- if node.has_key('width') and node.get('align', '') in ('left', 'right'):
+ ids = ''
+ for id in self.next_figure_ids:
+ ids += self.hypertarget(id, anchor=False)
+ self.next_figure_ids.clear()
+ if 'width' in node and node.get('align', '') in ('left', 'right'):
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
(node['align'] == 'right' and 'r' or 'l',
node['width']))
- self.context.append('\\end{wrapfigure}\n')
+ self.context.append(ids + '\\end{wrapfigure}\n')
else:
- if (not node.attributes.has_key('align') or
+ if (not 'align' in node.attributes or
node.attributes['align'] == 'center'):
# centering does not add vertical space like center.
align = '\n\\centering'
@@ -903,7 +912,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
align = '\\begin{flush%s}' % node.attributes['align']
align_end = '\\end{flush%s}' % node.attributes['align']
self.body.append('\\begin{figure}[htbp]%s\n' % align)
- self.context.append('%s\\end{figure}\n' % align_end)
+ self.context.append(ids + align_end + '\\end{figure}\n')
def depart_figure(self, node):
self.body.append(self.context.pop())
@@ -963,8 +972,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def add_target(id):
# indexing uses standard LaTeX index markup, so the targets
# will be generated differently
- if not id.startswith('index-'):
- self.body.append(self.hypertarget(id))
+ if id.startswith('index-'):
+ return
+ # do not generate \phantomsection in \section{}
+ anchor = not self.in_title
+ self.body.append(self.hypertarget(id, anchor=anchor))
# postpone the labels until after the sectioning command
parindex = node.parent.index(node)
@@ -980,6 +992,20 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.next_section_ids.add(node['refid'])
self.next_section_ids.update(node['ids'])
return
+ elif isinstance(next, nodes.figure):
+ # labels for figures go in the figure body, not before
+ if node.get('refid'):
+ self.next_figure_ids.add(node['refid'])
+ self.next_figure_ids.update(node['ids'])
+ return
+ elif isinstance(next, nodes.table):
+ # same for tables, but only if they have a caption
+ for n in node:
+ if isinstance(n, nodes.title):
+ if node.get('refid'):
+ self.next_table_ids.add(node['refid'])
+ self.next_table_ids.update(node['ids'])
+ return
except IndexError:
pass
if 'refuri' in node:
@@ -1048,9 +1074,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
id = self.curfilestack[-1] + ':' + uri[1:]
self.body.append(self.hyperlink(id))
if self.builder.config.latex_show_pagerefs:
- self.context.append('} (%s)' % self.hyperpageref(id))
+ self.context.append('}} (%s)' % self.hyperpageref(id))
else:
- self.context.append('}')
+ self.context.append('}}')
elif uri.startswith('%'):
# references to documents or labels inside documents
hashindex = uri.find('#')
@@ -1064,12 +1090,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
if len(node) and hasattr(node[0], 'attributes') and \
'std-term' in node[0].get('classes', []):
# don't add a pageref for glossary terms
- self.context.append('}')
+ self.context.append('}}')
else:
if self.builder.config.latex_show_pagerefs:
- self.context.append('} (%s)' % self.hyperpageref(id))
+ self.context.append('}} (%s)' % self.hyperpageref(id))
else:
- self.context.append('}')
+ self.context.append('}}')
elif uri.startswith('@token'):
if self.in_production_list:
self.body.append('\\token{')
@@ -1151,7 +1177,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.no_contractions -= 1
if self.in_title:
self.body.append(r'\texttt{%s}' % content)
- elif node.has_key('role') and node['role'] == 'samp':
+ elif node.get('role') == 'samp':
self.body.append(r'\samp{%s}' % content)
else:
self.body.append(r'\code{%s}' % content)
@@ -1180,10 +1206,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
code = self.verbatim.rstrip('\n')
lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
- if node.has_key('language'):
+ if 'language' in node:
# code-block directives
lang = node['language']
- if node.has_key('linenos'):
+ if 'linenos' in node:
linenos = node['linenos']
hlcode = self.highlighter.highlight_block(code, lang, linenos)
# workaround for Unicode issue
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 98528d5b..b28b2379 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -390,7 +390,7 @@ class TextTranslator(nodes.NodeVisitor):
self.add_text(''.join(out) + '\n')
def writerow(row):
- lines = map(None, *row)
+ lines = zip(*row)
for line in lines:
out = ['|']
for i, cell in enumerate(line):
diff --git a/tests/etree13/ElementTree.py b/tests/etree13/ElementTree.py
index d3732504..f459c7f8 100644
--- a/tests/etree13/ElementTree.py
+++ b/tests/etree13/ElementTree.py
@@ -1425,12 +1425,16 @@ class XMLParser(object):
err.position = value.lineno, value.offset
raise err
- def _fixtext(self, text):
- # convert text string to ascii, if possible
- try:
- return text.encode("ascii")
- except UnicodeError:
+ if sys.version_info >= (3, 0):
+ def _fixtext(self, text):
return text
+ else:
+ def _fixtext(self, text):
+ # convert text string to ascii, if possible
+ try:
+ return text.encode("ascii")
+ except UnicodeError:
+ return text
def _fixname(self, key):
# expand qname, and convert name string to ascii, if possible
diff --git a/tests/path.py b/tests/path.py
index ceb895f5..df96bce4 100644
--- a/tests/path.py
+++ b/tests/path.py
@@ -1,953 +1,200 @@
-""" path.py - An object representing a path to a file or directory.
-
-Example:
-
-from path import path
-d = path('/home/guido/bin')
-for f in d.files('*.py'):
- f.chmod(0755)
-
-This module requires Python 2.2 or later.
-
-
-URL: http://www.jorendorff.com/articles/python/path
-Author: Jason Orendorff <jason.orendorff\x40gmail\x2ecom> (and others - see the url!)
-Date: 9 Mar 2007
+#!/usr/bin/env python
+# coding: utf-8
"""
+ path
+ ~~~~
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import os
+import sys
+import shutil
+from codecs import open
-# TODO
-# - Tree-walking functions don't avoid symlink loops. Matt Harrison
-# sent me a patch for this.
-# - Bug in write_text(). It doesn't support Universal newline mode.
-# - Better error message in listdir() when self isn't a
-# directory. (On Windows, the error message really sucks.)
-# - Make sure everything has a good docstring.
-# - Add methods for regex find and replace.
-# - guess_content_type() method?
-# - Perhaps support arguments to touch().
-
-from __future__ import generators
-
-import sys, warnings, os, fnmatch, glob, shutil, codecs
-
-__version__ = '2.2'
-__all__ = ['path']
-
-# Platform-specific support for path.owner
-if os.name == 'nt':
- try:
- import win32security
- except ImportError:
- win32security = None
-else:
- try:
- import pwd
- except ImportError:
- pwd = None
-
-# Pre-2.3 support. Are unicode filenames supported?
-_base = str
-_getcwd = os.getcwd
-try:
- if os.path.supports_unicode_filenames:
- _base = unicode
- _getcwd = os.getcwdu
-except AttributeError:
- pass
-
-# Pre-2.3 workaround for booleans
-try:
- True, False
-except NameError:
- True, False = 1, 0
-
-# Pre-2.3 workaround for basestring.
-try:
- basestring
-except NameError:
- basestring = (str, unicode)
-
-# Universal newline support
-_textmode = 'r'
-if hasattr(file, 'newlines'):
- _textmode = 'U'
-
-
-class TreeWalkWarning(Warning):
- pass
-
-class path(_base):
- """ Represents a filesystem path.
-
- For documentation on individual methods, consult their
- counterparts in os.path.
- """
-
- # --- Special Python methods.
- def __repr__(self):
- return 'path(%s)' % _base.__repr__(self)
+FILESYSTEMENCODING = sys.getfilesystemencoding() or sys.getdefaultencoding()
- # Adding a path and a string yields a path.
- def __add__(self, more):
- try:
- resultStr = _base.__add__(self, more)
- except TypeError: #Python bug
- resultStr = NotImplemented
- if resultStr is NotImplemented:
- return resultStr
- return self.__class__(resultStr)
-
- def __radd__(self, other):
- if isinstance(other, basestring):
- return self.__class__(other.__add__(self))
- else:
- return NotImplemented
- # The / operator joins paths.
- def __div__(self, rel):
- """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
+class path(str):
+ """
+ Represents a path which behaves like a string.
+ """
+ if sys.version_info < (3, 0):
+ def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'):
+ if isinstance(s, unicode):
+ s = s.encode(encoding, errors=errors)
+ return str.__new__(cls, s)
+ return str.__new__(cls, s)
- Join two path components, adding a separator character if
- needed.
+ @property
+ def parent(self):
"""
- return self.__class__(os.path.join(self, rel))
-
- # Make the / operator work even when true division is enabled.
- __truediv__ = __div__
-
- def getcwd(cls):
- """ Return the current working directory as a path object. """
- return cls(_getcwd())
- getcwd = classmethod(getcwd)
-
-
- # --- Operations on path strings.
-
- isabs = os.path.isabs
- def abspath(self): return self.__class__(os.path.abspath(self))
- def normcase(self): return self.__class__(os.path.normcase(self))
- def normpath(self): return self.__class__(os.path.normpath(self))
- def realpath(self): return self.__class__(os.path.realpath(self))
- def expanduser(self): return self.__class__(os.path.expanduser(self))
- def expandvars(self): return self.__class__(os.path.expandvars(self))
- def dirname(self): return self.__class__(os.path.dirname(self))
- basename = os.path.basename
-
- def expand(self):
- """ Clean up a filename by calling expandvars(),
- expanduser(), and normpath() on it.
-
- This is commonly everything needed to clean up a filename
- read from a configuration file, for example.
+ The name of the directory the file or directory is in.
"""
- return self.expandvars().expanduser().normpath()
-
- def _get_namebase(self):
- base, ext = os.path.splitext(self.name)
- return base
-
- def _get_ext(self):
- f, ext = os.path.splitext(_base(self))
- return ext
-
- def _get_drive(self):
- drive, r = os.path.splitdrive(self)
- return self.__class__(drive)
-
- parent = property(
- dirname, None, None,
- """ This path's parent directory, as a new path object.
-
- For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
- """)
-
- name = property(
- basename, None, None,
- """ The name of this file or directory without the full path.
-
- For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
- """)
+ return self.__class__(os.path.dirname(self))
- namebase = property(
- _get_namebase, None, None,
- """ The same as path.name, but with one file extension stripped off.
-
- For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
- but path('/home/guido/python.tar.gz').namebase == 'python.tar'
- """)
-
- ext = property(
- _get_ext, None, None,
- """ The file extension, for example '.py'. """)
-
- drive = property(
- _get_drive, None, None,
- """ The drive specifier, for example 'C:'.
- This is always empty on systems that don't use drive specifiers.
- """)
-
- def splitpath(self):
- """ p.splitpath() -> Return (p.parent, p.name). """
- parent, child = os.path.split(self)
- return self.__class__(parent), child
-
- def splitdrive(self):
- """ p.splitdrive() -> Return (p.drive, <the rest of p>).
-
- Split the drive specifier from this path. If there is
- no drive specifier, p.drive is empty, so the return value
- is simply (path(''), p). This is always the case on Unix.
+ def abspath(self):
"""
- drive, rel = os.path.splitdrive(self)
- return self.__class__(drive), rel
-
- def splitext(self):
- """ p.splitext() -> Return (p.stripext(), p.ext).
-
- Split the filename extension from this path and return
- the two parts. Either part may be empty.
-
- The extension is everything from '.' to the end of the
- last path segment. This has the property that if
- (a, b) == p.splitext(), then a + b == p.
+ Returns the absolute path.
"""
- filename, ext = os.path.splitext(self)
- return self.__class__(filename), ext
-
- def stripext(self):
- """ p.stripext() -> Remove one file extension from the path.
+ return self.__class__(os.path.abspath(self))
- For example, path('/home/guido/python.tar.gz').stripext()
- returns path('/home/guido/python.tar').
+ def isabs(self):
"""
- return self.splitext()[0]
-
- if hasattr(os.path, 'splitunc'):
- def splitunc(self):
- unc, rest = os.path.splitunc(self)
- return self.__class__(unc), rest
-
- def _get_uncshare(self):
- unc, r = os.path.splitunc(self)
- return self.__class__(unc)
-
- uncshare = property(
- _get_uncshare, None, None,
- """ The UNC mount point for this path.
- This is empty for paths on local drives. """)
-
- def joinpath(self, *args):
- """ Join two or more path components, adding a separator
- character (os.sep) if needed. Returns a new path
- object.
- """
- return self.__class__(os.path.join(self, *args))
-
- def splitall(self):
- r""" Return a list of the path components in this path.
-
- The first item in the list will be a path. Its value will be
- either os.curdir, os.pardir, empty, or the root directory of
- this path (for example, '/' or 'C:\\'). The other items in
- the list will be strings.
-
- path.path.joinpath(*result) will yield the original path.
- """
- parts = []
- loc = self
- while loc != os.curdir and loc != os.pardir:
- prev = loc
- loc, child = prev.splitpath()
- if loc == prev:
- break
- parts.append(child)
- parts.append(loc)
- parts.reverse()
- return parts
-
- def relpath(self):
- """ Return this path as a relative path,
- based from the current working directory.
- """
- cwd = self.__class__(os.getcwd())
- return cwd.relpathto(self)
-
- def relpathto(self, dest):
- """ Return a relative path from self to dest.
-
- If there is no relative path from self to dest, for example if
- they reside on different drives in Windows, then this returns
- dest.abspath().
- """
- origin = self.abspath()
- dest = self.__class__(dest).abspath()
-
- orig_list = origin.normcase().splitall()
- # Don't normcase dest! We want to preserve the case.
- dest_list = dest.splitall()
-
- if orig_list[0] != os.path.normcase(dest_list[0]):
- # Can't get here from there.
- return dest
-
- # Find the location where the two paths start to differ.
- i = 0
- for start_seg, dest_seg in zip(orig_list, dest_list):
- if start_seg != os.path.normcase(dest_seg):
- break
- i += 1
-
- # Now i is the point where the two paths diverge.
- # Need a certain number of "os.pardir"s to work up
- # from the origin to the point of divergence.
- segments = [os.pardir] * (len(orig_list) - i)
- # Need to add the diverging part of dest_list.
- segments += dest_list[i:]
- if len(segments) == 0:
- # If they happen to be identical, use os.curdir.
- relpath = os.curdir
- else:
- relpath = os.path.join(*segments)
- return self.__class__(relpath)
-
- # --- Listing, searching, walking, and matching
+ Returns ``True`` if the path is absolute.
+ """
+ return os.path.isabs(self)
- def listdir(self, pattern=None):
- """ D.listdir() -> List of items in this directory.
+ def isdir(self):
+ """
+ Returns ``True`` if the path is a directory.
+ """
+ return os.path.isdir(self)
- Use D.files() or D.dirs() instead if you want a listing
- of just files or just subdirectories.
+ def isfile(self):
+ """
+ Returns ``True`` if the path is a file.
+ """
+ return os.path.isfile(self)
- The elements of the list are path objects.
+ def islink(self):
+ """
+ Returns ``True`` if the path is a symbolic link.
+ """
+ return os.path.islink(self)
- With the optional 'pattern' argument, this only lists
- items whose names match the given pattern.
+ def ismount(self):
+ """
+ Returns ``True`` if the path is a mount point.
"""
- names = os.listdir(self)
- if pattern is not None:
- names = fnmatch.filter(names, pattern)
- return [self / child for child in names]
+ return os.path.ismount(self)
- def dirs(self, pattern=None):
- """ D.dirs() -> List of this directory's subdirectories.
+ def rmtree(self, ignore_errors=False, onerror=None):
+ """
+ Removes the file or directory and any files or directories it may
+ contain.
- The elements of the list are path objects.
- This does not walk recursively into subdirectories
- (but see path.walkdirs).
+ :param ignore_errors:
+ If ``True`` errors are silently ignored, otherwise an exception
+ is raised in case an error occurs.
- With the optional 'pattern' argument, this only lists
- directories whose names match the given pattern. For
- example, d.dirs('build-*').
+ :param onerror:
+ A callback which gets called with the arguments `func`, `path` and
+ `exc_info`. `func` is one of :func:`os.listdir`, :func:`os.remove`
+ or :func:`os.rmdir`. `path` is the argument to the function which
+ caused it to fail and `exc_info` is a tuple as returned by
+ :func:`sys.exc_info`.
"""
- return [p for p in self.listdir(pattern) if p.isdir()]
+ shutil.rmtree(self, ignore_errors=ignore_errors, onerror=onerror)
- def files(self, pattern=None):
- """ D.files() -> List of the files in this directory.
+ def copytree(self, destination, symlinks=False, ignore=None):
+ """
+ Recursively copy a directory to the given `destination`. If the given
+ `destination` does not exist it will be created.
- The elements of the list are path objects.
- This does not walk into subdirectories (see path.walkfiles).
+ :param symlinks:
+ If ``True`` symbolic links in the source tree result in symbolic
+ links in the destination tree otherwise the contents of the files
+ pointed to by the symbolic links are copied.
- With the optional 'pattern' argument, this only lists files
- whose names match the given pattern. For example,
- d.files('*.pyc').
+ :param ignore:
+ A callback which gets called with the path of the directory being
+ copied and a list of paths as returned by :func:`os.listdir`.
"""
+ shutil.copytree(self, destination, symlinks=symlinks, ignore=ignore)
- return [p for p in self.listdir(pattern) if p.isfile()]
-
- def walk(self, pattern=None, errors='strict'):
- """ D.walk() -> iterator over files and subdirs, recursively.
+ def movetree(self, destination):
+ """
+ Recursively move the file or directory to the given `destination`
+ similar to the Unix "mv" command.
- The iterator yields path objects naming each child item of
- this directory and its descendants. This requires that
- D.isdir().
+ If the `destination` is a file it may be overwritten depending on the
+ :func:`os.rename` semantics.
+ """
+ shutil.move(self, destination)
- This performs a depth-first traversal of the directory tree.
- Each directory is returned just before all its children.
+ move = movetree
- The errors= keyword argument controls behavior when an
- error occurs. The default is 'strict', which causes an
- exception. The other allowed values are 'warn', which
- reports the error via warnings.warn(), and 'ignore'.
+ def unlink(self):
"""
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
-
- try:
- childList = self.listdir()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
-
- for child in childList:
- if pattern is None or child.fnmatch(pattern):
- yield child
- try:
- isdir = child.isdir()
- except Exception:
- if errors == 'ignore':
- isdir = False
- elif errors == 'warn':
- warnings.warn(
- "Unable to access '%s': %s"
- % (child, sys.exc_info()[1]),
- TreeWalkWarning)
- isdir = False
- else:
- raise
-
- if isdir:
- for item in child.walk(pattern, errors):
- yield item
-
- def walkdirs(self, pattern=None, errors='strict'):
- """ D.walkdirs() -> iterator over subdirs, recursively.
-
- With the optional 'pattern' argument, this yields only
- directories whose names match the given pattern. For
- example, mydir.walkdirs('*test') yields only directories
- with names ending in 'test'.
-
- The errors= keyword argument controls behavior when an
- error occurs. The default is 'strict', which causes an
- exception. The other allowed values are 'warn', which
- reports the error via warnings.warn(), and 'ignore'.
- """
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
+ Removes a file.
+ """
+ os.unlink(self)
+ def write_text(self, text, **kwargs):
+ """
+ Writes the given `text` to the file.
+ """
+ f = open(self, 'w', **kwargs)
try:
- dirs = self.dirs()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
-
- for child in dirs:
- if pattern is None or child.fnmatch(pattern):
- yield child
- for subsubdir in child.walkdirs(pattern, errors):
- yield subsubdir
-
- def walkfiles(self, pattern=None, errors='strict'):
- """ D.walkfiles() -> iterator over files in D, recursively.
-
- The optional argument, pattern, limits the results to files
- with names that match the pattern. For example,
- mydir.walkfiles('*.tmp') yields only files with the .tmp
- extension.
- """
- if errors not in ('strict', 'warn', 'ignore'):
- raise ValueError("invalid errors parameter")
+ f.write(text)
+ finally:
+ f.close()
+ def text(self, **kwargs):
+ """
+ Returns the text in the file.
+ """
+ f = open(self, mode='U', **kwargs)
try:
- childList = self.listdir()
- except Exception:
- if errors == 'ignore':
- return
- elif errors == 'warn':
- warnings.warn(
- "Unable to list directory '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- return
- else:
- raise
-
- for child in childList:
- try:
- isfile = child.isfile()
- isdir = not isfile and child.isdir()
- except:
- if errors == 'ignore':
- continue
- elif errors == 'warn':
- warnings.warn(
- "Unable to access '%s': %s"
- % (self, sys.exc_info()[1]),
- TreeWalkWarning)
- continue
- else:
- raise
-
- if isfile:
- if pattern is None or child.fnmatch(pattern):
- yield child
- elif isdir:
- for f in child.walkfiles(pattern, errors):
- yield f
-
- def fnmatch(self, pattern):
- """ Return True if self.name matches the given pattern.
-
- pattern - A filename pattern with wildcards,
- for example '*.py'.
- """
- return fnmatch.fnmatch(self.name, pattern)
-
- def glob(self, pattern):
- """ Return a list of path objects that match the pattern.
-
- pattern - a path relative to this directory, with wildcards.
-
- For example, path('/users').glob('*/bin/*') returns a list
- of all the files users have in their bin directories.
- """
- cls = self.__class__
- return [cls(s) for s in glob.glob(_base(self / pattern))]
-
-
- # --- Reading or writing an entire file at once.
-
- def open(self, mode='r'):
- """ Open this file. Return a file object. """
- return file(self, mode)
+ return f.read()
+ finally:
+ f.close()
def bytes(self):
- """ Open this file, read all bytes, return them as a string. """
- f = self.open('rb')
+ """
+ Returns the bytes in the file.
+ """
+ f = open(self, mode='rb')
try:
return f.read()
finally:
f.close()
def write_bytes(self, bytes, append=False):
- """ Open this file and write the given bytes to it.
+ """
+ Writes the given `bytes` to the file.
- Default behavior is to overwrite any existing file.
- Call p.write_bytes(bytes, append=True) to append instead.
+ :param append:
+ If ``True`` given `bytes` are added at the end of the file.
"""
if append:
mode = 'ab'
else:
mode = 'wb'
- f = self.open(mode)
+ f = open(self, mode=mode)
try:
f.write(bytes)
finally:
f.close()
- def text(self, encoding=None, errors='strict'):
- r""" Open this file, read it in, return the content as a string.
-
- This uses 'U' mode in Python 2.3 and later, so '\r\n' and '\r'
- are automatically translated to '\n'.
-
- Optional arguments:
-
- encoding - The Unicode encoding (or character set) of
- the file. If present, the content of the file is
- decoded and returned as a unicode object; otherwise
- it is returned as an 8-bit str.
- errors - How to handle Unicode errors; see help(str.decode)
- for the options. Default is 'strict'.
+ def exists(self):
"""
- if encoding is None:
- # 8-bit
- f = self.open(_textmode)
- try:
- return f.read()
- finally:
- f.close()
- else:
- # Unicode
- f = codecs.open(self, 'r', encoding, errors)
- # (Note - Can't use 'U' mode here, since codecs.open
- # doesn't support 'U' mode, even in Python 2.3.)
- try:
- t = f.read()
- finally:
- f.close()
- return (t.replace(u'\r\n', u'\n')
- .replace(u'\r\x85', u'\n')
- .replace(u'\r', u'\n')
- .replace(u'\x85', u'\n')
- .replace(u'\u2028', u'\n'))
-
- def write_text(self, text, encoding=None, errors='strict', linesep=os.linesep, append=False):
- r""" Write the given text to this file.
-
- The default behavior is to overwrite any existing file;
- to append instead, use the 'append=True' keyword argument.
-
- There are two differences between path.write_text() and
- path.write_bytes(): newline handling and Unicode handling.
- See below.
-
- Parameters:
-
- - text - str/unicode - The text to be written.
-
- - encoding - str - The Unicode encoding that will be used.
- This is ignored if 'text' isn't a Unicode string.
-
- - errors - str - How to handle Unicode encoding errors.
- Default is 'strict'. See help(unicode.encode) for the
- options. This is ignored if 'text' isn't a Unicode
- string.
-
- - linesep - keyword argument - str/unicode - The sequence of
- characters to be used to mark end-of-line. The default is
- os.linesep. You can also specify None; this means to
- leave all newlines as they are in 'text'.
-
- - append - keyword argument - bool - Specifies what to do if
- the file already exists (True: append to the end of it;
- False: overwrite it.) The default is False.
-
-
- --- Newline handling.
-
- write_text() converts all standard end-of-line sequences
- ('\n', '\r', and '\r\n') to your platform's default end-of-line
- sequence (see os.linesep; on Windows, for example, the
- end-of-line marker is '\r\n').
-
- If you don't like your platform's default, you can override it
- using the 'linesep=' keyword argument. If you specifically want
- write_text() to preserve the newlines as-is, use 'linesep=None'.
-
- This applies to Unicode text the same as to 8-bit text, except
- there are three additional standard Unicode end-of-line sequences:
- u'\x85', u'\r\x85', and u'\u2028'.
-
- (This is slightly different from when you open a file for
- writing with fopen(filename, "w") in C or file(filename, 'w')
- in Python.)
-
-
- --- Unicode
-
- If 'text' isn't Unicode, then apart from newline handling, the
- bytes are written verbatim to the file. The 'encoding' and
- 'errors' arguments are not used and must be omitted.
-
- If 'text' is Unicode, it is first converted to bytes using the
- specified 'encoding' (or the default encoding if 'encoding'
- isn't specified). The 'errors' argument applies only to this
- conversion.
-
- """
- if isinstance(text, unicode):
- if linesep is not None:
- # Convert all standard end-of-line sequences to
- # ordinary newline characters.
- text = (text.replace(u'\r\n', u'\n')
- .replace(u'\r\x85', u'\n')
- .replace(u'\r', u'\n')
- .replace(u'\x85', u'\n')
- .replace(u'\u2028', u'\n'))
- text = text.replace(u'\n', linesep)
- if encoding is None:
- encoding = sys.getdefaultencoding()
- bytes = text.encode(encoding, errors)
- else:
- # It is an error to specify an encoding if 'text' is
- # an 8-bit string.
- assert encoding is None
-
- if linesep is not None:
- text = (text.replace('\r\n', '\n')
- .replace('\r', '\n'))
- bytes = text.replace('\n', linesep)
-
- self.write_bytes(bytes, append)
-
- def lines(self, encoding=None, errors='strict', retain=True):
- r""" Open this file, read all lines, return them in a list.
-
- Optional arguments:
- encoding - The Unicode encoding (or character set) of
- the file. The default is None, meaning the content
- of the file is read as 8-bit characters and returned
- as a list of (non-Unicode) str objects.
- errors - How to handle Unicode errors; see help(str.decode)
- for the options. Default is 'strict'
- retain - If true, retain newline characters; but all newline
- character combinations ('\r', '\n', '\r\n') are
- translated to '\n'. If false, newline characters are
- stripped off. Default is True.
-
- This uses 'U' mode in Python 2.3 and later.
- """
- if encoding is None and retain:
- f = self.open(_textmode)
- try:
- return f.readlines()
- finally:
- f.close()
- else:
- return self.text(encoding, errors).splitlines(retain)
-
- def write_lines(self, lines, encoding=None, errors='strict',
- linesep=os.linesep, append=False):
- r""" Write the given lines of text to this file.
-
- By default this overwrites any existing file at this path.
-
- This puts a platform-specific newline sequence on every line.
- See 'linesep' below.
-
- lines - A list of strings.
-
- encoding - A Unicode encoding to use. This applies only if
- 'lines' contains any Unicode strings.
-
- errors - How to handle errors in Unicode encoding. This
- also applies only to Unicode strings.
-
- linesep - The desired line-ending. This line-ending is
- applied to every line. If a line already has any
- standard line ending ('\r', '\n', '\r\n', u'\x85',
- u'\r\x85', u'\u2028'), that will be stripped off and
- this will be used instead. The default is os.linesep,
- which is platform-dependent ('\r\n' on Windows, '\n' on
- Unix, etc.) Specify None to write the lines as-is,
- like file.writelines().
-
- Use the keyword argument append=True to append lines to the
- file. The default is to overwrite the file. Warning:
- When you use this with Unicode data, if the encoding of the
- existing data in the file is different from the encoding
- you specify with the encoding= parameter, the result is
- mixed-encoding data, which can really confuse someone trying
- to read the file later.
+ Returns ``True`` if the path exist.
"""
- if append:
- mode = 'ab'
- else:
- mode = 'wb'
- f = self.open(mode)
- try:
- for line in lines:
- isUnicode = isinstance(line, unicode)
- if linesep is not None:
- # Strip off any existing line-end and add the
- # specified linesep string.
- if isUnicode:
- if line[-2:] in (u'\r\n', u'\x0d\x85'):
- line = line[:-2]
- elif line[-1:] in (u'\r', u'\n',
- u'\x85', u'\u2028'):
- line = line[:-1]
- else:
- if line[-2:] == '\r\n':
- line = line[:-2]
- elif line[-1:] in ('\r', '\n'):
- line = line[:-1]
- line += linesep
- if isUnicode:
- if encoding is None:
- encoding = sys.getdefaultencoding()
- line = line.encode(encoding, errors)
- f.write(line)
- finally:
- f.close()
-
- # --- Methods for querying the filesystem.
-
- exists = os.path.exists
- isdir = os.path.isdir
- isfile = os.path.isfile
- islink = os.path.islink
- ismount = os.path.ismount
-
- if hasattr(os.path, 'samefile'):
- samefile = os.path.samefile
-
- getatime = os.path.getatime
- atime = property(
- getatime, None, None,
- """ Last access time of the file. """)
-
- getmtime = os.path.getmtime
- mtime = property(
- getmtime, None, None,
- """ Last-modified time of the file. """)
-
- if hasattr(os.path, 'getctime'):
- getctime = os.path.getctime
- ctime = property(
- getctime, None, None,
- """ Creation time of the file. """)
-
- getsize = os.path.getsize
- size = property(
- getsize, None, None,
- """ Size of the file, in bytes. """)
-
- if hasattr(os, 'access'):
- def access(self, mode):
- """ Return true if current user has access to this path.
-
- mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
- """
- return os.access(self, mode)
-
- def stat(self):
- """ Perform a stat() system call on this path. """
- return os.stat(self)
-
- def lstat(self):
- """ Like path.stat(), but do not follow symbolic links. """
- return os.lstat(self)
-
- def get_owner(self):
- r""" Return the name of the owner of this file or directory.
-
- This follows symbolic links.
-
- On Windows, this returns a name of the form ur'DOMAIN\User Name'.
- On Windows, a group can own a file or directory.
- """
- if os.name == 'nt':
- if win32security is None:
- raise Exception("path.owner requires win32all to be installed")
- desc = win32security.GetFileSecurity(
- self, win32security.OWNER_SECURITY_INFORMATION)
- sid = desc.GetSecurityDescriptorOwner()
- account, domain, typecode = win32security.LookupAccountSid(None, sid)
- return domain + u'\\' + account
- else:
- if pwd is None:
- raise NotImplementedError("path.owner is not implemented on this platform.")
- st = self.stat()
- return pwd.getpwuid(st.st_uid).pw_name
-
- owner = property(
- get_owner, None, None,
- """ Name of the owner of this file or directory. """)
-
- if hasattr(os, 'statvfs'):
- def statvfs(self):
- """ Perform a statvfs() system call on this path. """
- return os.statvfs(self)
-
- if hasattr(os, 'pathconf'):
- def pathconf(self, name):
- return os.pathconf(self, name)
-
-
- # --- Modifying operations on files and directories
-
- def utime(self, times):
- """ Set the access and modified times of this file. """
- os.utime(self, times)
-
- def chmod(self, mode):
- os.chmod(self, mode)
+ return os.path.exists(self)
- if hasattr(os, 'chown'):
- def chown(self, uid, gid):
- os.chown(self, uid, gid)
-
- def rename(self, new):
- os.rename(self, new)
-
- def renames(self, new):
- os.renames(self, new)
-
-
- # --- Create/delete operations on directories
-
- def mkdir(self, mode=0777):
- os.mkdir(self, mode)
+ def lexists(self):
+ """
+ Returns ``True`` if the path exists unless it is a broken symbolic
+ link.
+ """
+ return os.path.lexists(self)
def makedirs(self, mode=0777):
+ """
+ Recursively create directories.
+ """
os.makedirs(self, mode)
- def rmdir(self):
- os.rmdir(self)
-
- def removedirs(self):
- os.removedirs(self)
-
-
- # --- Modifying operations on files
-
- def touch(self):
- """ Set the access/modified times of this file to the current time.
- Create the file if it does not exist.
+ def joinpath(self, *args):
"""
- fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
- os.close(fd)
- os.utime(self, None)
-
- def remove(self):
- os.remove(self)
-
- def unlink(self):
- os.unlink(self)
-
-
- # --- Links
-
- if hasattr(os, 'link'):
- def link(self, newpath):
- """ Create a hard link at 'newpath', pointing to this file. """
- os.link(self, newpath)
-
- if hasattr(os, 'symlink'):
- def symlink(self, newlink):
- """ Create a symbolic link at 'newlink', pointing here. """
- os.symlink(self, newlink)
-
- if hasattr(os, 'readlink'):
- def readlink(self):
- """ Return the path to which this symbolic link points.
-
- The result may be an absolute or a relative path.
- """
- return self.__class__(os.readlink(self))
-
- def readlinkabs(self):
- """ Return the path to which this symbolic link points.
-
- The result is always an absolute path.
- """
- p = self.readlink()
- if p.isabs():
- return p
- else:
- return (self.parent / p).abspath()
-
-
- # --- High-level functions from shutil
-
- copyfile = shutil.copyfile
- copymode = shutil.copymode
- copystat = shutil.copystat
- copy = shutil.copy
- copy2 = shutil.copy2
- copytree = shutil.copytree
- if hasattr(shutil, 'move'):
- move = shutil.move
- rmtree = shutil.rmtree
-
-
- # --- Special stuff from os
-
- if hasattr(os, 'chroot'):
- def chroot(self):
- os.chroot(self)
+ Joins the path with the argument given and returns the result.
+ """
+ return self.__class__(os.path.join(self, *map(self.__class__, args)))
- if hasattr(os, 'startfile'):
- def startfile(self):
- os.startfile(self)
+ __div__ = __truediv__ = joinpath
+ def __repr__(self):
+ return '%s(%s)' % (self.__class__.__name__, str.__repr__(self))
diff --git a/tests/root/conf.py b/tests/root/conf.py
index 2b6d6a9a..b4734189 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -22,7 +22,7 @@ copyright = '2010, Georg Brandl & Team'
version = '0.6'
release = '0.6alpha1'
today_fmt = '%B %d, %Y'
-#unused_docs = []
+# unused_docs = []
exclude_patterns = ['_build', '**/excluded.*']
keep_warnings = True
pygments_style = 'sphinx'
diff --git a/tests/root/contents.txt b/tests/root/contents.txt
index e052e04b..280953b4 100644
--- a/tests/root/contents.txt
+++ b/tests/root/contents.txt
@@ -26,6 +26,7 @@ Contents:
extensions
doctest
extensions
+ versioning/index
Python <http://python.org/>
diff --git a/tests/root/doctest.txt b/tests/root/doctest.txt
index 35cdd589..6ac0b286 100644
--- a/tests/root/doctest.txt
+++ b/tests/root/doctest.txt
@@ -30,7 +30,7 @@ Special directives
.. testcode::
- print 1+1
+ print(1+1)
.. testoutput::
@@ -50,30 +50,30 @@ Special directives
.. testsetup:: *
- from math import floor
+ from math import factorial
.. doctest::
- >>> floor(1.2)
- 1.0
+ >>> factorial(1)
+ 1
.. testcode::
- print floor(1.2)
+ print(factorial(1))
.. testoutput::
- 1.0
+ 1
- >>> floor(1.2)
- 1.0
+ >>> factorial(1)
+ 1
* options for testcode/testoutput blocks
.. testcode::
:hide:
- print 'Output text.'
+ print('Output text.')
.. testoutput::
:hide:
@@ -85,36 +85,36 @@ Special directives
.. testsetup:: group1
- from math import ceil
+ from math import trunc
- ``ceil`` is now known in "group1", but not in others.
+ ``trunc`` is now known in "group1", but not in others.
.. doctest:: group1
- >>> ceil(0.8)
- 1.0
+ >>> trunc(1.1)
+ 1
.. doctest:: group2
- >>> ceil(0.8)
+ >>> trunc(1.1)
Traceback (most recent call last):
...
- NameError: name 'ceil' is not defined
+ NameError: name 'trunc' is not defined
Interleaving testcode/testoutput:
.. testcode:: group1
- print ceil(0.8)
+ print(factorial(3))
.. testcode:: group2
- print floor(0.8)
+ print(factorial(4))
.. testoutput:: group1
- 1.0
+ 6
.. testoutput:: group2
- 0.0
+ 24
diff --git a/tests/root/literal.inc b/tests/root/literal.inc
index d5b9890c..694f15ed 100644
--- a/tests/root/literal.inc
+++ b/tests/root/literal.inc
@@ -1,7 +1,7 @@
# Literally included file using Python highlighting
# -*- coding: utf-8 -*-
-foo = u"Including Unicode characters: üöä"
+foo = "Including Unicode characters: üöä"
class Foo:
pass
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index e286d26c..a72285ed 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -97,21 +97,23 @@ Inline markup
*Generic inline markup*
-* :command:`command`
-* :dfn:`dfn`
-* :guilabel:`guilabel with &accelerator`
-* :kbd:`kbd`
-* :mailheader:`mailheader`
-* :makevar:`makevar`
-* :manpage:`manpage`
-* :mimetype:`mimetype`
-* :newsgroup:`newsgroup`
-* :program:`program`
-* :regexp:`regexp`
-* :menuselection:`File --> Close`
+Adding \n to test unescaping.
+
+* :command:`command\\n`
+* :dfn:`dfn\\n`
+* :guilabel:`guilabel with &accelerator and \\n`
+* :kbd:`kbd\\n`
+* :mailheader:`mailheader\\n`
+* :makevar:`makevar\\n`
+* :manpage:`manpage\\n`
+* :mimetype:`mimetype\\n`
+* :newsgroup:`newsgroup\\n`
+* :program:`program\\n`
+* :regexp:`regexp\\n`
+* :menuselection:`File --> Close\\n`
* :menuselection:`&File --> &Print`
-* :file:`a/{varpart}/b`
-* :samp:`print {i}`
+* :file:`a/{varpart}/b\\n`
+* :samp:`print {i}\\n`
*Linking inline markup*
diff --git a/tests/root/objects.txt b/tests/root/objects.txt
index ca3d0eb7..e62f6d96 100644
--- a/tests/root/objects.txt
+++ b/tests/root/objects.txt
@@ -41,8 +41,17 @@ Testing object descriptions
.. function:: func_without_module2() -> annotation
+.. object:: long(parameter, \
+ list)
+ another one
+
.. class:: TimeInt
+ :param moo: |test|
+ :type moo: |test|
+
+.. |test| replace:: Moo
+
.. class:: Time(hour, minute, isdst)
:param hour: The year.
@@ -57,6 +66,8 @@ Testing object descriptions
:ivar int hour: like *hour*
:ivar minute: like *minute*
:vartype minute: int
+ :param hour: Duplicate param. Should not lead to crashes.
+ :type hour: Duplicate type.
C items
diff --git a/tests/root/versioning/added.txt b/tests/root/versioning/added.txt
new file mode 100644
index 00000000..22a70739
--- /dev/null
+++ b/tests/root/versioning/added.txt
@@ -0,0 +1,20 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. It's not really interesting, in fact it is *really*
+boring.
+
+Anyway I need more than one paragraph, at least three for the original
+document, I think, and another one for two different ones.
+
+So the previous paragraph was a bit short because I don't want to test this
+only on long paragraphs, I hope it was short enough to cover most stuff.
+Anyway I see this lacks ``some markup`` so I have to add a **little** bit.
+
+Woho another paragraph, if this test fails we really have a problem because
+this means the algorithm itself fails and not the diffing algorithm which is
+pretty much doomed anyway as it probably fails for some kind of language
+respecting certain nodes anyway but we can't work around that anyway.
diff --git a/tests/root/versioning/deleted.txt b/tests/root/versioning/deleted.txt
new file mode 100644
index 00000000..a1a9c4c9
--- /dev/null
+++ b/tests/root/versioning/deleted.txt
@@ -0,0 +1,12 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. It's not really interesting, in fact it is *really*
+boring.
+
+So the previous paragraph was a bit short because I don't want to test this
+only on long paragraphs, I hope it was short enough to cover most stuff.
+Anyway I see this lacks ``some markup`` so I have to add a **little** bit.
diff --git a/tests/root/versioning/deleted_end.txt b/tests/root/versioning/deleted_end.txt
new file mode 100644
index 00000000..f30e6300
--- /dev/null
+++ b/tests/root/versioning/deleted_end.txt
@@ -0,0 +1,11 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. It's not really interesting, in fact it is *really*
+boring.
+
+Anyway I need more than one paragraph, at least three for the original
+document, I think, and another one for two different ones.
diff --git a/tests/root/versioning/index.txt b/tests/root/versioning/index.txt
new file mode 100644
index 00000000..234e223f
--- /dev/null
+++ b/tests/root/versioning/index.txt
@@ -0,0 +1,11 @@
+Versioning Stuff
+================
+
+.. toctree::
+
+ original
+ added
+ insert
+ deleted
+ deleted_end
+ modified
diff --git a/tests/root/versioning/insert.txt b/tests/root/versioning/insert.txt
new file mode 100644
index 00000000..1c157cc9
--- /dev/null
+++ b/tests/root/versioning/insert.txt
@@ -0,0 +1,18 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. It's not really interesting, in fact it is *really*
+boring.
+
+So this paragraph is just something I inserted in this document to test if our
+algorithm notices that this paragraph is not just a changed version.
+
+Anyway I need more than one paragraph, at least three for the original
+document, I think, and another one for two different ones.
+
+So the previous paragraph was a bit short because I don't want to test this
+only on long paragraphs, I hope it was short enough to cover most stuff.
+Anyway I see this lacks ``some markup`` so I have to add a **little** bit.
diff --git a/tests/root/versioning/modified.txt b/tests/root/versioning/modified.txt
new file mode 100644
index 00000000..49cdad93
--- /dev/null
+++ b/tests/root/versioning/modified.txt
@@ -0,0 +1,17 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. Inserting something silly as a modification, btw. have
+you seen the typo below?. It's not really interesting, in fact it is *really*
+boring.
+
+Anyway I need more than one paragraph, at least three for the original
+document, I think, and another one for two different ones. So this is a small
+modification by adding something to this paragraph.
+
+So the previous paragraph was a bit short because I don't want to test this
+only on long paragraphs, I hoep it was short enough to cover most stuff.
+Anyway I see this lacks ``some markup`` so I have to add a **little** bit.
diff --git a/tests/root/versioning/original.txt b/tests/root/versioning/original.txt
new file mode 100644
index 00000000..b3fe0609
--- /dev/null
+++ b/tests/root/versioning/original.txt
@@ -0,0 +1,15 @@
+Versioning test text
+====================
+
+So the thing is I need some kind of text - not the lorem ipsum stuff, that
+doesn't work out that well - to test :mod:`sphinx.versioning`. I couldn't find
+a good text for that under public domain so I thought the easiest solution is
+to write one by myself. It's not really interesting, in fact it is *really*
+boring.
+
+Anyway I need more than one paragraph, at least three for the original
+document, I think, and another one for two different ones.
+
+So the previous paragraph was a bit short because I don't want to test this
+only on long paragraphs, I hope it was short enough to cover most stuff.
+Anyway I see this lacks ``some markup`` so I have to add a **little** bit.
diff --git a/tests/run.py b/tests/run.py
index 0cb41442..50567fbc 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -11,7 +11,17 @@
"""
import sys
-from os import path
+from os import path, chdir, listdir
+
+if sys.version_info >= (3, 0):
+ print('Copying and converting sources to build/lib/tests...')
+ from distutils.util import copydir_run_2to3
+ testroot = path.dirname(__file__) or '.'
+ newroot = path.join(testroot, path.pardir, 'build')
+ newroot = path.join(newroot, listdir(newroot)[0], 'tests')
+ copydir_run_2to3(testroot, newroot)
+ # switch to the converted dir so nose tests the right tests
+ chdir(newroot)
# always test the sphinx package from this directory
sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
@@ -19,8 +29,8 @@ sys.path.insert(0, path.join(path.dirname(__file__), path.pardir))
try:
import nose
except ImportError:
- print "The nose package is needed to run the Sphinx test suite."
+ print("The nose package is needed to run the Sphinx test suite.")
sys.exit(1)
-print "Running Sphinx test suite..."
+print("Running Sphinx test suite...")
nose.main()
diff --git a/tests/test_application.py b/tests/test_application.py
index 3d287a57..d1154863 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -45,9 +45,11 @@ def test_output():
app = TestApp(status=status, warning=warnings)
try:
status.truncate(0) # __init__ writes to status
+ status.seek(0)
app.info("Nothing here...")
assert status.getvalue() == "Nothing here...\n"
status.truncate(0)
+ status.seek(0)
app.info("Nothing here...", True)
assert status.getvalue() == "Nothing here..."
diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py
index 7e309367..20fb06e0 100644
--- a/tests/test_autosummary.py
+++ b/tests/test_autosummary.py
@@ -9,8 +9,6 @@
:license: BSD, see LICENSE for details.
"""
-import string
-
from util import *
from sphinx.ext.autosummary import mangle_signature
@@ -27,7 +25,7 @@ def test_mangle_signature():
(a, b, c='foobar()', d=123) :: (a, b[, c, d])
"""
- TEST = [map(string.strip, x.split("::")) for x in TEST.split("\n")
+ TEST = [map(lambda x: x.strip(), x.split("::")) for x in TEST.split("\n")
if '::' in x]
for inp, outp in TEST:
res = mangle_signature(inp).strip().replace(u"\u00a0", " ")
diff --git a/tests/test_build.py b/tests/test_build.py
index f18ff175..d571febd 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -21,6 +21,10 @@ def teardown_module():
def test_pickle(app):
app.builder.build_all()
+@with_app(buildername='json')
+def test_json(app):
+ app.builder.build_all()
+
@with_app(buildername='linkcheck')
def test_linkcheck(app):
app.builder.build_all()
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index fe249f44..a685fcb6 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -11,8 +11,8 @@
import os
import re
-import difflib
import htmlentitydefs
+import sys
from StringIO import StringIO
try:
@@ -37,8 +37,11 @@ ENV_WARNINGS = """\
http://www.python.org/logo.png
%(root)s/includes.txt:\\d*: \\(WARNING/2\\) Encoding 'utf-8-sig' used for \
reading included file u'wrongenc.inc' seems to be wrong, try giving an \
-:encoding: option
+:encoding: option\\n?
%(root)s/includes.txt:4: WARNING: download file not readable: nonexisting.png
+%(root)s/objects.txt:\\d*: WARNING: using old C markup; please migrate to \
+new-style markup \(e.g. c:function instead of cfunction\), see \
+http://sphinx.pocoo.org/domains.html
"""
HTML_WARNINGS = ENV_WARNINGS + """\
@@ -48,183 +51,204 @@ HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/markup.txt:: WARNING: invalid pair index entry u'keyword; '
"""
+if sys.version_info >= (3, 0):
+ ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS)
+ HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS)
+
+
+def tail_check(check):
+ rex = re.compile(check)
+ def checker(nodes):
+ for node in nodes:
+ if node.tail and rex.search(node.tail):
+ return True
+ assert False, '%r not found in tail of any nodes %s' % (check, nodes)
+ return checker
+
+
HTML_XPATH = {
- 'images.html': {
- ".//img[@src='_images/img.png']": '',
- ".//img[@src='_images/img1.png']": '',
- ".//img[@src='_images/simg.png']": '',
- ".//object[@data='_images/svgimg.svg']": '',
- ".//embed[@src='_images/svgimg.svg']": '',
- },
- 'subdir/images.html': {
- ".//img[@src='../_images/img1.png']": '',
- ".//img[@src='../_images/rimg.png']": '',
- },
- 'subdir/includes.html': {
- ".//a[@href='../_downloads/img.png']": '',
- ".//img[@src='../_images/img.png']": '',
- ".//p": 'This is an include file.',
- },
- 'includes.html': {
- ".//pre": u'Max Strauß',
- ".//a[@href='_downloads/img.png']": '',
- ".//a[@href='_downloads/img1.png']": '',
- ".//pre": u'"quotes"',
- ".//pre": u"'included'",
- },
- 'autodoc.html': {
- ".//dt[@id='test_autodoc.Class']": '',
- ".//dt[@id='test_autodoc.function']/em": r'\*\*kwds',
- ".//dd/p": r'Return spam\.',
- },
- 'extapi.html': {
- ".//strong": 'from function: Foo',
- ".//strong": 'from class: Bar',
- },
- 'markup.html': {
- ".//title": 'set by title directive',
- ".//p/em": 'Section author: Georg Brandl',
- ".//p/em": 'Module author: Georg Brandl',
+ 'images.html': [
+ (".//img[@src='_images/img.png']", ''),
+ (".//img[@src='_images/img1.png']", ''),
+ (".//img[@src='_images/simg.png']", ''),
+ (".//object[@data='_images/svgimg.svg']", ''),
+ (".//embed[@src='_images/svgimg.svg']", ''),
+ ],
+ 'subdir/images.html': [
+ (".//img[@src='../_images/img1.png']", ''),
+ (".//img[@src='../_images/rimg.png']", ''),
+ ],
+ 'subdir/includes.html': [
+ (".//a[@href='../_downloads/img.png']", ''),
+ (".//img[@src='../_images/img.png']", ''),
+ (".//p", 'This is an include file.'),
+ ],
+ 'includes.html': [
+ (".//pre", u'Max Strauß'),
+ (".//a[@href='_downloads/img.png']", ''),
+ (".//a[@href='_downloads/img1.png']", ''),
+ (".//pre", u'"quotes"'),
+ (".//pre", u"'included'"),
+ ],
+ 'autodoc.html': [
+ (".//dt[@id='test_autodoc.Class']", ''),
+ (".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'),
+ (".//dd/p", r'Return spam\.'),
+ ],
+ 'extapi.html': [
+ (".//strong", 'from function: Foo'),
+ (".//strong", 'from class: Bar'),
+ ],
+ 'markup.html': [
+ (".//title", 'set by title directive'),
+ (".//p/em", 'Section author: Georg Brandl'),
+ (".//p/em", 'Module author: Georg Brandl'),
# created by the meta directive
- ".//meta[@name='author'][@content='Me']": '',
- ".//meta[@name='keywords'][@content='docs, sphinx']": '',
+ (".//meta[@name='author'][@content='Me']", ''),
+ (".//meta[@name='keywords'][@content='docs, sphinx']", ''),
# a label created by ``.. _label:``
- ".//div[@id='label']": '',
+ (".//div[@id='label']", ''),
# code with standard code blocks
- ".//pre": '^some code$',
+ (".//pre", '^some code$'),
# an option list
- ".//span[@class='option']": '--help',
+ (".//span[@class='option']", '--help'),
# admonitions
- ".//p[@class='first admonition-title']": 'My Admonition',
- ".//p[@class='last']": 'Note text.',
- ".//p[@class='last']": 'Warning text.',
+ (".//p[@class='first admonition-title']", 'My Admonition'),
+ (".//p[@class='last']", 'Note text.'),
+ (".//p[@class='last']", 'Warning text.'),
# inline markup
- ".//li/strong": '^command$',
- ".//li/strong": '^program$',
- ".//li/em": '^dfn$',
- ".//li/tt/span[@class='pre']": '^kbd$',
- ".//li/em": u'File \N{TRIANGULAR BULLET} Close',
- ".//li/tt/span[@class='pre']": '^a/$',
- ".//li/tt/em/span[@class='pre']": '^varpart$',
- ".//li/tt/em/span[@class='pre']": '^i$',
- ".//a[@href='http://www.python.org/dev/peps/pep-0008']"
- "[@class='pep reference external']/strong": 'PEP 8',
- ".//a[@href='http://tools.ietf.org/html/rfc1.html']"
- "[@class='rfc reference external']/strong": 'RFC 1',
- ".//a[@href='objects.html#envvar-HOME']"
- "[@class='reference internal']/tt/span[@class='pre']": 'HOME',
- ".//a[@href='#with']"
- "[@class='reference internal']/tt/span[@class='pre']": '^with$',
- ".//a[@href='#grammar-token-try_stmt']"
- "[@class='reference internal']/tt/span": '^statement$',
- ".//a[@href='subdir/includes.html']"
- "[@class='reference internal']/em": 'Including in subdir',
- ".//a[@href='objects.html#cmdoption-python-c']"
- "[@class='reference internal']/em": 'Python -c option',
+ (".//li/strong", r'^command\\n$'),
+ (".//li/strong", r'^program\\n$'),
+ (".//li/em", r'^dfn\\n$'),
+ (".//li/tt/span[@class='pre']", r'^kbd\\n$'),
+ (".//li/em", u'File \N{TRIANGULAR BULLET} Close'),
+ (".//li/tt/span[@class='pre']", '^a/$'),
+ (".//li/tt/em/span[@class='pre']", '^varpart$'),
+ (".//li/tt/em/span[@class='pre']", '^i$'),
+ (".//a[@href='http://www.python.org/dev/peps/pep-0008']"
+ "[@class='pep reference external']/strong", 'PEP 8'),
+ (".//a[@href='http://tools.ietf.org/html/rfc1.html']"
+ "[@class='rfc reference external']/strong", 'RFC 1'),
+ (".//a[@href='objects.html#envvar-HOME']"
+ "[@class='reference internal']/tt/span[@class='pre']", 'HOME'),
+ (".//a[@href='#with']"
+ "[@class='reference internal']/tt/span[@class='pre']", '^with$'),
+ (".//a[@href='#grammar-token-try_stmt']"
+ "[@class='reference internal']/tt/span", '^statement$'),
+ (".//a[@href='subdir/includes.html']"
+ "[@class='reference internal']/em", 'Including in subdir'),
+ (".//a[@href='objects.html#cmdoption-python-c']"
+ "[@class='reference internal']/em", 'Python -c option'),
# abbreviations
- ".//abbr[@title='abbreviation']": '^abbr$',
+ (".//abbr[@title='abbreviation']", '^abbr$'),
# version stuff
- ".//span[@class='versionmodified']": 'New in version 0.6',
+ (".//span[@class='versionmodified']", 'New in version 0.6'),
# footnote reference
- ".//a[@class='footnote-reference']": r'\[1\]',
+ (".//a[@class='footnote-reference']", r'\[1\]'),
# created by reference lookup
- ".//a[@href='contents.html#ref1']": '',
+ (".//a[@href='contents.html#ref1']", ''),
# ``seealso`` directive
- ".//div/p[@class='first admonition-title']": 'See also',
+ (".//div/p[@class='first admonition-title']", 'See also'),
# a ``hlist`` directive
- ".//table[@class='hlist']/tr/td/ul/li": '^This$',
+ (".//table[@class='hlist']/tr/td/ul/li", '^This$'),
# a ``centered`` directive
- ".//p[@class='centered']/strong": 'LICENSE',
+ (".//p[@class='centered']/strong", 'LICENSE'),
# a glossary
- ".//dl/dt[@id='term-boson']": 'boson',
+ (".//dl/dt[@id='term-boson']", 'boson'),
# a production list
- ".//pre/strong": 'try_stmt',
- ".//pre/a[@href='#grammar-token-try1_stmt']/tt/span": 'try1_stmt',
+ (".//pre/strong", 'try_stmt'),
+ (".//pre/a[@href='#grammar-token-try1_stmt']/tt/span", 'try1_stmt'),
# tests for ``only`` directive
- ".//p": 'A global substitution.',
- ".//p": 'In HTML.',
- ".//p": 'In both.',
- ".//p": 'Always present',
- },
- 'objects.html': {
- ".//dt[@id='mod.Cls.meth1']": '',
- ".//dt[@id='errmod.Error']": '',
- ".//a[@href='#mod.Cls'][@class='reference internal']": '',
- ".//dl[@class='userdesc']": '',
- ".//dt[@id='userdesc-myobj']": '',
- ".//a[@href='#userdesc-myobj']": '',
+ (".//p", 'A global substitution.'),
+ (".//p", 'In HTML.'),
+ (".//p", 'In both.'),
+ (".//p", 'Always present'),
+ ],
+ 'objects.html': [
+ (".//dt[@id='mod.Cls.meth1']", ''),
+ (".//dt[@id='errmod.Error']", ''),
+ (".//dt/tt", r'long\(parameter,\s* list\)'),
+ (".//dt/tt", 'another one'),
+ (".//a[@href='#mod.Cls'][@class='reference internal']", ''),
+ (".//dl[@class='userdesc']", ''),
+ (".//dt[@id='userdesc-myobj']", ''),
+ (".//a[@href='#userdesc-myobj']", ''),
# C references
- ".//span[@class='pre']": 'CFunction()',
- ".//a[@href='#Sphinx_DoSomething']": '',
- ".//a[@href='#SphinxStruct.member']": '',
- ".//a[@href='#SPHINX_USE_PYTHON']": '',
- ".//a[@href='#SphinxType']": '',
- ".//a[@href='#sphinx_global']": '',
+ (".//span[@class='pre']", 'CFunction()'),
+ (".//a[@href='#Sphinx_DoSomething']", ''),
+ (".//a[@href='#SphinxStruct.member']", ''),
+ (".//a[@href='#SPHINX_USE_PYTHON']", ''),
+ (".//a[@href='#SphinxType']", ''),
+ (".//a[@href='#sphinx_global']", ''),
# reference from old C markup extension
- ".//a[@href='#Sphinx_Func']": '',
+ (".//a[@href='#Sphinx_Func']", ''),
# test global TOC created by toctree()
- ".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='']":
- 'Testing object descriptions',
- ".//li[@class='toctree-l1']/a[@href='markup.html']":
- 'Testing various markup',
+ (".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='']",
+ 'Testing object descriptions'),
+ (".//li[@class='toctree-l1']/a[@href='markup.html']",
+ 'Testing various markup'),
# custom sidebar
- ".//h4": 'Custom sidebar',
- },
- 'contents.html': {
- ".//meta[@name='hc'][@content='hcval']": '',
- ".//meta[@name='hc_co'][@content='hcval_co']": '',
- ".//meta[@name='testopt'][@content='testoverride']": '',
- ".//td[@class='label']": r'\[Ref1\]',
- ".//td[@class='label']": '',
- ".//li[@class='toctree-l1']/a": 'Testing various markup',
- ".//li[@class='toctree-l2']/a": 'Inline markup',
- ".//title": 'Sphinx <Tests>',
- ".//div[@class='footer']": 'Georg Brandl & Team',
- ".//a[@href='http://python.org/']"
- "[@class='reference external']": '',
- ".//li/a[@href='genindex.html']/em": 'Index',
- ".//li/a[@href='py-modindex.html']/em": 'Module Index',
- ".//li/a[@href='search.html']/em": 'Search Page',
+ (".//h4", 'Custom sidebar'),
+ # docfields
+ (".//td[@class='field-body']/ul/li/strong", '^moo$'),
+ (".//td[@class='field-body']/ul/li/strong",
+ tail_check(r'\(Moo\) .* Moo')),
+ ],
+ 'contents.html': [
+ (".//meta[@name='hc'][@content='hcval']", ''),
+ (".//meta[@name='hc_co'][@content='hcval_co']", ''),
+ (".//meta[@name='testopt'][@content='testoverride']", ''),
+ (".//td[@class='label']", r'\[Ref1\]'),
+ (".//td[@class='label']", ''),
+ (".//li[@class='toctree-l1']/a", 'Testing various markup'),
+ (".//li[@class='toctree-l2']/a", 'Inline markup'),
+ (".//title", 'Sphinx <Tests>'),
+ (".//div[@class='footer']", 'Georg Brandl & Team'),
+ (".//a[@href='http://python.org/']"
+ "[@class='reference external']", ''),
+ (".//li/a[@href='genindex.html']/em", 'Index'),
+ (".//li/a[@href='py-modindex.html']/em", 'Module Index'),
+ (".//li/a[@href='search.html']/em", 'Search Page'),
# custom sidebar only for contents
- ".//h4": 'Contents sidebar',
- },
- 'bom.html': {
- ".//title": " File with UTF-8 BOM",
- },
- 'extensions.html': {
- ".//a[@href='http://python.org/dev/']": "http://python.org/dev/",
- ".//a[@href='http://bugs.python.org/issue1000']": "issue 1000",
- ".//a[@href='http://bugs.python.org/issue1042']": "explicit caption",
- },
- '_static/statictmpl.html': {
- ".//project": 'Sphinx <Tests>',
- },
+ (".//h4", 'Contents sidebar'),
+ ],
+ 'bom.html': [
+ (".//title", " File with UTF-8 BOM"),
+ ],
+ 'extensions.html': [
+ (".//a[@href='http://python.org/dev/']", "http://python.org/dev/"),
+ (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"),
+ (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"),
+ ],
+ '_static/statictmpl.html': [
+ (".//project", 'Sphinx <Tests>'),
+ ],
}
if pygments:
- HTML_XPATH['includes.html'].update({
- ".//pre/span[@class='s']": u'üöä',
- ".//div[@class='inc-pyobj1 highlight-text']//pre":
- r'^class Foo:\n pass\n\s*$',
- ".//div[@class='inc-pyobj2 highlight-text']//pre":
- r'^ def baz\(\):\n pass\n\s*$',
- ".//div[@class='inc-lines highlight-text']//pre":
- r'^class Foo:\n pass\nclass Bar:\n$',
- ".//div[@class='inc-startend highlight-text']//pre":
- ur'^foo = u"Including Unicode characters: üöä"\n$',
- ".//div[@class='inc-preappend highlight-text']//pre":
- r'(?m)^START CODE$',
- ".//div[@class='inc-pyobj-dedent highlight-python']//span":
- r'def',
- ".//div[@class='inc-tab3 highlight-text']//pre":
- r'-| |-',
- ".//div[@class='inc-tab8 highlight-python']//pre":
- r'-| |-',
- })
- HTML_XPATH['subdir/includes.html'].update({
- ".//pre/span": 'line 1',
- ".//pre/span": 'line 2',
- })
+ HTML_XPATH['includes.html'].extend([
+ (".//pre/span[@class='s']", u'üöä'),
+ (".//div[@class='inc-pyobj1 highlight-text']//pre",
+ r'^class Foo:\n pass\n\s*$'),
+ (".//div[@class='inc-pyobj2 highlight-text']//pre",
+ r'^ def baz\(\):\n pass\n\s*$'),
+ (".//div[@class='inc-lines highlight-text']//pre",
+ r'^class Foo:\n pass\nclass Bar:\n$'),
+ (".//div[@class='inc-startend highlight-text']//pre",
+ ur'^foo = "Including Unicode characters: üöä"\n$'),
+ (".//div[@class='inc-preappend highlight-text']//pre",
+ r'(?m)^START CODE$'),
+ (".//div[@class='inc-pyobj-dedent highlight-python']//span",
+ r'def'),
+ (".//div[@class='inc-tab3 highlight-text']//pre",
+ r'-| |-'),
+ (".//div[@class='inc-tab8 highlight-python']//pre",
+ r'-| |-'),
+ ])
+ HTML_XPATH['subdir/includes.html'].extend([
+ (".//pre/span", 'line 1'),
+ (".//pre/span", 'line 2'),
+ ])
class NslessParser(ET.XMLParser):
"""XMLParser that throws away namespaces in tag names."""
@@ -282,14 +306,14 @@ def test_html(app):
html_warnings_exp = HTML_WARNINGS % {'root': re.escape(app.srcdir)}
assert re.match(html_warnings_exp + '$', html_warnings), \
'Warnings don\'t match:\n' + \
- '\n'.join(difflib.ndiff(html_warnings_exp.splitlines(),
- html_warnings.splitlines()))
+ '--- Expected (regex):\n' + html_warnings_exp + \
+ '--- Got:\n' + html_warnings
for fname, paths in HTML_XPATH.iteritems():
parser = NslessParser()
parser.entity.update(htmlentitydefs.entitydefs)
etree = ET.parse(os.path.join(app.outdir, fname), parser)
- for path, check in paths.iteritems():
+ for path, check in paths:
yield check_xpath, etree, fname, path, check
check_static_entries(app.builder.outdir)
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 6a20746b..6c1ccad9 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -12,7 +12,6 @@
import os
import re
import sys
-import difflib
from StringIO import StringIO
from subprocess import Popen, PIPE
@@ -33,6 +32,9 @@ None:None: WARNING: no matching candidate for image URI u'foo.\\*'
WARNING: invalid pair index entry u''
"""
+if sys.version_info >= (3, 0):
+ LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS)
+
@with_app(buildername='latex', warning=latex_warnfile, cleanenv=True)
def test_latex(app):
@@ -42,8 +44,9 @@ def test_latex(app):
latex_warnings_exp = LATEX_WARNINGS % {'root': app.srcdir}
assert re.match(latex_warnings_exp + '$', latex_warnings), \
'Warnings don\'t match:\n' + \
- '\n'.join(difflib.ndiff(latex_warnings_exp.splitlines(),
- latex_warnings.splitlines()))
+ '--- Expected (regex):\n' + latex_warnings_exp + \
+ '--- Got:\n' + latex_warnings
+
# file from latex_additional_files
assert (app.outdir / 'svgimg.svg').isfile()
diff --git a/tests/test_config.py b/tests/test_config.py
index cb4e1105..b5f88a6f 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -9,6 +9,7 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import sys
from util import *
@@ -84,11 +85,23 @@ def test_extension_values(app):
@with_tempdir
def test_errors_warnings(dir):
# test the error for syntax errors in the config file
- write_file(dir / 'conf.py', 'project = \n')
+ write_file(dir / 'conf.py', u'project = \n', 'ascii')
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
+ # test the automatic conversion of 2.x only code in configs
+ write_file(dir / 'conf.py', u'# -*- coding: utf-8\n\n'
+ u'project = u"Jägermeister"\n', 'utf-8')
+ cfg = Config(dir, 'conf.py', {}, None)
+ cfg.init_values()
+ assert cfg.project == u'Jägermeister'
+
# test the warning for bytestrings with non-ascii content
- write_file(dir / 'conf.py', '# -*- coding: latin-1\nproject = "foo\xe4"\n')
+ # bytestrings with non-ascii content are a syntax error in python3 so we
+ # skip the test there
+ if sys.version_info >= (3, 0):
+ return
+ write_file(dir / 'conf.py', u'# -*- coding: latin-1\nproject = "fooä"\n',
+ 'latin-1')
cfg = Config(dir, 'conf.py', {}, None)
warned = [False]
def warn(msg):
diff --git a/tests/test_coverage.py b/tests/test_coverage.py
index 1262ebf5..cb831635 100644
--- a/tests/test_coverage.py
+++ b/tests/test_coverage.py
@@ -33,7 +33,7 @@ def test_build(app):
assert 'api.h' in c_undoc
assert ' * Py_SphinxTest' in c_undoc
- undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').text())
+ undoc_py, undoc_c = pickle.loads((app.outdir / 'undoc.pickle').bytes())
assert len(undoc_c) == 1
# the key is the full path to the header file, which isn't testable
assert undoc_c.values()[0] == [('function', 'Py_SphinxTest')]
diff --git a/tests/test_env.py b/tests/test_env.py
index 4ecbaac4..124ed08c 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -8,6 +8,7 @@
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+import sys
from util import *
@@ -54,8 +55,10 @@ def test_images():
app._warning.reset()
htmlbuilder = StandaloneHTMLBuilder(app)
htmlbuilder.post_process_images(tree)
- assert "no matching candidate for image URI u'foo.*'" in \
- app._warning.content[-1]
+ image_uri_message = "no matching candidate for image URI u'foo.*'"
+ if sys.version_info >= (3, 0):
+ image_uri_message = remove_unicode_literals(image_uri_message)
+ assert image_uri_message in app._warning.content[-1]
assert set(htmlbuilder.images.keys()) == \
set(['subdir/img.png', 'img.png', 'subdir/simg.png', 'svgimg.svg'])
assert set(htmlbuilder.images.values()) == \
@@ -64,8 +67,7 @@ def test_images():
app._warning.reset()
latexbuilder = LaTeXBuilder(app)
latexbuilder.post_process_images(tree)
- assert "no matching candidate for image URI u'foo.*'" in \
- app._warning.content[-1]
+ assert image_uri_message in app._warning.content[-1]
assert set(latexbuilder.images.keys()) == \
set(['subdir/img.png', 'subdir/simg.png', 'img.png', 'img.pdf',
'svgimg.pdf'])
diff --git a/tests/test_intersphinx.py b/tests/test_intersphinx.py
index 622243e6..990e35bd 100644
--- a/tests/test_intersphinx.py
+++ b/tests/test_intersphinx.py
@@ -11,7 +11,10 @@
import zlib
import posixpath
-from cStringIO import StringIO
+try:
+ from io import BytesIO
+except ImportError:
+ from cStringIO import StringIO as BytesIO
from docutils import nodes
@@ -28,23 +31,23 @@ inventory_v1 = '''\
# Version: 1.0
module mod foo.html
module.cls class foo.html
-'''
+'''.encode('utf-8')
inventory_v2 = '''\
# Sphinx inventory version 2
# Project: foo
# Version: 2.0
# The remainder of this file is compressed with zlib.
-''' + zlib.compress('''\
+'''.encode('utf-8') + zlib.compress('''\
module1 py:module 0 foo.html#module-module1 Long Module desc
module2 py:module 0 foo.html#module-$ -
module1.func py:function 1 sub/foo.html#$ -
CFunc c:function 2 cfunc.html#CFunc -
-''')
+'''.encode('utf-8'))
def test_read_inventory_v1():
- f = StringIO(inventory_v1)
+ f = BytesIO(inventory_v1)
f.readline()
invdata = read_inventory_v1(f, '/util', posixpath.join)
assert invdata['py:module']['module'] == \
@@ -54,12 +57,12 @@ def test_read_inventory_v1():
def test_read_inventory_v2():
- f = StringIO(inventory_v2)
+ f = BytesIO(inventory_v2)
f.readline()
invdata1 = read_inventory_v2(f, '/util', posixpath.join)
# try again with a small buffer size to test the chunking algorithm
- f = StringIO(inventory_v2)
+ f = BytesIO(inventory_v2)
f.readline()
invdata2 = read_inventory_v2(f, '/util', posixpath.join, bufsize=5)
@@ -80,7 +83,10 @@ def test_read_inventory_v2():
def test_missing_reference(tempdir, app):
inv_file = tempdir / 'inventory'
write_file(inv_file, inventory_v2)
- app.config.intersphinx_mapping = {'http://docs.python.org/': inv_file}
+ app.config.intersphinx_mapping = {
+ 'http://docs.python.org/': inv_file,
+ 'py3k': ('http://docs.python.org/py3k/', inv_file),
+ }
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
@@ -91,22 +97,58 @@ def test_missing_reference(tempdir, app):
('foo', '2.0', 'http://docs.python.org/foo.html#module-module2', '-')
# create fake nodes and check referencing
- contnode = nodes.emphasis('foo')
- refnode = addnodes.pending_xref('')
- refnode['reftarget'] = 'module1.func'
- refnode['reftype'] = 'func'
- refnode['refdomain'] = 'py'
- rn = missing_reference(app, app.env, refnode, contnode)
+ def fake_node(domain, type, target, content, **attrs):
+ contnode = nodes.emphasis(content, content)
+ node = addnodes.pending_xref('')
+ node['reftarget'] = target
+ node['reftype'] = type
+ node['refdomain'] = domain
+ node.attributes.update(attrs)
+ node += contnode
+ return node, contnode
+
+ def reference_check(*args, **kwds):
+ node, contnode = fake_node(*args, **kwds)
+ return missing_reference(app, app.env, node, contnode)
+
+ # check resolution when a target is found
+ rn = reference_check('py', 'func', 'module1.func', 'foo')
assert isinstance(rn, nodes.reference)
assert rn['refuri'] == 'http://docs.python.org/sub/foo.html#module1.func'
assert rn['reftitle'] == '(in foo v2.0)'
- assert rn[0] is contnode
+ assert rn[0].astext() == 'foo'
# create unresolvable nodes and check None return value
- refnode['reftype'] = 'foo'
- assert missing_reference(app, app.env, refnode, contnode) is None
-
- refnode['reftype'] = 'function'
- refnode['reftarget'] = 'foo.func'
- assert missing_reference(app, app.env, refnode, contnode) is None
+ assert reference_check('py', 'foo', 'module1.func', 'foo') is None
+ assert reference_check('py', 'func', 'foo', 'foo') is None
+ assert reference_check('py', 'func', 'foo', 'foo') is None
+
+ # check handling of prefixes
+
+ # prefix given, target found: prefix is stripped
+ rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2')
+ assert rn[0].astext() == 'module2'
+
+ # prefix given, but not in title: nothing stripped
+ rn = reference_check('py', 'mod', 'py3k:module2', 'module2')
+ assert rn[0].astext() == 'module2'
+
+ # prefix given, but explicit: nothing stripped
+ rn = reference_check('py', 'mod', 'py3k:module2', 'py3k:module2',
+ refexplicit=True)
+ assert rn[0].astext() == 'py3k:module2'
+
+ # prefix given, target not found and nonexplicit title: prefix is stripped
+ node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
+ refexplicit=False)
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn is None
+ assert contnode[0].astext() == 'unknown'
+
+ # prefix given, target not found and explicit title: nothing is changed
+ node, contnode = fake_node('py', 'mod', 'py3k:unknown', 'py3k:unknown',
+ refexplicit=True)
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn is None
+ assert contnode[0].astext() == 'py3k:unknown'
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 31817df6..092113bb 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -17,6 +17,7 @@ from docutils import frontend, utils, nodes
from docutils.parsers import rst
from sphinx.util import texescape
+from sphinx.util.pycompat import b
from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
@@ -50,7 +51,7 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
def verify_re(rst, html_expected, latex_expected):
- document = utils.new_document('test data', settings)
+ document = utils.new_document(b('test data'), settings)
document['file'] = 'dummy'
parser.parse(rst, document)
for msg in document.traverse(nodes.system_message):
diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py
index cb40d27c..541959bd 100644
--- a/tests/test_quickstart.py
+++ b/tests/test_quickstart.py
@@ -36,8 +36,13 @@ def mock_raw_input(answers, needanswer=False):
return ''
return raw_input
+try:
+ real_raw_input = raw_input
+except NameError:
+ real_raw_input = input
+
def teardown_module():
- qs.raw_input = __builtin__.raw_input
+ qs.term_input = real_raw_input
qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None)
coloron()
@@ -51,7 +56,7 @@ def test_do_prompt():
'Q5': 'no',
'Q6': 'foo',
}
- qs.raw_input = mock_raw_input(answers)
+ qs.term_input = mock_raw_input(answers)
try:
qs.do_prompt(d, 'k1', 'Q1')
except AssertionError:
@@ -79,13 +84,18 @@ def test_quickstart_defaults(tempdir):
'Author name': 'Georg Brandl',
'Project version': '0.1',
}
- qs.raw_input = mock_raw_input(answers)
+ qs.term_input = mock_raw_input(answers)
qs.inner_main([])
conffile = tempdir / 'conf.py'
assert conffile.isfile()
ns = {}
- execfile(conffile, ns)
+ f = open(conffile, 'U')
+ try:
+ code = compile(f.read(), conffile, 'exec')
+ finally:
+ f.close()
+ exec code in ns
assert ns['extensions'] == []
assert ns['templates_path'] == ['_templates']
assert ns['source_suffix'] == '.rst'
@@ -112,8 +122,8 @@ def test_quickstart_all_answers(tempdir):
'Root path': tempdir,
'Separate source and build': 'y',
'Name prefix for templates': '.',
- 'Project name': 'STASI\xe2\x84\xa2',
- 'Author name': 'Wolfgang Sch\xc3\xa4uble & G\'Beckstein',
+ 'Project name': u'STASI™'.encode('utf-8'),
+ 'Author name': u'Wolfgang Schäuble & G\'Beckstein'.encode('utf-8'),
'Project version': '2.0',
'Project release': '2.0.1',
'Source file suffix': '.txt',
@@ -131,14 +141,19 @@ def test_quickstart_all_answers(tempdir):
'Create Windows command file': 'no',
'Do you want to use the epub builder': 'yes',
}
- qs.raw_input = mock_raw_input(answers, needanswer=True)
+ qs.term_input = mock_raw_input(answers, needanswer=True)
qs.TERM_ENCODING = 'utf-8'
qs.inner_main([])
conffile = tempdir / 'source' / 'conf.py'
assert conffile.isfile()
ns = {}
- execfile(conffile, ns)
+ f = open(conffile, 'U')
+ try:
+ code = compile(f.read(), conffile, 'exec')
+ finally:
+ f.close()
+ exec code in ns
assert ns['extensions'] == ['sphinx.ext.autodoc', 'sphinx.ext.doctest']
assert ns['templates_path'] == ['.templates']
assert ns['source_suffix'] == '.txt'
diff --git a/tests/test_search.py b/tests/test_search.py
index 0b5b158b..c0750366 100644
--- a/tests/test_search.py
+++ b/tests/test_search.py
@@ -13,6 +13,7 @@ from docutils import frontend, utils
from docutils.parsers import rst
from sphinx.search import IndexBuilder
+from sphinx.util.pycompat import b
settings = parser = None
@@ -31,7 +32,7 @@ test that non-comments are indexed: fermion
'''
def test_wordcollector():
- doc = utils.new_document('test data', settings)
+ doc = utils.new_document(b('test data'), settings)
doc['file'] = 'dummy'
parser.parse(FILE_CONTENTS, doc)
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
new file mode 100644
index 00000000..54a48f4a
--- /dev/null
+++ b/tests/test_versioning.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+"""
+ test_versioning
+ ~~~~~~~~~~~~~~~
+
+ Test the versioning implementation.
+
+ :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import pickle
+
+from util import *
+
+from docutils.statemachine import ViewList
+from docutils.parsers.rst.directives.html import MetaBody
+
+from sphinx import addnodes
+from sphinx.versioning import make_diff, add_uids, merge_doctrees
+
+def setup_module():
+ global app, original, original_uids
+ app = TestApp()
+ app.builder.env.app = app
+ app.connect('doctree-resolved', on_doctree_resolved)
+ app.build()
+ original = doctrees['versioning/original']
+ original_uids = [n.uid for n in add_uids(original, is_paragraph)]
+
+def teardown_module():
+ app.cleanup()
+ (test_root / '_build').rmtree(True)
+
+doctrees = {}
+
+def on_doctree_resolved(app, doctree, docname):
+ doctrees[docname] = doctree
+
+def test_make_diff():
+ tests = [
+ (('aaa', 'aaa'), (True, False, False)),
+ (('aaa', 'aab'), (False, True, False)),
+ (('aaa', 'abb'), (False, True, False)),
+ (('aaa', 'aba'), (False, True, False)),
+ (('aaa', 'baa'), (False, True, False)),
+ (('aaa', 'bbb'), (False, False, True))
+ ]
+ for args, result in tests:
+ assert make_diff(*args) == result
+
+def is_paragraph(node):
+ return node.__class__.__name__ == 'paragraph'
+
+def test_add_uids():
+ assert len(original_uids) == 3
+
+def test_picklablility():
+ # we have to modify the doctree so we can pickle it
+ copy = original.copy()
+ copy.reporter = None
+ copy.transformer = None
+ copy.settings.warning_stream = None
+ copy.settings.env = None
+ copy.settings.record_dependencies = None
+ for metanode in copy.traverse(MetaBody.meta):
+ metanode.__class__ = addnodes.meta
+ loaded = pickle.loads(pickle.dumps(copy, pickle.HIGHEST_PROTOCOL))
+ assert all(getattr(n, 'uid', False) for n in loaded.traverse(is_paragraph))
+
+def test_modified():
+ modified = doctrees['versioning/modified']
+ new_nodes = list(merge_doctrees(original, modified, is_paragraph))
+ uids = [n.uid for n in modified.traverse(is_paragraph)]
+ assert not new_nodes
+ assert original_uids == uids
+
+def test_added():
+ added = doctrees['versioning/added']
+ new_nodes = list(merge_doctrees(original, added, is_paragraph))
+ uids = [n.uid for n in added.traverse(is_paragraph)]
+ assert len(new_nodes) == 1
+ assert original_uids == uids[:-1]
+
+def test_deleted():
+ deleted = doctrees['versioning/deleted']
+ new_nodes = list(merge_doctrees(original, deleted, is_paragraph))
+ uids = [n.uid for n in deleted.traverse(is_paragraph)]
+ assert not new_nodes
+ assert original_uids[::2] == uids
+
+def test_deleted_end():
+ deleted_end = doctrees['versioning/deleted_end']
+ new_nodes = list(merge_doctrees(original, deleted_end, is_paragraph))
+ uids = [n.uid for n in deleted_end.traverse(is_paragraph)]
+ assert not new_nodes
+ assert original_uids[:-1] == uids
+
+def test_insert():
+ insert = doctrees['versioning/insert']
+ new_nodes = list(merge_doctrees(original, insert, is_paragraph))
+ uids = [n.uid for n in insert.traverse(is_paragraph)]
+ assert len(new_nodes) == 1
+ assert original_uids[0] == uids[0]
+ assert original_uids[1:] == uids[2:]
diff --git a/tests/util.py b/tests/util.py
index 1b24af0e..b81e15b6 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -11,6 +11,8 @@ import sys
import StringIO
import tempfile
import shutil
+import re
+from codecs import open
try:
from functools import wraps
@@ -31,7 +33,7 @@ __all__ = [
'raises', 'raises_msg', 'Struct',
'ListOutput', 'TestApp', 'with_app', 'gen_with_app',
'path', 'with_tempdir', 'write_file',
- 'sprint',
+ 'sprint', 'remove_unicode_literals',
]
@@ -191,11 +193,21 @@ def with_tempdir(func):
return new_func
-def write_file(name, contents):
- f = open(str(name), 'wb')
+def write_file(name, contents, encoding=None):
+ if encoding is None:
+ mode = 'wb'
+ if isinstance(contents, unicode):
+ contents = contents.encode('ascii')
+ else:
+ mode = 'w'
+ f = open(str(name), 'wb', encoding=encoding)
f.write(contents)
f.close()
def sprint(*args):
sys.stderr.write(' '.join(map(str, args)) + '\n')
+
+_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
+def remove_unicode_literals(s):
+ return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..2d9d18f8
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,17 @@
+[tox]
+envlist=du07,du06,du05
+
+[testenv]
+deps=nose
+commands=
+ nosetests
+ sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html
+
+[testenv:du05]
+deps=docutils==0.5
+
+[testenv:du06]
+deps=docutils==0.6
+
+[testenv:du07]
+deps=docutils==0.7
diff --git a/utils/check_sources.py b/utils/check_sources.py
index 0571ab1e..c412742b 100755
--- a/utils/check_sources.py
+++ b/utils/check_sources.py
@@ -12,10 +12,16 @@
"""
import sys, os, re
-import getopt
import cStringIO
+from optparse import OptionParser
from os.path import join, splitext, abspath
+if sys.version_info >= (3, 0):
+ def b(s):
+ return s.encode('utf-8')
+else:
+ b = str
+
checkers = {}
@@ -30,26 +36,26 @@ def checker(*suffixes, **kwds):
name_mail_re = r'[\w ]+(<.*?>)?'
-copyright_re = re.compile(r'^ :copyright: Copyright 200\d(-20\d\d)? '
- r'by %s(, %s)*[,.]$' %
- (name_mail_re, name_mail_re))
-license_re = re.compile(r" :license: (.*?).\n")
-copyright_2_re = re.compile(r'^ %s(, %s)*[,.]$' %
- (name_mail_re, name_mail_re))
-coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
-not_ix_re = re.compile(r'\bnot\s+\S+?\s+i[sn]\s\S+')
-is_const_re = re.compile(r'if.*?==\s+(None|False|True)\b')
-
-misspellings = ["developement", "adress", "verificate", # ALLOW-MISSPELLING
- "informations"] # ALLOW-MISSPELLING
-
-
-@checker('.py')
-def check_syntax(fn, lines):
- try:
- compile(''.join(lines), fn, "exec")
- except SyntaxError, err:
- yield 0, "not compilable: %s" % err
+copyright_re = re.compile(b(r'^ :copyright: Copyright 200\d(-20\d\d)? '
+ r'by %s(, %s)*[,.]$' %
+ (name_mail_re, name_mail_re)))
+license_re = re.compile(b(r" :license: (.*?).\n"))
+copyright_2_re = re.compile(b(r'^ %s(, %s)*[,.]$' %
+ (name_mail_re, name_mail_re)))
+coding_re = re.compile(b(r'coding[:=]\s*([-\w.]+)'))
+not_ix_re = re.compile(b(r'\bnot\s+\S+?\s+i[sn]\s\S+'))
+is_const_re = re.compile(b(r'if.*?==\s+(None|False|True)\b'))
+
+misspellings = [b("developement"), b("adress"), # ALLOW-MISSPELLING
+ b("verificate"), b("informations")] # ALLOW-MISSPELLING
+
+if sys.version_info < (3, 0):
+ @checker('.py')
+ def check_syntax(fn, lines):
+ try:
+ compile(b('').join(lines), fn, "exec")
+ except SyntaxError, err:
+ yield 0, "not compilable: %s" % err
@checker('.py')
@@ -61,8 +67,8 @@ def check_style_and_encoding(fn, lines):
if lno < 2:
co = coding_re.search(line)
if co:
- encoding = co.group(1)
- if line.strip().startswith('#'):
+ encoding = co.group(1).decode('ascii')
+ if line.strip().startswith(b('#')):
continue
#m = not_ix_re.search(line)
#if m:
@@ -82,7 +88,7 @@ def check_style_and_encoding(fn, lines):
def check_fileheader(fn, lines):
# line number correction
c = 1
- if lines[0:1] == ['#!/usr/bin/env python\n']:
+ if lines[0:1] == [b('#!/usr/bin/env python\n')]:
lines = lines[1:]
c = 2
@@ -91,38 +97,38 @@ def check_fileheader(fn, lines):
for lno, l in enumerate(lines):
llist.append(l)
if lno == 0:
- if l == '# -*- coding: rot13 -*-\n':
+ if l == b('# -*- coding: rot13 -*-\n'):
# special-case pony package
return
- elif l != '# -*- coding: utf-8 -*-\n':
+ elif l != b('# -*- coding: utf-8 -*-\n'):
yield 1, "missing coding declaration"
elif lno == 1:
- if l != '"""\n' and l != 'r"""\n':
+ if l != b('"""\n') and l != b('r"""\n'):
yield 2, 'missing docstring begin (""")'
else:
docopen = True
elif docopen:
- if l == '"""\n':
+ if l == b('"""\n'):
# end of docstring
if lno <= 4:
yield lno+c, "missing module name in docstring"
break
- if l != "\n" and l[:4] != ' ' and docopen:
+ if l != b("\n") and l[:4] != b(' ') and docopen:
yield lno+c, "missing correct docstring indentation"
if lno == 2:
# if not in package, don't check the module name
modname = fn[:-3].replace('/', '.').replace('.__init__', '')
while modname:
- if l.lower()[4:-1] == modname:
+ if l.lower()[4:-1] == b(modname):
break
modname = '.'.join(modname.split('.')[1:])
else:
yield 3, "wrong module name in docstring heading"
modnamelen = len(l.strip())
elif lno == 3:
- if l.strip() != modnamelen * "~":
+ if l.strip() != modnamelen * b("~"):
yield 4, "wrong module name underline, should be ~~~...~"
else:
@@ -145,16 +151,16 @@ def check_fileheader(fn, lines):
@checker('.py', '.html', '.rst')
def check_whitespace_and_spelling(fn, lines):
for lno, line in enumerate(lines):
- if "\t" in line:
+ if b("\t") in line:
yield lno+1, "OMG TABS!!!1 "
- if line[:-1].rstrip(' \t') != line[:-1]:
+ if line[:-1].rstrip(b(' \t')) != line[:-1]:
yield lno+1, "trailing whitespace"
for word in misspellings:
- if word in line and 'ALLOW-MISSPELLING' not in line:
+ if word in line and b('ALLOW-MISSPELLING') not in line:
yield lno+1, '"%s" used' % word
-bad_tags = ('<u>', '<s>', '<strike>', '<center>', '<font')
+bad_tags = map(b, ['<u>', '<s>', '<strike>', '<center>', '<font'])
@checker('.html')
def check_xhtml(fn, lines):
@@ -165,34 +171,32 @@ def check_xhtml(fn, lines):
def main(argv):
- try:
- gopts, args = getopt.getopt(argv[1:], "vi:")
- except getopt.GetoptError:
- print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
- return 2
- opts = {}
- for opt, val in gopts:
- if opt == '-i':
- val = abspath(val)
- opts.setdefault(opt, []).append(val)
+ parser = OptionParser(usage='Usage: %prog [-v] [-i ignorepath]* [path]')
+ parser.add_option('-v', '--verbose', dest='verbose', default=False,
+ action='store_true')
+ parser.add_option('-i', '--ignore-path', dest='ignored_paths',
+ default=[], action='append')
+ options, args = parser.parse_args(argv[1:])
if len(args) == 0:
path = '.'
elif len(args) == 1:
path = args[0]
else:
- print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
- return 2
+ print args
+ parser.error('No more then one path supported')
- verbose = '-v' in opts
+ verbose = options.verbose
+ ignored_paths = set(abspath(p) for p in options.ignored_paths)
num = 0
out = cStringIO.StringIO()
for root, dirs, files in os.walk(path):
- if '.svn' in dirs:
- dirs.remove('.svn')
- if '-i' in opts and abspath(root) in opts['-i']:
+ for vcs_dir in ['.svn', '.hg', '.git']:
+ if vcs_dir in dirs:
+ dirs.remove(vcs_dir)
+ if abspath(root) in ignored_paths:
del dirs[:]
continue
in_check_pkg = root.startswith('./sphinx')
@@ -201,7 +205,7 @@ def main(argv):
fn = join(root, fn)
if fn[:2] == './': fn = fn[2:]
- if '-i' in opts and abspath(fn) in opts['-i']:
+ if abspath(fn) in ignored_paths:
continue
ext = splitext(fn)[1]
@@ -213,8 +217,11 @@ def main(argv):
print "Checking %s..." % fn
try:
- f = open(fn, 'r')
- lines = list(f)
+ f = open(fn, 'rb')
+ try:
+ lines = list(f)
+ finally:
+ f.close()
except (IOError, OSError), err:
print "%s: cannot open: %s" % (fn, err)
num += 1
diff --git a/utils/convert.py b/utils/convert.py
new file mode 100644
index 00000000..f025c49a
--- /dev/null
+++ b/utils/convert.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# coding: utf-8
+"""
+ Converts files with 2to3
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Creates a Python 3 version of each file.
+
+ The Python3 version of a file foo.py will be called foo3.py.
+
+ :copyright: Copyright 2010 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import os
+import sys
+from glob import iglob
+from optparse import OptionParser
+from shutil import copy
+from distutils.util import run_2to3
+
+def main(argv):
+ parser = OptionParser(usage='%prog [path]')
+ parser.add_option('-i', '--ignorepath', dest='ignored_paths',
+ action='append', default=[])
+ options, args = parser.parse_args(argv)
+
+ ignored_paths = {os.path.abspath(p) for p in options.ignored_paths}
+
+ path = os.path.abspath(args[0]) if args else os.getcwd()
+ convertables = []
+ for filename in iglob(os.path.join(path, '*.py')):
+ if filename in ignored_paths:
+ continue
+ basename, ext = os.path.splitext(filename)
+ if basename.endswith('3'):
+ continue
+ filename3 = basename + '3' + ext
+ copy(filename, filename3)
+ convertables.append(filename3)
+ run_2to3(convertables)
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/utils/reindent.py b/utils/reindent.py
index c499f671..59828fd8 100755
--- a/utils/reindent.py
+++ b/utils/reindent.py
@@ -1,16 +1,14 @@
#! /usr/bin/env python
# Released to the public domain, by Tim Peters, 03 October 2000.
-# -B option added by Georg Brandl, 2006.
"""reindent [-d][-r][-v] [ path ... ]
--d (--dryrun) Dry run. Analyze, but don't make any changes to files.
--r (--recurse) Recurse. Search for all .py files in subdirectories too.
--B (--no-backup) Don't write .bak backup files.
--v (--verbose) Verbose. Print informative msgs; else only names of \
-changed files.
--h (--help) Help. Print this usage information and exit.
+-d (--dryrun) Dry run. Analyze, but don't make any changes to, files.
+-r (--recurse) Recurse. Search for all .py files in subdirectories too.
+-n (--nobackup) No backup. Does not make a ".bak" file before reindenting.
+-v (--verbose) Verbose. Print informative msgs; else no output.
+-h (--help) Help. Print this usage information and exit.
Change Python (.py) files to use 4-space indents and no hard tab characters.
Also trim excess spaces and tabs from ends of lines, and remove empty lines
@@ -34,18 +32,30 @@ resulting .py file won't change it again).
The hard part of reindenting is figuring out what to do with comment
lines. So long as the input files get a clean bill of health from
tabnanny.py, reindent should do a good job.
+
+The backup file is a copy of the one that is being reindented. The ".bak"
+file is generated with shutil.copy(), but some corner cases regarding
+user/group and permissions could leave the backup file more readable that
+you'd prefer. You can always use the --nobackup option to prevent this.
"""
__version__ = "1"
import tokenize
-import os
+import os, shutil
import sys
-verbose = 0
-recurse = 0
-dryrun = 0
-no_backup = 0
+if sys.version_info >= (3, 0):
+ def tokens(readline, tokeneater):
+ for token in tokenize.tokenize(readline):
+ yield tokeneater(*token)
+else:
+ tokens = tokenize.tokenize
+
+verbose = 0
+recurse = 0
+dryrun = 0
+makebackup = True
def usage(msg=None):
if msg is not None:
@@ -61,12 +71,10 @@ def errprint(*args):
def main():
import getopt
- global verbose, recurse, dryrun, no_backup
-
+ global verbose, recurse, dryrun, makebackup
try:
- opts, args = getopt.getopt(sys.argv[1:], "drvhB",
- ["dryrun", "recurse", "verbose", "help",
- "no-backup"])
+ opts, args = getopt.getopt(sys.argv[1:], "drnvh",
+ ["dryrun", "recurse", "nobackup", "verbose", "help"])
except getopt.error, msg:
usage(msg)
return
@@ -75,10 +83,10 @@ def main():
dryrun += 1
elif o in ('-r', '--recurse'):
recurse += 1
+ elif o in ('-n', '--nobackup'):
+ makebackup = False
elif o in ('-v', '--verbose'):
verbose += 1
- elif o in ('-B', '--no-backup'):
- no_backup += 1
elif o in ('-h', '--help'):
usage()
return
@@ -98,7 +106,8 @@ def check(file):
for name in names:
fullname = os.path.join(file, name)
if ((recurse and os.path.isdir(fullname) and
- not os.path.islink(fullname))
+ not os.path.islink(fullname) and
+ not os.path.split(fullname)[1].startswith("."))
or name.lower().endswith(".py")):
check(fullname)
return
@@ -118,26 +127,35 @@ def check(file):
print "changed."
if dryrun:
print "But this is a dry run, so leaving it alone."
- else:
- print "reindented", file, \
- (dryrun and "(dry run => not really)" or "")
if not dryrun:
- if not no_backup:
- bak = file + ".bak"
- if os.path.exists(bak):
- os.remove(bak)
- os.rename(file, bak)
+ bak = file + ".bak"
+ if makebackup:
+ shutil.copyfile(file, bak)
if verbose:
- print "renamed", file, "to", bak
+ print "backed up", file, "to", bak
f = open(file, "w")
r.write(f)
f.close()
if verbose:
print "wrote new", file
+ return True
else:
if verbose:
print "unchanged."
+ return False
+
+def _rstrip(line, JUNK='\n \t'):
+ """Return line stripped of trailing spaces, tabs, newlines.
+
+ Note that line.rstrip() instead also strips sundry control characters,
+ but at least one known Emacs user expects to keep junk like that, not
+ mentioning Barry by name or anything <wink>.
+ """
+ i = len(line)
+ while i > 0 and line[i-1] in JUNK:
+ i -= 1
+ return line[:i]
class Reindenter:
@@ -151,7 +169,7 @@ class Reindenter:
# File lines, rstripped & tab-expanded. Dummy at start is so
# that we can use tokenize's 1-based line numbering easily.
# Note that a line is all-blank iff it's "\n".
- self.lines = [line.rstrip('\n \t').expandtabs() + "\n"
+ self.lines = [_rstrip(line).expandtabs() + "\n"
for line in self.raw]
self.lines.insert(0, None)
self.index = 1 # index into self.lines of next line
@@ -163,7 +181,7 @@ class Reindenter:
self.stats = []
def run(self):
- tokenize.tokenize(self.getline, self.tokeneater)
+ tokens(self.getline, self.tokeneater)
# Remove trailing empty lines.
lines = self.lines
while lines and lines[-1] == "\n":