Add optional description argument to app.add_config_value (#12549)

Sphinx (+ extensions) has a lot of available configuration variables for users,
allowing for the co-location of a short description on the configuration value,
can be utilised by external tools to provide better sphinx authoring support.

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
Chris Sewell
2024-07-12 19:40:36 +02:00
committed by GitHub
parent 316451de81
commit 78c8b4d323
3 changed files with 36 additions and 19 deletions

View File

@@ -13,6 +13,9 @@ Deprecated
Features added
--------------
* Add optional ``description`` argument to
:meth:`~sphinx.application.Sphinx.add_config_value`.
Patch by Chris Sewell.
* #11165: Support the `officially recommended`_ ``.jinja`` suffix for template
files.
Patch by James Addison and Adam Turner

View File

@@ -527,9 +527,11 @@ class Sphinx:
"""
self.registry.add_builder(builder, override=override)
# TODO(stephenfin): Describe 'types' parameter
def add_config_value(self, name: str, default: Any, rebuild: _ConfigRebuild,
types: type | Collection[type] | ENUM = ()) -> None:
def add_config_value(
self, name: str, default: Any, rebuild: _ConfigRebuild,
types: type | Collection[type] | ENUM = (),
description: str | None = None,
) -> None:
"""Register a configuration value.
This is necessary for Sphinx to recognize new values and set default
@@ -550,6 +552,7 @@ class Sphinx:
:param types: The type of configuration value. A list of types can be specified. For
example, ``[str]`` is used to describe a configuration that takes string
value.
:param description: A short description of the configuration value.
.. versionchanged:: 0.4
If the *default* value is a callable, it will be called with the
@@ -561,9 +564,12 @@ class Sphinx:
Changed *rebuild* from a simple boolean (equivalent to ``''`` or
``'env'``) to a string. However, booleans are still accepted and
converted internally.
.. versionadded:: 7.4
The *description* parameter.
"""
logger.debug('[app] adding config value: %r', (name, default, rebuild, types))
self.config.add(name, default, rebuild, types)
self.config.add(name, default, rebuild, types, description)
def add_event(self, name: str) -> None:
"""Register an event called *name*.

View File

@@ -94,20 +94,23 @@ class ENUM:
_OptValidTypes = Union[tuple[()], tuple[type, ...], frozenset[type], ENUM]
_DescriptionType = Union[str, None]
class _Opt:
__slots__ = 'default', 'rebuild', 'valid_types'
__slots__ = 'default', 'rebuild', 'valid_types', 'description'
default: Any
rebuild: _ConfigRebuild
valid_types: _OptValidTypes
description: _DescriptionType
def __init__(
self,
default: Any,
rebuild: _ConfigRebuild,
valid_types: _OptValidTypes,
description: _DescriptionType = None,
) -> None:
"""Configuration option type for Sphinx.
@@ -120,52 +123,56 @@ class _Opt:
super().__setattr__('default', default)
super().__setattr__('rebuild', rebuild)
super().__setattr__('valid_types', valid_types)
super().__setattr__('description', description)
def __repr__(self) -> str:
return (
f'{self.__class__.__qualname__}('
f'default={self.default!r}, '
f'rebuild={self.rebuild!r}, '
f'valid_types={self.valid_types!r})'
f'valid_types={self.rebuild!r}, '
f'description={self.description!r})'
)
def __eq__(self, other: object) -> bool:
if isinstance(other, _Opt):
self_tpl = (self.default, self.rebuild, self.valid_types)
other_tpl = (other.default, other.rebuild, other.valid_types)
self_tpl = (self.default, self.rebuild, self.valid_types, self.description)
other_tpl = (other.default, other.rebuild, other.valid_types, self.description)
return self_tpl == other_tpl
return NotImplemented
def __lt__(self, other: _Opt) -> bool:
if self.__class__ is other.__class__:
self_tpl = (self.default, self.rebuild, self.valid_types)
other_tpl = (other.default, other.rebuild, other.valid_types)
self_tpl = (self.default, self.rebuild, self.valid_types, self.description)
other_tpl = (other.default, other.rebuild, other.valid_types, self.description)
return self_tpl > other_tpl
return NotImplemented
def __hash__(self) -> int:
return hash((self.default, self.rebuild, self.valid_types))
return hash((self.default, self.rebuild, self.valid_types, self.description))
def __setattr__(self, key: str, value: Any) -> None:
if key in {'default', 'rebuild', 'valid_types'}:
if key in {'default', 'rebuild', 'valid_types', 'description'}:
msg = f'{self.__class__.__name__!r} object does not support assignment to {key!r}'
raise TypeError(msg)
super().__setattr__(key, value)
def __delattr__(self, key: str) -> None:
if key in {'default', 'rebuild', 'valid_types'}:
if key in {'default', 'rebuild', 'valid_types', 'description'}:
msg = f'{self.__class__.__name__!r} object does not support deletion of {key!r}'
raise TypeError(msg)
super().__delattr__(key)
def __getstate__(self) -> tuple[Any, _ConfigRebuild, _OptValidTypes]:
return self.default, self.rebuild, self.valid_types
def __getstate__(self) -> tuple[Any, _ConfigRebuild, _OptValidTypes, _DescriptionType]:
return self.default, self.rebuild, self.valid_types, self.description
def __setstate__(self, state: tuple[Any, _ConfigRebuild, _OptValidTypes]) -> None:
default, rebuild, valid_types = state
def __setstate__(
self, state: tuple[Any, _ConfigRebuild, _OptValidTypes, _DescriptionType]) -> None:
default, rebuild, valid_types, description = state
super().__setattr__('default', default)
super().__setattr__('rebuild', rebuild)
super().__setattr__('valid_types', valid_types)
super().__setattr__('description', description)
def __getitem__(self, item: int | slice) -> Any:
warnings.warn(
@@ -445,7 +452,8 @@ class Config:
yield ConfigValue(name, getattr(self, name), opt.rebuild)
def add(self, name: str, default: Any, rebuild: _ConfigRebuild,
types: type | Collection[type] | ENUM) -> None:
types: type | Collection[type] | ENUM,
description: str | None = None) -> None:
if name in self._options:
raise ExtensionError(__('Config value %r already present') % name)
@@ -455,7 +463,7 @@ class Config:
# standardise valid_types
valid_types = _validate_valid_types(types)
self._options[name] = _Opt(default, rebuild, valid_types)
self._options[name] = _Opt(default, rebuild, valid_types, description)
def filter(self, rebuild: Set[_ConfigRebuild]) -> Iterator[ConfigValue]:
if isinstance(rebuild, str):