From 96a5f52443dbfcd183ce457ed858e0e4b545509d Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 15:58:26 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Use=20future=20annotations=20in?= =?UTF-8?q?=20all=20modules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just to keep things consistent across the code-base --- sphinx_needs/api/configuration.py | 12 ++-- sphinx_needs/api/exceptions.py | 2 + sphinx_needs/api/need.py | 36 +++++------ sphinx_needs/builder.py | 14 +++-- sphinx_needs/defaults.py | 6 +- sphinx_needs/diagrams_common.py | 16 ++--- sphinx_needs/directives/list2need.py | 6 +- sphinx_needs/directives/need.py | 30 +++++----- sphinx_needs/directives/needbar.py | 6 +- sphinx_needs/directives/needextend.py | 11 ++-- sphinx_needs/directives/needextract.py | 11 ++-- sphinx_needs/directives/needfilter.py | 8 ++- sphinx_needs/directives/needflow.py | 16 ++--- sphinx_needs/directives/needgantt.py | 8 ++- sphinx_needs/directives/needimport.py | 6 +- sphinx_needs/directives/needlist.py | 11 ++-- sphinx_needs/directives/needpie.py | 6 +- sphinx_needs/directives/needreport.py | 2 + sphinx_needs/directives/needsequence.py | 20 ++++--- sphinx_needs/directives/needservice.py | 10 ++-- sphinx_needs/directives/needtable.py | 6 +- sphinx_needs/directives/needuml.py | 6 +- sphinx_needs/directives/utils.py | 14 +++-- sphinx_needs/environment.py | 6 +- sphinx_needs/errors.py | 2 + sphinx_needs/external_needs.py | 2 + sphinx_needs/functions/common.py | 30 +++++----- sphinx_needs/functions/functions.py | 24 ++++---- sphinx_needs/layout.py | 70 +++++++++++----------- sphinx_needs/logging.py | 2 + sphinx_needs/need_constraints.py | 6 +- sphinx_needs/needs.py | 6 +- sphinx_needs/needsfile.py | 6 +- sphinx_needs/roles/need_count.py | 4 +- sphinx_needs/roles/need_func.py | 4 +- sphinx_needs/roles/need_incoming.py | 4 +- sphinx_needs/roles/need_outgoing.py | 4 +- sphinx_needs/roles/need_part.py | 10 ++-- sphinx_needs/roles/need_ref.py | 9 +-- sphinx_needs/services/base.py | 6 +- sphinx_needs/services/config/github.py | 2 + sphinx_needs/services/config/open_needs.py | 2 + sphinx_needs/services/github.py | 18 +++--- sphinx_needs/services/manager.py | 8 ++- sphinx_needs/services/open_needs.py | 16 ++--- sphinx_needs/utils.py | 61 ++++++++----------- sphinx_needs/warnings.py | 6 +- 47 files changed, 310 insertions(+), 261 deletions(-) diff --git a/sphinx_needs/api/configuration.py b/sphinx_needs/api/configuration.py index 54b88d8d8..8b06ab045 100644 --- a/sphinx_needs/api/configuration.py +++ b/sphinx_needs/api/configuration.py @@ -4,7 +4,9 @@ All functions here are available under ``sphinxcontrib.api``. So do not import this module directly. """ -from typing import Callable, List, Optional +from __future__ import annotations + +from typing import Callable from docutils.parsers.rst import directives from sphinx.application import Sphinx @@ -17,7 +19,7 @@ from sphinx_needs.functions.functions import DynamicFunction -def get_need_types(app: Sphinx) -> List[str]: +def get_need_types(app: Sphinx) -> list[str]: """ Returns a list of directive-names from all configured need_types. @@ -91,7 +93,7 @@ def add_extra_option(app: Sphinx, name: str) -> None: NEEDS_CONFIG.extra_options[name] = directives.unchanged -def add_dynamic_function(app: Sphinx, function: DynamicFunction, name: Optional[str] = None) -> None: +def add_dynamic_function(app: Sphinx, function: DynamicFunction, name: str | None = None) -> None: """ Registers a new dynamic function for sphinx-needs. @@ -122,9 +124,7 @@ def my_function(app, need, needs, *args, **kwargs): WarningCheck = Callable[[NeedsInfoType, SphinxLoggerAdapter], bool] -def add_warning( - app: Sphinx, name: str, function: Optional[WarningCheck] = None, filter_string: Optional[str] = None -) -> None: +def add_warning(app: Sphinx, name: str, function: WarningCheck | None = None, filter_string: str | None = None) -> None: """ Registers a warning. diff --git a/sphinx_needs/api/exceptions.py b/sphinx_needs/api/exceptions.py index 0d3bef918..1720441aa 100644 --- a/sphinx_needs/api/exceptions.py +++ b/sphinx_needs/api/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from sphinx.errors import SphinxError, SphinxWarning diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 5ae06f619..1a1b87f8b 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import hashlib import os import re -from typing import Any, List, Optional, Union +from typing import Any from docutils import nodes from docutils.parsers.rst.states import RSTState @@ -40,13 +42,13 @@ def add_need( lineno: int, need_type, title: str, - id: Optional[str] = None, + id: str | None = None, content: str = "", - status: Optional[str] = None, + status: str | None = None, tags=None, constraints=None, constraints_passed=None, - links_string: Optional[str] = None, + links_string: str | None = None, delete: bool = False, jinja_content: bool = False, hide: bool = False, @@ -56,10 +58,10 @@ def add_need( style=None, layout=None, template=None, - pre_template: Optional[str] = None, - post_template: Optional[str] = None, + pre_template: str | None = None, + post_template: str | None = None, is_external: bool = False, - external_url: Optional[str] = None, + external_url: str | None = None, external_css: str = "external_link", **kwargs, ): @@ -523,15 +525,15 @@ def del_need(app: Sphinx, need_id: str) -> None: def add_external_need( app: Sphinx, need_type, - title: Optional[str] = None, - id: Optional[str] = None, - external_url: Optional[str] = None, + title: str | None = None, + id: str | None = None, + external_url: str | None = None, external_css: str = "external_link", content: str = "", - status: Optional[str] = None, - tags: Optional[str] = None, - constraints: Optional[str] = None, - links_string: Optional[str] = None, + status: str | None = None, + tags: str | None = None, + constraints: str | None = None, + links_string: str | None = None, **kwargs: Any, ): """ @@ -621,7 +623,7 @@ def _render_plantuml_template(content: str, docname: str, lineno: int, state: RS return node_need_content -def _read_in_links(links_string: Union[str, List[str]]) -> List[str]: +def _read_in_links(links_string: str | list[str]) -> list[str]: # Get links links = [] if links_string: @@ -646,7 +648,7 @@ def _read_in_links(links_string: Union[str, List[str]]) -> List[str]: return _fix_list_dyn_func(links) -def make_hashed_id(app: Sphinx, need_type: str, full_title: str, content: str, id_length: Optional[int] = None) -> str: +def make_hashed_id(app: Sphinx, need_type: str, full_title: str, content: str, id_length: int | None = None) -> str: """ Creates an ID based on title or need. @@ -683,7 +685,7 @@ def make_hashed_id(app: Sphinx, need_type: str, full_title: str, content: str, i return f"{type_prefix}{cal_hashed_id[:id_length]}" -def _fix_list_dyn_func(list: List[str]) -> List[str]: +def _fix_list_dyn_func(list: list[str]) -> list[str]: """ This searches a list for dynamic function fragments, which may have been cut by generic searches for ",|;". diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index 5674d2323..e1eaf7994 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import os -from typing import Iterable, List, Optional, Sequence, Set +from typing import Iterable, Sequence from docutils import nodes from sphinx import version_info @@ -78,7 +80,7 @@ def finish(self) -> None: from sphinx_needs.filter_common import filter_needs filter_string = needs_config.builder_filter - filtered_needs: List[NeedsInfoType] = filter_needs( + filtered_needs: list[NeedsInfoType] = filter_needs( data.get_or_create_needs().values(), needs_config, filter_string ) @@ -96,11 +98,11 @@ def finish(self) -> None: else: LOGGER.info("Needs successfully exported") - def get_target_uri(self, _docname: str, _typ: Optional[str] = None) -> str: + def get_target_uri(self, _docname: str, _typ: str | None = None) -> str: # only needed if the write phase is run return "" - def prepare_writing(self, _docnames: Set[str]) -> None: + def prepare_writing(self, _docnames: set[str]) -> None: # only needed if the write phase is run pass @@ -242,7 +244,7 @@ def finish(self) -> None: def get_outdated_docs(self) -> Iterable[str]: return [] - def prepare_writing(self, _docnames: Set[str]) -> None: + def prepare_writing(self, _docnames: set[str]) -> None: pass def write_doc_serialized(self, _docname: str, _doctree: nodes.document) -> None: @@ -251,7 +253,7 @@ def write_doc_serialized(self, _docname: str, _doctree: nodes.document) -> None: def cleanup(self) -> None: pass - def get_target_uri(self, _docname: str, _typ: Optional[str] = None) -> str: + def get_target_uri(self, _docname: str, _typ: str | None = None) -> str: return "" diff --git a/sphinx_needs/defaults.py b/sphinx_needs/defaults.py index 2e7a11913..5445e2572 100644 --- a/sphinx_needs/defaults.py +++ b/sphinx_needs/defaults.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import os -from typing import Any, Dict +from typing import Any from docutils.parsers.rst import directives @@ -202,7 +204,7 @@ TITLE_REGEX = r'([^\s]+) as "([^"]+)"' -NEED_DEFAULT_OPTIONS: Dict[str, Any] = { +NEED_DEFAULT_OPTIONS: dict[str, Any] = { "id": directives.unchanged_required, "status": directives.unchanged_required, "tags": directives.unchanged_required, diff --git a/sphinx_needs/diagrams_common.py b/sphinx_needs/diagrams_common.py index 4adde2680..15de4321c 100644 --- a/sphinx_needs/diagrams_common.py +++ b/sphinx_needs/diagrams_common.py @@ -3,10 +3,12 @@ diagram related directive. E.g. needflow and needsequence. """ +from __future__ import annotations + import html import os import textwrap -from typing import Any, Dict, List, Optional, Tuple, TypedDict +from typing import Any, TypedDict from urllib.parse import urlparse from docutils import nodes @@ -27,14 +29,14 @@ class DiagramAttributesType(TypedDict): show_legend: bool show_filters: bool show_link_names: bool - link_types: List[str] + link_types: list[str] config: str config_names: str scale: str highlight: str - align: Optional[str] + align: str | None debug: bool - caption: Optional[str] + caption: str | None class DiagramBase(SphinxDirective): @@ -52,7 +54,7 @@ class DiagramBase(SphinxDirective): "debug": directives.flag, } - def create_target(self, target_name: str) -> Tuple[int, str, nodes.target]: + def create_target(self, target_name: str) -> tuple[int, str, nodes.target]: id = self.env.new_serialno(target_name) targetid = f"{target_name}-{self.env.docname}-{id}" targetnode = nodes.target("", "", ids=[targetid]) @@ -184,8 +186,8 @@ def calculate_link(app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: str return link -def create_legend(need_types: List[Dict[str, Any]]) -> str: - def create_row(need_type: Dict[str, Any]) -> str: +def create_legend(need_types: list[dict[str, Any]]) -> str: + def create_row(need_type: dict[str, Any]) -> str: return "\n| {color} | {name} |".format(color=need_type["color"], name=need_type["title"]) rows = map(create_row, need_types) diff --git a/sphinx_needs/directives/list2need.py b/sphinx_needs/directives/list2need.py index d0a1d7bfc..bbdd27643 100644 --- a/sphinx_needs/directives/list2need.py +++ b/sphinx_needs/directives/list2need.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import hashlib import re from contextlib import suppress -from typing import Any, List, Sequence +from typing import Any, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -201,7 +203,7 @@ def make_hashed_id(self, type_prefix: str, title: str, id_length: int) -> str: type_prefix, hashlib.sha1(hashable_content.encode("UTF-8")).hexdigest().upper()[:id_length] ) - def get_down_needs(self, list_needs: List[Any], index: int) -> List[str]: + def get_down_needs(self, list_needs: list[Any], index: int) -> list[str]: """ Return all needs which are directly under the one given by the index """ diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 5965e71a4..f4c527791 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import hashlib import re -from typing import Any, Dict, List, Optional, Sequence, Tuple +from typing import Any, Sequence from docutils import nodes from docutils.parsers.rst.states import RSTState, RSTStateMachine @@ -53,8 +55,8 @@ class NeedDirective(SphinxDirective): def __init__( self, name: str, - arguments: List[str], - options: Dict[str, Any], + arguments: list[str], + options: dict[str, Any], content: StringList, lineno: int, content_offset: int, @@ -149,7 +151,7 @@ def run(self) -> Sequence[nodes.Node]: add_doc(env, self.docname) return need_nodes # type: ignore[no-any-return] - def read_in_links(self, name: str) -> List[str]: + def read_in_links(self, name: str) -> list[str]: # Get links links_string = self.options.get(name) links = [] @@ -229,12 +231,12 @@ def _get_full_title(self) -> str: def get_sections_and_signature_and_needs( - need_node: Optional[nodes.Node], -) -> Tuple[List[str], Optional[nodes.Text], List[str]]: + need_node: nodes.Node | None, +) -> tuple[list[str], nodes.Text | None, list[str]]: """Gets the hierarchy of the section nodes as a list starting at the section of the current need and then its parent sections""" sections = [] - parent_needs: List[str] = [] + parent_needs: list[str] = [] signature = None current_node = need_node while current_node: @@ -299,7 +301,7 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None: needs = SphinxNeedsData(env).get_or_create_needs() - hidden_needs: List[Need] = [] + hidden_needs: list[Need] = [] for need_node in doctree.findall(Need): need_id = need_node["refid"] need_info = needs[need_id] @@ -346,7 +348,7 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None: need_node.parent.remove(need_node) -def previous_sibling(node: nodes.Node) -> Optional[nodes.Node]: +def previous_sibling(node: nodes.Node) -> nodes.Node | None: """Return preceding sibling node or ``None``.""" try: i = node.parent.index(node) @@ -408,7 +410,7 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - @profile("NEED_FORMAT") -def format_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, found_needs_nodes: List[Need]) -> None: +def format_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, found_needs_nodes: list[Need]) -> None: """Replace need nodes in the document with node trees suitable for output""" env = app.env needs = SphinxNeedsData(env).get_or_create_needs() @@ -428,7 +430,7 @@ def format_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, fo build_need(layout, node_need, app, fromdocname=fromdocname) -def check_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: +def check_links(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Checks if set links are valid or are dead (referenced need does not exist.) For needs with dead links, an extra ``has_dead_links`` field is added and, @@ -471,7 +473,7 @@ def check_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> N ) -def create_back_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: +def create_back_links(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Create back-links in all found needs. These are fields for each link type, ``_back``, @@ -482,7 +484,7 @@ def create_back_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig option_back = f"{option}_back" for key, need in needs.items(): - need_link_value: List[str] = [need[option]] if isinstance(need[option], str) else need[option] # type: ignore[literal-required] + need_link_value: list[str] = [need[option]] if isinstance(need[option], str) else need[option] # type: ignore[literal-required] for need_id_full in need_link_value: need_id_main, need_id_part = split_need_id(need_id_full) @@ -497,7 +499,7 @@ def create_back_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig needs[need_id_main]["parts"][need_id_part][option_back].append(key) # type: ignore[literal-required] -def _fix_list_dyn_func(list: List[str]) -> List[str]: +def _fix_list_dyn_func(list: list[str]) -> list[str]: """ This searches a list for dynamic function fragments, which may have been cut by generic searches for ",|;". diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index 1e048a3ce..623bef414 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import hashlib import math -from typing import List, Sequence +from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -165,7 +167,7 @@ def run(self) -> Sequence[nodes.Node]: # 8. create figure # 9. final storage # 10. cleanup matplotlib -def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: env = app.env needs_data = SphinxNeedsData(env) needs_config = NeedsSphinxConfig(env.config) diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 7f6d1e48d..886aaa123 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -1,10 +1,7 @@ -""" - - -""" +from __future__ import annotations import re -from typing import Any, Callable, Dict, Sequence +from typing import Any, Callable, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -34,7 +31,7 @@ class NeedextendDirective(SphinxDirective): optional_arguments = 0 final_argument_whitespace = True - option_spec: Dict[str, Callable[[str], Any]] = { + option_spec: dict[str, Callable[[str], Any]] = { "strict": directives.unchanged_required, } @@ -72,7 +69,7 @@ def run(self) -> Sequence[nodes.Node]: def extend_needs_data( - all_needs: Dict[str, NeedsInfoType], extends: Dict[str, NeedsExtendType], needs_config: NeedsSphinxConfig + all_needs: dict[str, NeedsInfoType], extends: dict[str, NeedsExtendType], needs_config: NeedsSphinxConfig ) -> None: """Use data gathered from needextend directives to modify fields of existing needs.""" diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index 9b61ac91e..18f5cb060 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -1,10 +1,7 @@ -""" - - -""" +from __future__ import annotations import re -from typing import List, Sequence +from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -70,7 +67,7 @@ def run(self) -> Sequence[nodes.Node]: def process_needextract( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: """ Replace all needextract nodes with a list of the collected needs. @@ -86,7 +83,7 @@ def process_needextract( id = node.attributes["ids"][0] current_needextract = SphinxNeedsData(env).get_or_create_extracts()[id] all_needs = SphinxNeedsData(env).get_or_create_needs() - content: List[nodes.Element] = [] + content: list[nodes.Element] = [] # check if filter argument and option filter both exist need_filter_arg = current_needextract["filter_arg"] diff --git a/sphinx_needs/directives/needfilter.py b/sphinx_needs/directives/needfilter.py index 8800a189d..ef0cb3d71 100644 --- a/sphinx_needs/directives/needfilter.py +++ b/sphinx_needs/directives/needfilter.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import os -from typing import List, Sequence, Union +from typing import Sequence from urllib.parse import urlparse from docutils import nodes @@ -70,7 +72,7 @@ def run(self) -> Sequence[nodes.Node]: def process_needfilters( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: # Replace all needlist nodes with a list of the collected needs. # Augment each need with a backlink to the original location. @@ -89,7 +91,7 @@ def process_needfilters( id = node.attributes["ids"][0] current_needfilter = SphinxNeedsData(env)._get_or_create_filters()[id] - content: Union[nodes.Element, List[nodes.Element]] + content: nodes.Element | list[nodes.Element] if current_needfilter["layout"] == "list": content = [] diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 749f84b8c..31572afb4 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import html import os -from typing import Dict, Iterable, List, Sequence +from typing import Iterable, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -32,7 +34,7 @@ logger = get_logger(__name__) -NEEDFLOW_TEMPLATES: Dict[str, Template] = {} +NEEDFLOW_TEMPLATES: dict[str, Template] = {} class Needflow(nodes.General, nodes.Element): @@ -163,7 +165,7 @@ def walk_curr_need_tree( fromdocname: str, current_needflow: NeedsFlowType, all_needs: Iterable[NeedsInfoType], - found_needs: List[NeedsPartsInfoType], + found_needs: list[NeedsPartsInfoType], need: NeedsPartsInfoType, ) -> str: """ @@ -230,7 +232,7 @@ def walk_curr_need_tree( return curr_need_tree -def get_root_needs(found_needs: List[NeedsPartsInfoType]) -> List[NeedsPartsInfoType]: +def get_root_needs(found_needs: list[NeedsPartsInfoType]) -> list[NeedsPartsInfoType]: return_list = [] for current_need in found_needs: if current_need["is_need"]: @@ -253,7 +255,7 @@ def cal_needs_node( fromdocname: str, current_needflow: NeedsFlowType, all_needs: Iterable[NeedsInfoType], - found_needs: List[NeedsPartsInfoType], + found_needs: list[NeedsPartsInfoType], ) -> str: """Calculate and get needs node representaion for plantuml including all child needs and need parts.""" top_needs = get_root_needs(found_needs) @@ -276,7 +278,7 @@ def cal_needs_node( @measure_time("needflow") -def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: # Replace all needflow nodes with a list of the collected needs. # Augment each need with a backlink to the original location. env = app.env @@ -321,7 +323,7 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou node.replace_self(error_node) continue - content: List[nodes.Element] = [] + content: list[nodes.Element] = [] found_needs = process_filters(app, all_needs.values(), current_needflow) diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index 1ad22c3ce..d0f4b9c11 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import os import re from datetime import datetime -from typing import List, Sequence +from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -118,7 +120,7 @@ def run(self) -> Sequence[nodes.Node]: return [targetnode] + [Needgantt("")] - def get_link_type_option(self, name: str, default: str = "") -> List[str]: + def get_link_type_option(self, name: str, default: str = "") -> list[str]: link_types = [x.strip() for x in re.split(";|,", self.options.get(name, default))] conf_link_types = NeedsSphinxConfig(self.env.config).extra_links conf_link_types_name = [x["option"] for x in conf_link_types] @@ -136,7 +138,7 @@ def get_link_type_option(self, name: str, default: str = "") -> List[str]: return final_link_types -def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: # Replace all needgantt nodes with a list of the collected needs. env = app.env needs_config = NeedsSphinxConfig(app.config) diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index 299d718d4..b66148079 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import json import os import re -from typing import Dict, Sequence +from typing import Sequence from urllib.parse import urlparse import requests @@ -125,7 +127,7 @@ def run(self) -> Sequence[nodes.Node]: needs_config = NeedsSphinxConfig(self.config) # TODO this is not exactly NeedsInfoType, because the export removes/adds some keys - needs_list: Dict[str, NeedsInfoType] = needs_import_list["versions"][version]["needs"] + needs_list: dict[str, NeedsInfoType] = needs_import_list["versions"][version]["needs"] # Filter imported needs needs_list_filtered = {} diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index 6a2b14ff9..91a2950f2 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -1,9 +1,6 @@ -""" +from __future__ import annotations - -""" - -from typing import List, Sequence +from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -63,7 +60,7 @@ def run(self) -> Sequence[nodes.Node]: return [targetnode, Needlist("")] -def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: """ Replace all needlist nodes with a list of the collected needs. Augment each need with a backlink to the original location. @@ -80,7 +77,7 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou id = node.attributes["ids"][0] current_needfilter = SphinxNeedsData(env).get_or_create_lists()[id] - content: List[nodes.Node] = [] + content: list[nodes.Node] = [] all_needs = list(SphinxNeedsData(env).get_or_create_needs().values()) found_needs = process_filters(app, all_needs, current_needfilter) diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index 72a0affea..f055dffe3 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import hashlib -from typing import Iterable, List, Sequence +from typing import Iterable, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -104,7 +106,7 @@ def run(self) -> Sequence[nodes.Node]: @measure_time("needpie") -def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: env = app.env needs_data = SphinxNeedsData(env) needs_config = NeedsSphinxConfig(env.config) diff --git a/sphinx_needs/directives/needreport.py b/sphinx_needs/directives/needreport.py index b72f7523f..97f4b5c08 100644 --- a/sphinx_needs/directives/needreport.py +++ b/sphinx_needs/directives/needreport.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pathlib import Path from typing import Sequence diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index 4a9737175..42b1e1e25 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import os import re -from typing import Any, Dict, List, Optional, Sequence, Tuple +from typing import Any, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -74,7 +76,7 @@ def run(self) -> Sequence[nodes.Node]: def process_needsequence( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: # Replace all needsequence nodes with a list of the collected needs. env = app.env @@ -224,18 +226,18 @@ def process_needsequence( def get_message_needs( app: Sphinx, sender: NeedsInfoType, - link_types: List[str], - all_needs_dict: Dict[str, NeedsInfoType], - tracked_receivers: Optional[List[str]] = None, - filter: Optional[str] = None, -) -> Tuple[Dict[str, Dict[str, Any]], str, str]: - msg_needs: List[Dict[str, Any]] = [] + link_types: list[str], + all_needs_dict: dict[str, NeedsInfoType], + tracked_receivers: list[str] | None = None, + filter: str | None = None, +) -> tuple[dict[str, dict[str, Any]], str, str]: + msg_needs: list[dict[str, Any]] = [] if tracked_receivers is None: tracked_receivers = [] for link_type in link_types: msg_needs += [all_needs_dict[x] for x in sender[link_type]] # type: ignore - messages: Dict[str, Dict[str, Any]] = {} + messages: dict[str, dict[str, Any]] = {} p_string = "" c_string = "" for msg_need in msg_needs: diff --git a/sphinx_needs/directives/needservice.py b/sphinx_needs/directives/needservice.py index 2adb2cf64..bf234f7fe 100644 --- a/sphinx_needs/directives/needservice.py +++ b/sphinx_needs/directives/needservice.py @@ -1,4 +1,6 @@ -from typing import Any, Dict, List, Sequence +from __future__ import annotations + +from typing import Any, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -37,8 +39,8 @@ class NeedserviceDirective(SphinxDirective): def __init__( self, name: str, - arguments: List[str], - options: Dict[str, Any], + arguments: list[str], + options: dict[str, Any], content: StringList, lineno: int, content_offset: int, @@ -55,7 +57,7 @@ def run(self) -> Sequence[nodes.Node]: needs_config = NeedsSphinxConfig(self.config) need_types = needs_config.types all_data = needs_config.service_all_data - needs_services: Dict[str, BaseService] = getattr(app, "needs_services", {}) + needs_services: dict[str, BaseService] = getattr(app, "needs_services", {}) service_name = self.arguments[0] service = needs_services.get(service_name) diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index b00b7cf48..c05dfb61b 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from typing import Any, Callable, List, Sequence +from typing import Any, Callable, Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -113,7 +115,7 @@ def run(self) -> Sequence[nodes.Node]: @measure_time("needtable") @profile("NEEDTABLE") def process_needtables( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: """ Replace all needtables nodes with a table of filtered nodes. diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index bd679cdbb..0b2efb482 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import html import os -from typing import List, Sequence +from typing import Sequence from docutils import nodes from docutils.parsers.rst import directives @@ -405,7 +407,7 @@ def is_element_of_need(node: nodes.Element) -> str: @measure_time("needuml") -def process_needuml(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_needuml(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: env = app.env # for node in doctree.findall(Needuml): diff --git a/sphinx_needs/directives/utils.py b/sphinx_needs/directives/utils.py index 3efc786fb..378342909 100644 --- a/sphinx_needs/directives/utils.py +++ b/sphinx_needs/directives/utils.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import re -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from docutils import nodes from sphinx.environment import BuildEnvironment @@ -9,7 +11,7 @@ from sphinx_needs.defaults import TITLE_REGEX -def no_needs_found_paragraph(message: Optional[str]) -> nodes.paragraph: +def no_needs_found_paragraph(message: str | None) -> nodes.paragraph: nothing_found = "No needs passed the filters" if message is None else message para = nodes.paragraph() para["classes"].append("needs_filter_warning") @@ -40,7 +42,7 @@ def used_filter_paragraph(current_needfilter: NeedsFilteredBaseType) -> nodes.pa return para -def get_title(option_string: str) -> Tuple[str, str]: +def get_title(option_string: str) -> tuple[str, str]: """ Returns a tuple of uppercase option and calculated title of given option string. @@ -59,7 +61,7 @@ def get_title(option_string: str) -> Tuple[str, str]: return option_name.upper(), title -def get_option_list(options: Dict[str, Any], name: str) -> List[str]: +def get_option_list(options: dict[str, Any], name: str) -> list[str]: """ Gets and creates a list of a given directive option value in a safe way :param options: List of options @@ -74,7 +76,7 @@ def get_option_list(options: Dict[str, Any], name: str) -> List[str]: return values_list -def analyse_needs_metrics(env: BuildEnvironment) -> Dict[str, Any]: +def analyse_needs_metrics(env: BuildEnvironment) -> dict[str, Any]: """ Function to generate metrics about need objects. @@ -82,7 +84,7 @@ def analyse_needs_metrics(env: BuildEnvironment) -> Dict[str, Any]: :return: Dictionary consisting of needs metrics. """ needs = SphinxNeedsData(env).get_or_create_needs() - metric_data: Dict[str, Any] = {"needs_amount": len(needs)} + metric_data: dict[str, Any] = {"needs_amount": len(needs)} needs_types = {i["directive"]: 0 for i in NeedsSphinxConfig(env.config).types} for i in needs.values(): diff --git a/sphinx_needs/environment.py b/sphinx_needs/environment.py index 513f5e981..61dbf95dc 100644 --- a/sphinx_needs/environment.py +++ b/sphinx_needs/environment.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from pathlib import Path, PurePosixPath -from typing import Iterable, List +from typing import Iterable from jinja2 import Environment, PackageLoader, select_autoescape from sphinx.application import Sphinx @@ -133,7 +135,7 @@ def install_static_files( app: Sphinx, source_dir: Path, destination_dir: Path, - files_to_copy: List[Path], + files_to_copy: list[Path], message: str, ) -> None: builder = app.builder diff --git a/sphinx_needs/errors.py b/sphinx_needs/errors.py index a75ba9fc8..958790cc1 100644 --- a/sphinx_needs/errors.py +++ b/sphinx_needs/errors.py @@ -1,3 +1,5 @@ +from __future__ import annotations + try: # Sphinx 3.0 from sphinx.errors import NoUri diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index 4288c37c5..fc8003648 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import json import os from functools import lru_cache diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index 69d6f4e37..f5d96bf06 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -4,9 +4,11 @@ .. note:: The function parameters ``app``, ``need``, ``needs`` are set automatically and can not be overridden by user. """ +from __future__ import annotations + import contextlib import re -from typing import Any, Dict, List, Optional +from typing import Any from sphinx.application import Sphinx @@ -17,7 +19,7 @@ from sphinx_needs.utils import logger -def test(app: Sphinx, need: NeedsInfoType, needs: Dict[str, NeedsInfoType], *args: Any, **kwargs: Any) -> str: +def test(app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], *args: Any, **kwargs: Any) -> str: """ Test function for dynamic functions in sphinx needs. @@ -39,7 +41,7 @@ def test(app: Sphinx, need: NeedsInfoType, needs: Dict[str, NeedsInfoType], *arg def echo( - app: Sphinx, need: NeedsInfoType, needs: Dict[str, NeedsInfoType], text: str, *args: Any, **kwargs: Any + app: Sphinx, need: NeedsInfoType, needs: dict[str, NeedsInfoType], text: str, *args: Any, **kwargs: Any ) -> str: """ .. versionadded:: 0.6.3 @@ -60,12 +62,12 @@ def echo( def copy( app: Sphinx, need: NeedsInfoType, - needs: Dict[str, NeedsInfoType], + needs: dict[str, NeedsInfoType], option: str, - need_id: Optional[str] = None, + need_id: str | None = None, lower: bool = False, upper: bool = False, - filter: Optional[str] = None, + filter: str | None = None, ) -> Any: """ Copies the value of one need option to another @@ -171,11 +173,11 @@ def copy( def check_linked_values( app: Sphinx, need: NeedsInfoType, - needs: Dict[str, NeedsInfoType], + needs: dict[str, NeedsInfoType], result: Any, search_option: str, search_value: Any, - filter_string: Optional[str] = None, + filter_string: str | None = None, one_hit: bool = False, ) -> Any: """ @@ -335,9 +337,9 @@ def check_linked_values( def calc_sum( app: Sphinx, need: NeedsInfoType, - needs: Dict[str, NeedsInfoType], + needs: dict[str, NeedsInfoType], option: str, - filter: Optional[str] = None, + filter: str | None = None, links_only: bool = False, ) -> float: """ @@ -443,10 +445,10 @@ def calc_sum( def links_from_content( app: Sphinx, need: NeedsInfoType, - needs: Dict[str, NeedsInfoType], - need_id: Optional[str] = None, - filter: Optional[str] = None, -) -> List[str]: + needs: dict[str, NeedsInfoType], + need_id: str | None = None, + filter: str | None = None, +) -> list[str]: """ Extracts links from content of a need. diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index a8c640ce2..694435d17 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -6,9 +6,11 @@ in need configurations. """ +from __future__ import annotations + import ast import re -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Callable, Dict, List, Union from docutils import nodes from sphinx.application import Sphinx @@ -31,7 +33,7 @@ ] -def register_func(need_function: DynamicFunction, name: Optional[str] = None) -> None: +def register_func(need_function: DynamicFunction, name: str | None = None) -> None: """ Registers a new sphinx-needs function for the given sphinx environment. :param env: Sphinx environment @@ -153,7 +155,7 @@ def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: return node -def resolve_dynamic_values(needs: Dict[str, NeedsInfoType], app: Sphinx) -> None: +def resolve_dynamic_values(needs: dict[str, NeedsInfoType], app: Sphinx) -> None: """ Resolve dynamic values inside need data. @@ -178,7 +180,7 @@ def resolve_dynamic_values(needs: Dict[str, NeedsInfoType], app: Sphinx) -> None # dynamic values in this data are not allowed. continue if not isinstance(need[need_option], (list, set)): - func_call: Optional[str] = "init" + func_call: str | None = "init" while func_call: try: func_call, func_return = _detect_and_execute(need[need_option], need, app) @@ -231,7 +233,7 @@ def resolve_dynamic_values(needs: Dict[str, NeedsInfoType], app: Sphinx) -> None def resolve_variants_options( - needs: Dict[str, NeedsInfoType], needs_config: NeedsSphinxConfig, tags: Dict[str, bool] + needs: dict[str, NeedsInfoType], needs_config: NeedsSphinxConfig, tags: dict[str, bool] ) -> None: """ Resolve variants options inside need data. @@ -252,7 +254,7 @@ def resolve_variants_options( for need in needs.values(): # Data to use as filter context. - need_context: Dict[str, Any] = {**need} + need_context: dict[str, Any] = {**need} need_context.update(**needs_config.filter_data) # Add needs_filter_data to filter context need_context.update(**tags) # Add sphinx tags to filter context @@ -295,7 +297,7 @@ def check_and_get_content(content: str, need: NeedsInfoType, env: BuildEnvironme return content -def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> Tuple[Optional[str], Any]: +def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> tuple[str | None, Any]: """Detects if given content is a function call and executes it.""" try: content = str(content) @@ -312,7 +314,7 @@ def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> Tuple return func_call, func_return -def _analyze_func_string(func_string: str, need: Optional[NeedsInfoType]) -> Tuple[str, List[Any], Dict[str, Any]]: +def _analyze_func_string(func_string: str, need: NeedsInfoType | None) -> tuple[str, list[Any], dict[str, Any]]: """ Analyze given function string and extract: @@ -336,14 +338,14 @@ def _analyze_func_string(func_string: str, need: Optional[NeedsInfoType]) -> Tup except AttributeError: raise SphinxError(f"Given dynamic function string is not a valid python call. Got: {func_string}") - func_args: List[Any] = [] + func_args: list[Any] = [] for arg in func_call.args: if isinstance(arg, ast.Num): func_args.append(arg.n) elif isinstance(arg, (ast.Str, ast.BoolOp)): func_args.append(arg.s) # type: ignore elif isinstance(arg, ast.List): - arg_list: List[Any] = [] + arg_list: list[Any] = [] for element in arg.elts: if isinstance(element, ast.Num): arg_list.append(element.n) @@ -367,7 +369,7 @@ def _analyze_func_string(func_string: str, need: Optional[NeedsInfoType]) -> Tup "Unsupported type found in function definition: {}. " "Supported are numbers, strings, bool and list".format(func_string) ) - func_kargs: Dict[str, Any] = {} + func_kargs: dict[str, Any] = {} for keyword in func_call.keywords: kvalue = keyword.value kkey = keyword.arg diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index 068f35d5e..d9c573945 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -4,13 +4,15 @@ Based on https://github.com/useblocks/sphinxcontrib-needs/issues/102 """ +from __future__ import annotations + import os import re import uuid from contextlib import suppress from functools import lru_cache from optparse import Values -from typing import Callable, Dict, List, Optional, Tuple, Union +from typing import Callable from urllib.parse import urlparse import requests @@ -31,7 +33,7 @@ @measure_time("need") def create_need( - need_id: str, app: Sphinx, layout: Optional[str] = None, style: Optional[str] = None, docname: Optional[str] = None + need_id: str, app: Sphinx, layout: str | None = None, style: str | None = None, docname: str | None = None ) -> nodes.container: """ Creates a new need-node for a given layout. @@ -127,7 +129,7 @@ def replace_pending_xref_refdoc(node: nodes.Element, new_refdoc: str) -> None: @measure_time("need") def build_need( - layout: str, node: nodes.Element, app: Sphinx, style: Optional[str] = None, fromdocname: Optional[str] = None + layout: str, node: nodes.Element, app: Sphinx, style: str | None = None, fromdocname: str | None = None ) -> None: """ Builds a need based on a given layout for a given need-node. @@ -175,7 +177,7 @@ def build_need( @lru_cache(1) -def _generate_inline_parser() -> Tuple[Values, Inliner]: +def _generate_inline_parser() -> tuple[Values, Inliner]: doc_settings = OptionParser(components=(Parser,)).get_default_values() inline_parser = Inliner() inline_parser.init_customizations(doc_settings) # type: ignore @@ -193,8 +195,8 @@ def __init__( need: NeedsInfoType, layout: str, node: nodes.Element, - style: Optional[str] = None, - fromdocname: Optional[str] = None, + style: str | None = None, + fromdocname: str | None = None, ) -> None: self.app = app self.need = need @@ -302,7 +304,7 @@ def __init__( inliner=None, ) - self.functions: Dict[str, Callable[..., Union[None, nodes.Node, List[nodes.Node]]]] = { + self.functions: dict[str, Callable[..., None | nodes.Node | list[nodes.Node]]] = { "meta": self.meta, # type: ignore[dict-item] "meta_all": self.meta_all, "meta_links": self.meta_links, @@ -343,7 +345,7 @@ def get_need_table(self) -> nodes.table: return self.node_table - def get_section(self, section: str) -> Union[nodes.line_block, List[nodes.Element]]: + def get_section(self, section: str) -> nodes.line_block | list[nodes.Element]: try: lines = self.layout["layout"][section] except KeyError: @@ -367,7 +369,7 @@ def get_section(self, section: str) -> Union[nodes.line_block, List[nodes.Elemen return lines_container - def _parse(self, line: str) -> List[nodes.Node]: + def _parse(self, line: str) -> list[nodes.Node]: """ Parses a single line/string for inline rst statements, like strong, emphasis, literal, ... @@ -379,7 +381,7 @@ def _parse(self, line: str) -> List[nodes.Node]: raise SphinxNeedLayoutException(message) return result # type: ignore[no-any-return] - def _func_replace(self, section_nodes: List[nodes.Node]) -> List[nodes.Node]: + def _func_replace(self, section_nodes: list[nodes.Node]) -> list[nodes.Node]: """ Replaces a function definition like ``<>`` with the related docutils nodes. @@ -390,7 +392,7 @@ def _func_replace(self, section_nodes: List[nodes.Node]) -> List[nodes.Node]: :return: docutils nodes """ return_nodes = [] - result: Union[None, nodes.Node, List[nodes.Node]] + result: None | nodes.Node | list[nodes.Node] for node in section_nodes: if not isinstance(node, nodes.Text): for child in node.children: @@ -483,8 +485,8 @@ def _replace_place_holder(self, data: str) -> str: return data def meta( - self, name: str, prefix: Optional[str] = None, show_empty: bool = False - ) -> Union[nodes.inline, List[nodes.Element]]: + self, name: str, prefix: str | None = None, show_empty: bool = False + ) -> nodes.inline | list[nodes.Element]: """ Returns the specific metadata of a need inside docutils nodes. Usage:: @@ -520,11 +522,11 @@ def meta( if len(data) == 0 and not show_empty: return [] - needs_string_links_option: List[str] = [] + needs_string_links_option: list[str] = [] for v in self.needs_config.string_links.values(): needs_string_links_option.extend(v["options"]) - data_list: List[str] = ( + data_list: list[str] = ( [i.strip() for i in re.split(r",|;", data) if len(i) != 0] if name in needs_string_links_option else [data] @@ -605,7 +607,7 @@ def meta_all( self, prefix: str = "", postfix: str = "", - exclude: Optional[List[str]] = None, + exclude: list[str] | None = None, no_links: bool = False, defaults: bool = True, show_empty: bool = False, @@ -701,9 +703,7 @@ def meta_links(self, name: str, incoming: bool = False) -> nodes.inline: data_container.append(node_links) return data_container - def meta_links_all( - self, prefix: str = "", postfix: str = "", exclude: Optional[List[str]] = None - ) -> List[nodes.line]: + def meta_links_all(self, prefix: str = "", postfix: str = "", exclude: list[str] | None = None) -> list[nodes.line]: """ Documents all used link types for the current need automatically. @@ -736,14 +736,14 @@ def meta_links_all( def image( self, url: str, - height: Optional[str] = None, - width: Optional[str] = None, - align: Optional[str] = None, + height: str | None = None, + width: str | None = None, + align: str | None = None, no_link: bool = False, prefix: str = "", is_external: bool = False, img_class: str = "", - ) -> Union[nodes.inline, List[nodes.Element]]: + ) -> nodes.inline | list[nodes.Element]: """ See https://docutils.sourceforge.io/docs/ref/rst/directives.html#images @@ -875,10 +875,10 @@ def image( def link( self, url: str, - text: Optional[str] = None, - image_url: Optional[str] = None, - image_height: Optional[str] = None, - image_width: Optional[str] = None, + text: str | None = None, + image_url: str | None = None, + image_height: str | None = None, + image_width: str | None = None, prefix: str = "", is_dynamic: bool = False, ) -> nodes.inline: @@ -927,7 +927,7 @@ def link( def collapse_button( self, target: str = "meta", collapsed: str = "Show", visible: str = "Close", initial: bool = False - ) -> Optional[nodes.inline]: + ) -> nodes.inline | None: """ To show icons instead of text on the button, use collapse_button() like this:: @@ -989,10 +989,10 @@ def collapse_button( def permalink( self, - image_url: Optional[str] = None, - image_height: Optional[str] = None, - image_width: Optional[str] = None, - text: Optional[str] = None, + image_url: str | None = None, + image_height: str | None = None, + image_width: str | None = None, + text: str | None = None, prefix: str = "", ) -> nodes.inline: """ @@ -1034,9 +1034,7 @@ def permalink( prefix=prefix, ) - def _grid_simple( - self, colwidths: List[int], side_left: Union[bool, str], side_right: Union[bool, str], footer: bool - ) -> None: + def _grid_simple(self, colwidths: list[int], side_left: bool | str, side_right: bool | str, footer: bool) -> None: """ Creates most "simple" grid layouts. Side parts and footer can be activated via config. @@ -1209,7 +1207,7 @@ def _grid_complex(self) -> None: # Construct table node_tgroup += self.node_tbody - def _grid_content(self, colwidths: List[int], side_left: bool, side_right: bool, footer: bool) -> None: + def _grid_content(self, colwidths: list[int], side_left: bool, side_right: bool, footer: bool) -> None: """ Creates most "content" based grid layouts. Side parts and footer can be activated via config. diff --git a/sphinx_needs/logging.py b/sphinx_needs/logging.py index 90c34f2f0..c328fce23 100644 --- a/sphinx_needs/logging.py +++ b/sphinx_needs/logging.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from sphinx.util import logging from sphinx.util.logging import SphinxLoggerAdapter diff --git a/sphinx_needs/need_constraints.py b/sphinx_needs/need_constraints.py index 77db7d03b..e068a6bdf 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -1,4 +1,4 @@ -from typing import Dict +from __future__ import annotations import jinja2 @@ -11,7 +11,7 @@ logger = get_logger(__name__) -def process_constraints(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: +def process_constraints(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Analyse constraints of all needs, and set corresponding fields on the need data item: ``constraints_passed`` and ``constraints_results``. @@ -21,7 +21,7 @@ def process_constraints(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConf """ config_constraints = config.constraints - error_templates_cache: Dict[str, jinja2.Template] = {} + error_templates_cache: dict[str, jinja2.Template] = {} for need in needs.values(): need_id = need["id"] diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 03bbce6c0..e252dfc7f 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from timeit import default_timer as timer # Used for timing measurements from typing import Any, Callable, Dict, List, Type @@ -135,7 +137,7 @@ LOGGER = get_logger(__name__) -def setup(app: Sphinx) -> Dict[str, Any]: +def setup(app: Sphinx) -> dict[str, Any]: LOGGER.debug("Starting setup of Sphinx-Needs") LOGGER.debug("Load Sphinx-Data-Viewer for Sphinx-Needs") app.setup_extension("sphinx_data_viewer") @@ -282,7 +284,7 @@ def process_caller(app: Sphinx, doctree: nodes.document, fromdocname: str) -> No and fromdocname != f"{app.config.root_doc}" ): return - current_nodes: Dict[Type[nodes.Element], List[nodes.Element]] = {} + current_nodes: dict[type[nodes.Element], list[nodes.Element]] = {} check_nodes = list(node_list.keys()) for node_need in doctree.findall(node_match(check_nodes)): for check_node in node_list: diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index 17825637f..ece2bbd66 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -4,11 +4,13 @@ Creates, checks and imports ``needs.json`` files. """ +from __future__ import annotations + import json import os import sys from datetime import datetime -from typing import Any, List +from typing import Any from jsonschema import Draft7Validator from sphinx.config import Config @@ -150,7 +152,7 @@ def load_json(self, file: str) -> None: class Errors: - def __init__(self, schema_errors: List[Any]): + def __init__(self, schema_errors: list[Any]): self.schema = schema_errors diff --git a/sphinx_needs/roles/need_count.py b/sphinx_needs/roles/need_count.py index ba0a137f1..8ff1299a8 100644 --- a/sphinx_needs/roles/need_count.py +++ b/sphinx_needs/roles/need_count.py @@ -4,7 +4,7 @@ Based on https://github.com/useblocks/sphinxcontrib-needs/issues/37 """ -from typing import List +from __future__ import annotations from docutils import nodes from sphinx.application import Sphinx @@ -23,7 +23,7 @@ class NeedCount(nodes.Inline, nodes.Element): def process_need_count( - app: Sphinx, doctree: nodes.document, _fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, _fromdocname: str, found_nodes: list[nodes.Element] ) -> None: needs_config = NeedsSphinxConfig(app.config) for node_need_count in found_nodes: diff --git a/sphinx_needs/roles/need_func.py b/sphinx_needs/roles/need_func.py index 50852465a..23daee4ac 100644 --- a/sphinx_needs/roles/need_func.py +++ b/sphinx_needs/roles/need_func.py @@ -2,7 +2,7 @@ Provide the role ``need_func``, which executes a dynamic function. """ -from typing import List +from __future__ import annotations from docutils import nodes from sphinx.application import Sphinx @@ -18,7 +18,7 @@ class NeedFunc(nodes.Inline, nodes.Element): def process_need_func( - app: Sphinx, doctree: nodes.document, _fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, _fromdocname: str, found_nodes: list[nodes.Element] ) -> None: env = app.env # for node_need_func in doctree.findall(NeedFunc): diff --git a/sphinx_needs/roles/need_incoming.py b/sphinx_needs/roles/need_incoming.py index dc882b74e..fcdf5251b 100644 --- a/sphinx_needs/roles/need_incoming.py +++ b/sphinx_needs/roles/need_incoming.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations from docutils import nodes from sphinx.application import Sphinx @@ -15,7 +15,7 @@ class NeedIncoming(nodes.Inline, nodes.Element): def process_need_incoming( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: builder = app.builder env = app.env diff --git a/sphinx_needs/roles/need_outgoing.py b/sphinx_needs/roles/need_outgoing.py index 8248f1ce4..1939b7e4e 100644 --- a/sphinx_needs/roles/need_outgoing.py +++ b/sphinx_needs/roles/need_outgoing.py @@ -1,4 +1,4 @@ -from typing import List +from __future__ import annotations from docutils import nodes from sphinx.application import Sphinx @@ -18,7 +18,7 @@ class NeedOutgoing(nodes.Inline, nodes.Element): def process_need_outgoing( - app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element] + app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element] ) -> None: builder = app.builder env = app.env diff --git a/sphinx_needs/roles/need_part.py b/sphinx_needs/roles/need_part.py index 8b398ac2d..242d197d4 100644 --- a/sphinx_needs/roles/need_part.py +++ b/sphinx_needs/roles/need_part.py @@ -7,9 +7,11 @@ """ +from __future__ import annotations + import hashlib import re -from typing import List, cast +from typing import cast from docutils import nodes from sphinx.application import Sphinx @@ -25,14 +27,14 @@ class NeedPart(nodes.Inline, nodes.Element): pass -def process_need_part(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_need_part(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: pass part_pattern = re.compile(r"\(([\w-]+)\)(.*)") -def update_need_with_parts(env: BuildEnvironment, need: NeedsInfoType, part_nodes: List[NeedPart]) -> None: +def update_need_with_parts(env: BuildEnvironment, need: NeedsInfoType, part_nodes: list[NeedPart]) -> None: app = env.app builder = app.builder for part_node in part_nodes: @@ -86,7 +88,7 @@ def update_need_with_parts(env: BuildEnvironment, need: NeedsInfoType, part_node part_node.append(node_need_part_line) -def find_parts(node: nodes.Node) -> List[NeedPart]: +def find_parts(node: nodes.Node) -> list[NeedPart]: found_nodes = [] for child in node.children: if isinstance(child, NeedPart): diff --git a/sphinx_needs/roles/need_ref.py b/sphinx_needs/roles/need_ref.py index 2f1526a13..d817b9274 100644 --- a/sphinx_needs/roles/need_ref.py +++ b/sphinx_needs/roles/need_ref.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import contextlib from collections.abc import Iterable -from typing import Dict, List, Union from docutils import nodes from sphinx.application import Sphinx @@ -19,7 +20,7 @@ class NeedRef(nodes.Inline, nodes.Element): pass -def transform_need_to_dict(need: NeedsInfoType) -> Dict[str, str]: +def transform_need_to_dict(need: NeedsInfoType) -> dict[str, str]: """ The function will transform a need in a dictionary of strings. Used to be given e.g. to a python format string. @@ -50,7 +51,7 @@ def transform_need_to_dict(need: NeedsInfoType) -> Dict[str, str]: return dict_need -def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: +def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: list[nodes.Element]) -> None: builder = app.builder env = app.env needs_config = NeedsSphinxConfig(env.config) @@ -93,7 +94,7 @@ def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, fou title = f"{title[: max_length - 3]}..." dict_need["title"] = title - ref_name: Union[None, str, nodes.Text] = node_need_ref.children[0].children[0] # type: ignore[assignment] + ref_name: None | str | nodes.Text = node_need_ref.children[0].children[0] # type: ignore[assignment] # Only use ref_name, if it differs from ref_id if str(need_id_full) == str(ref_name): ref_name = None diff --git a/sphinx_needs/services/base.py b/sphinx_needs/services/base.py index 36c60e82d..7189c115d 100644 --- a/sphinx_needs/services/base.py +++ b/sphinx_needs/services/base.py @@ -1,10 +1,12 @@ -from typing import Any, ClassVar, List +from __future__ import annotations + +from typing import Any, ClassVar from sphinx_needs.logging import get_logger class BaseService: - options: ClassVar[List[str]] + options: ClassVar[list[str]] def __init__(self, *args: Any, **kwargs: Any) -> None: self.log = get_logger(__name__) diff --git a/sphinx_needs/services/config/github.py b/sphinx_needs/services/config/github.py index aa90d7303..ed2c2a6e2 100644 --- a/sphinx_needs/services/config/github.py +++ b/sphinx_needs/services/config/github.py @@ -1,3 +1,5 @@ +from __future__ import annotations + EXTRA_DATA_OPTIONS = ["user", "created_at", "updated_at", "closed_at", "service"] EXTRA_LINK_OPTIONS = ["url"] EXTRA_IMAGE_OPTIONS = ["avatar"] diff --git a/sphinx_needs/services/config/open_needs.py b/sphinx_needs/services/config/open_needs.py index 4ce37684d..5968e58fb 100644 --- a/sphinx_needs/services/config/open_needs.py +++ b/sphinx_needs/services/config/open_needs.py @@ -1,3 +1,5 @@ +from __future__ import annotations + EXTRA_DATA_OPTIONS = ["params", "prefix"] EXTRA_LINK_OPTIONS = ["url", "url_postfix"] CONFIG_OPTIONS = ["query", "max_content_lines", "id_prefix"] diff --git a/sphinx_needs/services/github.py b/sphinx_needs/services/github.py index 0a8b6efdd..b2fc6da72 100644 --- a/sphinx_needs/services/github.py +++ b/sphinx_needs/services/github.py @@ -1,8 +1,10 @@ +from __future__ import annotations + import os import textwrap import time from contextlib import suppress -from typing import Any, Dict, List, Optional, Tuple +from typing import Any from urllib.parse import urlparse import requests @@ -25,7 +27,7 @@ class GithubService(BaseService): options = CONFIG_OPTIONS + EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS - def __init__(self, app: Sphinx, name: str, config: Dict[str, Any], **kwargs: Any) -> None: + def __init__(self, app: Sphinx, name: str, config: dict[str, Any], **kwargs: Any) -> None: self.app = app self.name = name self.config = config @@ -74,7 +76,7 @@ def __init__(self, app: Sphinx, name: str, config: Dict[str, Any], **kwargs: Any super().__init__() - def _send(self, query: str, options: Dict[str, Any], specific: bool = False) -> Dict[str, Any]: + def _send(self, query: str, options: dict[str, Any], specific: bool = False) -> dict[str, Any]: headers = {} if self.gh_type == "commit": headers["Accept"] = "application/vnd.github.cloak-preview+json" @@ -105,7 +107,7 @@ def _send(self, query: str, options: Dict[str, Any], specific: bool = False) -> self.log.info(f"Service {self.name} requesting data for query: {query}") - auth: Optional[Tuple[str, str]] + auth: tuple[str, str] | None if self.username: # TODO token can be None auth = (self.username, self.token) # type: ignore @@ -146,7 +148,7 @@ def _send(self, query: str, options: Dict[str, Any], specific: bool = False) -> return {"items": [resp.json()]} return resp.json() # type: ignore - def request(self, options: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]: + def request(self, options: dict[str, Any] | None = None) -> list[dict[str, Any]]: if options is None: options = {} self.log.debug(f"Requesting data for service {self.name}") @@ -180,7 +182,7 @@ def request(self, options: Optional[Dict[str, Any]] = None) -> List[Dict[str, An return data - def prepare_issue_data(self, items: List[Dict[str, Any]], options: Dict[str, Any]) -> List[Dict[str, Any]]: + def prepare_issue_data(self, items: list[dict[str, Any]], options: dict[str, Any]) -> list[dict[str, Any]]: data = [] for item in items: # ensure that "None" can not reach .splitlines() @@ -240,7 +242,7 @@ def prepare_issue_data(self, items: List[Dict[str, Any]], options: Dict[str, Any return data - def prepare_commit_data(self, items: List[Dict[str, Any]], options: Dict[str, Any]) -> List[Dict[str, Any]]: + def prepare_commit_data(self, items: list[dict[str, Any]], options: dict[str, Any]) -> list[dict[str, Any]]: data = [] for item in items: @@ -314,7 +316,7 @@ def _get_avatar(self, avatar_url: str) -> str: return avatar_file_path - def _add_given_options(self, options: Dict[str, Any], element_data: Dict[str, Any]) -> None: + def _add_given_options(self, options: dict[str, Any], element_data: dict[str, Any]) -> None: """ Add data from options, which was defined by user but is not set by this service diff --git a/sphinx_needs/services/manager.py b/sphinx_needs/services/manager.py index a0f3f8668..ecdacadaf 100644 --- a/sphinx_needs/services/manager.py +++ b/sphinx_needs/services/manager.py @@ -1,4 +1,6 @@ -from typing import Any, Dict, Type +from __future__ import annotations + +from typing import Any from docutils.parsers.rst import directives from sphinx.application import Sphinx @@ -15,9 +17,9 @@ def __init__(self, app: Sphinx): self.app = app self.log = get_logger(__name__) - self.services: Dict[str, BaseService] = {} + self.services: dict[str, BaseService] = {} - def register(self, name: str, klass: Type[BaseService], **kwargs: Any) -> None: + def register(self, name: str, klass: type[BaseService], **kwargs: Any) -> None: try: config = NeedsSphinxConfig(self.app.config).services[name] except KeyError: diff --git a/sphinx_needs/services/open_needs.py b/sphinx_needs/services/open_needs.py index 24368bf92..6a9ea2195 100644 --- a/sphinx_needs/services/open_needs.py +++ b/sphinx_needs/services/open_needs.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import re from random import choices -from typing import Any, Dict, List +from typing import Any import requests from jinja2 import Template @@ -22,7 +24,7 @@ class OpenNeedsService(BaseService): options = CONFIG_OPTIONS + EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS - def __init__(self, app: Sphinx, name: str, config: Dict[str, Any], **kwargs: Any) -> None: + def __init__(self, app: Sphinx, name: str, config: dict[str, Any], **kwargs: Any) -> None: self.app = app self.name = name self.config = config @@ -38,10 +40,10 @@ def __init__(self, app: Sphinx, name: str, config: Dict[str, Any], **kwargs: Any self.id_prefix = self.config.get("id_prefix", "OPEN_NEEDS_") self.query = self.config.get("query", "") self.content = self.config.get("content", DEFAULT_CONTENT) - self.mappings: Dict[str, Any] = self.config.get("mappings", {}) + self.mappings: dict[str, Any] = self.config.get("mappings", {}) self.mapping_replaces = self.config.get("mappings_replaces", MAPPINGS_REPLACES_DEFAULT) - self.extra_data: Dict[str, Any] = self.config.get("extra_data", {}) + self.extra_data: dict[str, Any] = self.config.get("extra_data", {}) self.params = self.config.get("params", "skip=0,limit=100") super().__init__(**kwargs) @@ -70,8 +72,8 @@ def _prepare_request(self, options: Any) -> Any: url: str = options.get("url", self.url) url = url + str(self.url_postfix) - headers: Dict[str, str] = {"Authorization": f"{self.token_type} {self.access_token}"} - params: List[str] = [param.strip() for param in re.split(r";|,", options.get("params", self.params))] + headers: dict[str, str] = {"Authorization": f"{self.token_type} {self.access_token}"} + params: list[str] = [param.strip() for param in re.split(r";|,", options.get("params", self.params))] new_params: str = "&".join(params) url = f"{url}?{new_params}" @@ -94,7 +96,7 @@ def _send_request(request: Any) -> Any: raise OpenNeedsServiceException(f"Problem accessing {result.url}.\nReason: {result.text}") return result - def _extract_data(self, data: List[Dict[str, Any]], options: Dict[str, Any]) -> List[Dict[str, Any]]: + def _extract_data(self, data: list[dict[str, Any]], options: dict[str, Any]) -> list[dict[str, Any]]: """ Extract data of a list/dictionary, which was retrieved via send_request. :param data: list or dict diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 018ec9691..eb0fcccb7 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import cProfile import importlib import operator @@ -5,18 +7,7 @@ import re from functools import lru_cache, reduce, wraps from re import Pattern -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - List, - Optional, - Tuple, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, TypeVar from urllib.parse import urlparse from docutils import nodes @@ -44,10 +35,10 @@ class NeedFunctionsType(TypedDict): name: str - function: "DynamicFunction" + function: DynamicFunction -NEEDS_FUNCTIONS: Dict[str, NeedFunctionsType] = {} +NEEDS_FUNCTIONS: dict[str, NeedFunctionsType] = {} # List of internal need option names. They should not be used by or presented to user. INTERNALS = [ @@ -109,7 +100,7 @@ class NeedFunctionsType(TypedDict): ] -def split_need_id(need_id_full: str) -> Tuple[str, Optional[str]]: +def split_need_id(need_id_full: str) -> tuple[str, str | None]: """A need id can be a combination of a main id and a part id, split by a dot. This function splits them: @@ -128,7 +119,7 @@ def split_need_id(need_id_full: str) -> Tuple[str, Optional[str]]: def row_col_maker( app: Sphinx, fromdocname: str, - all_needs: Dict[str, NeedsInfoType], + all_needs: dict[str, NeedsInfoType], need_info: NeedsInfoType, need_key: str, make_ref: bool = False, @@ -155,7 +146,7 @@ def row_col_maker( row_col = nodes.entry(classes=["needs_" + need_key]) para_col = nodes.paragraph() - needs_string_links_option: List[str] = [] + needs_string_links_option: list[str] = [] for v in needs_config.string_links.values(): needs_string_links_option.extend(v["options"]) @@ -252,7 +243,7 @@ def row_col_maker( return row_col -def rstjinja(app: Sphinx, docname: str, source: List[str]) -> None: +def rstjinja(app: Sphinx, docname: str, source: list[str]) -> None: """ Render our pages as a jinja template for fancy templating goodness. """ @@ -267,7 +258,7 @@ def rstjinja(app: Sphinx, docname: str, source: List[str]) -> None: source[0] = rendered -def import_prefix_link_edit(needs: Dict[str, Any], id_prefix: str, needs_extra_links: List[Dict[str, Any]]) -> None: +def import_prefix_link_edit(needs: dict[str, Any], id_prefix: str, needs_extra_links: list[dict[str, Any]]) -> None: """ Changes existing links to support given prefix. Only link-ids get touched, which are part of ``needs`` (so are linking them). @@ -348,7 +339,7 @@ def check_and_calc_base_url_rel_path(external_url: str, fromdocname: str) -> str return ref_uri -def check_and_get_external_filter_func(filter_func_ref: Optional[str]) -> Tuple[Any, str]: +def check_and_get_external_filter_func(filter_func_ref: str | None) -> tuple[Any, str]: """Check and import filter function from external python file.""" # Check if external filter code is defined filter_func = None @@ -379,7 +370,7 @@ def check_and_get_external_filter_func(filter_func_ref: Optional[str]) -> Tuple[ return filter_func, filter_args -def jinja_parse(context: Dict[str, Any], jinja_string: str) -> str: +def jinja_parse(context: dict[str, Any], jinja_string: str) -> str: """ Function to parse mapping options set to a string containing jinja template format. @@ -401,7 +392,7 @@ def jinja_parse(context: Dict[str, Any], jinja_string: str) -> str: @lru_cache -def import_matplotlib() -> Optional["matplotlib"]: +def import_matplotlib() -> matplotlib | None: """Import and return matplotlib, or return None if it cannot be imported. Also sets the interactive backend to ``Agg``, if ``DISPLAY`` is not set. @@ -416,7 +407,7 @@ def import_matplotlib() -> Optional["matplotlib"]: return matplotlib -def save_matplotlib_figure(app: Sphinx, figure: "FigureBase", basename: str, fromdocname: str) -> nodes.image: +def save_matplotlib_figure(app: Sphinx, figure: FigureBase, basename: str, fromdocname: str) -> nodes.image: builder = app.builder env = app.env @@ -455,7 +446,7 @@ def save_matplotlib_figure(app: Sphinx, figure: "FigureBase", basename: str, fro return image_node -def dict_get(root: Dict[str, Any], items: Any, default: Any = None) -> Any: +def dict_get(root: dict[str, Any], items: Any, default: Any = None) -> Any: """ Access a nested object in root by item sequence. @@ -473,7 +464,7 @@ def dict_get(root: Dict[str, Any], items: Any, default: Any = None) -> Any: def match_string_link( - text_item: str, data: str, need_key: str, matching_link_confs: List[Dict[str, Any]], render_context: Dict[str, Any] + text_item: str, data: str, need_key: str, matching_link_confs: list[dict[str, Any]], render_context: dict[str, Any] ) -> Any: try: link_name = None @@ -499,8 +490,8 @@ def match_string_link( def match_variants( - option_value: Union[str, List[str]], keywords: Dict[str, Any], needs_variants: Dict[str, str] -) -> Union[None, str, List[str]]: + option_value: str | list[str], keywords: dict[str, Any], needs_variants: dict[str, str] +) -> None | str | list[str]: """ Function to handle variant option management. @@ -512,8 +503,8 @@ def match_variants( """ def variant_handling( - variant_definitions: List[str], variant_data: Dict[str, Any], variant_pattern: Pattern # type: ignore[type-arg] - ) -> Optional[str]: + variant_definitions: list[str], variant_data: dict[str, Any], variant_pattern: Pattern # type: ignore[type-arg] + ) -> str | None: filter_context = variant_data # filter_result = [] no_variants_in_option = False @@ -564,7 +555,7 @@ def variant_handling( # Handling multiple variant definitions if isinstance(option_value, str): - multiple_variants: List[str] = variant_splitting.split(rf"""{option_value}""") + multiple_variants: list[str] = variant_splitting.split(rf"""{option_value}""") multiple_variants = [ re.sub(r"^([;, ]+)|([;, ]+$)", "", i) for i in multiple_variants if i not in (None, ";", "", " ") ] @@ -605,7 +596,7 @@ def clean_log(data: str) -> str: return clean_credentials -def node_match(node_types: Union[Type[nodes.Element], List[Type[nodes.Element]]]) -> Callable[[nodes.Node], bool]: +def node_match(node_types: type[nodes.Element] | list[type[nodes.Element]]) -> Callable[[nodes.Node], bool]: """ Returns a condition function for doctuils.nodes.findall() @@ -627,13 +618,13 @@ def node_match(node_types: Union[Type[nodes.Element], List[Type[nodes.Element]]] """ node_types_list = node_types if isinstance(node_types, list) else [node_types] - def condition(node: nodes.Node, node_types: List[Type[nodes.Element]] = node_types_list) -> bool: + def condition(node: nodes.Node, node_types: list[type[nodes.Element]] = node_types_list) -> bool: return any(isinstance(node, x) for x in node_types) return condition -def add_doc(env: BuildEnvironment, docname: str, category: Optional[str] = None) -> None: +def add_doc(env: BuildEnvironment, docname: str, category: str | None = None) -> None: """Stores a docname, to know later all need-relevant docs""" docs = SphinxNeedsData(env).get_or_create_docs() if docname not in docs["all"]: @@ -646,7 +637,7 @@ def add_doc(env: BuildEnvironment, docname: str, category: Optional[str] = None) docs[category].append(docname) -def split_link_types(link_types: str, location: Any) -> List[str]: +def split_link_types(link_types: str, location: Any) -> list[str]: """Split link_types string into list of link_types.""" def _is_valid(link_type: str) -> bool: @@ -667,7 +658,7 @@ def _is_valid(link_type: str) -> bool: ) -def get_scale(options: Dict[str, Any], location: Any) -> str: +def get_scale(options: dict[str, Any], location: Any) -> str: """Get scale for diagram, from directive option.""" scale: str = options.get("scale", "100").replace("%", "") if not scale.isdigit(): diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 8025b33ff..c110e1496 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -3,7 +3,7 @@ """ -from typing import Dict, Optional +from __future__ import annotations from sphinx.application import Sphinx from sphinx.util import logging @@ -16,7 +16,7 @@ logger = get_logger(__name__) -def process_warnings(app: Sphinx, exception: Optional[Exception]) -> None: +def process_warnings(app: Sphinx, exception: Exception | None) -> None: """ Checks the configured warnings. @@ -47,7 +47,7 @@ def process_warnings(app: Sphinx, exception: Optional[Exception]) -> None: env.needs_warnings_executed = True # type: ignore[attr-defined] # Exclude external needs for warnings check - checked_needs: Dict[str, NeedsInfoType] = {} + checked_needs: dict[str, NeedsInfoType] = {} for need_id, need in needs.items(): if not need["is_external"]: checked_needs[need_id] = need