diff --git a/.travis.yml b/.travis.yml index fe93e50d2..7549a579d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,11 @@ python: - "3.5" - "pypy" env: - - DOCUTILS=0.11 - - DOCUTILS=0.12 + global: + - TEST=-v + matrix: + - DOCUTILS=0.11 + - DOCUTILS=0.12 install: - pip install -U pip - pip install docutils==$DOCUTILS diff --git a/CHANGES b/CHANGES index 4ea9543c2..0ac6985f5 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,7 @@ Bugs fixed * #2436: Sphinx does not check version by :confval:`needs_sphinx` if loading extensions failed * #2397: Setup shorthandoff for turkish documents * #2447: VerbatimBorderColor wrongly used also for captions of PDF +* #2456: C++, fix crash related to document merging (e.g., singlehtml and Latex builders). Release 1.4.1 (released Apr 12, 2016) diff --git a/doc/devguide.rst b/doc/devguide.rst index 313b026be..b621f622a 100644 --- a/doc/devguide.rst +++ b/doc/devguide.rst @@ -111,7 +111,7 @@ These are the basic steps needed to start developing on Sphinx. * Run the unit tests:: - pip install nose mock + pip install -r test-reqs.txt make test * Build the documentation and check the output for different builders:: diff --git a/sphinx/application.py b/sphinx/application.py index 1ac64508a..5ec129d4a 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -133,12 +133,14 @@ class Sphinx(object): self.config.check_unicode(self.warn) # defer checking types until i18n has been initialized + # initialize some limited config variables before loading extensions + self.config.pre_init_values(self.warn) + # check the Sphinx version if requested - needs_sphinx = self.config.get_needs_sphinx() - if needs_sphinx and needs_sphinx > sphinx.__display_version__: + if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: raise VersionRequirementError( 'This project needs at least Sphinx v%s and therefore cannot ' - 'be built with this version.' % needs_sphinx) + 'be built with this version.' % self.config.needs_sphinx) # set confdir to srcdir if -C given (!= no confdir); a few pieces # of code expect a confdir to be set diff --git a/sphinx/config.py b/sphinx/config.py index 5728b6d11..d03a7880b 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -338,44 +338,60 @@ class Config(object): 'characters; this can lead to Unicode errors occurring. ' 'Please use Unicode strings, e.g. %r.' % (name, u'Content')) - def get_needs_sphinx(self): - """Obtain the value of ``needs_sphinx``""" - return self.overrides.get('needs_sphinx') or self._raw_config.get('needs_sphinx') + def convert_overrides(self, name, value): + if not isinstance(value, string_types): + return value + else: + defvalue = self.values[name][0] + if isinstance(defvalue, dict): + raise ValueError('cannot override dictionary config setting %r, ' + 'ignoring (use %r to set individual elements)' % + (name, name + '.key=value')) + elif isinstance(defvalue, list): + return value.split(',') + elif isinstance(defvalue, integer_types): + try: + return int(value) + except ValueError: + raise ValueError('invalid number %r for config value %r, ignoring' % + (value, name)) + elif hasattr(defvalue, '__call__'): + return value + elif defvalue is not None and not isinstance(defvalue, string_types): + raise ValueError('cannot override config setting %r with unsupported ' + 'type, ignoring' % name) + else: + return value + + def pre_init_values(self, warn): + """Initialize some limited config variables before loading extensions""" + variables = ['needs_sphinx', 'suppress_warnings'] + for name in variables: + try: + if name in self.overrides: + self.__dict__[name] = self.convert_overrides(name, self.overrides[name]) + elif name in self._raw_config: + self.__dict__[name] = self._raw_config[name] + except ValueError as exc: + warn(exc) def init_values(self, warn): config = self._raw_config for valname, value in iteritems(self.overrides): - if '.' in valname: - realvalname, key = valname.split('.', 1) - config.setdefault(realvalname, {})[key] = value - continue - elif valname not in self.values: - warn('unknown config value %r in override, ignoring' % valname) - continue - defvalue = self.values[valname][0] - if isinstance(value, string_types): - if isinstance(defvalue, dict): - warn('cannot override dictionary config setting %r, ' - 'ignoring (use %r to set individual elements)' % - (valname, valname + '.key=value')) + try: + if '.' in valname: + realvalname, key = valname.split('.', 1) + config.setdefault(realvalname, {})[key] = value continue - elif isinstance(defvalue, list): - config[valname] = value.split(',') - elif isinstance(defvalue, integer_types): - try: - config[valname] = int(value) - except ValueError: - warn('invalid number %r for config value %r, ignoring' - % (value, valname)) - elif hasattr(defvalue, '__call__'): - config[valname] = value - elif defvalue is not None and not isinstance(defvalue, string_types): - warn('cannot override config setting %r with unsupported ' - 'type, ignoring' % valname) + elif valname not in self.values: + warn('unknown config value %r in override, ignoring' % valname) + continue + if isinstance(value, string_types): + config[valname] = self.convert_overrides(valname, value) else: config[valname] = value - else: - config[valname] = value + except ValueError as exc: + warn(exc) for name in config: if name in self.values: self.__dict__[name] = config[name] diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 6847d5abe..6306ac901 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -572,7 +572,7 @@ class ASTIdentifier(ASTBase): classname=None) key = symbol.get_lookup_key() assert key - pnode['cpp:parentKey'] = key + pnode['cpp:parent_key'] = key pnode += nodes.Text(self.identifier) signode += pnode elif mode == 'lastIsName': @@ -3768,10 +3768,10 @@ class CPPObject(ObjectDescription): raise NotImplementedError() def handle_signature(self, sig, signode): - if 'cpp:parentSymbol' not in self.env.ref_context: - root = self.env.domaindata['cpp']['rootSymbol'] - self.env.ref_context['cpp:parentSymbol'] = root - parentSymbol = self.env.ref_context['cpp:parentSymbol'] + if 'cpp:parent_symbol' not in self.env.ref_context: + root = self.env.domaindata['cpp']['root_symbol'] + self.env.ref_context['cpp:parent_symbol'] = root + parentSymbol = self.env.ref_context['cpp:parent_symbol'] parser = DefinitionParser(sig, self) try: @@ -3783,16 +3783,16 @@ class CPPObject(ObjectDescription): # the possibly inner declarations. name = _make_phony_error_name() symbol = parentSymbol.add_name(name) - self.env.ref_context['cpp:lastSymbol'] = symbol + self.env.ref_context['cpp:last_symbol'] = symbol raise ValueError try: symbol = parentSymbol.add_declaration(ast, docname=self.env.docname) - self.env.ref_context['cpp:lastSymbol'] = symbol + self.env.ref_context['cpp:last_symbol'] = symbol except _DuplicateSymbolError as e: # Assume we are actually in the old symbol, # instead of the newly created duplicate. - self.env.ref_context['cpp:lastSymbol'] = e.symbol + self.env.ref_context['cpp:last_symbol'] = e.symbol if ast.objectType == 'enumerator': self._add_enumerator_to_parent(ast) @@ -3839,13 +3839,13 @@ class CPPClassObject(CPPObject): return _('%s (C++ class)') % name def before_content(self): - lastSymbol = self.env.ref_context['cpp:lastSymbol'] + lastSymbol = self.env.ref_context['cpp:last_symbol'] assert lastSymbol - self.oldParentSymbol = self.env.ref_context['cpp:parentSymbol'] - self.env.ref_context['cpp:parentSymbol'] = lastSymbol + self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol'] + self.env.ref_context['cpp:parent_symbol'] = lastSymbol def after_content(self): - self.env.ref_context['cpp:parentSymbol'] = self.oldParentSymbol + self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol def parse_definition(self, parser): return parser.parse_declaration("class") @@ -3859,13 +3859,13 @@ class CPPEnumObject(CPPObject): return _('%s (C++ enum)') % name def before_content(self): - lastSymbol = self.env.ref_context['cpp:lastSymbol'] + lastSymbol = self.env.ref_context['cpp:last_symbol'] assert lastSymbol - self.oldParentSymbol = self.env.ref_context['cpp:parentSymbol'] - self.env.ref_context['cpp:parentSymbol'] = lastSymbol + self.oldParentSymbol = self.env.ref_context['cpp:parent_symbol'] + self.env.ref_context['cpp:parent_symbol'] = lastSymbol def after_content(self): - self.env.ref_context['cpp:parentSymbol'] = self.oldParentSymbol + self.env.ref_context['cpp:parent_symbol'] = self.oldParentSymbol def parse_definition(self, parser): ast = parser.parse_declaration("enum") @@ -3912,7 +3912,7 @@ class CPPNamespaceObject(Directive): def run(self): env = self.state.document.settings.env - rootSymbol = env.domaindata['cpp']['rootSymbol'] + rootSymbol = env.domaindata['cpp']['root_symbol'] if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): symbol = rootSymbol stack = [] @@ -3927,8 +3927,8 @@ class CPPNamespaceObject(Directive): ast = ASTNamespace(name, None) symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix) stack = [symbol] - env.ref_context['cpp:parentSymbol'] = symbol - env.temp_data['cpp:namespaceStack'] = stack + env.ref_context['cpp:parent_symbol'] = symbol + env.temp_data['cpp:namespace_stack'] = stack return [] @@ -3954,14 +3954,14 @@ class CPPNamespacePushObject(Directive): self.warn(e.description) name = _make_phony_error_name() ast = ASTNamespace(name, None) - oldParent = env.ref_context.get('cpp:parentSymbol', None) + oldParent = env.ref_context.get('cpp:parent_symbol', None) if not oldParent: - oldParent = env.domaindata['cpp']['rootSymbol'] + oldParent = env.domaindata['cpp']['root_symbol'] symbol = oldParent.add_name(ast.nestedName, ast.templatePrefix) - stack = env.temp_data.get('cpp:namespaceStack', []) + stack = env.temp_data.get('cpp:namespace_stack', []) stack.append(symbol) - env.ref_context['cpp:parentSymbol'] = symbol - env.temp_data['cpp:namespaceStack'] = stack + env.ref_context['cpp:parent_symbol'] = symbol + env.temp_data['cpp:namespace_stack'] = stack return [] @@ -3977,7 +3977,7 @@ class CPPNamespacePopObject(Directive): def run(self): env = self.state.document.settings.env - stack = env.temp_data.get('cpp:namespaceStack', None) + stack = env.temp_data.get('cpp:namespace_stack', None) if not stack or len(stack) == 0: self.warn("C++ namespace pop on empty stack. Defaulting to gobal scope.") stack = [] @@ -3986,17 +3986,17 @@ class CPPNamespacePopObject(Directive): if len(stack) > 0: symbol = stack[-1] else: - symbol = env.domaindata['cpp']['rootSymbol'] - env.ref_context['cpp:parentSymbol'] = symbol - env.temp_data['cpp:namespaceStack'] = stack + symbol = env.domaindata['cpp']['root_symbol'] + env.ref_context['cpp:parent_symbol'] = symbol + env.temp_data['cpp:namespace_stack'] = stack return [] class CPPXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): - parent = env.ref_context.get('cpp:parentSymbol', None) + parent = env.ref_context.get('cpp:parent_symbol', None) if parent: - refnode['cpp:parentKey'] = parent.get_lookup_key() + refnode['cpp:parent_key'] = parent.get_lookup_key() if refnode['reftype'] == 'any': # Assume the removal part of fix_parens for :any: refs. # The addition part is done with the reference is resolved. @@ -4055,12 +4055,12 @@ class CPPDomain(Domain): 'enumerator': CPPXRefRole() } initial_data = { - 'rootSymbol': Symbol(None, None, None, None, None, None), + 'root_symbol': Symbol(None, None, None, None, None, None), 'names': {} # full name for indexing -> docname } def clear_doc(self, docname): - rootSymbol = self.data['rootSymbol'] + rootSymbol = self.data['root_symbol'] rootSymbol.clear_doc(docname) for name, nDocname in list(self.data['names'].items()): if nDocname == docname: @@ -4069,12 +4069,12 @@ class CPPDomain(Domain): def process_doc(self, env, docname, document): # just for debugging # print(docname) - # print(self.data['rootSymbol'].dump(0)) + # print(self.data['root_symbol'].dump(0)) pass def merge_domaindata(self, docnames, otherdata): - self.data['rootSymbol'].merge_with(otherdata['rootSymbol'], - docnames, self.env) + self.data['root_symbol'].merge_with(otherdata['root_symbol'], + docnames, self.env) ourNames = self.data['names'] for name, docname in otherdata['names'].items(): if docname in docnames: @@ -4102,8 +4102,8 @@ class CPPDomain(Domain): warner.warn('Unparseable C++ cross-reference: %r\n%s' % (target, str(e.description))) return None, None - parentKey = node.get("cpp:parentKey", None) - rootSymbol = self.data['rootSymbol'] + parentKey = node.get("cpp:parent_key", None) + rootSymbol = self.data['root_symbol'] if parentKey: parentSymbol = rootSymbol.direct_lookup(parentKey) if not parentSymbol: @@ -4152,7 +4152,7 @@ class CPPDomain(Domain): return [] def get_objects(self): - rootSymbol = self.data['rootSymbol'] + rootSymbol = self.data['root_symbol'] for symbol in rootSymbol.get_all_symbols(): if symbol.declaration is None: continue diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 37aa7176d..20dea3ad7 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -8,6 +8,12 @@ \NeedsTeXFormat{LaTeX2e}[1995/12/01] \ProvidesPackage{sphinx}[2010/01/15 LaTeX package (Sphinx markup)] +\ifx\directlua\undefined\else +% if compiling with lualatex 0.85 or later load compatibility patch issued by +% the LaTeX team for older packages relying on \pdf named primitives. + \IfFileExists{luatex85.sty}{\RequirePackage{luatex85}}{} +\fi + \@ifclassloaded{memoir}{}{\RequirePackage{fancyhdr}} \RequirePackage{textcomp} @@ -240,6 +246,7 @@ \setlength\parskip{0pt}% \setlength\itemsep{0ex}% \setlength\topsep{0ex}% + \setlength\parsep{0pt}% let's not forget this one! \setlength\partopsep{0pt}% \setlength\leftmargin{0pt}% }%