Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Remove full_title need field and only trim generated titles #1407

Merged
merged 1 commit into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions sphinx_needs/api/need.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
is_external: bool = False,
external_url: str | None = None,
external_css: str = "external_link",
full_title: str | None = None,
**kwargs: str,
) -> NeedsInfoType:
"""Creates a validated need data entry, without adding it to the project.
Expand Down Expand Up @@ -103,6 +104,8 @@
:param state: Current state object.
:param need_type: Name of the need type to create.
:param title: String as title.
:param full_title: This is given, if an auto-generated title was trimmed.
It is used to auto-generate the ID, if required.
:param id: ID as string. If not given, an id will get generated.
:param content: Content of the need
:param status: Status as string.
Expand Down Expand Up @@ -143,7 +146,12 @@
"missing_id", "No ID defined, but 'needs_id_required' is set to True."
)
need_id = (
_make_hashed_id(need_type_data["prefix"], title, content, needs_config)
_make_hashed_id(
need_type_data["prefix"],
title if full_title is None else full_title,
content,
needs_config,
)
if id is None
else id
)
Expand Down Expand Up @@ -182,15 +190,6 @@
f"Constraints {unknown_constraints!r} not in 'needs_constraints'.",
)

# Trim title if it is too long
max_length = needs_config.max_title_length
if max_length == -1 or len(title) <= max_length:
trimmed_title = title
elif max_length <= 3:
trimmed_title = title[:max_length]
else:
trimmed_title = title[: max_length - 3] + "..."

# Add the need and all needed information
needs_info: NeedsInfoType = {
"docname": docname,
Expand All @@ -209,8 +208,7 @@
"constraints_passed": True,
"constraints_results": {},
"id": need_id,
"title": trimmed_title,
"full_title": title,
"title": title,
"collapse": collapse or False,
"arch": arch or {},
"style": style,
Expand Down Expand Up @@ -347,6 +345,7 @@
is_external: bool = False,
external_url: str | None = None,
external_css: str = "external_link",
full_title: str | None = None,
**kwargs: Any,
) -> list[nodes.Node]:
"""
Expand Down Expand Up @@ -384,6 +383,8 @@
:param state: Current state object.
:param need_type: Name of the need type to create.
:param title: String as title.
:param full_title: This is given, if an auto-generated title was trimmed.
It is used to auto-generate the ID, if required.
:param id: ID as string. If not given, an id will get generated.
:param content: Content of the need, either as a ``str``
or a ``StringList`` (a string with mapping to the source text).
Expand Down Expand Up @@ -415,6 +416,7 @@
needs_config=NeedsSphinxConfig(app.config),
need_type=need_type,
title=title,
full_title=full_title,
docname=docname,
lineno=lineno,
id=id,
Expand Down Expand Up @@ -446,7 +448,7 @@
if SphinxNeedsData(app.env).has_need(needs_info["id"]):
if id is None:
# this is a generated ID
message = f"Unique ID could not be generated for need with title {needs_info['full_title']!r}."
message = f"Unique ID could not be generated for need with title {needs_info['title']!r}."

Check warning on line 451 in sphinx_needs/api/need.py

View check run for this annotation

Codecov / codecov/patch

sphinx_needs/api/need.py#L451

Added line #L451 was not covered by tests
else:
message = f"A need with ID {needs_info['id']!r} already exists."
raise InvalidNeedException("duplicate_id", message)
Expand Down
11 changes: 2 additions & 9 deletions sphinx_needs/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,8 @@ class CoreFieldParameters(TypedDict):
"exclude_external": True,
"exclude_import": True,
},
"full_title": {
"description": "Title of the need, of unlimited length.",
"schema": {"type": "string", "default": ""},
"allow_df": True,
},
"title": {
"description": "Title of the need, trimmed to a maximum length.",
"description": "Title of the need.",
"schema": {"type": "string"},
"allow_df": True,
},
Expand Down Expand Up @@ -374,10 +369,8 @@ class NeedsInfoType(TypedDict, total=False):
"""Line number on which the need content starts (None if external)."""

# meta information
full_title: Required[str]
"""Title of the need, of unlimited length."""
title: Required[str]
"""Title of the need, trimmed to a maximum length."""
"""Title of the need."""
status: Required[None | str]
tags: Required[list[str]]

Expand Down
21 changes: 16 additions & 5 deletions sphinx_needs/directives/need.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
post_template = self.options.get("post_template")
constraints = self.options.get("constraints", [])

title = self._get_title(needs_config)
title, full_title = self._get_title(needs_config)

need_extra_options = {}
for extra_link in needs_config.extra_links:
Expand All @@ -94,6 +94,7 @@
self.lineno,
need_type=self.name,
title=title,
full_title=full_title,
id=id,
content=self.content,
lineno_content=self.content_offset + 1,
Expand Down Expand Up @@ -121,10 +122,12 @@
add_doc(self.env, self.env.docname)
return need_nodes

def _get_title(self, config: NeedsSphinxConfig) -> str:
def _get_title(self, config: NeedsSphinxConfig) -> tuple[str, str | None]:
"""Determines the title for the need in order of precedence:
directive argument, first sentence of requirement
(if `:title_from_content:` was set, and '' if no title is to be derived).

:return: The title and the full title (if title was trimmed)
"""
if len(self.arguments) > 0: # a title was passed
if "title_from_content" in self.options:
Expand All @@ -134,7 +137,7 @@
"title",
location=self.get_location(),
)
return self.arguments[0] # type: ignore[no-any-return]
return self.arguments[0], None
elif "title_from_content" in self.options or config.title_from_content:
first_sentence = re.split(r"[.\n]", "\n".join(self.content))[0]
if not first_sentence:
Expand All @@ -144,9 +147,17 @@
"title",
location=self.get_location(),
)
return first_sentence or ""
title = first_sentence or ""
# Trim title if it is too long
max_length = config.max_title_length
if max_length == -1 or len(title) <= max_length:
return title, None
elif max_length <= 3:
return title[:max_length], title

