Skip to content

Commit f3a293c

Browse files
authored
♻️ split off source fields in NeedItem internal data (#1491)
1 parent 7311f16 commit f3a293c

File tree

11 files changed

+448
-175
lines changed

11 files changed

+448
-175
lines changed

docs/api.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ Data
3838
----
3939

4040
.. automodule:: sphinx_needs.need_item
41-
:members: NeedItem, NeedPartItem
41+
:members: NeedItem, NeedPartItem, NeedItemSourceProtocol
4242

4343
.. automodule:: sphinx_needs.data
44-
:members: NeedsInfoType, NeedsMutable, NeedsPartType
44+
:members: NeedsInfoType, NeedsSourceInfoType, NeedsMutable, NeedsPartType
4545

4646
Views
4747
-----

sphinx_needs/api/need.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
apply_default_predicate,
2626
)
2727
from sphinx_needs.logging import get_logger, log_warning
28-
from sphinx_needs.need_item import NeedItem
28+
from sphinx_needs.need_item import (
29+
NeedItem,
30+
NeedItemSourceProtocol,
31+
NeedItemSourceUnknown,
32+
)
2933
from sphinx_needs.nodes import Need
3034
from sphinx_needs.roles.need_part import find_parts, update_need_with_parts
3135
from sphinx_needs.utils import jinja_parse
@@ -47,6 +51,7 @@ def generate_need(
4751
need_type: str,
4852
title: str,
4953
*,
54+
need_source: NeedItemSourceProtocol | None = None,
5055
docname: None | str = None,
5156
lineno: None | int = None,
5257
id: str | None = None,
@@ -128,8 +133,25 @@ def generate_need(
128133
:param pre_template: Template name to use for content added before need
129134
:param post_template: Template name to use for the content added after need
130135
"""
136+
source = (
137+
NeedItemSourceUnknown(
138+
docname=docname,
139+
lineno=lineno,
140+
lineno_content=lineno_content,
141+
is_import=is_import,
142+
is_external=is_external,
143+
external_url=external_url if is_external else None,
144+
)
145+
if need_source is None
146+
else need_source
147+
)
148+
131149
# location is used to provide source mapped warnings
132-
location = (docname, lineno) if docname else None
150+
location = (
151+
(source.dict_repr["docname"], source.dict_repr["lineno"])
152+
if source.dict_repr["docname"]
153+
else None
154+
)
133155

134156
# validate kwargs
135157
allowed_kwargs = {x["option"] for x in needs_config.extra_links} | set(
@@ -230,9 +252,9 @@ def generate_need(
230252
"title": title,
231253
"tags": tuple(tags or []),
232254
"status": status,
233-
"docname": docname,
234-
"is_import": is_import,
235-
"is_external": is_external,
255+
"docname": source.dict_repr["docname"],
256+
"is_import": source.dict_repr["is_import"],
257+
"is_external": source.dict_repr["is_external"],
236258
}
237259
defaults_extras = extras_no_defaults.copy()
238260
defaults_links = {k: tuple(v or []) for k, v in links_no_defaults.items()}
@@ -306,9 +328,6 @@ def generate_need(
306328

307329
# Add the need and all needed information
308330
needs_data: NeedsInfoType = {
309-
"docname": docname,
310-
"lineno": lineno,
311-
"lineno_content": lineno_content,
312331
"doctype": doctype,
313332
"content": content,
314333
"pre_content": None,
@@ -340,9 +359,6 @@ def generate_need(
340359
"is_need": True,
341360
"id_parent": need_id,
342361
"id_complete": need_id,
343-
"is_import": is_import,
344-
"is_external": is_external,
345-
"external_url": external_url if is_external else None,
346362
"external_css": external_css or "external_link",
347363
"is_modified": False,
348364
"modifications": 0,
@@ -354,7 +370,9 @@ def generate_need(
354370
"parent_need": parent_need,
355371
}
356372

357-
needs_info = NeedItem(core=needs_data, extras=extras, links=links, _validate=False)
373+
needs_info = NeedItem(
374+
core=needs_data, extras=extras, links=links, source=source, _validate=False
375+
)
358376

359377
if jinja_content:
360378
need_content_context: dict[str, Any] = {**needs_info}
@@ -390,12 +408,13 @@ def generate_need(
390408

391409
def add_need(
392410
app: Sphinx,
393-
state: None | RSTState,
394-
docname: None | str,
395-
lineno: None | int,
396-
need_type: str,
397-
title: str,
411+
state: None | RSTState = None,
412+
docname: None | str = None,
413+
lineno: None | int = None,
414+
need_type: str = "",
415+
title: str = "",
398416
*,
417+
need_source: NeedItemSourceProtocol | None = None,
399418
id: str | None = None,
400419
content: str | StringList = "",
401420
lineno_content: None | int = None,
@@ -485,11 +504,16 @@ def add_need(
485504
)
486505
kwargs = {k: v for k, v in kwargs.items() if k not in _deprecated_kwargs}
487506

488-
if doctype is None and not is_external and docname:
489-
doctype = os.path.splitext(app.env.doc2path(docname))[1]
507+
if (
508+
doctype is None
509+
and not (need_source.dict_repr["is_external"] if need_source else is_external)
510+
and (_docname := need_source.dict_repr["docname"] if need_source else docname)
511+
):
512+
doctype = os.path.splitext(app.env.doc2path(_docname))[1]
490513

491514
needs_info = generate_need(
492515
needs_config=NeedsSphinxConfig(app.config),
516+
need_source=need_source,
493517
need_type=need_type,
494518
title=title,
495519
full_title=full_title,
@@ -697,6 +721,7 @@ def add_external_need(
697721
need_type: str,
698722
title: str | None = None,
699723
id: str | None = None,
724+
need_source: NeedItemSourceProtocol | None = None,
700725
external_url: str | None = None,
701726
external_css: str = "external_link",
702727
content: str = "",
@@ -738,9 +763,7 @@ def add_external_need(
738763

739764
return add_need(
740765
app=app,
741-
state=None,
742-
docname=None,
743-
lineno=None,
766+
need_source=need_source,
744767
need_type=need_type,
745768
id=id,
746769
content=content,

sphinx_needs/data.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -385,18 +385,28 @@ class CoreFieldParameters(TypedDict):
385385
}
386386

387387

388-
class NeedsInfoType(TypedDict):
389-
"""Data for a single need."""
390-
391-
id: str
392-
"""ID of the data."""
388+
class NeedsSourceInfoType(TypedDict):
389+
"""Data for the source of a single need."""
393390

394391
docname: str | None
395392
"""Name of the document where the need is defined (None if external)."""
396393
lineno: int | None
397394
"""Line number where the need is defined (None if external)."""
398395
lineno_content: int | None
399396
"""Line number on which the need content starts (None if external)."""
397+
external_url: None | str
398+
"""URL of the need, if it is an external need."""
399+
is_import: bool
400+
"""If true, the need was derived from an import."""
401+
is_external: bool
402+
"""If true, no node is created and need is referencing external url."""
403+
404+
405+
class NeedsInfoType(TypedDict):
406+
"""Data for a single need."""
407+
408+
id: str
409+
"""ID of the data."""
400410

401411
# meta information
402412
title: str
@@ -418,14 +428,6 @@ class NeedsInfoType(TypedDict):
418428
arch: dict[str, str]
419429
"""Mapping of uml key to uml content."""
420430

421-
is_import: bool
422-
"""If true, the need was derived from an import."""
423-
424-
# external reference information
425-
is_external: bool
426-
"""If true, no node is created and need is referencing external url."""
427-
external_url: None | str
428-
"""URL of the need, if it is an external need."""
429431
external_css: str
430432
"""CSS class name, added to the external reference."""
431433

sphinx_needs/directives/need.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from sphinx_needs.layout import build_need_repr
2525
from sphinx_needs.logging import WarningSubTypes, get_logger, log_warning
2626
from sphinx_needs.need_constraints import process_constraints
27-
from sphinx_needs.need_item import NeedItem
27+
from sphinx_needs.need_item import NeedItem, NeedItemSourceDirective
2828
from sphinx_needs.nodes import Need
2929
from sphinx_needs.utils import (
3030
DummyOptionSpec,
@@ -166,18 +166,22 @@ def run(self) -> Sequence[nodes.Node]:
166166
self._log_warning(f"Invalid value for '{key}' option: {err}")
167167
return []
168168

169+
source = NeedItemSourceDirective(
170+
docname=self.env.docname,
171+
lineno=self.lineno,
172+
lineno_content=self.content_offset + 1,
173+
)
174+
169175
try:
170176
need_nodes = add_need(
171-
self.env.app,
172-
self.state,
173-
self.env.docname,
174-
self.lineno,
177+
app=self.env.app,
178+
state=self.state,
179+
need_source=source,
175180
need_type=self.name,
176181
title=title,
177182
full_title=full_title,
178183
id=id,
179184
content=self.content,
180-
lineno_content=self.content_offset + 1,
181185
status=status,
182186
tags=tags,
183187
template=template,

sphinx_needs/directives/needimport.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from sphinx_needs.debug import measure_time
2020
from sphinx_needs.filter_common import filter_import_item
2121
from sphinx_needs.logging import log_warning
22+
from sphinx_needs.need_item import NeedItemSourceImport
2223
from sphinx_needs.needsfile import SphinxNeedsFileException, check_needs_data
2324
from sphinx_needs.utils import (
2425
add_doc,
@@ -241,14 +242,20 @@ def run(self) -> Sequence[nodes.Node]:
241242
# Replace id, to get unique ids
242243
need_id = need_params["id"] = id_prefix + need_params["id"]
243244

244-
# override location
245-
need_params["docname"] = self.docname
246-
need_params["lineno"] = self.lineno
247-
248-
need_params["is_import"] = True
245+
# set location
246+
need_source = NeedItemSourceImport(
247+
docname=self.env.docname,
248+
lineno=self.lineno,
249+
path=need_import_path,
250+
)
249251

250252
try:
251-
nodes = add_need(self.env.app, self.state, **need_params)
253+
nodes = add_need(
254+
app=self.env.app,
255+
state=self.state,
256+
need_source=need_source,
257+
**need_params,
258+
)
252259
except InvalidNeedException as err:
253260
log_warning(
254261
logger,

sphinx_needs/directives/needservice.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sphinx_needs.config import NeedsSphinxConfig
1515
from sphinx_needs.data import SphinxNeedsData
1616
from sphinx_needs.logging import get_logger, log_warning
17+
from sphinx_needs.need_item import NeedItemSourceService
1718
from sphinx_needs.utils import DummyOptionSpec, add_doc, coerce_to_boolean
1819

1920

@@ -135,14 +136,16 @@ def run(self) -> Sequence[nodes.Node]:
135136
datum.update(options)
136137

137138
# ToDo: Tags and Status are not set (but exist in data)
139+
source = NeedItemSourceService(
140+
docname=self.env.docname, lineno=self.lineno
141+
)
138142
try:
139143
section += add_need(
140-
self.env.app,
141-
self.state,
142-
self.env.docname,
143-
self.lineno,
144-
need_type,
145-
need_title,
144+
app=self.env.app,
145+
state=self.state,
146+
need_source=source,
147+
need_type=need_type,
148+
title=need_title,
146149
**datum,
147150
)
148151
except InvalidNeedException as err:

sphinx_needs/external_needs.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sphinx_needs.config import NeedsSphinxConfig
1515
from sphinx_needs.data import NeedsCoreFields, SphinxNeedsData
1616
from sphinx_needs.logging import get_logger, log_warning
17+
from sphinx_needs.need_item import NeedItemSourceExternal
1718
from sphinx_needs.utils import clean_log, import_prefix_link_edit
1819

1920
log = get_logger(__name__)
@@ -170,11 +171,9 @@ def load_external_needs(
170171
# render jinja content
171172
mem_template = get_target_template(target_url)
172173
cal_target_url = mem_template.render(**{"need": need})
173-
need_params["external_url"] = f"{source['base_url']}/{cal_target_url}"
174+
external_url = f"{source['base_url']}/{cal_target_url}"
174175
else:
175-
need_params["external_url"] = (
176-
f"{source['base_url']}/{need.get('docname', '__error__')}.html#{need['id']}"
177-
)
176+
external_url = f"{source['base_url']}/{need.get('docname', '__error__')}.html#{need['id']}"
178177

179178
# check if external needs already exist
180179
ext_need_id = need_params["id"]
@@ -190,7 +189,11 @@ def load_external_needs(
190189
del_need(app, ext_need_id)
191190

192191
try:
193-
add_external_need(app, **need_params)
192+
add_external_need(
193+
app,
194+
need_source=NeedItemSourceExternal(url=external_url),
195+
**need_params,
196+
)
194197
except InvalidNeedException as err:
195198
location = source.get("json_url", "") or source.get("json_path", "")
196199
log_warning(

0 commit comments

Comments
 (0)