Merge branch 'stable'

Conflicts:
	CHANGES
	sphinx/__init__.py
This commit is contained in:
shimizukawa 2015-03-17 18:26:27 +09:00
commit e2da583be8
26 changed files with 152 additions and 70 deletions

15
CHANGES
View File

@ -14,8 +14,8 @@ Documentation
-------------
Release 1.3.1 (in development)
===============================
Release 1.3.1 (released Mar 17, 2015)
=====================================
Bugs fixed
----------
@ -23,7 +23,16 @@ Bugs fixed
* #1769: allows generating quickstart files/dirs for destination dir that
doesn't overwrite existent files/dirs. Thanks to WAKAYAMA shirou.
* #1773: sphinx-quickstart doesn't accept non-ASCII character as a option argument.
* #1766: the message "least Python 2.6 to run" is at best misleading.
* #1772: cross reference in docstrings like ``:param .write:`` breaks building.
* #1770, #1774: ``literalinclude`` with empty file occurs exception. Thanks to
Takayuki Hirai.
* #1777: Sphinx 1.3 can't load extra theme. Thanks to tell-k.
* #1776: ``source_suffix = ['.rst']`` cause unfriendly error on prior version.
* #1771: automated .mo building doesn't work properly.
* #1783: Autodoc: Python2 Allow unicode string in __all__.
Thanks to Jens Hedegaard Nielsen.
* #1781: Setting `html_domain_indices` to a list raises a type check warnings.
Release 1.3 (released Mar 10, 2015)
===================================

View File

@ -263,5 +263,5 @@ following directive exists:
format and the builder name.
Note that the current builder tag is not available in ``conf.py``, it is
only available after the builder is intialized.
only available after the builder is initialized.

View File

@ -55,7 +55,7 @@ def build_main(argv=sys.argv):
"""Sphinx build "main" command-line entry."""
if (sys.version_info[:3] < (2, 6, 0) or
(3, 0, 0) <= sys.version_info[:3] < (3, 3, 0)):
sys.stderr.write('Error: Sphinx requires at least Python 2.6 to run.\n')
sys.stderr.write('Error: Sphinx requires at least Python 2.6 or 3.3 to run.\n')
return 1
try:
from sphinx import cmdline

View File

@ -21,7 +21,8 @@ except ImportError:
from docutils import nodes
from sphinx.util import i18n, path_stabilize
from sphinx.util.osutil import SEP, relative_uri, find_catalog
from sphinx.util.osutil import SEP, relative_uri
from sphinx.util.i18n import find_catalog
from sphinx.util.console import bold, darkgreen
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \
parallel_available
@ -166,9 +167,11 @@ class Builder(object):
catalog.write_mo(self.config.language)
def compile_all_catalogs(self):
catalogs = i18n.get_catalogs(
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language, True)
self.config.language,
gettext_compact=self.config.gettext_compact,
force_all=True)
message = 'all of %d po files' % len(catalogs)
self.compile_catalogs(catalogs, message)
@ -179,17 +182,19 @@ class Builder(object):
return dom
specified_domains = set(map(to_domain, specified_files))
catalogs = i18n.get_catalogs(
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language, True)
catalogs = [f for f in catalogs if f.domain in specified_domains]
self.config.language,
domains=list(specified_domains),
gettext_compact=self.config.gettext_compact)
message = 'targets for %d po files that are specified' % len(catalogs)
self.compile_catalogs(catalogs, message)
def compile_update_catalogs(self):
catalogs = i18n.get_catalogs(
catalogs = i18n.find_catalog_source_files(
[path.join(self.srcdir, x) for x in self.config.locale_dirs],
self.config.language)
self.config.language,
gettext_compact=self.config.gettext_compact)
message = 'targets for %d po files that are out of date' % len(catalogs)
self.compile_catalogs(catalogs, message)

View File

@ -23,7 +23,8 @@ from six import iteritems
from sphinx.builders import Builder
from sphinx.util import split_index_msg
from sphinx.util.nodes import extract_messages, traverse_translatable_index
from sphinx.util.osutil import safe_relpath, ensuredir, find_catalog, SEP
from sphinx.util.osutil import safe_relpath, ensuredir, SEP
from sphinx.util.i18n import find_catalog
from sphinx.util.console import darkgreen, purple, bold
from sphinx.locale import pairindextypes

