Skip to content

Commit 78c8b4d

Browse files
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 <[email protected]>
1 parent 316451d commit 78c8b4d

File tree

3 files changed

+36
-19
lines changed

3 files changed

+36
-19
lines changed

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ Deprecated
1313
Features added
1414
--------------
1515

16+
* Add optional ``description`` argument to
17+
:meth:`~sphinx.application.Sphinx.add_config_value`.
18+
Patch by Chris Sewell.
1619
* #11165: Support the `officially recommended`_ ``.jinja`` suffix for template
1720
files.
1821
Patch by James Addison and Adam Turner

sphinx/application.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,11 @@ def add_builder(self, builder: type[Builder], override: bool = False) -> None:
527527
"""
528528
self.registry.add_builder(builder, override=override)
529529

530-
# TODO(stephenfin): Describe 'types' parameter
531-
def add_config_value(self, name: str, default: Any, rebuild: _ConfigRebuild,
532-
types: type | Collection[type] | ENUM = ()) -> None:
530+
def add_config_value(
531+
self, name: str, default: Any, rebuild: _ConfigRebuild,
532+
types: type | Collection[type] | ENUM = (),
533+
description: str | None = None,
534+
) -> None:
533535
"""Register a configuration value.
534536
535537
This is necessary for Sphinx to recognize new values and set default
@@ -550,6 +552,7 @@ def add_config_value(self, name: str, default: Any, rebuild: _ConfigRebuild,
550552
:param types: The type of configuration value. A list of types can be specified. For
551553
example, ``[str]`` is used to describe a configuration that takes string
552554
value.
555+
:param description: A short description of the configuration value.
553556
554557
.. versionchanged:: 0.4
555558
If the *default* value is a callable, it will be called with the
@@ -561,9 +564,12 @@ def add_config_value(self, name: str, default: Any, rebuild: _ConfigRebuild,
561564
Changed *rebuild* from a simple boolean (equivalent to ``''`` or
562565
``'env'``) to a string. However, booleans are still accepted and
563566
converted internally.
567+
568+
.. versionadded:: 7.4
569+
The *description* parameter.
564570
"""
565571
logger.debug('[app] adding config value: %r', (name, default, rebuild, types))
566-
self.config.add(name, default, rebuild, types)
572+
self.config.add(name, default, rebuild, types, description)
567573

568574
def add_event(self, name: str) -> None:
569575
"""Register an event called *name*.

sphinx/config.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,23 @@ def match(self, value: str | list | tuple) -> bool:
9494

9595

9696
_OptValidTypes = Union[tuple[()], tuple[type, ...], frozenset[type], ENUM]
97+
_DescriptionType = Union[str, None]
9798

9899

99100
class _Opt:
100-
__slots__ = 'default', 'rebuild', 'valid_types'
101+
__slots__ = 'default', 'rebuild', 'valid_types', 'description'
101102

102103
default: Any
103104
rebuild: _ConfigRebuild
104105
valid_types: _OptValidTypes
106+
description: _DescriptionType
105107

106108
def __init__(
107109
self,
108110
default: Any,
109111
rebuild: _ConfigRebuild,
110112
valid_types: _OptValidTypes,
113+
description: _DescriptionType = None,
111114
) -> None:
112115
"""Configuration option type for Sphinx.
113116
@@ -120,52 +123,56 @@ def __init__(
120123
super().__setattr__('default', default)
121124
super().__setattr__('rebuild', rebuild)
122125
super().__setattr__('valid_types', valid_types)
126+
super().__setattr__('description', description)
123127

124128
def __repr__(self) -> str:
125129
return (
126130
f'{self.__class__.__qualname__}('
127131
f'default={self.default!r}, '
128132
f'rebuild={self.rebuild!r}, '
129-
f'valid_types={self.valid_types!r})'
133+
f'valid_types={self.rebuild!r}, '
134+
f'description={self.description!r})'
130135
)
131136

132137
def __eq__(self, other: object) -> bool:
133138
if isinstance(other, _Opt):
134-
self_tpl = (self.default, self.rebuild, self.valid_types)
135-
other_tpl = (other.default, other.rebuild, other.valid_types)
139+
self_tpl = (self.default, self.rebuild, self.valid_types, self.description)
140+
other_tpl = (other.default, other.rebuild, other.valid_types, self.description)
136141
return self_tpl == other_tpl
137142
return NotImplemented
138143

139144
def __lt__(self, other: _Opt) -> bool:
140145
if self.__class__ is other.__class__:
141-
self_tpl = (self.default, self.rebuild, self.valid_types)
142-
other_tpl = (other.default, other.rebuild, other.valid_types)
146+
self_tpl = (self.default, self.rebuild, self.valid_types, self.description)
147+
other_tpl = (other.default, other.rebuild, other.valid_types, self.description)
143148
return self_tpl > other_tpl
144149
return NotImplemented
145150

146151
def __hash__(self) -> int:
147-
return hash((self.default, self.rebuild, self.valid_types))
152+
return hash((self.default, self.rebuild, self.valid_types, self.description))
148153

149154
def __setattr__(self, key: str, value: Any) -> None:
150-
if key in {'default', 'rebuild', 'valid_types'}:
155+
if key in {'default', 'rebuild', 'valid_types', 'description'}:
151156
msg = f'{self.__class__.__name__!r} object does not support assignment to {key!r}'
152157
raise TypeError(msg)
153158
super().__setattr__(key, value)
154159

155160
def __delattr__(self, key: str) -> None:
156-
if key in {'default', 'rebuild', 'valid_types'}:
161+
if key in {'default', 'rebuild', 'valid_types', 'description'}:
157162
msg = f'{self.__class__.__name__!r} object does not support deletion of {key!r}'
158163
raise TypeError(msg)
159164
super().__delattr__(key)
160165

161-
def __getstate__(self) -> tuple[Any, _ConfigRebuild, _OptValidTypes]:
162-
return self.default, self.rebuild, self.valid_types
166+
def __getstate__(self) -> tuple[Any, _ConfigRebuild, _OptValidTypes, _DescriptionType]:
167+
return self.default, self.rebuild, self.valid_types, self.description
163168

164-
def __setstate__(self, state: tuple[Any, _ConfigRebuild, _OptValidTypes]) -> None:
165-
default, rebuild, valid_types = state
169+
def __setstate__(
170+
self, state: tuple[Any, _ConfigRebuild, _OptValidTypes, _DescriptionType]) -> None:
171+
default, rebuild, valid_types, description = state
166172
super().__setattr__('default', default)
167173
super().__setattr__('rebuild', rebuild)
168174
super().__setattr__('valid_types', valid_types)
175+
super().__setattr__('description', description)
169176

170177
def __getitem__(self, item: int | slice) -> Any:
171178
warnings.warn(
@@ -445,7 +452,8 @@ def __iter__(self) -> Iterator[ConfigValue]:
445452
yield ConfigValue(name, getattr(self, name), opt.rebuild)
446453

447454
def add(self, name: str, default: Any, rebuild: _ConfigRebuild,
448-
types: type | Collection[type] | ENUM) -> None:
455+
types: type | Collection[type] | ENUM,
456+
description: str | None = None) -> None:
449457
if name in self._options:
450458
raise ExtensionError(__('Config value %r already present') % name)
451459

@@ -455,7 +463,7 @@ def add(self, name: str, default: Any, rebuild: _ConfigRebuild,
455463

456464
# standardise valid_types
457465
valid_types = _validate_valid_types(types)
458-
self._options[name] = _Opt(default, rebuild, valid_types)
466+
self._options[name] = _Opt(default, rebuild, valid_types, description)
459467

460468
def filter(self, rebuild: Set[_ConfigRebuild]) -> Iterator[ConfigValue]:
461469
if isinstance(rebuild, str):

0 commit comments

Comments
 (0)