Make `Config.values` private

This commit is contained in:
Adam Turner 2024-01-03 22:07:01 +00:00
parent 259118d182
commit 68af0ac2be
2 changed files with 25 additions and 21 deletions

View File

@ -157,7 +157,7 @@ class _Opt:
class Config: class Config:
r"""Configuration file abstraction. r"""Configuration file abstraction.
The config object makes the values of all config values available as The Config object makes the values of all config options available as
attributes. attributes.
It is exposed via the :py:class:`~sphinx.application.Sphinx`\ ``.config`` It is exposed via the :py:class:`~sphinx.application.Sphinx`\ ``.config``
@ -250,7 +250,7 @@ class Config:
overrides: dict[str, Any] | None = None) -> None: overrides: dict[str, Any] | None = None) -> None:
raw_config: dict[str, Any] = config or {} raw_config: dict[str, Any] = config or {}
self.overrides = dict(overrides) if overrides is not None else {} self.overrides = dict(overrides) if overrides is not None else {}
self.values = Config.config_values.copy() self._options = Config.config_values.copy()
self._raw_config = raw_config self._raw_config = raw_config
self.setup: _ExtensionSetupFunc | None = raw_config.get('setup') self.setup: _ExtensionSetupFunc | None = raw_config.get('setup')
@ -262,6 +262,10 @@ class Config:
raw_config['extensions'] = extensions raw_config['extensions'] = extensions
self.extensions: list[str] = raw_config.get('extensions', []) self.extensions: list[str] = raw_config.get('extensions', [])
@property
def values(self) -> dict[str, _Opt]:
return self._options
@classmethod @classmethod
def read(cls, confdir: str | os.PathLike[str], overrides: dict | None = None, def read(cls, confdir: str | os.PathLike[str], overrides: dict | None = None,
tags: Tags | None = None) -> Config: tags: Tags | None = None) -> Config:
@ -288,7 +292,7 @@ class Config:
if not isinstance(value, str): if not isinstance(value, str):
return value return value
else: else:
opt = self.values[name] opt = self._options[name]
default = opt.default default = opt.default
valid_types = opt.valid_types valid_types = opt.valid_types
if valid_types == Any: if valid_types == Any:
@ -351,7 +355,7 @@ class Config:
real_name, key = name.split('.', 1) real_name, key = name.split('.', 1)
config.setdefault(real_name, {})[key] = value config.setdefault(real_name, {})[key] = value
continue continue
if name not in self.values: if name not in self._options:
logger.warning(__('unknown config value %r in override, ignoring'), logger.warning(__('unknown config value %r in override, ignoring'),
name) name)
continue continue
@ -362,7 +366,7 @@ class Config:
except ValueError as exc: except ValueError as exc:
logger.warning("%s", exc) logger.warning("%s", exc)
for name in config: for name in config:
if name in self.values: if name in self._options:
self.__dict__[name] = config[name] self.__dict__[name] = config[name]
def post_init_values(self) -> None: def post_init_values(self) -> None:
@ -371,14 +375,14 @@ class Config:
""" """
config = self._raw_config config = self._raw_config
for name in config: for name in config:
if name not in self.__dict__ and name in self.values: if name not in self.__dict__ and name in self._options:
self.__dict__[name] = config[name] self.__dict__[name] = config[name]
check_confval_types(None, self) check_confval_types(None, self)
def __repr__(self): def __repr__(self):
values = [] values = []
for opt_name in self.values: for opt_name in self._options:
try: try:
opt_value = getattr(self, opt_name) opt_value = getattr(self, opt_name)
except Exception: except Exception:
@ -389,9 +393,9 @@ class Config:
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
if name.startswith('_'): if name.startswith('_'):
raise AttributeError(name) raise AttributeError(name)
if name not in self.values: if name not in self._options:
raise AttributeError(__('No such config value: %s') % name) raise AttributeError(__('No such config value: %s') % name)
default = self.values[name].default default = self._options[name].default
if callable(default): if callable(default):
return default(self) return default(self)
return default return default
@ -406,15 +410,15 @@ class Config:
delattr(self, name) delattr(self, name)
def __contains__(self, name: str) -> bool: def __contains__(self, name: str) -> bool:
return name in self.values return name in self._options
def __iter__(self) -> Iterator[ConfigValue]: def __iter__(self) -> Iterator[ConfigValue]:
for name, opt in self.values.items(): for name, opt in self._options.items():
yield ConfigValue(name, getattr(self, name), opt.rebuild) yield ConfigValue(name, getattr(self, name), opt.rebuild)
def add(self, name: str, default: Any, rebuild: _ConfigRebuild, def add(self, name: str, default: Any, rebuild: _ConfigRebuild,
types: type | Collection[type] | ENUM) -> None: types: type | Collection[type] | ENUM) -> None:
if name in self.values: if name in self._options:
raise ExtensionError(__('Config value %r already present') % name) raise ExtensionError(__('Config value %r already present') % name)
# standardise rebuild # standardise rebuild
@ -423,7 +427,7 @@ class Config:
# standardise valid_types # standardise valid_types
valid_types = _validate_valid_types(types) valid_types = _validate_valid_types(types)
self.values[name] = _Opt(default, rebuild, valid_types) self._options[name] = _Opt(default, rebuild, valid_types)
def filter(self, rebuild: str | Sequence[str]) -> Iterator[ConfigValue]: def filter(self, rebuild: str | Sequence[str]) -> Iterator[ConfigValue]:
if isinstance(rebuild, str): if isinstance(rebuild, str):
@ -438,22 +442,22 @@ class Config:
for key, value in self.__dict__.items() for key, value in self.__dict__.items()
if not key.startswith('_') and is_serializable(value) if not key.startswith('_') and is_serializable(value)
} }
# create a picklable copy of values list # create a picklable copy of ``self._options``
__dict__['values'] = values = {} __dict__['_options'] = _options = {}
for name, opt in self.values.items(): for name, opt in self._options.items():
real_value = getattr(self, name) real_value = getattr(self, name)
if not is_serializable(real_value): if not is_serializable(real_value):
# omit unserializable value # omit unserializable value
real_value = None real_value = None
# valid_types is also omitted # valid_types is also omitted
values[name] = real_value, opt.rebuild _options[name] = real_value, opt.rebuild
return __dict__ return __dict__
def __setstate__(self, state: dict) -> None: def __setstate__(self, state: dict) -> None:
self.values = { self._options = {
name: _Opt(real_value, rebuild, ()) name: _Opt(real_value, rebuild, ())
for name, (real_value, rebuild) in state.pop('values').items() for name, (real_value, rebuild) in state.pop('_options').items()
} }
self.__dict__.update(state) self.__dict__.update(state)
@ -610,7 +614,7 @@ def check_confval_types(app: Sphinx | None, config: Config) -> None:
"""Check all values for deviation from the default value's type, since """Check all values for deviation from the default value's type, since
that can result in TypeErrors all over the place NB. that can result in TypeErrors all over the place NB.
""" """
for name, opt in config.values.items(): for name, opt in config._options.items():
default = opt.default default = opt.default
valid_types = opt.valid_types valid_types = opt.valid_types
value = getattr(config, name) value = getattr(config, name)

View File

@ -91,7 +91,7 @@ def test_config_pickle_protocol(tmp_path, protocol: int):
pickled_config = pickle.loads(pickle.dumps(config, protocol)) pickled_config = pickle.loads(pickle.dumps(config, protocol))
assert list(config.values) == list(pickled_config.values) assert list(config._options) == list(pickled_config._options)
assert repr(config) == repr(pickled_config) assert repr(config) == repr(pickled_config)