Make Config picklable

This commit is contained in:
Takeshi KOMIYA 2018-05-10 00:43:48 +09:00
parent 169297d0b7
commit 0e2cb4793e
2 changed files with 34 additions and 13 deletions

View File

@ -11,12 +11,15 @@
import re import re
import traceback import traceback
import types
import warnings import warnings
from collections import OrderedDict from collections import OrderedDict
from os import path, getenv from os import path, getenv
from typing import Any, NamedTuple, Union 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.deprecation import RemovedInSphinx30Warning
from sphinx.errors import ConfigError, ExtensionError from sphinx.errors import ConfigError, ExtensionError
@ -35,6 +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)
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
if PY3: if PY3:
@ -308,6 +312,34 @@ class Config(object):
rebuild = [rebuild] rebuild = [rebuild]
return (value for value in self if value.rebuild in 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): def eval_config_file(filename, tags):
# type: (unicode, Tags) -> Dict[unicode, Any] # type: (unicode, Tags) -> Dict[unicode, Any]

View File

@ -12,14 +12,13 @@
import os import os
import re import re
import sys import sys
import types
import warnings import warnings
from collections import defaultdict from collections import defaultdict
from copy import copy from copy import copy
from os import path from os import path
from docutils.utils import Reporter, get_source_line 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 six.moves import cPickle as pickle
from sphinx import addnodes from sphinx import addnodes
@ -126,21 +125,11 @@ class BuildEnvironment(object):
# remove unpicklable attributes # remove unpicklable attributes
app = env.app app = env.app
del env.app del env.app
values = env.config.values
del env.config.values
domains = env.domains domains = env.domains
del 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) pickle.dump(env, f, pickle.HIGHEST_PROTOCOL)
# reset attributes # reset attributes
env.domains = domains env.domains = domains
env.config.values = values
env.app = app env.app = app
@classmethod @classmethod