Fix #5422: lambda object causes PicklingError on storing environment

This commit is contained in:
Takeshi KOMIYA 2018-09-14 02:05:04 +09:00
parent c8b6928300
commit c83630bd1f
2 changed files with 19 additions and 3 deletions

View File

@ -18,6 +18,7 @@ Bugs fixed
* #5418: Incorrect default path for sphinx-build -d/doctrees files * #5418: Incorrect default path for sphinx-build -d/doctrees files
* #5421: autodoc emits deprecation warning for :confval:`autodoc_default_flags` * #5421: autodoc emits deprecation warning for :confval:`autodoc_default_flags`
* #5422: lambda object causes PicklingError on storing environment
Testing Testing
-------- --------

View File

@ -38,7 +38,7 @@ if False:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CONFIG_FILENAME = 'conf.py' 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})(?=[ ,])') copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
if PY3: if PY3:
@ -49,6 +49,21 @@ ConfigValue = NamedTuple('ConfigValue', [('name', str),
('rebuild', Union[bool, unicode])]) ('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): class ENUM(object):
"""represents the config value should be a one of candidates. """represents the config value should be a one of candidates.
@ -317,7 +332,7 @@ class Config(object):
# remove potentially pickling-problematic values from config # remove potentially pickling-problematic values from config
__dict__ = {} __dict__ = {}
for key, value in iteritems(self.__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 pass
else: else:
__dict__[key] = value __dict__[key] = value
@ -326,7 +341,7 @@ class Config(object):
__dict__['values'] = {} __dict__['values'] = {}
for key, value in iteritems(self.values): # type: ignore for key, value in iteritems(self.values): # type: ignore
real_value = getattr(self, key) real_value = getattr(self, key)
if isinstance(real_value, UNSERIALIZEABLE_TYPES): if not is_serializable(real_value):
# omit unserializable value # omit unserializable value
real_value = None real_value = None