Close #4460: extensions should return the version of data structure as metadata

This commit is contained in:
Takeshi KOMIYA 2018-01-24 21:37:54 +09:00
parent f886f08cb2
commit 32c5e8346f
14 changed files with 57 additions and 8 deletions

View File

@ -7,6 +7,10 @@ Dependencies
Incompatible changes
--------------------
* #4460: extensions which stores any data to environment should return the
version of its env data structure as metadata. In detail, please see
:ref:`ext-metadata`.
Deprecated
----------

View File

@ -52,6 +52,8 @@ Note that it is still necessary to register the builder using
.. _entry points: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
.. _ext-metadata:
Extension metadata
------------------
@ -63,6 +65,11 @@ as metadata of the extension. Metadata keys currently recognized are:
* ``'version'``: a string that identifies the extension version. It is used for
extension version requirement checking (see :confval:`needs_extensions`) and
informational purposes. If not given, ``"unknown version"`` is substituted.
* ``'env_version'``: an integer that identifies the version of env data
structure if the extension stores any data to environment. It is used to
detect the data structure has been changed from last build. The extensions
have to increment the version when data structure has changed. If not given,
Sphinx considers the extension does not stores any data to environment.
* ``'parallel_read_safe'``: a boolean that specifies if parallel reading of
source files can be used when the extension is loaded. It defaults to
``False``, i.e. you have to explicitly specify your extension to be

View File

@ -305,6 +305,9 @@ class Sphinx(object):
logger.info(bold(__('loading pickled environment... ')), nonl=True)
filename = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
self.env = BuildEnvironment.frompickle(filename, self)
needed, reason = self.env.need_refresh(self)
if needed:
raise IOError(reason)
self.env.domains = {}
for domain in self.registry.create_domains(self.env):
# this can raise if the data version doesn't fit

View File

@ -330,6 +330,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -6099,6 +6099,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -415,6 +415,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -912,6 +912,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -182,6 +182,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -980,6 +980,7 @@ def setup(app):
return {
'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

View File

@ -107,13 +107,9 @@ class BuildEnvironment(object):
# This can happen for example when the pickle is from a
# different version of Sphinx.
raise IOError(exc)
if env.version != ENV_VERSION:
raise IOError('build environment version not current')
if app:
env.app = app
env.config.values = app.config.values
if env.srcdir != app.srcdir:
raise IOError('source directory has changed')
return env
@classmethod
@ -187,7 +183,7 @@ class BuildEnvironment(object):
self._warnfunc = None # type: Callable
# this is to invalidate old pickles
self.version = ENV_VERSION
self.version = app.registry.get_envversion(app)
# All "docnames" here are /-separated and relative and exclude
# the source suffix.
@ -304,6 +300,19 @@ class BuildEnvironment(object):
"""Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs)
def need_refresh(self, app):
# type: (Sphinx) -> Tuple[bool, unicode]
"""Check refresh environment is needed.
If needed, this method returns the reason for refresh.
"""
if self.version != app.registry.get_envversion(app):
return True, 'build environment version not current'
elif self.srcdir != app.srcdir:
return True, 'source directory has changed'
else:
return False, None
def clear_doc(self, docname):
# type: (unicode) -> None
"""Remove all traces of a source file in the inventory."""

View File

@ -347,7 +347,11 @@ def setup(app):
app.add_config_value('intersphinx_timeout', None, False)
app.connect('missing-reference', missing_reference)
app.connect('builder-inited', load_mappings)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
return {
'version': sphinx.__display_version__,
'env_version': 1,
'parallel_read_safe': True
}
def debug(argv):

View File

@ -258,4 +258,8 @@ def setup(app):
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)
app.connect('env-merge-info', merge_info)
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
return {
'version': sphinx.__display_version__,
'env_version': 1,
'parallel_read_safe': True
}

View File

@ -241,4 +241,8 @@ def setup(app):
app.connect('missing-reference', missing_reference)
# app.add_config_value('viewcode_include_modules', [], 'env')
# app.add_config_value('viewcode_exclude_modules', [], 'env')
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
return {
'version': sphinx.__display_version__,
'env_version': 1,
'parallel_read_safe': True
}

View File

@ -341,3 +341,11 @@ class SphinxComponentRegistry(object):
app.extensions[extname] = Extension(extname, mod, **metadata)
app._setting_up_extension.pop()
def get_envversion(self, app):
# type: (Sphinx) -> Dict[unicode, unicode]
from sphinx.environment import ENV_VERSION
envversion = {ext.name: ext.metadata['env_version'] for ext in app.extensions.values()
if ext.metadata.get('env_version')}
envversion['sphinx'] = ENV_VERSION
return envversion