Skip to content

Commit 54d82a8

Browse files
authored
‼️ Disallow add_extra_option overriding an internal field (#1477)
This is an issue, because all need fields (internal and custom user ones) are stored / serialized in a "flat" dictionary, and so they cannot overlap. Previously there was no check in place to disallow the API from doing this
1 parent 6e4134e commit 54d82a8

File tree

4 files changed

+21
-13
lines changed

4 files changed

+21
-13
lines changed

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
if DOCS_THEME == "sphinx_immaterial":
4141
extensions.append("sphinx_immaterial")
4242

43-
suppress_warnings = ["needs.link_outgoing"]
43+
suppress_warnings = ["needs.link_outgoing", "needs.github"]
4444

4545
nitpicky = True
4646
nitpick_ignore = [

sphinx_needs/api/need.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -776,17 +776,10 @@ def _add_extra_fields(
776776
needs_info: NeedsInfoType, kwargs: dict[str, Any], config: NeedsSphinxConfig
777777
) -> None:
778778
"""Add extra option fields to the needs_info dictionary."""
779-
extra_keys = set(kwargs).difference(set(needs_info))
780-
for key in config.extra_options:
781-
if key in extra_keys:
782-
# TODO really we should not need to do this,
783-
# but the `add_extra_option` function does not guard against the keys clashing with existing internal fields,
784-
# this occurs in the core code with the github service adding `type` as an extra option
785-
786-
# note we already warn if value not string in external/import code
787-
needs_info[key] = str(kwargs.get(key, ""))
788-
elif key not in needs_info:
789-
needs_info[key] = ""
779+
# note we already warn if value not string in external/import code,
780+
# but still allow it and convert it here, for backward-comptibility
781+
extras = {key: str(kwargs.get(key, "")) for key in config.extra_options}
782+
needs_info.update(extras) # type: ignore[typeddict-item]
790783

791784

792785
def _add_link_fields(

sphinx_needs/config.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ def add_extra_option(
121121
override: bool = False,
122122
) -> None:
123123
"""Adds an extra option to the configuration."""
124+
if name in NeedsCoreFields:
125+
from sphinx_needs.exceptions import (
126+
NeedsApiConfigWarning, # avoid circular import
127+
)
128+
129+
raise NeedsApiConfigWarning(
130+
f"Cannot add extra option with name {name!r}"
131+
+ (f" ({description!r})" if description else "")
132+
+ ", as it is already used as a core field name."
133+
)
124134
if name in self._extra_options:
125135
if override:
126136
log_warning(

sphinx_needs/services/manager.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from typing import Any
44

5+
from docutils.parsers.rst import directives
56
from sphinx.application import Sphinx
67

78
from sphinx_needs.config import _NEEDS_CONFIG, NeedsSphinxConfig
@@ -28,7 +29,11 @@ def register(self, name: str, klass: type[BaseService], **kwargs: Any) -> None:
2829

2930
# Register options from service class
3031
for option in klass.options:
31-
if option not in _NEEDS_CONFIG.extra_options:
32+
if option == "type":
33+
# TODO this should probably be done a bit more systematically;
34+
# the github service adds a "type" option, but this is related to the core need field NOT an extra option
35+
NeedserviceDirective.option_spec["type"] = directives.unchanged
36+
elif option not in _NEEDS_CONFIG.extra_options:
3237
self.log.debug(f'Register option "{option}" for service "{name}"')
3338
_NEEDS_CONFIG.add_extra_option(option, f"Added by service {name}")
3439
# Register new option directly in Service directive, as its class options got already

0 commit comments

Comments
 (0)