From c83630bd1f9a8d602f253e068bd018b79eb463d9 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 14 Sep 2018 02:05:04 +0900 Subject: [PATCH] Fix #5422: lambda object causes PicklingError on storing environment --- CHANGES | 1 + sphinx/config.py | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index c3012787a..d95a4026f 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Bugs fixed * #5418: Incorrect default path for sphinx-build -d/doctrees files * #5421: autodoc emits deprecation warning for :confval:`autodoc_default_flags` +* #5422: lambda object causes PicklingError on storing environment Testing -------- diff --git a/sphinx/config.py b/sphinx/config.py index ec54f1691..579aa232e 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -38,7 +38,7 @@ if False: logger = logging.getLogger(__name__) CONFIG_FILENAME = 'conf.py' -UNSERIALIZEABLE_TYPES = class_types + (types.ModuleType, types.FunctionType) +UNSERIALIZABLE_TYPES = class_types + (types.ModuleType, types.FunctionType) copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') if PY3: @@ -49,6 +49,21 @@ ConfigValue = NamedTuple('ConfigValue', [('name', str), ('rebuild', Union[bool, unicode])]) +def is_serializable(obj): + # type: (Any) -> bool + """Check if object is serializable or not.""" + if isinstance(obj, UNSERIALIZABLE_TYPES): + return False + elif isinstance(obj, dict): + for key, value in iteritems(obj): + if not is_serializable(key) or not is_serializable(value): + return False + elif isinstance(obj, (list, tuple, set)): + return all(is_serializable(i) for i in obj) + + return True + + class ENUM(object): """represents the config value should be a one of candidates. @@ -317,7 +332,7 @@ class Config(object): # remove potentially pickling-problematic values from config __dict__ = {} for key, value in iteritems(self.__dict__): - if key.startswith('_') or isinstance(value, UNSERIALIZEABLE_TYPES): + if key.startswith('_') or not is_serializable(value): pass else: __dict__[key] = value @@ -326,7 +341,7 @@ class Config(object): __dict__['values'] = {} for key, value in iteritems(self.values): # type: ignore real_value = getattr(self, key) - if isinstance(real_value, UNSERIALIZEABLE_TYPES): + if not is_serializable(real_value): # omit unserializable value real_value = None