View File

@ -28,6 +28,10 @@ if PY3:
CONFIG_EXIT_ERROR = "The configuration file (or one of the modules it imports) " \
"called sys.exit()"
IGNORE_CONFIG_TYPE_CHECKS = (
'html_domain_indices', 'latex_domain_indices', 'texinfo_domain_indices'
)
class Config(object):
"""
@ -288,6 +292,8 @@ class Config(object):
# NB. since config values might use l_() we have to wait with calling
# this method until i18n is initialized
for name in self._raw_config:
if name in IGNORE_CONFIG_TYPE_CHECKS:
continue # for a while, ignore multiple types config value. see #1781
if name not in Config.config_values:
continue # we don't know a default value
default, dummy_rebuild = Config.config_values[name]

View File

@ -219,7 +219,7 @@ class LiteralInclude(Directive):
lines = self.read_with_encoding(filename, document,
codec_info, encoding)
if not isinstance(lines[0], string_types):
if lines and not isinstance(lines[0], string_types):
return lines
diffsource = self.options.get('diff')

View File

@ -90,13 +90,15 @@ class PyXrefMixin(object):
result = super(PyXrefMixin, self).make_xref(rolename, domain, target,
innernode, contnode)
result['refspecific'] = True
if target.startswith('.'):
result['reftarget'] = target[1:]
result[0][0] = nodes.Text(target[1:])
if target.startswith('~'):
result['reftarget'] = target[1:]
title = target.split('.')[-1]
result[0][0] = nodes.Text(title)
if target.startswith(('.', '~')):
prefix, result['reftarget'] = target[0], target[1:]
if prefix == '.':
text = target[1:]
elif prefix == '~':
text = target.split('.')[-1]
for node in result.traverse(nodes.Text):
node.parent[node.parent.index(node)] = nodes.Text(text)
break
return result

View File

@ -40,7 +40,8 @@ from sphinx import addnodes
from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
FilenameUniqDict, get_figtype, import_object
from sphinx.util.nodes import clean_astext, make_refnode, WarningStream, is_translatable
from sphinx.util.osutil import SEP, find_catalog_files, getcwd, fs_encoding
from sphinx.util.osutil import SEP, getcwd, fs_encoding
from sphinx.util.i18n import find_catalog_files
from sphinx.util.console import bold, purple
from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks

View File

@ -17,7 +17,7 @@ import inspect
import traceback
from types import FunctionType, BuiltinFunctionType, MethodType
from six import iteritems, itervalues, text_type, class_types
from six import iteritems, itervalues, text_type, class_types, string_types
from docutils import nodes
from docutils.utils import assemble_option_dict
from docutils.statemachine import ViewList
@ -888,7 +888,7 @@ class ModuleDocumenter(Documenter):
memberlist = self.object.__all__
# Sometimes __all__ is broken...
if not isinstance(memberlist, (list, tuple)) or not \
all(isinstance(entry, str) for entry in memberlist):
all(isinstance(entry, string_types) for entry in memberlist):
self.directive.warn(
'__all__ should be a list of strings, not %r '
'(in module %s) -- ignoring __all__' %

View File

@ -208,6 +208,14 @@ def init(locale_dirs, language, catalog='sphinx'):
translator = None
# the None entry is the system's default locale path
has_translation = True
# compile mo files if po file is updated
# TODO: remove circular importing
from sphinx.util.i18n import find_catalog_source_files
for catinfo in find_catalog_source_files(locale_dirs, language, domains=[catalog]):
catinfo.write_mo(language)
# loading
for dir_ in locale_dirs:
try:
trans = gettext.translation(catalog, localedir=dir_,

View File

@ -108,7 +108,9 @@ extensions = [%(extensions)s]
templates_path = ['%(dot)stemplates']
# The suffix(es) of source filenames.
source_suffix = ['%(suffix)s']
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
source_suffix = '%(suffix)s'
# The encoding of source files.
#source_encoding = 'utf-8-sig'

View File

@ -86,7 +86,7 @@ class Theme(object):
if not path.isdir(themedir):
continue
for theme in os.listdir(themedir):
if theme != 'name':
if theme != name:
continue
if not path.isfile(path.join(themedir, theme, THEMECONF)):
continue

View File

@ -23,7 +23,8 @@ from sphinx.util import split_index_msg
from sphinx.util.nodes import (
traverse_translatable_index, extract_messages, LITERAL_TYPE_NODES, IMAGE_TYPE_NODES,
)
from sphinx.util.osutil import ustrftime, find_catalog
from sphinx.util.osutil import ustrftime
from sphinx.util.i18n import find_catalog
from sphinx.util.pycompat import indent
from sphinx.domains.std import (
make_term_from_paragraph_node,

View File

@ -8,7 +8,7 @@
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import gettext
from os import path
from collections import namedtuple
@ -16,6 +16,7 @@ from babel.messages.pofile import read_po
from babel.messages.mofile import write_mo
from sphinx.util.osutil import walk
from sphinx.util import SEP
LocaleFileInfoBase = namedtuple('CatalogInfo', 'base_dir,domain')
@ -50,13 +51,36 @@ class CatalogInfo(LocaleFileInfoBase):
write_mo(mo, read_po(po, locale))
def get_catalogs(locale_dirs, locale, gettext_compact=False, force_all=False):
def find_catalog(docname, compaction):
if compaction:
ret = docname.split(SEP, 1)[0]
else:
ret = docname
return ret
def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction):
if not(lang and locale_dirs):
return []
domain = find_catalog(docname, compaction)
files = [gettext.find(domain, path.join(srcdir, dir_), [lang])
for dir_ in locale_dirs]
files = [path.relpath(f, srcdir) for f in files if f]
return files
def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact=False,
force_all=False):
"""
:param list locale_dirs:
list of path as `['locale_dir1', 'locale_dir2', ...]` to find
translation catalogs. Each path contains a structure such as
`<locale>/LC_MESSAGES/domain.po`.
:param str locale: a language as `'en'`
:param list domains: list of domain names to get. If empty list or None
is specified, get all domain names. default is None.
:param boolean gettext_compact:
* False: keep domains directory structure (default).
* True: domains in the sub directory will be merged into 1 file.
@ -70,6 +94,9 @@ def get_catalogs(locale_dirs, locale, gettext_compact=False, force_all=False):
catalogs = set()
for locale_dir in locale_dirs:
if not locale_dir:
continue # skip system locale directory
base_dir = path.join(locale_dir, locale, 'LC_MESSAGES')
if not path.exists(base_dir):
@ -82,6 +109,9 @@ def get_catalogs(locale_dirs, locale, gettext_compact=False, force_all=False):
domain = path.relpath(path.join(dirpath, base), base_dir)
if gettext_compact and path.sep in domain:
domain = path.split(domain)[0]
domain = domain.replace(path.sep, SEP)
if domains and domain not in domains:
continue
cat = CatalogInfo(base_dir, domain)
if force_all or cat.is_outdated():
catalogs.add(cat)

