diff --git a/pyproject.toml b/pyproject.toml index 436886e93..bfe17e46f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -106,7 +106,6 @@ module = [ 'sphinx_needs.directives.needfilter', 'sphinx_needs.directives.needflow', 'sphinx_needs.directives.needpie', - 'sphinx_needs.directives.needtable', 'sphinx_needs.directives.needuml', 'sphinx_needs.directives.utils', 'sphinx_needs.external_needs', diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index e8af90b97..989487aaf 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -309,7 +309,6 @@ def run(): "docname": docname, "doctype": doctype, "lineno": lineno, - # "target_node": target_node, "target_id": need_id, "external_url": external_url, "content_node": None, # gets set after rst parsing diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index c19f3732f..4a2a05b2b 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -362,6 +362,7 @@ class NeedsTableType(NeedsFilteredBaseType): caption: None | str classes: list[str] columns: list[tuple[str, str]] + """List of (name, title)""" colwidths: list[int] style: str style_row: str diff --git a/sphinx_needs/diagrams_common.py b/sphinx_needs/diagrams_common.py index 39ce8157f..c34f22af6 100644 --- a/sphinx_needs/diagrams_common.py +++ b/sphinx_needs/diagrams_common.py @@ -179,7 +179,6 @@ def calculate_link(app: Sphinx, need_info: Dict[str, Any], _fromdocname: str) -> # only need to add ../ or ..\ to get out of the image folder link = ".." + os.path.sep + need_info["external_url"] else: - # link = "../" + builder.get_target_uri(need_info["docname"]) + "#" + need_info["target_node"]["refid"] link = "../" + builder.get_target_uri(need_info["docname"]) + "#" + need_info["target_id"] if need_info["is_part"]: link = f"{link}.{need_info['id']}" diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index 98f498005..d4cc0406e 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -111,13 +111,6 @@ def process_needextract( found_needs = process_filters(app, all_needs.values(), current_needextract) for need_info in found_needs: - # if "is_target" is True: - # extract_target_node = current_needextract['target_node'] - # extract_target_node[ids=[need_info["id"]]] - # - # # Original need id replacement (needextract-{docname}-{id}) - # need_info['target_node']['ids'] = [f"replaced_{need['id']}"] - # filter out need_part from found_needs, in order to generate # copies of filtered needs with custom layout and style if need_info["is_need"] and not need_info["is_part"]: diff --git a/sphinx_needs/directives/needfilter.py b/sphinx_needs/directives/needfilter.py index ca424b5cf..09d52c586 100644 --- a/sphinx_needs/directives/needfilter.py +++ b/sphinx_needs/directives/needfilter.py @@ -161,10 +161,7 @@ def process_needfilters( line_block = nodes.line_block() for need_info in found_needs: - if "target_node" in need_info: - target_id = need_info["target_node"]["refid"] - else: - target_id = need_info["target_id"] + target_id = need_info["target_id"] if current_needfilter["layout"] == "list": para = nodes.line() diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index 9c51e49ca..29f6fda4c 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -204,9 +204,9 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo gantt_element = "[{}] as [{}] lasts 0 days\n".format(need["title"], need["id"]) else: # Normal gantt element handling duration_option = current_needgantt["duration_option"] - duration = need[duration_option] + duration = need[duration_option] # type: ignore[literal-required] complete_option = current_needgantt["completion_option"] - complete = need[complete_option] + complete = need[complete_option] # type: ignore[literal-required] if not (duration and duration.isdigit()): logger.warning( "Duration not set or invalid for needgantt chart. " @@ -258,7 +258,7 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo start_end_sync = "start" for link_type in current_needgantt[con_type]: # type: ignore[literal-required] - start_with_links = need[link_type] + start_with_links = need[link_type] # type: ignore[literal-required] for start_with_link in start_with_links: start_need = all_needs_dict[start_with_link] gantt_constraint = "[{}] {} at [{}]'s " "{}\n".format( diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index 475cf7eee..f35f4a904 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -107,6 +107,7 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou if need_info["hide"]: para += title elif need_info["is_external"]: + assert need_info["external_url"] is not None, "External need without URL" ref = nodes.reference("", "") ref["refuri"] = check_and_calc_base_url_rel_path(need_info["external_url"], fromdocname) @@ -115,12 +116,7 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou ref.append(title) para += ref else: - # target_node should not be stored, but it may be still the case - if "target_node" in need_info: - target_id = need_info["target_node"]["refid"] - else: - target_id = need_info["target_id"] - + target_id = need_info["target_id"] ref = nodes.reference("", "") ref["refdocname"] = need_info["docname"] ref["refuri"] = builder.get_relative_uri(fromdocname, need_info["docname"]) diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index 1e974bb6a..c7c890086 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -1,5 +1,5 @@ import re -from typing import List, Sequence +from typing import Any, Callable, List, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -7,7 +7,7 @@ from sphinx_needs.api.exceptions import NeedsInvalidException from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.debug import measure_time from sphinx_needs.directives.utils import ( get_option_list, @@ -53,13 +53,15 @@ def run(self) -> Sequence[nodes.Node]: targetid = "needtable-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needtable")) targetnode = nodes.target("", "", ids=[targetid]) - columns = str(self.options.get("columns", "")) - if len(columns) == 0: - columns = NeedsSphinxConfig(env.app.config).table_columns - if isinstance(columns, str): - columns = [col.strip() for col in re.split(";|,", columns)] + columns_str = str(self.options.get("columns", "")) + if len(columns_str) == 0: + columns_str = NeedsSphinxConfig(env.app.config).table_columns + if isinstance(columns_str, str): + _columns = [col.strip() for col in re.split(";|,", columns_str)] + else: + _columns = columns_str - columns = [get_title(col) for col in columns] + columns = [get_title(col) for col in _columns] colwidths = str(self.options.get("colwidths", "")) colwidths_list = [] @@ -213,24 +215,25 @@ def process_needtables( except Exception as e: raise e - def get_sorter(key): + def get_sorter(key: str) -> Callable[[NeedsInfoType], Any]: """ Returns a sort-function for a given need-key. :param key: key of need object as string :return: function to use in sort(key=x) """ - def sort(need): + def sort(need: NeedsInfoType) -> Any: """ Returns a given value of need, which is used for list sorting. :param need: need-element, which gets sort :return: value of need """ - if isinstance(need[key], str): + value = need[key] # type: ignore[literal-required] + if isinstance(value, str): # if we filter for string (e.g. id) everything should be lowercase. # Otherwise, "Z" will be above "a" - return need[key].lower() - return need[key] + return value.lower() + return value return sort @@ -246,7 +249,9 @@ def sort(need): prefix = "" else: row = nodes.row(classes=["need_part", style_row]) - temp_need["id"] = temp_need["id_complete"] + temp_need["id"] = temp_need[ + "id_complete" # type: ignore[typeddict-item] # TODO this is set in prepare_need_list + ] prefix = needs_config.part_prefix temp_need["title"] = temp_need["content"] @@ -279,10 +284,10 @@ def sort(need): for part in need_info["parts"].values(): # update the part with all information from its parent # this is required to make ID links work - temp_part = part.copy() # The dict has to be manipulated, so that row_col_maker() can be used - temp_part = {**need_info, **temp_part} - temp_part["id_complete"] = f"{need_info['id']}.{temp_part['id']}" - temp_part["id_parent"] = need_info["id"] + # The dict has to be manipulated, so that row_col_maker() can be used + temp_part: NeedsInfoType = {**need_info, **part.copy()} # type: ignore[typeddict-unknown-key] + temp_part["id_complete"] = f"{need_info['id']}.{temp_part['id']}" # type: ignore[typeddict-unknown-key] + temp_part["id_parent"] = need_info["id"] # type: ignore[typeddict-unknown-key] temp_part["docname"] = need_info["docname"] row = nodes.row(classes=["need_part"]) diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 13744a2dd..8a1603e8a 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -86,7 +86,7 @@ def collect_filter_attributes(self) -> FilterAttributesType: def process_filters( app: Sphinx, all_needs: List[NeedsInfoType], filter_data: NeedsFilteredBaseType, include_external: bool = True -): +) -> List[NeedsInfoType]: """ Filters all needs with given configuration. Used by needlist, needtable and needflow. @@ -205,13 +205,7 @@ def process_filters( filter_list = SphinxNeedsData(env).get_or_create_filters() found_needs_ids = [need["id_complete"] for need in found_needs] - if "target_node" in filter_data: - target_id = filter_data["target_node"]["refid"] - else: - target_id = filter_data["target_id"] - - filter_list[target_id] = { - # "target_node": current_needlist["target_node"], + filter_list[filter_data["target_id"]] = { "filter": filter_data["filter"] or "", "status": filter_data["status"], "tags": filter_data["tags"], @@ -243,9 +237,9 @@ def prepare_need_list(need_list: List[NeedsInfoType]) -> List[NeedsInfoType]: # Be sure extra attributes, which makes only sense for need_parts, are also available on # need level so that no KeyError gets raised, if search/filter get executed on needs with a need-part argument. - if "id_parent" not in need.keys(): + if "id_parent" not in need: need["id_parent"] = need["id"] - if "id_complete" not in need.keys(): + if "id_complete" not in need: need["id_complete"] = need["id"] return all_needs_incl_parts diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 60f95071e..9c15aa03d 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -174,7 +174,7 @@ def resolve_dynamic_values(env: BuildEnvironment): needs = data.get_or_create_needs() for need in needs.values(): for need_option in need: - if need_option in ["docname", "lineno", "target_node", "content", "content_node", "content_id"]: + if need_option in ["docname", "lineno", "content", "content_node", "content_id"]: # dynamic values in this data are not allowed. continue if not isinstance(need[need_option], (list, set)): diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index a7786b1bc..6bd126c9e 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -22,7 +22,6 @@ class NeedsList: "links_back", "type_color", "hide_status", - "target_node", "hide", "type_prefix", "lineno", @@ -37,7 +36,6 @@ class NeedsList: "links_back", "type_color", "hide_status", - "target_node", "hide", "type_prefix", "lineno", diff --git a/sphinx_needs/roles/need_incoming.py b/sphinx_needs/roles/need_incoming.py index de9e6f564..7837dcb18 100644 --- a/sphinx_needs/roles/need_incoming.py +++ b/sphinx_needs/roles/need_incoming.py @@ -59,7 +59,6 @@ def process_need_incoming( builder, fromdocname, target_need["docname"], - # target_need["target_node"]["refid"], target_need["target_id"], node_need_backref[0].deepcopy(), node_need_backref["reftarget"], diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 9f3194a52..b7d8a4db4 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -27,7 +27,6 @@ "docname", "doctype", "lineno", - "target_node", "refid", "content", "pre_content", @@ -87,12 +86,12 @@ def row_col_maker( app: Sphinx, fromdocname: str, all_needs: Dict[str, NeedsInfoType], - need_info, - need_key, + need_info: NeedsInfoType, + need_key: str, make_ref: bool = False, ref_lookup: bool = False, prefix: str = "", -): +) -> nodes.entry: """ Creates and returns a column. @@ -254,7 +253,10 @@ def import_prefix_link_edit(needs: Dict[str, Any], id_prefix: str, needs_extra_l need["description"] = need["description"].replace(id, "".join([id_prefix, id])) -def profile(keyword: str): +FuncT = TypeVar("FuncT") + + +def profile(keyword: str) -> Callable[[FuncT], FuncT]: """ Activate profiling for a specific function.