Merge pull request #4486 from tk0miya/4460_env_version_of_extensions

Close #4460: extensions should return the version of data structure as metadata
This commit is contained in:
Takeshi KOMIYA 2018-01-28 21:15:07 +09:00 committed by GitHub
commit 0f4a4b2ae9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 57 additions and 8 deletions

View File

@ -7,6 +7,10 @@ Dependencies
Incompatible changes 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 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 .. _entry points: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
.. _ext-metadata:
Extension 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 * ``'version'``: a string that identifies the extension version. It is used for
extension version requirement checking (see :confval:`needs_extensions`) and extension version requirement checking (see :confval:`needs_extensions`) and
informational purposes. If not given, ``"unknown version"`` is substituted. 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 * ``'parallel_read_safe'``: a boolean that specifies if parallel reading of
source files can be used when the extension is loaded. It defaults to 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 ``False``, i.e. you have to explicitly specify your extension to be

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -980,6 +980,7 @@ def setup(app):
return { return {
'version': 'builtin', 'version': 'builtin',
'env_version': 1,
'parallel_read_safe': True, 'parallel_read_safe': True,
'parallel_write_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 # This can happen for example when the pickle is from a
# different version of Sphinx. # different version of Sphinx.
raise IOError(exc) raise IOError(exc)
if env.version != ENV_VERSION:
raise IOError('build environment version not current')
if app: if app:
env.app = app env.app = app
env.config.values = app.config.values env.config.values = app.config.values
if env.srcdir != app.srcdir:
raise IOError('source directory has changed')
return env return env
@classmethod @classmethod
@ -187,7 +183,7 @@ class BuildEnvironment(object):
self._warnfunc = None # type: Callable self._warnfunc = None # type: Callable
# this is to invalidate old pickles # 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 # All "docnames" here are /-separated and relative and exclude
# the source suffix. # the source suffix.
@ -304,6 +300,19 @@ class BuildEnvironment(object):
"""Like :meth:`warn`, but with source information taken from *node*.""" """Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs) 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): def clear_doc(self, docname):
# type: (unicode) -> None # type: (unicode) -> None
"""Remove all traces of a source file in the inventory.""" """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.add_config_value('intersphinx_timeout', None, False)
app.connect('missing-reference', missing_reference) app.connect('missing-reference', missing_reference)
app.connect('builder-inited', load_mappings) 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): def debug(argv):

View File

@ -258,4 +258,8 @@ def setup(app):
app.connect('doctree-resolved', process_todo_nodes) app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos) app.connect('env-purge-doc', purge_todos)
app.connect('env-merge-info', merge_info) 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.connect('missing-reference', missing_reference)
# app.add_config_value('viewcode_include_modules', [], 'env') # app.add_config_value('viewcode_include_modules', [], 'env')
# app.add_config_value('viewcode_exclude_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

@ -339,3 +339,11 @@ class SphinxComponentRegistry(object):
app.extensions[extname] = Extension(extname, mod, **metadata) app.extensions[extname] = Extension(extname, mod, **metadata)
app._setting_up_extension.pop() 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