View File

@ -17,7 +17,6 @@ import time
import errno
import locale
import shutil
import gettext
from os import path
import contextlib
@ -170,26 +169,6 @@ def safe_relpath(path, start=None):
return path
def find_catalog(docname, compaction):
if compaction:
ret = docname.split(SEP, 1)[0]
else:
ret = docname
return ret
def find_catalog_files(docname, srcdir, locale_dirs, lang, compaction):
if not(lang and locale_dirs):
return []
domain = find_catalog(docname, compaction)
files = [gettext.find(domain, path.join(srcdir, dir_), [lang])
for dir_ in locale_dirs]
files = [path.relpath(f, srcdir) for f in files if f]
return files
fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()

View File

@ -0,0 +1,2 @@
recursive-include test_theme *.conf

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
html_theme = 'test-theme'
master_doc = 'index'

View File

@ -0,0 +1,5 @@
=======
Theming
=======

View File

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages
setup(
name='test-theme',
packages=find_packages(),
include_package_data=True,
entry_points="""
[sphinx_themes]
path = test_theme:get_path
""",
)

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
import os
def get_path():
return os.path.dirname(os.path.abspath(__file__))

View File

@ -0,0 +1,2 @@
[theme]
inherit = classic

View File

@ -55,10 +55,13 @@ def test_compile_all_catalogs(app, status, warning):
@with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_specific_catalogs(app, status, warning):
app.builder.compile_specific_catalogs(['admonitions'])
catalog_dir = locale_dir / app.config.language / 'LC_MESSAGES'
actual = set(find_files(catalog_dir, '.mo'))
def get_actual():
return set(find_files(catalog_dir, '.mo'))
actual_on_boot = get_actual() # sphinx.mo might be included
app.builder.compile_specific_catalogs(['admonitions'])
actual = get_actual() - actual_on_boot
assert actual == set(['admonitions.mo'])

