From 0e2cb4793e97bd7736ae6742c15ca5eb1afa497c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 10 May 2018 00:43:48 +0900 Subject: [PATCH] Make Config picklable --- sphinx/config.py | 34 +++++++++++++++++++++++++++++++++- sphinx/environment/__init__.py | 13 +------------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 58e5be277..0a2b184c0 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -11,12 +11,15 @@ import re import traceback +import types import warnings from collections import OrderedDict from os import path, getenv from typing import Any, NamedTuple, Union -from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types +from six import ( + PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types, class_types +) from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.errors import ConfigError, ExtensionError @@ -35,6 +38,7 @@ if False: logger = logging.getLogger(__name__) CONFIG_FILENAME = 'conf.py' +UNSERIALIZEABLE_TYPES = class_types + (types.ModuleType, types.FunctionType) copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') if PY3: @@ -308,6 +312,34 @@ class Config(object): rebuild = [rebuild] return (value for value in self if value.rebuild in rebuild) + def __getstate__(self): + # type: () -> Dict + """Obtains serializable data for pickling.""" + # remove potentially pickling-problematic values from config + __dict__ = {} + for key, value in iteritems(self.__dict__): + if key.startswith('_') or isinstance(value, UNSERIALIZEABLE_TYPES): + pass + else: + __dict__[key] = value + + # create a picklable copy of values list + __dict__['values'] = {} + for key, value in iteritems(self.values): # type: ignore + real_value = getattr(self, key) + if isinstance(real_value, UNSERIALIZEABLE_TYPES): + # omit unserializable value + real_value = None + + # types column is also omitted + __dict__['values'][key] = (real_value, value[1], None) + + return __dict__ + + def __setstate__(self, state): + # type: (Dict) -> None + self.__dict__.update(state) + def eval_config_file(filename, tags): # type: (unicode, Tags) -> Dict[unicode, Any] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index e8d9dd9d4..6a3951e9d 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -12,14 +12,13 @@ import os import re import sys -import types import warnings from collections import defaultdict from copy import copy from os import path from docutils.utils import Reporter, get_source_line -from six import BytesIO, class_types, next +from six import BytesIO, next from six.moves import cPickle as pickle from sphinx import addnodes @@ -126,21 +125,11 @@ class BuildEnvironment(object): # remove unpicklable attributes app = env.app del env.app - values = env.config.values - del env.config.values domains = env.domains del env.domains - # remove potentially pickling-problematic values from config - for key, val in list(vars(env.config).items()): - if key.startswith('_') or \ - isinstance(val, types.ModuleType) or \ - isinstance(val, types.FunctionType) or \ - isinstance(val, class_types): - del env.config[key] pickle.dump(env, f, pickle.HIGHEST_PROTOCOL) # reset attributes env.domains = domains - env.config.values = values env.app = app @classmethod