Check warning on line 156 in sphinx_needs/directives/need.py

View check run for this annotation

Codecov / codecov/patch

sphinx_needs/directives/need.py#L156

Added line #L156 was not covered by tests
else:
return title[: max_length - 3] + "...", title
else:
return ""
return "", None


def get_sections_and_signature_and_needs(
Expand Down
6 changes: 2 additions & 4 deletions sphinx_needs/directives/needimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,15 @@ def run(self) -> Sequence[nodes.Node]:

# all known need fields in the project
known_keys = {
"full_title", # legacy
*NeedsCoreFields,
*(x["option"] for x in needs_config.extra_links),
*(x["option"] + "_back" for x in needs_config.extra_links),
*needs_config.extra_options,
}
# all keys that should not be imported from external needs
omitted_keys = {
"full_title", # legacy
*(k for k, v in NeedsCoreFields.items() if v.get("exclude_import")),
*(x["option"] + "_back" for x in needs_config.extra_links),
}
Expand Down Expand Up @@ -255,10 +257,6 @@ def run(self) -> Sequence[nodes.Node]:

# These keys need to be different for add_need() api call.
need_params["need_type"] = need_params.pop("type", "") # type: ignore[misc,typeddict-unknown-key]
need_params["title"] = need_params.pop(
"full_title", # type: ignore[misc]
need_params.get("title", ""),
)

# Replace id, to get unique ids
need_id = need_params["id"] = id_prefix + need_params["id"]
Expand Down
5 changes: 2 additions & 3 deletions sphinx_needs/external_needs.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,15 @@ def load_external_needs(

# all known need fields in the project
known_keys = {
"full_title", # legacy
*NeedsCoreFields,
*(x["option"] for x in needs_config.extra_links),
*(x["option"] + "_back" for x in needs_config.extra_links),
*needs_config.extra_options,
}
# all keys that should not be imported from external needs
omitted_keys = {
"full_title", # legacy
*(k for k, v in NeedsCoreFields.items() if v.get("exclude_external")),
*(x["option"] + "_back" for x in needs_config.extra_links),
}
Expand Down Expand Up @@ -158,9 +160,6 @@ def load_external_needs(

# These keys need to be different for add_need() api call.
need_params["need_type"] = need_params.pop("type", "")
need_params["title"] = need_params.pop(
"full_title", need_params.get("title", "")
)

# Replace id, to get unique ids
need_params["id"] = id_prefix + need["id"]
Expand Down
2 changes: 1 addition & 1 deletion sphinx_needs/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ def meta_all(
"""
``meta_all()`` excludes by default the output of: ``docname``, ``lineno``, ``refid``,
``content``, ``collapse``, ``parts``, ``id_parent``,
``id_complete``, ``title``, ``full_title``, ``is_part``, ``is_need``,
``id_complete``, ``title``, ``is_part``, ``is_need``,
``type_prefix``, ``type_color``, ``type_style``, ``type``, ``type_name``, ``id``,
``hide``, ``sections``, ``section_name``.

Expand Down
3 changes: 0 additions & 3 deletions tests/__snapshots__/test_add_sections_sigs.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
'content': 'The Tool **shall** have a command line interface.',
'docname': 'index',
'external_css': 'external_link',
'full_title': 'Command line interface',
'id': 'R_12345',
'impacts': 'component_a',
'introduced': '1.0.0',
Expand All @@ -29,7 +28,6 @@
'content': 'The Tool **shall** have a command line interface.',
'docname': 'index',
'external_css': 'external_link',
'full_title': 'Another Requirement',
'id': 'R_12346',
'impacts': 'component_b',
'introduced': '1.1.1',
Expand All @@ -52,7 +50,6 @@
'T_001': dict({
'docname': 'index',
'external_css': 'external_link',
'full_title': 'test method',
'id': 'T_001',
'layout': '',
'lineno': 40,
Expand Down
10 changes: 1 addition & 9 deletions tests/__snapshots__/test_basic_doc.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
'duration': '',
'external_css': 'external_link',
'external_url': None,
'full_title': 'Test story',
'has_dead_links': False,
'has_forbidden_dead_links': False,
'id': 'ST_001',
Expand Down Expand Up @@ -92,7 +91,6 @@
'duration': '',
'external_css': 'external_link',
'external_url': None,
'full_title': 'No ID',
'has_dead_links': False,
'has_forbidden_dead_links': False,
'id': 'US_38823',
Expand Down Expand Up @@ -256,12 +254,6 @@
'null',
]),
}),
'full_title': dict({
'default': '',
'description': 'Title of the need, of unlimited length.',
'field_type': 'core',
'type': 'string',
}),
'has_dead_links': dict({
'default': False,
'description': 'True if any links reference need ids that are not found in the need list.',
Expand Down Expand Up @@ -527,7 +519,7 @@
]),
}),
'title': dict({
'description': 'Title of the need, trimmed to a maximum length.',
'description': 'Title of the need.',
'field_type': 'core',
'type': 'string',
}),
Expand Down
Loading
Loading