View File

@ -144,7 +144,7 @@ def test_quickstart_defaults(tempdir):
execfile_(conffile, ns)
assert ns['extensions'] == []
assert ns['templates_path'] == ['_templates']
assert ns['source_suffix'] == ['.rst']
assert ns['source_suffix'] == '.rst'
assert ns['master_doc'] == 'index'
assert ns['project'] == 'Sphinx Test'
assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y')
@ -203,7 +203,7 @@ def test_quickstart_all_answers(tempdir):
'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo'
]
assert ns['templates_path'] == ['.templates']
assert ns['source_suffix'] == ['.txt']
assert ns['source_suffix'] == '.txt'
assert ns['master_doc'] == 'contents'
assert ns['project'] == u'STASI™'
assert ns['copyright'] == u'%s, Wolfgang Schäuble & G\'Beckstein' % \

View File

@ -14,7 +14,7 @@ import zipfile
from sphinx.theming import Theme, ThemeError
from util import with_app, raises
from util import with_app, raises, TestApp
@with_app(confoverrides={'html_theme': 'ziptheme',

View File

@ -72,13 +72,13 @@ def test_get_catalogs_for_xx(dir):
(dir / 'loc1' / 'xx' / 'LC_ALL').makedirs()
(dir / 'loc1' / 'xx' / 'LC_ALL' / 'test7.po').write_text('#')
catalogs = i18n.get_catalogs([dir / 'loc1'], 'xx', force_all=False)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=False)
domains = set(c.domain for c in catalogs)
assert domains == set([
'test1',
'test2',
path.normpath('sub/test4'),
path.normpath('sub/test5'),
'sub/test4',
'sub/test5',
])
@ -89,22 +89,22 @@ def test_get_catalogs_for_en(dir):
(dir / 'loc1' / 'en' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'en' / 'LC_MESSAGES' / 'en_dom.po').write_text('#')
catalogs = i18n.get_catalogs([dir / 'loc1'], 'en', force_all=False)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'en', force_all=False)
domains = set(c.domain for c in catalogs)
assert domains == set(['en_dom'])
@with_tempdir
def test_get_catalogs_with_non_existent_locale(dir):
catalogs = i18n.get_catalogs([dir / 'loc1'], 'xx')
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx')
assert not catalogs
catalogs = i18n.get_catalogs([dir / 'loc1'], None)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], None)
assert not catalogs
def test_get_catalogs_with_non_existent_locale_dirs():
catalogs = i18n.get_catalogs(['dummy'], 'xx')
catalogs = i18n.find_catalog_source_files(['dummy'], 'xx')
assert not catalogs
@ -123,16 +123,16 @@ def test_get_catalogs_for_xx_without_outdated(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.mo').write_text('#')
catalogs = i18n.get_catalogs([dir / 'loc1'], 'xx', force_all=False)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=False)
assert not catalogs
catalogs = i18n.get_catalogs([dir / 'loc1'], 'xx', force_all=True)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=True)
domains = set(c.domain for c in catalogs)
assert domains == set([
'test1',
'test2',
path.normpath('sub/test4'),
path.normpath('sub/test5'),
'sub/test4',
'sub/test5',
])
@ -144,7 +144,7 @@ def test_get_catalogs_from_multiple_locale_dirs(dir):
(dir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
catalogs = i18n.get_catalogs([dir / 'loc1', dir / 'loc2'], 'xx')
catalogs = i18n.find_catalog_source_files([dir / 'loc1', dir / 'loc2'], 'xx')
domains = sorted(c.domain for c in catalogs)
assert domains == ['test1', 'test1', 'test2']
@ -158,6 +158,6 @@ def test_get_catalogs_with_compact(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test3.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
catalogs = i18n.get_catalogs([dir / 'loc1'], 'xx', gettext_compact=True)
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', gettext_compact=True)
domains = set(c.domain for c in catalogs)
assert domains == set(['test1', 'test2', 'sub'])