From c67c5c83e1e5f4ccaed48fea5efaa794e60026fb Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 13:49:20 +0000 Subject: [PATCH 01/24] =?UTF-8?q?=F0=9F=94=A7=20Update=20pre-commit=20hook?= =?UTF-8?q?s=20(#1109)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update hooks, fix issues, and also pin dependencies for mypy more strictly (to reduce potential for differences between local and remote runs of mypy) --- .pre-commit-config.yaml | 15 ++++----- codecov.yml | 10 ++++++ performance/performance_test.py | 1 + pyproject.toml | 6 ---- sphinx_needs/api/configuration.py | 1 + sphinx_needs/data.py | 1 + sphinx_needs/debug.py | 1 + sphinx_needs/diagrams_common.py | 2 +- sphinx_needs/directives/need.py | 11 +++---- sphinx_needs/directives/needextend.py | 1 + sphinx_needs/directives/needextract.py | 3 +- sphinx_needs/directives/needfilter.py | 28 ++++++++--------- sphinx_needs/directives/needflow.py | 13 ++++---- sphinx_needs/directives/needlist.py | 1 + sphinx_needs/directives/needtable.py | 5 +-- sphinx_needs/environment.py | 4 +-- sphinx_needs/external_needs.py | 6 ++-- sphinx_needs/filter_common.py | 3 +- sphinx_needs/functions/functions.py | 4 +-- sphinx_needs/layout.py | 42 ++++++++++++++------------ sphinx_needs/needsfile.py | 1 + sphinx_needs/roles/need_count.py | 2 +- sphinx_needs/roles/need_func.py | 2 +- sphinx_needs/roles/need_incoming.py | 2 +- sphinx_needs/roles/need_outgoing.py | 2 +- sphinx_needs/roles/need_part.py | 3 +- sphinx_needs/roles/need_ref.py | 2 +- sphinx_needs/utils.py | 10 +++--- sphinx_needs/warnings.py | 1 + tests/conftest.py | 1 + tests/no_mpl_tests.py | 1 + tests/test_github_issues.py | 4 +-- 32 files changed, 104 insertions(+), 85 deletions(-) create mode 100644 codecov.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cbc338edd..4312eb7b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/psf/black - rev: 23.10.1 + rev: 24.2.0 hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 additional_dependencies: @@ -15,7 +15,7 @@ repos: - pep8-naming - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort @@ -24,17 +24,18 @@ repos: hooks: - id: pyupgrade args: - - --py36-plus + - --py38-plus - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.8.0 hooks: - id: mypy files: sphinx_needs/.* args: [] additional_dependencies: - - sphinx==6 - - types-docutils + - sphinx==6.2.1 + - docutils==0.19 + - types-docutils==0.20.0.20240201 - types-jsonschema - types-requests diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 000000000..03fd4e928 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + target: 80% + threshold: 0.5% + patch: + default: + target: 70% + threshold: 0.5% diff --git a/performance/performance_test.py b/performance/performance_test.py index 5a8f438c3..1733f6328 100644 --- a/performance/performance_test.py +++ b/performance/performance_test.py @@ -2,6 +2,7 @@ Executes several performance tests. """ + import os.path import shutil import subprocess diff --git a/pyproject.toml b/pyproject.toml index bfe10e434..054de6c18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,12 +151,6 @@ module = [ ] disable_error_code = ["attr-defined", "no-any-return"] -[[tool.mypy.overrides]] -module = [ - "sphinx_needs.directives.needextract", -] -disable_error_code = "no-untyped-call" - [build-system] requires = ["setuptools", "poetry_core>=1.0.8"] # setuptools for deps like plantuml build-backend = "poetry.core.masonry.api" diff --git a/sphinx_needs/api/configuration.py b/sphinx_needs/api/configuration.py index 156f76c5b..54b88d8d8 100644 --- a/sphinx_needs/api/configuration.py +++ b/sphinx_needs/api/configuration.py @@ -3,6 +3,7 @@ All functions here are available under ``sphinxcontrib.api``. So do not import this module directly. """ + from typing import Callable, List, Optional from docutils.parsers.rst import directives diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 465e626a4..abea4ce70 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -1,6 +1,7 @@ """Module to control access to sphinx-needs data, which is stored in the Sphinx environment. """ + from __future__ import annotations from typing import TYPE_CHECKING, Literal, TypedDict diff --git a/sphinx_needs/debug.py b/sphinx_needs/debug.py index 253bd5725..1fca7239b 100644 --- a/sphinx_needs/debug.py +++ b/sphinx_needs/debug.py @@ -2,6 +2,7 @@ Contains debug features to track down runtime and other problems with Sphinx-Needs """ + from __future__ import annotations import inspect diff --git a/sphinx_needs/diagrams_common.py b/sphinx_needs/diagrams_common.py index c6fb33d6c..4adde2680 100644 --- a/sphinx_needs/diagrams_common.py +++ b/sphinx_needs/diagrams_common.py @@ -140,7 +140,7 @@ def get_debug_container(puml_node: nodes.Element) -> nodes.container: """Return container containing the raw plantuml code""" debug_container = nodes.container() if isinstance(puml_node, nodes.figure): - data = puml_node.children[0]["uml"] + data = puml_node.children[0]["uml"] # type: ignore else: data = puml_node["uml"] data = "\n".join([html.escape(line) for line in data.split("\n")]) diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 202b0f07c..3e38947b5 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -1,6 +1,5 @@ import hashlib import re -import typing from typing import Any, Dict, List, Optional, Sequence, Tuple from docutils import nodes @@ -240,7 +239,7 @@ def get_sections_and_signature_and_needs( current_node = need_node while current_node: if isinstance(current_node, nodes.section): - title = typing.cast(str, current_node.children[0].astext()) + title = current_node.children[0].astext() # If using auto-section numbering, then Sphinx inserts # multiple non-breaking space unicode characters into the title # we'll replace those with a simple space to make them easier to @@ -344,16 +343,16 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None: # we can remove the hidden needs from the doctree for need_node in hidden_needs: if need_node.parent is not None: - need_node.parent.remove(need_node) # type: ignore[attr-defined] + need_node.parent.remove(need_node) def previous_sibling(node: nodes.Node) -> Optional[nodes.Node]: """Return preceding sibling node or ``None``.""" try: - i = node.parent.index(node) # type: ignore + i = node.parent.index(node) except AttributeError: return None - return node.parent[i - 1] if i > 0 else None # type: ignore + return node.parent[i - 1] if i > 0 else None @profile("NEEDS_POST_PROCESS") @@ -391,7 +390,7 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - if not needs_config.include_needs: for node in doctree.findall(Need): if node.parent is not None: - node.parent.remove(node) # type: ignore + node.parent.remove(node) return needs_data = SphinxNeedsData(app.env) diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index bcd218c0b..7f6d1e48d 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -2,6 +2,7 @@ """ + import re from typing import Any, Callable, Dict, Sequence diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index b3ed41b67..9b61ac91e 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -2,6 +2,7 @@ """ + import re from typing import List, Sequence @@ -131,4 +132,4 @@ def process_needextract( # Run docutils/sphinx transformers for the by needextract added nodes. # Transformers use the complete document (doctree), so we perform this action once per # needextract. No matter if one or multiple needs got copied - Substitutions(doctree).apply() + Substitutions(doctree).apply() # type: ignore[no-untyped-call] diff --git a/sphinx_needs/directives/needfilter.py b/sphinx_needs/directives/needfilter.py index 3ef350e9c..8800a189d 100644 --- a/sphinx_needs/directives/needfilter.py +++ b/sphinx_needs/directives/needfilter.py @@ -1,5 +1,5 @@ import os -from typing import List, Sequence +from typing import List, Sequence, Union from urllib.parse import urlparse from docutils import nodes @@ -89,7 +89,7 @@ def process_needfilters( id = node.attributes["ids"][0] current_needfilter = SphinxNeedsData(env)._get_or_create_filters()[id] - content: List[nodes.Element] + content: Union[nodes.Element, List[nodes.Element]] if current_needfilter["layout"] == "list": content = [] @@ -100,12 +100,12 @@ def process_needfilters( raise ImportError from sphinxcontrib.plantuml import plantuml except ImportError: - content = nodes.error() - para = nodes.paragraph() + error_node = nodes.error() + para_node = nodes.paragraph() text = nodes.Text("PlantUML is not available!") - para += text - content.append(para) - node.replace_self(content) + para_node += text + error_node.append(para_node) + node.replace_self(error_node) continue plantuml_block_text = ".. plantuml::\n" "\n" " @startuml" " @enduml" @@ -151,7 +151,7 @@ def process_needfilters( target_id = need_info["target_id"] if current_needfilter["layout"] == "list": - para = nodes.line() + line_node = nodes.line() description = "{}: {}".format(need_info["id"], need_info["title"]) if current_needfilter["show_status"] and need_info["status"]: @@ -164,16 +164,16 @@ def process_needfilters( # Create a reference if need_info["hide"]: - para += title + line_node += title else: ref = nodes.reference("", "") ref["refdocname"] = need_info["docname"] ref["refuri"] = builder.get_relative_uri(fromdocname, need_info["docname"]) ref["refuri"] += "#" + target_id ref.append(title) - para += ref + line_node += ref - line_block.append(para) + line_block.append(line_node) elif current_needfilter["layout"] == "table": row = nodes.row() row += row_col_maker(app, fromdocname, all_needs, need_info, "id", make_ref=True) @@ -231,7 +231,7 @@ def process_needfilters( if len(content) == 0: content.append(no_needs_found_paragraph(current_needfilter.get("filter_warning"))) if current_needfilter["show_filters"]: - para = nodes.paragraph() + para_node = nodes.paragraph() filter_text = "Used filter:" filter_text += ( " status(%s)" % " OR ".join(current_needfilter["status"]) @@ -252,7 +252,7 @@ def process_needfilters( ) filter_node = nodes.emphasis(filter_text, filter_text) - para += filter_node - content.append(para) + para_node += filter_node + content.append(para_node) node.replace_self(content) diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index c67381514..749f84b8c 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -308,20 +308,21 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou type="needs", ) - content = [] try: if "sphinxcontrib.plantuml" not in app.config.extensions: raise ImportError from sphinxcontrib.plantuml import plantuml except ImportError: - content = nodes.error() + error_node = nodes.error() para = nodes.paragraph() text = nodes.Text("PlantUML is not available!") para += text - content.append(para) - node.replace_self(content) + error_node.append(para) + node.replace_self(error_node) continue + content: List[nodes.Element] = [] + found_needs = process_filters(app, all_needs.values(), current_needflow) if found_needs: @@ -490,13 +491,13 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou # Otherwise it was not been set, or we get outdated data debug_container = nodes.container() if isinstance(puml_node, nodes.figure): - data = puml_node.children[0]["uml"] + data = puml_node.children[0]["uml"] # type: ignore else: data = puml_node["uml"] data = "\n".join([html.escape(line) for line in data.split("\n")]) debug_para = nodes.raw("", f"
{data}
", format="html") debug_container += debug_para - content += debug_container + content.append(debug_container) node.replace_self(content) diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index 25a19b433..6a2b14ff9 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -2,6 +2,7 @@ """ + from typing import List, Sequence from docutils import nodes diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index c647f95a5..b00b7cf48 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -317,6 +317,7 @@ def sort(need: NeedsInfoType) -> Any: tbody += row + content: nodes.Element if len(filtered_needs) == 0: content = no_needs_found_paragraph(current_needtable.get("filter_warning")) else: @@ -333,7 +334,7 @@ def sort(need: NeedsInfoType) -> Any: if current_needtable["caption"]: title_text = current_needtable["caption"] - title = nodes.title(title_text, "", nodes.Text(title_text)) - table_node.insert(0, title) + title_node = nodes.title(title_text, "", nodes.Text(title_text)) + table_node.insert(0, title_node) node.replace_self(content) diff --git a/sphinx_needs/environment.py b/sphinx_needs/environment.py index 0b5de8688..513f5e981 100644 --- a/sphinx_needs/environment.py +++ b/sphinx_needs/environment.py @@ -11,10 +11,10 @@ from sphinx_needs.utils import logger try: - from sphinx.util.display import status_iterator # type: ignore + from sphinx.util.display import status_iterator except ImportError: # will be removed in Sphinx 8.0 - from sphinx.util import status_iterator + from sphinx.util import status_iterator # type: ignore IMAGE_DIR_NAME = "_static" diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index f46f7bd1f..4288c37c5 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -110,9 +110,9 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No cal_target_url = mem_template.render(**{"need": need}) need_params["external_url"] = f'{source["base_url"]}/{cal_target_url}' else: - need_params[ - "external_url" - ] = f'{source["base_url"]}/{need.get("docname", "__error__")}.html#{need["id"]}' + need_params["external_url"] = ( + f'{source["base_url"]}/{need.get("docname", "__error__")}.html#{need["id"]}' + ) need_params["content"] = need["description"] need_params["links"] = need.get("links", []) diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 20e1fe74b..896256860 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -2,6 +2,7 @@ filter_base is used to provide common filter functionality for directives like needtable, needlist and needflow. """ + from __future__ import annotations import re @@ -236,7 +237,7 @@ def prepare_need_list(need_list: Iterable[NeedsInfoType]) -> list[NeedsPartsInfo for need in need_list: for part in need["parts"].values(): id_complete = ".".join([need["id"], part["id"]]) - filter_part: NeedsPartsInfoType = {**need, **part, **{"id_parent": need["id"], "id_complete": id_complete}} + filter_part: NeedsPartsInfoType = {**need, **part, **{"id_parent": need["id"], "id_complete": id_complete}} # type: ignore[typeddict-item] all_needs_incl_parts.append(filter_part) # Be sure extra attributes, which makes only sense for need_parts, are also available on diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 52a8bb441..a8c640ce2 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -111,7 +111,7 @@ def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: except KeyError: # If no refuri is set, we don't need to modify anything. # So stop here and return the untouched node. - return node # type: ignore + return node else: new_text = node func_match = func_pattern.findall(new_text) @@ -144,7 +144,7 @@ def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: node.children = new_children else: node = nodes.Text(new_text) - return node # type: ignore + return node else: for child in node.children: new_child = find_and_replace_node_content(child, env, need) diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index 38b123e50..068f35d5e 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -3,6 +3,7 @@ Based on https://github.com/useblocks/sphinxcontrib-needs/issues/102 """ + import os import re import uuid @@ -87,7 +88,7 @@ def create_need( # Therefore, we need to manipulate this first, before we can ask Sphinx to perform the normal # reference handling for us. replace_pending_xref_refdoc(node_container, docname) - env.resolve_references(node_container, docname, env.app.builder) + env.resolve_references(node_container, docname, env.app.builder) # type: ignore[arg-type] node_container.attributes["ids"].append(need_id) @@ -98,8 +99,8 @@ def create_need( build_need(layout, node_container, app, style, docname) # set the layout and style for the new need - node_container[0].attributes = node_container.parent.children[0].attributes - node_container[0].children[0].attributes = node_container.parent.children[0].children[0].attributes + node_container[0].attributes = node_container.parent.children[0].attributes # type: ignore + node_container[0].children[0].attributes = node_container.parent.children[0].children[0].attributes # type: ignore node_container.attributes["ids"] = [] @@ -154,7 +155,7 @@ def build_need( if need_data["hide"]: if node.parent: - node.parent.replace(node, []) # type: ignore + node.parent.replace(node, []) return if fromdocname is None: @@ -170,7 +171,7 @@ def build_need( # We need to replace the current need-node (containing content only) with our new table need node. # node.parent.replace(node, node_container) - node.parent.replace(node, node_container) # type: ignore + node.parent.replace(node, node_container) @lru_cache(1) @@ -201,10 +202,10 @@ def __init__( self.layout_name = layout available_layouts = self.needs_config.layouts - if self.layout_name not in available_layouts.keys(): + if self.layout_name not in available_layouts: raise SphinxNeedLayoutException( 'Given layout "{}" is unknown for need {}. Registered layouts are: {}'.format( - self.layout_name, need["id"], " ,".join(available_layouts.keys()) + self.layout_name, need["id"], " ,".join(available_layouts) ) ) self.layout = available_layouts[self.layout_name] @@ -302,12 +303,12 @@ def __init__( ) self.functions: Dict[str, Callable[..., Union[None, nodes.Node, List[nodes.Node]]]] = { - "meta": self.meta, + "meta": self.meta, # type: ignore[dict-item] "meta_all": self.meta_all, "meta_links": self.meta_links, - "meta_links_all": self.meta_links_all, + "meta_links_all": self.meta_links_all, # type: ignore[dict-item] "meta_id": self.meta_id, - "image": self.image, + "image": self.image, # type: ignore[dict-item] "link": self.link, "collapse_button": self.collapse_button, "permalink": self.permalink, @@ -342,16 +343,16 @@ def get_need_table(self) -> nodes.table: return self.node_table - def get_section(self, section: str) -> Optional[nodes.line_block]: + def get_section(self, section: str) -> Union[nodes.line_block, List[nodes.Element]]: try: lines = self.layout["layout"][section] except KeyError: # Return nothing, if not specific configuration is given for layout section - return None + return [] # Needed for PDF/Latex output, where empty line_blocks raise exceptions during build if len(lines) == 0: - return None + return [] lines_container = nodes.line_block(classes=[f"needs_{section}"]) @@ -481,7 +482,9 @@ def _replace_place_holder(self, data: str) -> str: data = data.replace(replace_string, self.need[item]) # type: ignore[literal-required] return data - def meta(self, name: str, prefix: Optional[str] = None, show_empty: bool = False) -> nodes.inline: + def meta( + self, name: str, prefix: Optional[str] = None, show_empty: bool = False + ) -> Union[nodes.inline, List[nodes.Element]]: """ Returns the specific metadata of a need inside docutils nodes. Usage:: @@ -689,10 +692,11 @@ def meta_links(self, name: str, incoming: bool = False) -> nodes.inline: from sphinx_needs.roles.need_incoming import NeedIncoming from sphinx_needs.roles.need_outgoing import NeedOutgoing - if incoming: - node_links = NeedIncoming(reftarget=self.need["id"], link_type=f"{name}_back") - else: - node_links = NeedOutgoing(reftarget=self.need["id"], link_type=f"{name}") + node_links = ( + NeedIncoming(reftarget=self.need["id"], link_type=f"{name}_back") + if incoming + else NeedOutgoing(reftarget=self.need["id"], link_type=f"{name}") + ) node_links.append(nodes.inline(self.need["id"], self.need["id"])) data_container.append(node_links) return data_container @@ -739,7 +743,7 @@ def image( prefix: str = "", is_external: bool = False, img_class: str = "", - ) -> nodes.inline: + ) -> Union[nodes.inline, List[nodes.Element]]: """ See https://docutils.sourceforge.io/docs/ref/rst/directives.html#images diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index 1c6fbb771..17825637f 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -3,6 +3,7 @@ Creates, checks and imports ``needs.json`` files. """ + import json import os import sys diff --git a/sphinx_needs/roles/need_count.py b/sphinx_needs/roles/need_count.py index cfe996c7b..ba0a137f1 100644 --- a/sphinx_needs/roles/need_count.py +++ b/sphinx_needs/roles/need_count.py @@ -18,7 +18,7 @@ log = get_logger(__name__) -class NeedCount(nodes.Inline, nodes.Element): # type: ignore +class NeedCount(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/roles/need_func.py b/sphinx_needs/roles/need_func.py index 32ae6a7c6..50852465a 100644 --- a/sphinx_needs/roles/need_func.py +++ b/sphinx_needs/roles/need_func.py @@ -13,7 +13,7 @@ log = get_logger(__name__) -class NeedFunc(nodes.Inline, nodes.Element): # type: ignore +class NeedFunc(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/roles/need_incoming.py b/sphinx_needs/roles/need_incoming.py index bbeb9b141..dc882b74e 100644 --- a/sphinx_needs/roles/need_incoming.py +++ b/sphinx_needs/roles/need_incoming.py @@ -10,7 +10,7 @@ from sphinx_needs.utils import check_and_calc_base_url_rel_path, logger -class NeedIncoming(nodes.Inline, nodes.Element): # type: ignore +class NeedIncoming(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/roles/need_outgoing.py b/sphinx_needs/roles/need_outgoing.py index 092d89a95..be119c735 100644 --- a/sphinx_needs/roles/need_outgoing.py +++ b/sphinx_needs/roles/need_outgoing.py @@ -13,7 +13,7 @@ log = get_logger(__name__) -class NeedOutgoing(nodes.Inline, nodes.Element): # type: ignore +class NeedOutgoing(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/roles/need_part.py b/sphinx_needs/roles/need_part.py index e6be611c5..8b398ac2d 100644 --- a/sphinx_needs/roles/need_part.py +++ b/sphinx_needs/roles/need_part.py @@ -6,6 +6,7 @@ Most voodoo is done in need.py """ + import hashlib import re from typing import List, cast @@ -20,7 +21,7 @@ log = get_logger(__name__) -class NeedPart(nodes.Inline, nodes.Element): # type: ignore +class NeedPart(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/roles/need_ref.py b/sphinx_needs/roles/need_ref.py index 23f9a5b1e..8795cde24 100644 --- a/sphinx_needs/roles/need_ref.py +++ b/sphinx_needs/roles/need_ref.py @@ -15,7 +15,7 @@ log = get_logger(__name__) -class NeedRef(nodes.Inline, nodes.Element): # type: ignore +class NeedRef(nodes.Inline, nodes.Element): pass diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 4bbddf122..018ec9691 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -400,7 +400,7 @@ def jinja_parse(context: Dict[str, Any], jinja_string: str) -> str: return content -@lru_cache() +@lru_cache def import_matplotlib() -> Optional["matplotlib"]: """Import and return matplotlib, or return None if it cannot be imported. @@ -484,11 +484,9 @@ def match_string_link( render_content = match.groupdict() link_url = link_conf["url_template"].render(**render_content, **render_context) link_name = link_conf["name_template"].render(**render_content, **render_context) - if link_name: - ref_item = nodes.reference(link_name, link_name, refuri=link_url) - else: - # if no string_link match was made, we handle it as normal string value - ref_item = nodes.Text(text_item) + + # if no string_link match was made, we handle it as normal string value + ref_item = nodes.reference(link_name, link_name, refuri=link_url) if link_name else nodes.Text(text_item) except Exception as e: logger.warning( diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 8754551dc..8025b33ff 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -2,6 +2,7 @@ Cares about handling and execution warnings. """ + from typing import Dict, Optional from sphinx.application import Sphinx diff --git a/tests/conftest.py b/tests/conftest.py index 36bd5aa9f..2e679dc21 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ """Pytest conftest module containing common test configuration and fixtures.""" + import json import os.path import secrets diff --git a/tests/no_mpl_tests.py b/tests/no_mpl_tests.py index 17cd6aece..95aaf0747 100644 --- a/tests/no_mpl_tests.py +++ b/tests/no_mpl_tests.py @@ -1,4 +1,5 @@ """These tests should only be run in an environment without matplotlib installed.""" + import pytest diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 6dd514323..2551af045 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -17,9 +17,7 @@ def test_doc_github_44(test_app): app = test_app output = str( - check_output( - ["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], stderr=STDOUT, universal_newlines=True - ) + check_output(["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], stderr=STDOUT, text=True) ) # app.build() Uncomment, if build should stop on breakpoints html = Path(app.outdir, "index.html").read_text() From a85d49be05949424e38c08233871f32064075828 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:50:31 +0100 Subject: [PATCH 02/24] Bump actions/setup-python from 4 to 5 (#1083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
Release notes

Sourced from actions/setup-python's releases.

v5.0.0

What's Changed

In scope of this release, we update node version runtime from node16 to node20 (actions/setup-python#772). Besides, we update dependencies to the latest versions.

Full Changelog: https://github.com/actions/setup-python/compare/v4.8.0...v5.0.0

v4.8.0

What's Changed

In scope of this release we added support for GraalPy (actions/setup-python#694). You can use this snippet to set up GraalPy:

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
  with:
    python-version: 'graalpy-22.3'
- run: python my_script.py

Besides, the release contains such changes as:

New Contributors

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.8.0

v4.7.1

What's Changed

Full Changelog: https://github.com/actions/setup-python/compare/v4...v4.7.1

v4.7.0

In scope of this release, the support for reading python version from pyproject.toml was added (actions/setup-python#669).

      - name: Setup Python
        uses: actions/setup-python@v4
</tr></table>

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-python&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yaml | 2 +- .github/workflows/ci.yaml | 8 ++++---- .github/workflows/docs.yaml | 2 +- .github/workflows/js_test.yml | 2 +- .github/workflows/release.yaml | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index da28dd0e6..e7e5c2ba5 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Update pip diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5b9b0797e..abccea3cf 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.8' - uses: pre-commit/action@v3.0.0 @@ -34,7 +34,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Update pip @@ -78,7 +78,7 @@ jobs: - name: Install Cypress Test Framework run: npm install cypress - name: Set Up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Update pip @@ -97,7 +97,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Update pip diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 119dbfde1..de20c3b81 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set Up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Update pip diff --git a/.github/workflows/js_test.yml b/.github/workflows/js_test.yml index 3012c9856..2cf3dba0c 100644 --- a/.github/workflows/js_test.yml +++ b/.github/workflows/js_test.yml @@ -5,7 +5,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set Up Python 3.10.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.10.8 - name: Use Node.js diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9ea325cba..1cc8c589a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: install Poetry @@ -30,7 +30,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - uses: actions/download-artifact@v3 @@ -57,7 +57,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.9 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.9 - uses: actions/download-artifact@v2 From 050bec750ff2c5acf881415fa2b5efb5fcce8414 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:04:06 +0100 Subject: [PATCH 03/24] Bump actions/cache from 3 to 4 (#1092) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yaml | 2 +- .github/workflows/js_test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index e7e5c2ba5..4efe5c00a 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -22,7 +22,7 @@ jobs: run: pytest --benchmark-json output.json -k _time tests/benchmarks - name: Download previous benchmark data - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./cache key: ${{ runner.os }}-benchmark diff --git a/.github/workflows/js_test.yml b/.github/workflows/js_test.yml index 2cf3dba0c..bf7070bc9 100644 --- a/.github/workflows/js_test.yml +++ b/.github/workflows/js_test.yml @@ -23,7 +23,7 @@ jobs: run: | echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: Pip cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} From e9582edb45ffbb7bb90296d910b7c00352d7c800 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 14:10:36 +0000 Subject: [PATCH 04/24] =?UTF-8?q?=F0=9F=91=8C=20Make=20needtable=20titles?= =?UTF-8?q?=20more=20permissive=20(#1102)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For `key as "title"` columns, the regex has been changed, to allow for the key to contain anything except whitespace characters, and the title to contain anything except the `"` character --- sphinx_needs/defaults.py | 2 +- tests/doc_test/doc_needtable/conf.py | 1 + tests/doc_test/doc_needtable/test_titles.rst | 3 ++- tests/test_needtable.py | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sphinx_needs/defaults.py b/sphinx_needs/defaults.py index c23903fcd..2e7a11913 100644 --- a/sphinx_needs/defaults.py +++ b/sphinx_needs/defaults.py @@ -199,7 +199,7 @@ """, } -TITLE_REGEX = r'([\w]+) as "([\w ]+)"' +TITLE_REGEX = r'([^\s]+) as "([^"]+)"' NEED_DEFAULT_OPTIONS: Dict[str, Any] = { diff --git a/tests/doc_test/doc_needtable/conf.py b/tests/doc_test/doc_needtable/conf.py index 585e4438d..73fd61077 100644 --- a/tests/doc_test/doc_needtable/conf.py +++ b/tests/doc_test/doc_needtable/conf.py @@ -23,6 +23,7 @@ "github", "value", "unit", + "special-chars!", ] needs_string_links = { diff --git a/tests/doc_test/doc_needtable/test_titles.rst b/tests/doc_test/doc_needtable/test_titles.rst index e1a598685..75db1cca4 100644 --- a/tests/doc_test/doc_needtable/test_titles.rst +++ b/tests/doc_test/doc_needtable/test_titles.rst @@ -12,7 +12,8 @@ TEST Titles .. spec:: need 3 :id: titles_003 :links: titles_001 + :special-chars!: special-chars value .. needtable:: - :columns: id;title as "Headline" ;outgoing as "Links"; incoming as "To this need123";status;tags as "My Tags" + :columns: id;title as "Headline" ;outgoing as "Links"; incoming as "To this need123";status;tags as "My Tags";special-chars! as "Special Characters!" :style: table diff --git a/tests/test_needtable.py b/tests/test_needtable.py index 40bb9b33d..211be333e 100644 --- a/tests/test_needtable.py +++ b/tests/test_needtable.py @@ -149,3 +149,5 @@ def test_doc_needtable_titles(test_app): html = Path(app.outdir, "test_titles.html").read_text() assert '

Headline

' in html assert '

To this need123

' in html + assert '

Special Characters!

' in html + assert '

special-chars value

' in html From ab6d6c2880620ad8c352d949b67bf7177635fd91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:14:37 +0100 Subject: [PATCH 05/24] Bump actions/upload-artifact from 3 to 4 (#1085) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 1cc8c589a..bbb4b6750 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,7 +17,7 @@ jobs: run: python -m pip install poetry - name: poetry build run: poetry build - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: poetry_build path: dist/ From f083bbd5e1d5148ed17f80697a0f39352ebb48d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:21:13 +0100 Subject: [PATCH 06/24] Bump actions/download-artifact from 2 to 4 (#1086) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index bbb4b6750..9c7713d0f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -33,7 +33,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.9 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: poetry_build path: dist/ @@ -60,7 +60,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.9 - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v4 with: name: poetry_build path: dist/ From 6b26526759fb97810968c882788d99a1aceee5f8 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 14:39:01 +0000 Subject: [PATCH 07/24] =?UTF-8?q?=F0=9F=90=9B=F0=9F=91=8C=20Centralise=20n?= =?UTF-8?q?eed=20missing=20link=20reporting=20(#1104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, warnings for need outgoing links that reference a non-existent ID, were only generated "indirectly" in the render phase; where `NeedOutgoing` nodes are converted to nodes that are understood by sphinx builders (HTML, PDF, ...). Since this render phase is no longer needed/run for simply creating the `needs.json`, using the `needs` builder, these warnings were no longer generated. Additionally, any needs that are not explicitly rendered in the documentation, like externally imported needs, were also skipped. This commit moves the reporting of unknown links to the `check_links` function, meaning it is now run for all needs and all builders. The warnings have also been given a subtype `link_outgoing` or `external_link_outgoing`, e.g. ``` srcdir/index.rst:12: WARNING: Need 'SP_TOO_002' has unknown outgoing link 'NOT_WORKING_LINK' in field 'links' [needs.link_outgoing] WARNING: http://my_company.com/docs/v1/index.html#TEST_01: Need 'EXT_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing] ``` This means they can be suppressed using the standard Sphinx config: ```python suppress_warnings = ["needs.link_outgoing", "needs.external_link_outgoing"]` ``` This deprecates the need for the `needs_report_dead_links` configuration, which now emits a deprecation warning if set by the user. --- docs/conf.py | 2 +- docs/configuration.rst | 8 ++- sphinx_needs/config.py | 1 + sphinx_needs/directives/need.py | 29 ++++++-- sphinx_needs/needs.py | 44 ++++++------ sphinx_needs/roles/need_outgoing.py | 39 ++-------- sphinx_needs/roles/need_ref.py | 3 +- tests/conftest.py | 14 ++-- .../doc_report_dead_links_false/conf.py | 2 +- tests/test_basic_doc.py | 24 +++++-- tests/test_broken_links.py | 17 +++-- tests/test_github_issues.py | 14 ++-- tests/test_needs_external_needs_build.py | 5 +- tests/test_report_dead_links.py | 72 +++++++++---------- 14 files changed, 148 insertions(+), 126 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0c8d8188e..c9826cfd5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -90,7 +90,7 @@ # Absolute path to the needs_report_template_file based on the conf.py directory # needs_report_template = "/needs_templates/report_template.need" # Use custom report template -needs_report_dead_links = False +suppress_warnings = ["needs.link_outgoing"] needs_types = [ # Architecture types diff --git a/docs/configuration.rst b/docs/configuration.rst index 6d59ab252..627060ff9 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -294,7 +294,13 @@ In this cases, you can provide a list of tuples. needs_report_dead_links ~~~~~~~~~~~~~~~~~~~~~~~ -Deactivate/activate log messages of outgoing dead links. If set to ``False``, then deactivate. +.. deprecated:: 2.1.0 + + Instead add ``needs.link_outgoing`` to the `suppress_warnings `__ list:: + + suppress_warnings = ["needs.link_outgoing"] + +Deactivate/activate log messages of disallowed outgoing dead links. If set to ``False``, then deactivate. Default value is ``True``. diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index 6b7daf4a3..a988074fe 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -191,6 +191,7 @@ def __setattr__(self, name: str, value: Any) -> None: Example: [{"name": "blocks, "incoming": "is blocked by", "copy_link": True, "color": "#ffcc00"}] """ report_dead_links: bool = field(default=True, metadata={"rebuild": "html", "types": (bool,)}) + """DEPRECATED: Use ``suppress_warnings = ["needs.link_outgoing"]`` instead.""" filter_data: dict[str, Any] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) allow_unsafe_filters: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) flow_show_links: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 3e38947b5..5965e71a4 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -29,7 +29,7 @@ from sphinx_needs.nodes import Need from sphinx_needs.utils import add_doc, profile, remove_node_from_tree, split_need_id -logger = get_logger(__name__) +LOGGER = get_logger(__name__) NON_BREAKING_SPACE = re.compile("\xa0+") @@ -156,7 +156,7 @@ def read_in_links(self, name: str) -> List[str]: if links_string: for link in re.split(r";|,", links_string): if link.isspace(): - logger.warning( + LOGGER.warning( f"Grubby link definition found in need '{self.trimmed_title}'. " "Defined link contains spaces only. [needs]", type="needs", @@ -436,10 +436,10 @@ def check_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> N the ``has_forbidden_dead_links`` field is also added. """ extra_links = config.extra_links + report_dead_links = config.report_dead_links for need in needs.values(): for link_type in extra_links: - dead_links_allowed = link_type.get("allow_dead_links", False) - need_link_value: List[str] = ( + need_link_value = ( [need[link_type["option"]]] if isinstance(need[link_type["option"]], str) else need[link_type["option"]] # type: ignore ) for need_id_full in need_link_value: @@ -449,9 +449,26 @@ def check_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> N need_id_main in needs and need_id_part and need_id_part not in needs[need_id_main]["parts"] ): need["has_dead_links"] = True - if not dead_links_allowed: + if not link_type.get("allow_dead_links", False): need["has_forbidden_dead_links"] = True - break # One found dead link is enough + if report_dead_links: + message = f"Need '{need['id']}' has unknown outgoing link '{need_id_full}' in field '{link_type['option']}'" + # if the need has been imported from an external URL, + # we want to provide that URL as the location of the warning, + # otherwise we use the location of the need in the source file + if need.get("is_external", False): + LOGGER.warning( + f"{need['external_url']}: {message} [needs.external_link_outgoing]", + type="needs", + subtype="external_link_outgoing", + ) + else: + LOGGER.warning( + f"{message} [needs.link_outgoing]", + location=(need["docname"], need["lineno"]), + type="needs", + subtype="link_outgoing", + ) def create_back_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 077d5d65b..03bbce6c0 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -132,11 +132,12 @@ NeedFunc: process_need_func, } +LOGGER = get_logger(__name__) + def setup(app: Sphinx) -> Dict[str, Any]: - log = get_logger(__name__) - log.debug("Starting setup of Sphinx-Needs") - log.debug("Load Sphinx-Data-Viewer for Sphinx-Needs") + LOGGER.debug("Starting setup of Sphinx-Needs") + LOGGER.debug("Load Sphinx-Data-Viewer for Sphinx-Needs") app.setup_extension("sphinx_data_viewer") app.setup_extension("sphinxcontrib.jquery") @@ -304,12 +305,10 @@ def load_config(app: Sphinx, *_args: Any) -> None: """ Register extra options and directive based on config from conf.py """ - log = get_logger(__name__) - needs_config = NeedsSphinxConfig(app.config) if isinstance(needs_config.extra_options, dict): - log.info( + LOGGER.info( 'Config option "needs_extra_options" supports list and dict. However new default type since ' "Sphinx-Needs 0.7.2 is list. Please see docs for details." ) @@ -317,7 +316,9 @@ def load_config(app: Sphinx, *_args: Any) -> None: extra_options = NEEDS_CONFIG.extra_options for option in needs_config.extra_options: if option in extra_options: - log.warning(f'extra_option "{option}" already registered. [needs.config]', type="needs", subtype="config") + LOGGER.warning( + f'extra_option "{option}" already registered. [needs.config]', type="needs", subtype="config" + ) NEEDS_CONFIG.extra_options[option] = directives.unchanged # Get extra links and create a dictionary of needed options. @@ -393,17 +394,24 @@ def load_config(app: Sphinx, *_args: Any) -> None: if name not in NEEDS_CONFIG.warnings: NEEDS_CONFIG.warnings[name] = check else: - log.warning( + LOGGER.warning( f"{name!r} in 'needs_warnings' is already registered. [needs.config]", type="needs", subtype="config" ) if needs_config.constraints_failed_color: - log.warning( + LOGGER.warning( 'Config option "needs_constraints_failed_color" is deprecated. Please use "needs_constraint_failed_options" styles instead. [needs.config]', type="needs", subtype="config", ) + if needs_config.report_dead_links is not True: + LOGGER.warning( + 'Config option "needs_constraints_failed_color" is deprecated. Please use `suppress_warnings = ["needs.link_outgoing"]` instead. [needs.config]', + type="needs", + subtype="config", + ) + def visitor_dummy(*_args: Any, **_kwargs: Any) -> None: """ @@ -497,19 +505,15 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: def check_configuration(_app: Sphinx, config: Config) -> None: - """ - Checks the configuration for invalid options. + """Checks the configuration for invalid options. E.g. defined need-option, which is already defined internally - - :param app: - :param config: - :return: """ - extra_options = config["needs_extra_options"] - link_types = [x["option"] for x in config["needs_extra_links"]] + needs_config = NeedsSphinxConfig(config) + extra_options = needs_config.extra_options + link_types = [x["option"] for x in needs_config.extra_links] - external_filter = getattr(config, "needs_filter_data", {}) + external_filter = needs_config.filter_data for extern_filter, value in external_filter.items(): # Check if external filter values is really a string if not isinstance(value, str): @@ -545,8 +549,8 @@ def check_configuration(_app: Sphinx, config: Config) -> None: " This is not allowed.".format(link + "_back") ) - external_variants = getattr(config, "needs_variants", {}) - external_variant_options = getattr(config, "needs_variant_options", []) + external_variants = needs_config.variants + external_variant_options = needs_config.variant_options for value in external_variants.values(): # Check if external filter values is really a string if not isinstance(value, str): diff --git a/sphinx_needs/roles/need_outgoing.py b/sphinx_needs/roles/need_outgoing.py index be119c735..8248f1ce4 100644 --- a/sphinx_needs/roles/need_outgoing.py +++ b/sphinx_needs/roles/need_outgoing.py @@ -23,7 +23,8 @@ def process_need_outgoing( builder = app.builder env = app.env needs_config = NeedsSphinxConfig(app.config) - report_dead_links = needs_config.report_dead_links + link_lookup = {link["option"]: link for link in needs_config.extra_links} + # for node_need_ref in doctree.findall(NeedOutgoing): for node_need_ref in found_nodes: node_link_container = nodes.inline() @@ -107,39 +108,11 @@ def process_need_outgoing( dead_link_para.append(dead_link_text) node_link_container += dead_link_para - extra_links = getattr(env.config, "needs_extra_links", []) - extra_links_dict = {x["option"]: x for x in extra_links} - - # Reduce log level to INFO, if dead links are allowed - if ( - "allow_dead_links" in extra_links_dict[link_type] - and extra_links_dict[link_type]["allow_dead_links"] - ): - log_level = "INFO" - kwargs = {} - else: - # Set an extra css class, if link type is not configured to allow dead links + # add a CSS class for disallowed unknown links + # note a warning is already emitted when validating the needs list + # so we don't need to do it here + if not link_lookup.get(link_type, {}).get("allow_dead_links", False): dead_link_para.attributes["classes"].append("forbidden") - log_level = "WARNING" - kwargs = {"type": "needs"} - - if report_dead_links: - if node_need_ref and node_need_ref.line: - log.log( - log_level, - f"linked need {need_id_main} not found " - f"(Line {node_need_ref.line} of file {node_need_ref.source}) [needs]", - **kwargs, - ) - else: - log.log( - log_level, - "outgoing linked need {} not found (document: {}, " - "source need {} on line {} ) [needs]".format( - need_id_main, ref_need["docname"], ref_need["id"], ref_need["lineno"] - ), - **kwargs, - ) # If we have several links, we add an empty text between them if (index + 1) < len(link_list): diff --git a/sphinx_needs/roles/need_ref.py b/sphinx_needs/roles/need_ref.py index 8795cde24..2f1526a13 100644 --- a/sphinx_needs/roles/need_ref.py +++ b/sphinx_needs/roles/need_ref.py @@ -141,8 +141,9 @@ def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, fou else: log.warning( - f"linked need {node_need_ref['reftarget']} not found [needs]", + f"linked need {node_need_ref['reftarget']} not found [needs.link_ref]", type="needs", + subtype="link_ref", location=node_need_ref, ) diff --git a/tests/conftest.py b/tests/conftest.py index 2e679dc21..c631b27fc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,7 @@ from sphinx import version_info from sphinx.application import Sphinx from sphinx.testing.path import path +from sphinx.testing.util import SphinxTestApp from syrupy.extensions.single_file import SingleFileSnapshotExtension, WriteMode from xprocess import ProcessStarter @@ -231,10 +232,11 @@ def test_app(make_app, sphinx_test_tempdir, request): builder_params = request.param sphinx_conf_overrides = builder_params.get("confoverrides", {}) - # Since we don't want copy the plantuml.jar file for each test function, - # we need to override the plantuml conf variable and set it to what we have already - plantuml = "java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") - sphinx_conf_overrides.update(plantuml=plantuml) + if not builder_params.get("no_plantuml", False): + # Since we don't want copy the plantuml.jar file for each test function, + # we need to override the plantuml conf variable and set it to what we have already + plantuml = "java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") + sphinx_conf_overrides.update(plantuml=plantuml) # copy test srcdir to test temporary directory sphinx_test_tempdir srcdir = builder_params.get("srcdir") @@ -245,7 +247,7 @@ def test_app(make_app, sphinx_test_tempdir, request): src_dir = Path(str(src_dir)) # return sphinx.testing fixture make_app and new srcdir which is in sphinx_test_tempdir - app: Sphinx = make_app( + app: SphinxTestApp = make_app( buildername=builder_params.get("buildername", "html"), srcdir=src_dir, freshenv=builder_params.get("freshenv"), @@ -268,6 +270,8 @@ def test_app(make_app, sphinx_test_tempdir, request): yield app + app.cleanup() + # Clean up the srcdir of each Sphinx app after the test function has executed if request.config.getoption("--sn-build-dir") is None: shutil.rmtree(parent_path, ignore_errors=True) diff --git a/tests/doc_test/doc_report_dead_links_false/conf.py b/tests/doc_test/doc_report_dead_links_false/conf.py index 3e75ba02f..813f445a4 100644 --- a/tests/doc_test/doc_report_dead_links_false/conf.py +++ b/tests/doc_test/doc_report_dead_links_false/conf.py @@ -10,7 +10,7 @@ {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, ] -needs_report_dead_links = False +suppress_warnings = ["needs.link_outgoing"] needs_extra_links = [ { diff --git a/tests/test_basic_doc.py b/tests/test_basic_doc.py index d47906aca..3c12b8ac7 100644 --- a/tests/test_basic_doc.py +++ b/tests/test_basic_doc.py @@ -9,7 +9,9 @@ import pytest import responses +from sphinx import version_info from sphinx.application import Sphinx +from sphinx.testing.util import SphinxTestApp from syrupy.filters import props from sphinx_needs.api.need import NeedsNoIdException @@ -234,14 +236,24 @@ def test_sphinx_api_build(): temp_dir = tempfile.mkdtemp() src_dir = os.path.join(os.path.dirname(__file__), "doc_test", "doc_basic") - sphinx_app = Sphinx( + if version_info >= (7, 2): + src_dir = Path(src_dir) + temp_dir = Path(temp_dir) + else: + from sphinx.testing.path import path + + src_dir = path(src_dir) + temp_dir = path(temp_dir) + + sphinx_app = SphinxTestApp( srcdir=src_dir, - confdir=src_dir, - outdir=temp_dir, - doctreedir=temp_dir, + builddir=temp_dir, buildername="html", parallel=4, freshenv=True, ) - sphinx_app.build() - assert sphinx_app.statuscode == 0 + try: + sphinx_app.build() + assert sphinx_app.statuscode == 0 + finally: + sphinx_app.cleanup() diff --git a/tests/test_broken_links.py b/tests/test_broken_links.py index 4b2710749..0bc1613b9 100644 --- a/tests/test_broken_links.py +++ b/tests/test_broken_links.py @@ -1,13 +1,18 @@ import pytest +from sphinx.util.console import strip_colors -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_links"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/broken_links", "no_plantuml": True}], indirect=True +) def test_doc_build_html(test_app): app = test_app app.build() - warning = app._warning - # stdout warnings - warnings = warning.getvalue() - - assert "linked need BROKEN_LINK not found" in warnings + # check there are expected warnings + warnings = strip_colors(app._warning.getvalue().replace(str(app.srcdir), "srcdir")) + print(warnings.splitlines()) + assert warnings.splitlines() == [ + "srcdir/index.rst:12: WARNING: Need 'SP_TOO_002' has unknown outgoing link 'NOT_WORKING_LINK' in field 'links' [needs.link_outgoing]", + "srcdir/index.rst:21: WARNING: linked need BROKEN_LINK not found [needs.link_ref]", + ] diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 2551af045..717f37e5f 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -1,6 +1,6 @@ import re +import subprocess from pathlib import Path -from subprocess import STDOUT, check_output import pytest @@ -16,9 +16,10 @@ def test_doc_github_44(test_app): # So we call the needed command directly, but still use the sphinx_testing app to create the outdir for us. app = test_app - output = str( - check_output(["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], stderr=STDOUT, text=True) + output = subprocess.run( + ["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], check=True, capture_output=True ) + # app.build() Uncomment, if build should stop on breakpoints html = Path(app.outdir, "index.html").read_text() assert "

Github Issue 44 test" in html @@ -26,8 +27,11 @@ def test_doc_github_44(test_app): assert "Test 2" in html assert "Test 3" in html - assert "linked need test_3 not found" not in output - assert "outgoing linked need test_123_broken not found" in output + stderr = output.stderr.decode("utf-8") + stderr = stderr.replace(str(app.srcdir), "srcdir") + assert stderr.splitlines() == [ + "srcdir/index.rst:11: WARNING: Need 'test_3' has unknown outgoing link 'test_123_broken' in field 'links' [needs.link_outgoing]" + ] @pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_61"}], indirect=True) diff --git a/tests/test_needs_external_needs_build.py b/tests/test_needs_external_needs_build.py index 0a3c8876b..a5a4ad0a8 100644 --- a/tests/test_needs_external_needs_build.py +++ b/tests/test_needs_external_needs_build.py @@ -22,7 +22,10 @@ def test_doc_build_html(test_app, sphinx_test_tempdir): output = subprocess.run( ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True ) - assert not output.stderr, f"Build failed with stderr: {output.stderr}" + assert output.stderr.decode("utf-8").splitlines() == [ + "WARNING: http://my_company.com/docs/v1/index.html#TEST_01: Need 'EXT_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing]", + "WARNING: ../../_build/html/index.html#TEST_01: Need 'EXT_REL_PATH_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing]", + ] # run second time and check output_second = subprocess.run( diff --git a/tests/test_report_dead_links.py b/tests/test_report_dead_links.py index 24a8d9008..fd6f88aa0 100644 --- a/tests/test_report_dead_links.py +++ b/tests/test_report_dead_links.py @@ -1,3 +1,4 @@ +import subprocess from pathlib import Path import pytest @@ -6,61 +7,52 @@ @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_true"}], indirect=True ) -def test_needs_report_dead_links_true(test_app): - import subprocess - +def test_needs_dead_links_warnings(test_app): app = test_app - # Check config value of needs_report_dead_links - assert app.config.needs_report_dead_links - src_dir = Path(app.srcdir) out_dir = Path(app.outdir) output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) - # Check log info msg of dead links - assert ( - "outgoing linked need DEAD_LINK_ALLOWED not found (document: index, source need REQ_001 on line 7 ) [needs]" - in output.stdout.decode("utf-8") - ) - # Check log warning msg of dead links - assert ( - "WARNING: outgoing linked need ANOTHER_DEAD_LINK not found (document: index, " - "source need REQ_004 on line 17 ) [needs]" in output.stderr.decode("utf-8") - ) - assert ( - "WARNING: outgoing linked need REQ_005 not found (document: index, source need TEST_004 on line 45 ) [needs]" - in output.stderr.decode("utf-8") - ) + # check there are expected warnings + stderr = output.stderr.decode("utf-8") + stderr = stderr.replace(str(src_dir), "srcdir") + assert stderr.splitlines() == [ + "srcdir/index.rst:17: WARNING: Need 'REQ_004' has unknown outgoing link 'ANOTHER_DEAD_LINK' in field 'links' [needs.link_outgoing]", + "srcdir/index.rst:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'links' [needs.link_outgoing]", + "srcdir/index.rst:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'tests' [needs.link_outgoing]", + ] @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_false"}], indirect=True + "test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_report_dead_links_true"}], indirect=True ) -def test_needs_report_dead_links_false(test_app): - import subprocess - +def test_needs_dead_links_warnings_needs_builder(test_app): app = test_app - # Check config value of needs_report_dead_links - assert not app.config.needs_report_dead_links + src_dir = Path(app.srcdir) + out_dir = Path(app.outdir) + output = subprocess.run(["sphinx-build", "-M", "needs", src_dir, out_dir], capture_output=True) + + # check there are expected warnings + stderr = output.stderr.decode("utf-8") + stderr = stderr.replace(str(src_dir), "srcdir") + assert stderr.splitlines() == [ + "srcdir/index.rst:17: WARNING: Need 'REQ_004' has unknown outgoing link 'ANOTHER_DEAD_LINK' in field 'links' [needs.link_outgoing]", + "srcdir/index.rst:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'links' [needs.link_outgoing]", + "srcdir/index.rst:45: WARNING: Need 'TEST_004' has unknown outgoing link 'REQ_005.invalid' in field 'tests' [needs.link_outgoing]", + ] + + +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_false"}], indirect=True +) +def test_needs_dead_links_suppress_warnings(test_app): + app = test_app src_dir = Path(app.srcdir) out_dir = Path(app.outdir) output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) - # Check log info msg of dead links deactivated - assert ( - "outgoing linked need DEAD_LINK_ALLOWED not found (document: index, source need REQ_001 on line 7 ) [needs]" - not in output.stdout.decode("utf-8") - ) - # Check log warning msg of dead links deactivated - assert ( - "WARNING: outgoing linked need ANOTHER_DEAD_LINK not found (document: index, " - "source need REQ_004 on line 17 ) [needs]" not in output.stderr.decode("utf-8") - ) - assert ( - "WARNING: outgoing linked need REQ_005 not found (document: index, source need TEST_004 on line 45 ) [needs]" - not in output.stderr.decode("utf-8") - ) + # check there are no warnings assert not output.stderr From e0bc813741e28edf32a4291f2cf9a7e8a3d455e3 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 15:11:17 +0000 Subject: [PATCH 08/24] =?UTF-8?q?=F0=9F=94=A7=20Use=20future=20annotations?= =?UTF-8?q?=20in=20all=20modules=20(#1111)?= 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 From bbdf7bf0ff906fb88c4ef232aa845ef9b470d53b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:11:33 +0100 Subject: [PATCH 09/24] Bump jinja2 from 3.1.2 to 3.1.3 (#1091) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9aaf90977..88dd7b3f0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -228,6 +228,7 @@ files = [ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"}, {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, @@ -236,6 +237,7 @@ files = [ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"}, {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, @@ -244,6 +246,7 @@ files = [ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"}, {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, @@ -252,6 +255,7 @@ files = [ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"}, {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, @@ -642,13 +646,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.2" +version = "3.1.3" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] @@ -966,6 +970,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1686,6 +1700,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1693,8 +1708,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1711,6 +1734,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1718,6 +1742,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, From 8e2fcd687f00ec7af977ef0a77c40bc7fde5c714 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:12:21 +0100 Subject: [PATCH 10/24] Bump pillow from 10.1.0 to 10.2.0 (#1094) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 130 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/poetry.lock b/poetry.lock index 88dd7b3f0..b5317979b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1211,70 +1211,88 @@ files = [ [[package]] name = "pillow" -version = "10.1.0" +version = "10.2.0" description = "Python Imaging Library (Fork)" -optional = true +optional = false python-versions = ">=3.8" files = [ - {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, - {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, - {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, - {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, - {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, - {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, - {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, - {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, - {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, - {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, - {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, - {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, - {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, - {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, - {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, - {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, - {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, - {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, - {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, - {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, - {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, - {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, - {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, - {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, + {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, + {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, + {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, + {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, + {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, + {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, + {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, + {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, + {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, + {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, + {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, + {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, + {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, + {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, + {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, + {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, + {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, + {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, + {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, + {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, + {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, + {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, + {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, + {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, + {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, + {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, + {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, + {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, + {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, + {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, + {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, + {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, + {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, ] [package.extras] docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] [[package]] name = "pkgutil-resolve-name" From 84a5f72f2e72ab1471ab2d1bb5c570d6115ef199 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 15 Feb 2024 15:51:32 +0000 Subject: [PATCH 11/24] =?UTF-8?q?=F0=9F=94=A7=20Replace=20black/isort/pyup?= =?UTF-8?q?grade/flake8=20with=20ruff=20(#1080)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .flake8 | 3 - .pre-commit-config.yaml | 29 +- docs/conf.py | 108 ++++++-- noxfile.py | 10 +- performance/performance_test.py | 54 +++- pyproject.toml | 18 +- sphinx_needs/api/configuration.py | 35 ++- sphinx_needs/api/need.py | 78 ++++-- sphinx_needs/builder.py | 12 +- sphinx_needs/config.py | 260 +++++++++++++----- sphinx_needs/data.py | 11 +- sphinx_needs/debug.py | 27 +- sphinx_needs/diagrams_common.py | 45 ++- sphinx_needs/directives/list2need.py | 51 +++- sphinx_needs/directives/need.py | 80 ++++-- sphinx_needs/directives/needbar.py | 70 +++-- sphinx_needs/directives/needextend.py | 46 +++- sphinx_needs/directives/needextract.py | 21 +- sphinx_needs/directives/needfilter.py | 74 +++-- sphinx_needs/directives/needflow.py | 105 +++++-- sphinx_needs/directives/needgantt.py | 80 ++++-- sphinx_needs/directives/needimport.py | 57 +++- sphinx_needs/directives/needlist.py | 29 +- sphinx_needs/directives/needpie.py | 51 +++- sphinx_needs/directives/needreport.py | 20 +- sphinx_needs/directives/needsequence.py | 76 +++-- sphinx_needs/directives/needservice.py | 38 ++- sphinx_needs/directives/needtable.py | 64 ++++- sphinx_needs/directives/needuml.py | 107 +++++-- sphinx_needs/directives/utils.py | 24 +- sphinx_needs/environment.py | 23 +- sphinx_needs/external_needs.py | 43 ++- sphinx_needs/filter_common.py | 66 ++++- sphinx_needs/functions/common.py | 36 ++- sphinx_needs/functions/functions.py | 119 ++++++-- sphinx_needs/layout.py | 206 +++++++++++--- sphinx_needs/need_constraints.py | 16 +- sphinx_needs/needs.py | 116 ++++++-- sphinx_needs/needsfile.py | 32 ++- sphinx_needs/roles/need_count.py | 5 +- sphinx_needs/roles/need_func.py | 11 +- sphinx_needs/roles/need_incoming.py | 18 +- sphinx_needs/roles/need_outgoing.py | 19 +- sphinx_needs/roles/need_part.py | 19 +- sphinx_needs/roles/need_ref.py | 22 +- sphinx_needs/services/config/github.py | 33 ++- sphinx_needs/services/github.py | 115 +++++--- sphinx_needs/services/manager.py | 8 +- sphinx_needs/services/open_needs.py | 38 ++- sphinx_needs/utils.py | 142 +++++++--- sphinx_needs/warnings.py | 24 +- tests/benchmarks/test_basic.py | 8 +- tests/benchmarks/test_official.py | 18 +- tests/conftest.py | 39 ++- tests/data/service_github.py | 24 +- tests/doc_test/api_doc/conf.py | 32 ++- tests/doc_test/api_doc_awesome/conf.py | 32 ++- tests/doc_test/arch_doc/conf.py | 32 ++- tests/doc_test/broken_doc/conf.py | 32 ++- tests/doc_test/broken_links/conf.py | 32 ++- tests/doc_test/broken_statuses/conf.py | 32 ++- tests/doc_test/broken_syntax_doc/conf.py | 32 ++- tests/doc_test/broken_tags/conf.py | 32 ++- tests/doc_test/broken_tags_2/conf.py | 32 ++- tests/doc_test/doc_basic/conf.py | 32 ++- tests/doc_test/doc_basic_latex/conf.py | 32 ++- tests/doc_test/doc_build_latex/conf.py | 32 ++- tests/doc_test/doc_df_calc_sum/conf.py | 38 ++- .../doc_df_check_linked_values/conf.py | 32 ++- tests/doc_test/doc_df_user_functions/conf.py | 32 ++- tests/doc_test/doc_dynamic_functions/conf.py | 32 ++- tests/doc_test/doc_export_id/conf.py | 32 ++- tests/doc_test/doc_extra_links/conf.py | 32 ++- tests/doc_test/doc_github_issue_21/conf.py | 32 ++- tests/doc_test/doc_github_issue_44/conf.py | 32 ++- tests/doc_test/doc_global_options/conf.py | 32 ++- tests/doc_test/doc_layout/conf.py | 47 +++- tests/doc_test/doc_list2need/conf.py | 32 ++- tests/doc_test/doc_measure_time/conf.py | 32 ++- tests/doc_test/doc_need_count/conf.py | 32 ++- tests/doc_test/doc_need_delete/conf.py | 32 ++- tests/doc_test/doc_need_id_from_title/conf.py | 32 ++- tests/doc_test/doc_need_jinja_content/conf.py | 32 ++- tests/doc_test/doc_need_parts/conf.py | 32 ++- tests/doc_test/doc_needarch/conf.py | 41 ++- .../doc_needarch_jinja_func_import/conf.py | 48 +++- .../doc_needarch_jinja_func_need/conf.py | 41 ++- .../doc_needarch_negative_tests/conf.py | 41 ++- tests/doc_test/doc_needbar/conf.py | 16 +- tests/doc_test/doc_needextend/conf.py | 32 ++- tests/doc_test/doc_needextend_strict/conf.py | 32 ++- tests/doc_test/doc_needextract/conf.py | 32 ++- tests/doc_test/doc_needflow/conf.py | 32 ++- .../doc_needflow_incl_child_needs/conf.py | 32 ++- .../conf.py | 32 ++- .../conf.py | 32 ++- tests/doc_test/doc_needlist/conf.py | 32 ++- tests/doc_test/doc_needpie/conf.py | 32 ++- tests/doc_test/doc_needs_builder/conf.py | 32 ++- .../doc_needs_builder_negative_tests/conf.py | 32 ++- .../doc_needs_builder_parallel/conf.py | 32 ++- .../doc_test/doc_needs_external_needs/conf.py | 44 ++- .../doc_needs_external_needs_remote/conf.py | 32 ++- .../conf.py | 38 ++- tests/doc_test/doc_needs_filter_data/conf.py | 32 ++- .../conf.py | 16 +- tests/doc_test/doc_needs_warnings/conf.py | 44 ++- .../conf.py | 32 ++- tests/doc_test/doc_needsfile/conf.py | 32 ++- tests/doc_test/doc_needtable/conf.py | 32 ++- tests/doc_test/doc_needuml/conf.py | 41 ++- .../doc_needuml_diagram_allowmixing/conf.py | 41 ++- .../doc_needuml_duplicate_key/conf.py | 41 ++- tests/doc_test/doc_needuml_filter/conf.py | 41 ++- .../doc_needuml_jinja_func_flow/conf.py | 41 ++- .../conf.py | 48 +++- .../conf.py | 41 ++- .../doc_needuml_jinja_func_ref/conf.py | 41 ++- .../doc_needuml_key_name_diagram/conf.py | 41 ++- tests/doc_test/doc_needuml_save/conf.py | 41 ++- .../doc_needuml_save_with_abs_path/conf.py | 41 ++- tests/doc_test/doc_open_needs_service/conf.py | 32 ++- .../doc_report_dead_links_false/conf.py | 32 ++- .../doc_report_dead_links_true/conf.py | 32 ++- .../doc_role_need_max_title_length/conf.py | 32 ++- .../conf.py | 32 ++- tests/doc_test/doc_role_need_template/conf.py | 32 ++- tests/doc_test/doc_style_blank/conf.py | 32 ++- tests/doc_test/doc_style_custom/conf.py | 32 ++- tests/doc_test/doc_style_modern/conf.py | 32 ++- tests/doc_test/doc_style_unknown/conf.py | 32 ++- tests/doc_test/external_doc/conf.py | 46 +++- tests/doc_test/filter_doc/conf.py | 56 +++- tests/doc_test/generic_doc/conf.py | 32 ++- tests/doc_test/import_doc/conf.py | 40 ++- tests/doc_test/import_doc_empty/conf.py | 40 ++- tests/doc_test/import_doc_invalid/conf.py | 40 ++- tests/doc_test/need_constraints/conf.py | 44 ++- .../doc_test/need_constraints_failed/conf.py | 44 ++- .../needextract_with_nested_needs/conf.py | 32 ++- .../doc_test/needpie_with_zero_needs/conf.py | 32 ++- tests/doc_test/non_exists_file_import/conf.py | 40 ++- tests/doc_test/parallel_doc/conf.py | 32 ++- tests/doc_test/role_need_doc/conf.py | 46 +++- tests/doc_test/service_doc/conf.py | 32 ++- tests/doc_test/unicode_support/conf.py | 32 ++- tests/doc_test/variant_doc/conf.py | 32 ++- tests/doc_test/variant_options/conf.py | 32 ++- tests/no_mpl_tests.py | 12 +- tests/test_add_sections.py | 6 +- tests/test_api_configuration.py | 10 +- tests/test_api_usage_in_extension.py | 4 +- tests/test_arch.py | 4 +- tests/test_basic_doc.py | 96 +++++-- tests/test_broken_doc.py | 6 +- tests/test_broken_links.py | 4 +- tests/test_broken_statuses.py | 6 +- tests/test_broken_syntax_doc.py | 6 +- tests/test_broken_tags.py | 12 +- tests/test_clean_log.py | 8 +- tests/test_complex_builders.py | 18 +- tests/test_doc_build_latex.py | 6 +- tests/test_dynamic_functions.py | 48 +++- tests/test_export_id.py | 12 +- tests/test_external.py | 28 +- tests/test_extra_links.py | 16 +- tests/test_extra_options.py | 10 +- tests/test_filter.py | 12 +- tests/test_github_issues.py | 20 +- tests/test_global_options.py | 6 +- tests/test_import.py | 62 ++++- tests/test_jinja_content_option.py | 4 +- tests/test_layouts.py | 9 +- tests/test_list2need.py | 14 +- tests/test_multiple_link_backs.py | 6 +- tests/test_need_constraints.py | 23 +- tests/test_need_count.py | 6 +- tests/test_need_delete_option.py | 6 +- tests/test_need_id_from_title.py | 12 +- tests/test_need_parts.py | 10 +- tests/test_needarch.py | 26 +- tests/test_needbar.py | 6 +- tests/test_needextend.py | 29 +- tests/test_needextract.py | 24 +- tests/test_needextract_with_nested_needs.py | 10 +- tests/test_needflow.py | 30 +- tests/test_needimport_noindex.py | 6 +- tests/test_needlist.py | 6 +- tests/test_needpie.py | 6 +- tests/test_needpie_with_zero_needs.py | 4 +- tests/test_needreport.py | 11 +- tests/test_needs_builder.py | 27 +- tests/test_needs_external_needs_build.py | 190 ++++++++++--- tests/test_needs_filter_data.py | 16 +- tests/test_needs_id_builder.py | 8 +- tests/test_needs_warning.py | 39 ++- tests/test_needsfile.py | 6 +- tests/test_needtable.py | 30 +- tests/test_needuml.py | 105 +++++-- tests/test_open_needs_service.py | 30 +- tests/test_parallel_execution.py | 8 +- tests/test_report_dead_links.py | 24 +- tests/test_role_need.py | 9 +- tests/test_role_need_max_title_length.py | 11 +- tests/test_role_need_template.py | 4 +- tests/test_services/test_service_basics.py | 11 +- tests/test_styles/test_style_blank.py | 6 +- .../test_style_css_js_registration.py | 24 +- tests/test_styles/test_style_custom.py | 6 +- tests/test_styles/test_style_modern.py | 6 +- tests/test_styles/test_style_unknown.py | 6 +- tests/test_test_doc.py | 6 +- tests/test_title_from_content.py | 6 +- tests/test_title_optional.py | 10 +- tests/test_unicode.py | 10 +- tests/test_unsafe_filter_for_filter_func.py | 9 +- tests/test_variants.py | 11 +- tests/util.py | 12 +- 218 files changed, 6067 insertions(+), 1358 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index ba265fe89..000000000 --- a/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -max-line-length = 120 -extend-ignore = E501, E203, B028 \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4312eb7b9..a755133cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,30 +1,11 @@ repos: - - repo: https://github.com/psf/black - rev: 24.2.0 - hooks: - - id: black - - - repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 - hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear - - flake8-comprehensions - - flake8-simplify - - pep8-naming - - - repo: https://github.com/pycqa/isort - rev: 5.13.2 - hooks: - - id: isort - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 hooks: - - id: pyupgrade - args: - - --py38-plus + - id: ruff + args: [--fix] + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.8.0 diff --git a/docs/conf.py b/docs/conf.py index c9826cfd5..f9355ad77 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -55,7 +55,9 @@ needs_debug_measurement = True add_module_names = False # Used to shorten function name output -autodoc_docstring_signature = True # Used to read spec. func-defs from docstring (e.g. get rid of self) +autodoc_docstring_signature = ( + True # Used to read spec. func-defs from docstring (e.g. get rid of self) +) NOTE_TEMPLATE = """ .. _{{id}}: @@ -80,9 +82,7 @@ {% endif %} """ -DEFAULT_DIAGRAM_TEMPLATE = ( - "{{type_name}}\\n**{{title|wordwrap(15, wrapstring='**\\\\n**')}}**\\n{{id}}" -) +DEFAULT_DIAGRAM_TEMPLATE = "{{type_name}}\\n**{{title|wordwrap(15, wrapstring='**\\\\n**')}}**\\n{{id}}" # You can uncomment some of the following lines to override the default configuration for Sphinx-Needs. # needs_diagram_template = DEFAULT_DIAGRAM_TEMPLATE @@ -110,16 +110,71 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "title": "System", "content": "plantuml", "prefix": "S_", "color": "#BFD8D2", "style": "card"}, + { + "directive": "sys", + "title": "System", + "content": "plantuml", + "prefix": "S_", + "color": "#BFD8D2", + "style": "card", + }, # Normal types - {"directive": "req", "title": "Requirement", "prefix": "R_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "S_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "I_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "T_", "color": "#DCB239", "style": "node"}, - {"directive": "feature", "title": "Feature", "prefix": "F_", "color": "#FFCC00", "style": "node"}, - {"directive": "user", "title": "User", "prefix": "U_", "color": "#777777", "style": "node"}, - {"directive": "action", "title": "Action", "prefix": "A_", "color": "#FFCC00", "style": "node"}, - {"directive": "milestone", "title": "Milestone", "prefix": "M_", "color": "#FF3333", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "R_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "S_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "I_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "T_", + "color": "#DCB239", + "style": "node", + }, + { + "directive": "feature", + "title": "Feature", + "prefix": "F_", + "color": "#FFCC00", + "style": "node", + }, + { + "directive": "user", + "title": "User", + "prefix": "U_", + "color": "#777777", + "style": "node", + }, + { + "directive": "action", + "title": "Action", + "prefix": "A_", + "color": "#FFCC00", + "style": "node", + }, + { + "directive": "milestone", + "title": "Milestone", + "prefix": "M_", + "color": "#FF3333", + "style": "node", + }, ] needs_extra_links = [ @@ -208,7 +263,9 @@ needs_id_required = False # needs_css = "dark.css" -local_plantuml_path = os.path.join(os.path.dirname(__file__), "utils", "plantuml-1.2022.14.jar") +local_plantuml_path = os.path.join( + os.path.dirname(__file__), "utils", "plantuml-1.2022.14.jar" +) plantuml = f"java -Djava.awt.headless=true -jar {local_plantuml_path}" # plantuml_output_format = 'png' @@ -246,7 +303,10 @@ "grid": "simple_side_right_partial", "layout": { "head": ['**<>** for *<>*'], - "meta": ['**status**: <>', '**author**: <>'], + "meta": [ + '**status**: <>', + '**author**: <>', + ], "side": ['<>'], }, }, @@ -450,14 +510,22 @@ def custom_defined_func(): # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, "needstestdocs.tex", "needs test docs Documentation", "team useblocks", "manual"), + ( + master_doc, + "needstestdocs.tex", + "needs test docs Documentation", + "team useblocks", + "manual", + ), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [(master_doc, "needstestdocs", "needs test docs Documentation", [author], 1)] +man_pages = [ + (master_doc, "needstestdocs", "needs test docs Documentation", [author], 1) +] # -- Options for Texinfo output ------------------------------------------- @@ -478,7 +546,11 @@ def custom_defined_func(): # contains different constraints needs_constraints = { - "critical": {"check_0": "'critical' in tags", "check_1": "'SECURITY_REQ' in links", "severity": "CRITICAL"}, + "critical": { + "check_0": "'critical' in tags", + "check_1": "'SECURITY_REQ' in links", + "severity": "CRITICAL", + }, "security": {"check_0": "'security' in tags", "severity": "HIGH"}, "team": {"check_0": 'author == "Bob"', "severity": "LOW"}, } diff --git a/noxfile.py b/noxfile.py index 467cb545e..a67fef8da 100644 --- a/noxfile.py +++ b/noxfile.py @@ -72,7 +72,15 @@ def pre_commit(session): def linkcheck(session): session.install(".[docs]") with session.chdir("docs"): - session.run("sphinx-build", "-b", "linkcheck", ".", "_build/linkcheck", *session.posargs, external=True) + session.run( + "sphinx-build", + "-b", + "linkcheck", + ".", + "_build/linkcheck", + *session.posargs, + external=True, + ) @session(python="3.11") diff --git a/performance/performance_test.py b/performance/performance_test.py index 1733f6328..c7ca7e368 100644 --- a/performance/performance_test.py +++ b/performance/performance_test.py @@ -23,7 +23,15 @@ def cli(): def start( - needs=1000, needtables=0, dummies=0, pages=1, parallel=1, keep=False, browser=False, debug=False, basic=False + needs=1000, + needtables=0, + dummies=0, + pages=1, + parallel=1, + keep=False, + browser=False, + debug=False, + basic=False, ): """ Test run implementation @@ -163,11 +171,31 @@ def start( @cli.command() -@click.option("--profile", default=[], type=str, multiple=True, help="Activates profiling for given area") -@click.option("--needs", default=[50, 10], type=int, multiple=True, help="Number of maximum needs.") -@click.option("--needtables", default=-1, type=int, help="Number of maximum needtables.") +@click.option( + "--profile", + default=[], + type=str, + multiple=True, + help="Activates profiling for given area", +) +@click.option( + "--needs", + default=[50, 10], + type=int, + multiple=True, + help="Number of maximum needs.", +) +@click.option( + "--needtables", default=-1, type=int, help="Number of maximum needtables." +) @click.option("--dummies", default=-1, type=int, help="Number of standard rst dummies.") -@click.option("--pages", default=[5, 1], type=int, multiple=True, help="Number of additional pages with needs.") +@click.option( + "--pages", + default=[5, 1], + type=int, + multiple=True, + help="Number of additional pages with needs.", +) @click.option( "--parallel", default=[1, 4], @@ -177,9 +205,19 @@ def start( ) @click.option("--keep", is_flag=True, help="Keeps the temporary src and build folders") @click.option("--browser", is_flag=True, help="Opens the project in your browser") -@click.option("--snakeviz", is_flag=True, help="Opens snakeviz view for measured profiles in browser") -@click.option("--debug", is_flag=True, help="Prints more information, incl. sphinx build output") -@click.option("--basic", is_flag=True, help="Use only default config of Sphinx-Needs (e.g. no extra options)") +@click.option( + "--snakeviz", + is_flag=True, + help="Opens snakeviz view for measured profiles in browser", +) +@click.option( + "--debug", is_flag=True, help="Prints more information, incl. sphinx build output" +) +@click.option( + "--basic", + is_flag=True, + help="Use only default config of Sphinx-Needs (e.g. no extra options)", +) def series( profile, needs, diff --git a/pyproject.toml b/pyproject.toml index 054de6c18..23fe436da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,11 +101,19 @@ markers = [ "jstest: marks tests as JavaScript test (deselect with '-m \"not jstest\"')", ] -[tool.black] -line-length = 120 - -[tool.isort] -profile = "black" +[tool.ruff] +extend-select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "I", # isort + "ICN", # flake8-import-conventions + "ISC", # flake8-implicit-str-concat + "N", # pep8-naming + "RUF", # Ruff-specific rules + "SIM", # flake8-simplify + "UP", # pyupgrade +] +extend-ignore = ["B904", "ISC001", "ICN001", "N818", "RUF005", "RUF013", "RUF012", "SIM108", "SIM118"] [tool.mypy] files = "sphinx_needs" diff --git a/sphinx_needs/api/configuration.py b/sphinx_needs/api/configuration.py index 8b06ab045..1197cd8c4 100644 --- a/sphinx_needs/api/configuration.py +++ b/sphinx_needs/api/configuration.py @@ -37,7 +37,12 @@ def get_need_types(app: Sphinx) -> list[str]: def add_need_type( - app: Sphinx, directive: str, title: str, prefix: str, color: str = "#ffffff", style: str = "node" + app: Sphinx, + directive: str, + title: str, + prefix: str, + color: str = "#ffffff", + style: str = "node", ) -> None: """ Adds a new need_type to the configuration. @@ -68,7 +73,15 @@ def add_need_type( if directive in type_names: raise NeedsApiConfigException(f"{directive} already exists as need type") - needs_types.append({"directive": directive, "title": title, "prefix": prefix, "color": color, "style": style}) + needs_types.append( + { + "directive": directive, + "title": title, + "prefix": prefix, + "color": color, + "style": style, + } + ) app.add_directive(directive, sphinx_needs.directives.need.NeedDirective) @@ -93,7 +106,9 @@ 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: str | None = None) -> None: +def add_dynamic_function( + app: Sphinx, function: DynamicFunction, name: str | None = None +) -> None: """ Registers a new dynamic function for sphinx-needs. @@ -124,7 +139,12 @@ def my_function(app, need, needs, *args, **kwargs): WarningCheck = Callable[[NeedsInfoType, SphinxLoggerAdapter], bool] -def add_warning(app: Sphinx, name: str, function: WarningCheck | None = None, filter_string: str | None = None) -> None: +def add_warning( + app: Sphinx, + name: str, + function: WarningCheck | None = None, + filter_string: str | None = None, +) -> None: """ Registers a warning. @@ -137,11 +157,14 @@ def add_warning(app: Sphinx, name: str, function: WarningCheck | None = None, fi :return: None """ if function is None and filter_string is None: - raise NeedsApiConfigException("Function or filter_string must be given for add_warning_func") + raise NeedsApiConfigException( + "Function or filter_string must be given for add_warning_func" + ) if function is not None and filter_string is not None: raise NeedsApiConfigException( - "For add_warning_func only function or filter_string is allowed to be set, " "not both." + "For add_warning_func only function or filter_string is allowed to be set, " + "not both." ) warning_check = function or filter_string diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 1a1b87f8b..32dec8513 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -152,8 +152,8 @@ def run(): configured_need_types = [ntype["directive"] for ntype in types] if need_type not in configured_need_types: logger.warning( - "Couldn't create need {}. Reason: The need-type (i.e. `{}`) is not set " - "in the project's 'need_types' configuration in conf.py. [needs]".format(id, need_type), + f"Couldn't create need {id}. Reason: The need-type (i.e. `{need_type}`) is not set " + "in the project's 'need_types' configuration in conf.py. [needs]", type="needs", ) @@ -161,7 +161,9 @@ def run(): if ntype["directive"] == need_type: type_name = ntype["title"] type_prefix = ntype["prefix"] - type_color = ntype["color"] or "#000000" # if no color set up user in config + type_color = ( + ntype["color"] or "#000000" + ) # if no color set up user in config type_style = ntype["style"] or "node" # if no style set up user in config found = True break @@ -184,7 +186,7 @@ def run(): if id is None and needs_config.id_required: raise NeedsNoIdException( "An id is missing for this need and must be set, because 'needs_id_required' " - "is set to True in conf.py. Need '{}' in {} ({})".format(title, docname, lineno) + f"is set to True in conf.py. Need '{title}' in {docname} ({lineno})" ) if id is None: @@ -193,13 +195,18 @@ def run(): need_id = id if needs_config.id_regex and not re.match(needs_config.id_regex, need_id): - raise NeedsInvalidException(f"Given ID '{need_id}' does not match configured regex '{needs_config.id_regex}'") + raise NeedsInvalidException( + f"Given ID '{need_id}' does not match configured regex '{needs_config.id_regex}'" + ) # Handle status # Check if status is in needs_statuses. If not raise an error. - if needs_config.statuses and status not in [stat["name"] for stat in needs_config.statuses]: + if needs_config.statuses and status not in [ + stat["name"] for stat in needs_config.statuses + ]: raise NeedsStatusNotAllowed( - f"Status {status} of need id {need_id} is not allowed " "by config value 'needs_statuses'." + f"Status {status} of need id {need_id} is not allowed " + "by config value 'needs_statuses'." ) if tags is None: @@ -212,7 +219,8 @@ def run(): for i in range(len(tags)): if len(tags[i]) == 0 or tags[i].isspace(): logger.warning( - f"Scruffy tag definition found in need {need_id}. " "Defined tag contains spaces only. [needs]", + f"Scruffy tag definition found in need {need_id}. " + "Defined tag contains spaces only. [needs]", type="needs", ) else: @@ -225,7 +233,8 @@ def run(): needs_tags = [tag["name"] for tag in needs_config.tags] if tag not in needs_tags: raise NeedsTagNotAllowed( - f"Tag {tag} of need id {need_id} is not allowed " "by config value 'needs_tags'." + f"Tag {tag} of need id {need_id} is not allowed " + "by config value 'needs_tags'." ) # This may have cut also dynamic function strings, as they can contain , as well. # So let put them together again @@ -237,7 +246,9 @@ def run(): if len(constraints) > 0: # tags should be a string, but it can also be already a list,which can be used. if isinstance(constraints, str): - constraints = [constraint.strip() for constraint in re.split("[;,]", constraints)] + constraints = [ + constraint.strip() for constraint in re.split("[;,]", constraints) + ] new_constraints = [] # Shall contain only valid constraints for i in range(len(constraints)): @@ -356,9 +367,9 @@ def run(): for keyword in kwargs: if keyword not in needs_extra_option_names and keyword not in link_names: raise NeedsInvalidOption( - "Unknown Option {}. " + f"Unknown Option {keyword}. " "Use needs_extra_options or needs_extra_links in conf.py" - "to define this option.".format(keyword) + "to define this option." ) # Merge links @@ -366,11 +377,15 @@ def run(): for link_type in needs_config.extra_links: # Check, if specific link-type got some arguments during method call - if link_type["option"] not in kwargs and link_type["option"] not in needs_global_options: + if ( + link_type["option"] not in kwargs + and link_type["option"] not in needs_global_options + ): # if not we set no links, but entry in needS_info must be there links = [] elif link_type["option"] in needs_global_options and ( - link_type["option"] not in kwargs or len(str(kwargs[link_type["option"]])) == 0 + link_type["option"] not in kwargs + or len(str(kwargs[link_type["option"]])) == 0 ): # If it is in global option, value got already set during prior handling of them links_string = needs_info[link_type["option"]] @@ -585,7 +600,9 @@ def _prepare_template(app: Sphinx, needs_info, template_key: str) -> str: template_folder = os.path.join(app.srcdir, template_folder) if not os.path.isdir(template_folder): - raise NeedsTemplateException(f"Template folder does not exist: {template_folder}") + raise NeedsTemplateException( + f"Template folder does not exist: {template_folder}" + ) template_file_name = needs_info[template_key] + ".need" template_path = os.path.join(template_folder, template_file_name) @@ -600,7 +617,9 @@ def _prepare_template(app: Sphinx, needs_info, template_key: str) -> str: return new_content -def _render_template(content: str, docname: str, lineno: int, state: RSTState) -> nodes.Element: +def _render_template( + content: str, docname: str, lineno: int, state: RSTState +) -> nodes.Element: rst = StringList() for line in content.split("\n"): rst.append(line, docname, lineno) @@ -610,7 +629,9 @@ def _render_template(content: str, docname: str, lineno: int, state: RSTState) - return node_need_content -def _render_plantuml_template(content: str, docname: str, lineno: int, state: RSTState) -> nodes.Element: +def _render_plantuml_template( + content: str, docname: str, lineno: int, state: RSTState +) -> nodes.Element: rst = StringList() rst.append(".. needuml::", docname, lineno) rst.append("", docname, lineno) # Empty option line for needuml @@ -636,7 +657,8 @@ def _read_in_links(links_string: str | list[str]) -> list[str]: for link in link_list: if link.isspace(): logger.warning( - f"Grubby link definition found in need {id}. " "Defined link contains spaces only. [needs]", + f"Grubby link definition found in need {id}. " + "Defined link contains spaces only. [needs]", type="needs", ) else: @@ -648,7 +670,13 @@ def _read_in_links(links_string: 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: int | None = 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. @@ -671,7 +699,9 @@ def make_hashed_id(app: Sphinx, need_type: str, full_title: str, content: str, i type_prefix = ntype["prefix"] break if type_prefix is None: - raise NeedsInvalidException(f"Given need_type {need_type} is unknown. File {app.env.docname}") + raise NeedsInvalidException( + f"Given need_type {need_type} is unknown. File {app.env.docname}" + ) hashable_content = full_title or "\n".join(content) hashed_id = hashlib.sha1(hashable_content.encode("UTF-8")).hexdigest().upper() @@ -764,11 +794,15 @@ def _merge_global_options(app: Sphinx, needs_info, global_options) -> None: for single_value in values: if len(single_value) < 2 or len(single_value) > 3: - raise NeedsInvalidException(f"global option tuple has wrong amount of parameters: {key}") + raise NeedsInvalidException( + f"global option tuple has wrong amount of parameters: {key}" + ) if filter_single_need(needs_info, config, single_value[1]): # Set value, if filter has matched needs_info[key] = single_value[0] - elif len(single_value) == 3 and (key not in needs_info.keys() or len(str(needs_info[key])) > 0): + elif len(single_value) == 3 and ( + key not in needs_info.keys() or len(str(needs_info[key])) > 0 + ): # Otherwise set default, but only if no value was set before or value is "" and a default is defined needs_info[key] = single_value[2] else: diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index e1eaf7994..b4ebea207 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -50,7 +50,9 @@ def write( if not SphinxNeedsData(self.env).has_export_filters: return LOGGER.warning( - "At least one use of `export_id` directive option, requires a slower build", type="needs", subtype="build" + "At least one use of `export_id` directive option, requires a slower build", + type="needs", + subtype="build", ) return super().write(build_docnames, updated_docnames, method) @@ -70,7 +72,9 @@ def finish(self) -> None: # check if needs.json file exists in conf.py directory needs_json = os.path.join(self.srcdir, "needs.json") if os.path.exists(needs_json): - LOGGER.info("needs.json found, but will not be used because needs_file not configured.") + LOGGER.info( + "needs.json found, but will not be used because needs_file not configured." + ) # Clean needs_list from already stored needs of the current version. # This is needed as needs could have been removed from documentation and if this is the case, @@ -170,7 +174,9 @@ def finish(self) -> None: post_process_needs_data(self.app) data = SphinxNeedsData(self.env) - needs = data.get_or_create_needs().values() # We need a list of needs for later filter checks + needs = ( + data.get_or_create_needs().values() + ) # We need a list of needs for later filter checks version = getattr(self.env.config, "version", "unset") needs_config = NeedsSphinxConfig(self.env.config) filter_string = needs_config.builder_filter diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index a988074fe..785436f7e 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -26,7 +26,9 @@ class Config: def __init__(self) -> None: self._extra_options: dict[str, Callable[[str], Any]] = {} - self._warnings: dict[str, str | Callable[[NeedsInfoType, SphinxLoggerAdapter], bool]] = {} + self._warnings: dict[ + str, str | Callable[[NeedsInfoType, SphinxLoggerAdapter], bool] + ] = {} def clear(self) -> None: self._extra_options = {} @@ -44,7 +46,9 @@ def extra_options(self) -> dict[str, Callable[[str], Any]]: return self._extra_options @property - def warnings(self) -> dict[str, str | Callable[[NeedsInfoType, SphinxLoggerAdapter], bool]]: + def warnings( + self + ) -> dict[str, str | Callable[[NeedsInfoType, SphinxLoggerAdapter], bool]]: """Warning handlers that are added by the user, then called at the end of the build. """ @@ -130,58 +134,117 @@ def __setattr__(self, name: str, value: Any) -> None: metadata={"rebuild": "html", "types": ()}, ) """Custom user need types""" - include_needs: bool = field(default=True, metadata={"rebuild": "html", "types": (bool,)}) - need_name: str = field(default="Need", metadata={"rebuild": "html", "types": (str,)}) - spec_name: str = field(default="Specification", metadata={"rebuild": "html", "types": (str,)}) - id_prefix_needs: str = field(default="", metadata={"rebuild": "html", "types": (str,)}) - id_prefix_specs: str = field(default="", metadata={"rebuild": "html", "types": (str,)}) + include_needs: bool = field( + default=True, metadata={"rebuild": "html", "types": (bool,)} + ) + need_name: str = field( + default="Need", metadata={"rebuild": "html", "types": (str,)} + ) + spec_name: str = field( + default="Specification", metadata={"rebuild": "html", "types": (str,)} + ) + id_prefix_needs: str = field( + default="", metadata={"rebuild": "html", "types": (str,)} + ) + id_prefix_specs: str = field( + default="", metadata={"rebuild": "html", "types": (str,)} + ) id_length: int = field(default=5, metadata={"rebuild": "html", "types": (int,)}) - id_from_title: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - specs_show_needlist: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - id_required: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - id_regex: str = field(default="^[A-Z0-9_]{5,}", metadata={"rebuild": "html", "types": ()}) - show_link_type: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - show_link_title: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - show_link_id: bool = field(default=True, metadata={"rebuild": "html", "types": (bool,)}) + id_from_title: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + specs_show_needlist: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + id_required: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + id_regex: str = field( + default="^[A-Z0-9_]{5,}", metadata={"rebuild": "html", "types": ()} + ) + show_link_type: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + show_link_title: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + show_link_id: bool = field( + default=True, metadata={"rebuild": "html", "types": (bool,)} + ) file: None | str = field(default=None, metadata={"rebuild": "html", "types": ()}) table_columns: str = field( - default="ID;TITLE;STATUS;TYPE;OUTGOING;TAGS", metadata={"rebuild": "html", "types": (str,)} - ) - table_style: str = field(default="DATATABLES", metadata={"rebuild": "html", "types": (str,)}) - role_need_template: str = field(default="{title} ({id})", metadata={"rebuild": "html", "types": (str,)}) - role_need_max_title_length: int = field(default=30, metadata={"rebuild": "html", "types": (int,)}) - extra_options: list[str] = field(default_factory=list, metadata={"rebuild": "html", "types": (list,)}) - title_optional: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - max_title_length: int = field(default=-1, metadata={"rebuild": "html", "types": (int,)}) - title_from_content: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) + default="ID;TITLE;STATUS;TYPE;OUTGOING;TAGS", + metadata={"rebuild": "html", "types": (str,)}, + ) + table_style: str = field( + default="DATATABLES", metadata={"rebuild": "html", "types": (str,)} + ) + role_need_template: str = field( + default="{title} ({id})", metadata={"rebuild": "html", "types": (str,)} + ) + role_need_max_title_length: int = field( + default=30, metadata={"rebuild": "html", "types": (int,)} + ) + extra_options: list[str] = field( + default_factory=list, metadata={"rebuild": "html", "types": (list,)} + ) + title_optional: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + max_title_length: int = field( + default=-1, metadata={"rebuild": "html", "types": (int,)} + ) + title_from_content: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) diagram_template: str = field( default=DEFAULT_DIAGRAM_TEMPLATE, metadata={"rebuild": "html", "types": (str,)}, ) - functions: list[Callable[..., Any]] = field(default_factory=list, metadata={"rebuild": "html", "types": (list,)}) - global_options: dict[str, Any] = field(default_factory=dict, metadata={"rebuild": "html", "types": (dict,)}) - duration_option: str = field(default="duration", metadata={"rebuild": "html", "types": (str,)}) - completion_option: str = field(default="completion", metadata={"rebuild": "html", "types": (str,)}) - needextend_strict: bool = field(default=True, metadata={"rebuild": "html", "types": (bool,)}) - statuses: list[dict[str, str]] = field(default_factory=list, metadata={"rebuild": "html", "types": ()}) + functions: list[Callable[..., Any]] = field( + default_factory=list, metadata={"rebuild": "html", "types": (list,)} + ) + global_options: dict[str, Any] = field( + default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} + ) + duration_option: str = field( + default="duration", metadata={"rebuild": "html", "types": (str,)} + ) + completion_option: str = field( + default="completion", metadata={"rebuild": "html", "types": (str,)} + ) + needextend_strict: bool = field( + default=True, metadata={"rebuild": "html", "types": (bool,)} + ) + statuses: list[dict[str, str]] = field( + default_factory=list, metadata={"rebuild": "html", "types": ()} + ) """If given, only the defined status are allowed. Values needed for each status: * name * description Example: [{"name": "open", "description": "open status"}, {...}, {...}] """ - tags: list[dict[str, str]] = field(default_factory=list, metadata={"rebuild": "html", "types": (list,)}) + tags: list[dict[str, str]] = field( + default_factory=list, metadata={"rebuild": "html", "types": (list,)} + ) """If given, only the defined tags are allowed. Values needed for each tag: * name * description Example: [{"name": "new", "description": "new needs"}, {...}, {...}] """ - css: str = field(default="modern.css", metadata={"rebuild": "html", "types": (str,)}) + css: str = field( + default="modern.css", metadata={"rebuild": "html", "types": (str,)} + ) """Path of css file, which shall be used for need style""" - part_prefix: str = field(default="→\xa0", metadata={"rebuild": "html", "types": (str,)}) + part_prefix: str = field( + default="→\xa0", metadata={"rebuild": "html", "types": (str,)} + ) """Prefix for need_part output in tables""" - extra_links: list[dict[str, Any]] = field(default_factory=list, metadata={"rebuild": "html", "types": ()}) + extra_links: list[dict[str, Any]] = field( + default_factory=list, metadata={"rebuild": "html", "types": ()} + ) """List of additional links, which can be used by setting related option Values needed for each new link: * option (will also be the option name) @@ -190,48 +253,97 @@ def __setattr__(self, name: str, value: Any) -> None: * color (used for needflow. Default: #000000) Example: [{"name": "blocks, "incoming": "is blocked by", "copy_link": True, "color": "#ffcc00"}] """ - report_dead_links: bool = field(default=True, metadata={"rebuild": "html", "types": (bool,)}) + report_dead_links: bool = field( + default=True, metadata={"rebuild": "html", "types": (bool,)} + ) """DEPRECATED: Use ``suppress_warnings = ["needs.link_outgoing"]`` instead.""" - filter_data: dict[str, Any] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) - allow_unsafe_filters: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - flow_show_links: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - flow_link_types: list[str] = field(default_factory=lambda: ["links"], metadata={"rebuild": "html", "types": ()}) + filter_data: dict[str, Any] = field( + default_factory=dict, metadata={"rebuild": "html", "types": ()} + ) + allow_unsafe_filters: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + flow_show_links: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + flow_link_types: list[str] = field( + default_factory=lambda: ["links"], metadata={"rebuild": "html", "types": ()} + ) """Defines the link_types to show in a needflow diagram.""" - warnings: dict[str, Any] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) - warnings_always_warn: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - layouts: dict[str, dict[str, Any]] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) - default_layout: str = field(default="clean", metadata={"rebuild": "html", "types": (str,)}) - default_style: None | str = field(default=None, metadata={"rebuild": "html", "types": ()}) - flow_configs: dict[str, str] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) - template_folder: str = field(default="needs_templates/", metadata={"rebuild": "html", "types": (str,)}) - services: dict[str, dict[str, Any]] = field(default_factory=dict, metadata={"rebuild": "html", "types": ()}) - service_all_data: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - debug_no_external_calls: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - external_needs: list[dict[str, Any]] = field(default_factory=list, metadata={"rebuild": "html", "types": ()}) + warnings: dict[str, Any] = field( + default_factory=dict, metadata={"rebuild": "html", "types": ()} + ) + warnings_always_warn: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + layouts: dict[str, dict[str, Any]] = field( + default_factory=dict, metadata={"rebuild": "html", "types": ()} + ) + default_layout: str = field( + default="clean", metadata={"rebuild": "html", "types": (str,)} + ) + default_style: None | str = field( + default=None, metadata={"rebuild": "html", "types": ()} + ) + flow_configs: dict[str, str] = field( + default_factory=dict, metadata={"rebuild": "html", "types": ()} + ) + template_folder: str = field( + default="needs_templates/", metadata={"rebuild": "html", "types": (str,)} + ) + services: dict[str, dict[str, Any]] = field( + default_factory=dict, metadata={"rebuild": "html", "types": ()} + ) + service_all_data: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + debug_no_external_calls: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + external_needs: list[dict[str, Any]] = field( + default_factory=list, metadata={"rebuild": "html", "types": ()} + ) """Reference external needs, outside of the documentation.""" - builder_filter: str = field(default="is_external==False", metadata={"rebuild": "html", "types": (str,)}) + builder_filter: str = field( + default="is_external==False", metadata={"rebuild": "html", "types": (str,)} + ) table_classes: list[str] = field( - default_factory=lambda: NEEDS_TABLES_CLASSES, metadata={"rebuild": "html", "types": (list,)} + default_factory=lambda: NEEDS_TABLES_CLASSES, + metadata={"rebuild": "html", "types": (list,)}, ) """Additional classes to set for needs and needtable.""" string_links: dict[str, dict[str, Any]] = field( default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} ) - build_json: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) + build_json: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) """If True, the JSON needs file should be built.""" - reproducible_json: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) + reproducible_json: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) """If True, the JSON needs file should be idempotent for multiple builds fo the same documentation.""" - build_needumls: str = field(default="", metadata={"rebuild": "html", "types": (str,)}) - permalink_file: str = field(default="permalink.html", metadata={"rebuild": "html", "types": (str,)}) + build_needumls: str = field( + default="", metadata={"rebuild": "html", "types": (str,)} + ) + permalink_file: str = field( + default="permalink.html", metadata={"rebuild": "html", "types": (str,)} + ) """Permalink related config values. path to permalink.html; absolute path from web-root """ - permalink_data: str = field(default="needs.json", metadata={"rebuild": "html", "types": (str,)}) + permalink_data: str = field( + default="needs.json", metadata={"rebuild": "html", "types": (str,)} + ) """path to needs.json relative to permalink.html""" - report_template: str = field(default="", metadata={"rebuild": "html", "types": (str,)}) + report_template: str = field( + default="", metadata={"rebuild": "html", "types": (str,)} + ) """path to needs_report_template file which is based on the conf.py directory.""" - constraints: dict[str, dict[str, str]] = field(default_factory=dict, metadata={"rebuild": "html", "types": (dict,)}) + constraints: dict[str, dict[str, str]] = field( + default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} + ) """Mapping of constraint name, to check name, to filter string. There are also some special keys for a constraint: @@ -242,21 +354,35 @@ def __setattr__(self, name: str, value: Any) -> None: default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} ) """Mapping of constraint severity to what to do if a constraint is not fulfilled.""" - constraints_failed_color: str = field(default="", metadata={"rebuild": "html", "types": (str,)}) + constraints_failed_color: str = field( + default="", metadata={"rebuild": "html", "types": (str,)} + ) """DEPRECATED: Use constraint_failed_options instead.""" # add variants option - variants: dict[str, str] = field(default_factory=dict, metadata={"rebuild": "html", "types": (dict,)}) - variant_options: list[str] = field(default_factory=list, metadata={"rebuild": "html", "types": (list,)}) + variants: dict[str, str] = field( + default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} + ) + variant_options: list[str] = field( + default_factory=list, metadata={"rebuild": "html", "types": (list,)} + ) # add render context option - render_context: dict[str, Any] = field(default_factory=dict, metadata={"rebuild": "html", "types": (dict,)}) + render_context: dict[str, Any] = field( + default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} + ) """Jinja context for rendering templates""" - debug_measurement: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) + debug_measurement: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) # add config for needs_id_builder - build_json_per_id: bool = field(default=False, metadata={"rebuild": "html", "types": (bool,)}) - build_json_per_id_path: str = field(default="needs_id", metadata={"rebuild": "html", "types": (str,)}) + build_json_per_id: bool = field( + default=False, metadata={"rebuild": "html", "types": (bool,)} + ) + build_json_per_id_path: str = field( + default="needs_id", metadata={"rebuild": "html", "types": (str,)} + ) @classmethod def add_config_values(cls, app: Sphinx) -> None: @@ -267,7 +393,9 @@ def add_config_values(cls, app: Sphinx) -> None: elif item.default is not MISSING: default = item.default else: - raise Exception(f"Config item {item.name} has no default value or factory.") + raise Exception( + f"Config item {item.name} has no default value or factory." + ) app.add_config_value( f"needs_{item.name}", default, diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index abea4ce70..a36e362e3 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -592,7 +592,9 @@ def get_or_create_umls(self) -> dict[str, NeedsUmlType]: return self.env.needs_all_needumls -def merge_data(_app: Sphinx, env: BuildEnvironment, _docnames: list[str], other: BuildEnvironment) -> None: +def merge_data( + _app: Sphinx, env: BuildEnvironment, _docnames: list[str], other: BuildEnvironment +) -> None: """ Performs data merge of parallel executed workers. Used only for parallel builds. @@ -621,14 +623,17 @@ def _merge(name: str, is_complex_dict: bool = False) -> None: for other_key, other_value in other_objects.items(): # other_value is a list from here on! if other_key in objects: - objects[other_key] = list(set(objects[other_key]) | set(other_value)) + objects[other_key] = list( + set(objects[other_key]) | set(other_value) + ) else: objects[other_key] = other_value elif isinstance(other_objects, list) and isinstance(objects, list): objects = list(set(objects) | set(other_objects)) else: raise TypeError( - f'Objects to "merge" must be dict or list, ' f"not {type(other_objects)} and {type(objects)}" + f'Objects to "merge" must be dict or list, ' + f"not {type(other_objects)} and {type(objects)}" ) _merge("needs_all_docs", is_complex_dict=True) diff --git a/sphinx_needs/debug.py b/sphinx_needs/debug.py index 1fca7239b..ee426a076 100644 --- a/sphinx_needs/debug.py +++ b/sphinx_needs/debug.py @@ -18,14 +18,18 @@ from sphinx.application import Sphinx TIME_MEASUREMENTS: dict[str, Any] = {} # Stores the timing results -EXECUTE_TIME_MEASUREMENTS = False # Will be used to de/activate measurements. Set during a Sphinx Event +EXECUTE_TIME_MEASUREMENTS = ( + False # Will be used to de/activate measurements. Set during a Sphinx Event +) START_TIME = 0.0 T = TypeVar("T", bound=Callable[..., Any]) -def measure_time(category: str | None = None, source: str = "internal", name: str | None = None) -> Callable[[T], T]: +def measure_time( + category: str | None = None, source: str = "internal", name: str | None = None +) -> Callable[[T], T]: """ Decorator for measuring the needed execution time of a specific function. @@ -108,9 +112,13 @@ def wrapper(*args: list[object], **kwargs: dict[object, object]) -> Any: runtime_dict["max"] = runtime runtime_dict["max_params"] = { # Store parameters as a shorten string "args": str([str(arg)[:80] for arg in args]), - "kwargs": str({key: str(value)[:80] for key, value in kwargs.items()}), + "kwargs": str( + {key: str(value)[:80] for key, value in kwargs.items()} + ), } - runtime_dict["min_max_spread"] = runtime_dict["max"] / runtime_dict["min"] * 100 + runtime_dict["min_max_spread"] = ( + runtime_dict["max"] / runtime_dict["min"] * 100 + ) runtime_dict["avg"] = runtime_dict["overall"] / runtime_dict["amount"] return result @@ -119,7 +127,12 @@ def wrapper(*args: list[object], **kwargs: dict[object, object]) -> Any: return inner -def measure_time_func(func: T, category: str | None = None, source: str = "internal", name: str | None = None) -> T: +def measure_time_func( + func: T, + category: str | None = None, + source: str = "internal", + name: str | None = None, +) -> T: """Wrapper for measuring the needed execution time of a specific function. Usage as function:: @@ -154,7 +167,9 @@ def store_timing_results_json(outdir: str, build_data: dict[str, Any]) -> None: def store_timing_results_html(outdir: str, build_data: dict[str, Any]) -> None: - jinja_env = Environment(loader=PackageLoader("sphinx_needs"), autoescape=select_autoescape()) + jinja_env = Environment( + loader=PackageLoader("sphinx_needs"), autoescape=select_autoescape() + ) template = jinja_env.get_template("time_measurements.html") out_file = Path(outdir) / "debug_measurement.html" with open(out_file, "w", encoding="utf-8") as f: diff --git a/sphinx_needs/diagrams_common.py b/sphinx_needs/diagrams_common.py index 15de4321c..0eced3edb 100644 --- a/sphinx_needs/diagrams_common.py +++ b/sphinx_needs/diagrams_common.py @@ -114,7 +114,9 @@ def add_config(config: str) -> str: uml = "" if config and len(config) >= 3: # Remove all empty lines - config = "\n".join([line.strip() for line in config.split("\n") if line.strip()]) + config = "\n".join( + [line.strip() for line in config.split("\n") if line.strip()] + ) uml += "\n' Config\n\n" uml += config uml += "\n\n" @@ -125,13 +127,27 @@ def get_filter_para(node_element: NeedsFilteredBaseType) -> nodes.paragraph: """Return paragraph containing the used filter description""" para = nodes.paragraph() filter_text = "Used filter:" - filter_text += " status(%s)" % " OR ".join(node_element["status"]) if len(node_element["status"]) > 0 else "" + filter_text += ( + " status(%s)" % " OR ".join(node_element["status"]) + if len(node_element["status"]) > 0 + else "" + ) if len(node_element["status"]) > 0 and len(node_element["tags"]) > 0: filter_text += " AND " - filter_text += " tags(%s)" % " OR ".join(node_element["tags"]) if len(node_element["tags"]) > 0 else "" - if (len(node_element["status"]) > 0 or len(node_element["tags"]) > 0) and len(node_element["types"]) > 0: + filter_text += ( + " tags(%s)" % " OR ".join(node_element["tags"]) + if len(node_element["tags"]) > 0 + else "" + ) + if (len(node_element["status"]) > 0 or len(node_element["tags"]) > 0) and len( + node_element["types"] + ) > 0: filter_text += " AND " - filter_text += " types(%s)" % " OR ".join(node_element["types"]) if len(node_element["types"]) > 0 else "" + filter_text += ( + " types(%s)" % " OR ".join(node_element["types"]) + if len(node_element["types"]) > 0 + else "" + ) filter_node = nodes.emphasis(filter_text, filter_text) para += filter_node @@ -152,7 +168,9 @@ def get_debug_container(puml_node: nodes.Element) -> nodes.container: return debug_container -def calculate_link(app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: str) -> str: +def calculate_link( + app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: str +) -> str: """ Link calculation All links we can get from docutils functions will be relative. @@ -168,7 +186,9 @@ def calculate_link(app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: str builder = app.builder try: if need_info["is_external"]: - assert need_info["external_url"] is not None, "external_url must be set for external needs" + assert ( + need_info["external_url"] is not None + ), "external_url must be set for external needs" link = need_info["external_url"] # check if need_info["external_url"] is relative path parsed_url = urlparse(need_info["external_url"]) @@ -176,7 +196,12 @@ def calculate_link(app: Sphinx, need_info: NeedsPartsInfoType, _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_id"] + link = ( + "../" + + builder.get_target_uri(need_info["docname"]) + + "#" + + need_info["target_id"] + ) if need_info["is_part"]: link = f"{link}.{need_info['id']}" @@ -188,7 +213,9 @@ def calculate_link(app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: 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"]) + return "\n| {color} | {name} |".format( + color=need_type["color"], name=need_type["title"] + ) rows = map(create_row, need_types) table = "|= Color |= Type |" + "".join(rows) diff --git a/sphinx_needs/directives/list2need.py b/sphinx_needs/directives/list2need.py index bbdd27643..928a73d1a 100644 --- a/sphinx_needs/directives/list2need.py +++ b/sphinx_needs/directives/list2need.py @@ -23,8 +23,12 @@ """ -LINE_REGEX = re.compile(r"(?P[^\S\n]*)\*\s*(?P.*)|[\S\*]*(?P.*)") -ID_REGEX = re.compile(r"(\((?P[^\"'=\n]+)?\))") # Exclude some chars, which are used by option list +LINE_REGEX = re.compile( + r"(?P[^\S\n]*)\*\s*(?P.*)|[\S\*]*(?P.*)" +) +ID_REGEX = re.compile( + r"(\((?P[^\"'=\n]+)?\))" +) # Exclude some chars, which are used by option list OPTION_AREA_REGEX = re.compile(r"\(\((.*)\)\)") OPTIONS_REGEX = re.compile(r"([^=,\s]*)=[\"']([^\"]*)[\"']") @@ -83,7 +87,9 @@ def run(self) -> Sequence[nodes.Node]: for x in range(0, len(types_raw_list)): types[x] = types_raw_list[x] if types[x] not in conf_types: - raise SphinxError(f"Unknown type configured: {types[x]}. Allowed are {', '.join(conf_types)}") + raise SphinxError( + f"Unknown type configured: {types[x]}. Allowed are {', '.join(conf_types)}" + ) down_links_raw = self.options.get("links-down") if down_links_raw is None or down_links_raw == "": @@ -99,7 +105,10 @@ def run(self) -> Sequence[nodes.Node]: for i, down_link_raw in enumerate(down_links_raw_list): down_links_types[i] = down_link_raw if down_link_raw not in link_types: - raise SphinxError(f"Unknown link configured: {down_link_raw}. " f"Allowed are {', '.join(link_types)}") + raise SphinxError( + f"Unknown link configured: {down_link_raw}. " + f"Allowed are {', '.join(link_types)}" + ) list_needs = [] # Storing the data in a sorted list for content_line in content_raw.split("\n"): @@ -112,23 +121,30 @@ def run(self) -> Sequence[nodes.Node]: if text: indent = len(indent) if not indent % 2 == 0: - raise IndentationError("Indentation for list must be always a multiply of 2.") + raise IndentationError( + "Indentation for list must be always a multiply of 2." + ) level = int(indent / 2) if level not in types: raise SphinxWarning( - f"No need type defined for indentation level {level}." f" Defined types {types}" + f"No need type defined for indentation level {level}." + f" Defined types {types}" ) if down_links_types and level > len(down_links_types): - raise SphinxWarning(f"Not enough links-down defined for indentation level {level}.") + raise SphinxWarning( + f"Not enough links-down defined for indentation level {level}." + ) splitted_text = text.split(delimiter) title = splitted_text[0] content = "" with suppress(IndexError): - content = delimiter.join(splitted_text[1:]) # Put the content together again + content = delimiter.join( + splitted_text[1:] + ) # Put the content together again need_id_result = ID_REGEX.search(title) if need_id_result: @@ -158,7 +174,9 @@ def run(self) -> Sequence[nodes.Node]: more_text = more_text.lstrip() if more_text.startswith(":"): more_text = f" {more_text}" - list_needs[-1]["content"] = f"{list_needs[-1]['content']}\n {more_text}" + list_needs[-1][ + "content" + ] = f"{list_needs[-1]['content']}\n {more_text}" # Finally creating the rst code overall_text = [] @@ -179,7 +197,11 @@ def run(self) -> Sequence[nodes.Node]: data = list_need need_links_down = self.get_down_needs(list_needs, index) - if down_links_types and list_need["level"] in down_links_types and need_links_down: + if ( + down_links_types + and list_need["level"] in down_links_types + and need_links_down + ): data["links_down"] = need_links_down data["links_down_type"] = down_links_types[list_need["level"]] data["set_links_down"] = True @@ -193,14 +215,19 @@ def run(self) -> Sequence[nodes.Node]: text_list = indented_text_list overall_text += text_list - self.state_machine.insert_input(overall_text, self.state_machine.document.attributes["source"]) + self.state_machine.insert_input( + overall_text, self.state_machine.document.attributes["source"] + ) return [] def make_hashed_id(self, type_prefix: str, title: str, id_length: int) -> str: hashable_content = title return "{}{}".format( - type_prefix, hashlib.sha1(hashable_content.encode("UTF-8")).hexdigest().upper()[:id_length] + 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]: diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index f4c527791..7863b161f 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -64,7 +64,17 @@ def __init__( state: RSTState, state_machine: RSTStateMachine, ): - super().__init__(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine) + super().__init__( + name, + arguments, + options, + content, + lineno, + content_offset, + block_text, + state, + state_machine, + ) self.needs_config = NeedsSphinxConfig(self.env.config) self.log = get_logger(__name__) self.full_title = self._get_full_title() @@ -109,7 +119,9 @@ def run(self) -> Sequence[nodes.Node]: content = "\n".join(self.content) status = self.options.get("status") if status: - status = status.replace("__", "") # Support for multiline options, which must use __ for empty lines + status = status.replace( + "__", "" + ) # Support for multiline options, which must use __ for empty lines tags = self.options.get("tags", "") style = self.options.get("style") layout = self.options.get("layout", "") @@ -121,7 +133,9 @@ def run(self) -> Sequence[nodes.Node]: need_extra_options = {"duration": duration, "completion": completion} for extra_link in self.needs_config.extra_links: - need_extra_options[extra_link["option"]] = self.options.get(extra_link["option"], "") + need_extra_options[extra_link["option"]] = self.options.get( + extra_link["option"], "" + ) for extra_option in NEEDS_CONFIG.extra_options: need_extra_options[extra_option] = self.options.get(extra_option, "") @@ -175,12 +189,17 @@ def read_in_links(self, name: str) -> list[str]: def make_hashed_id(self, type_prefix: str, id_length: int) -> str: hashable_content = self.full_title or "\n".join(self.content) return "{}{}".format( - type_prefix, hashlib.sha1(hashable_content.encode("UTF-8")).hexdigest().upper()[:id_length] + type_prefix, + hashlib.sha1(hashable_content.encode("UTF-8")) + .hexdigest() + .upper()[:id_length], ) @property def title_from_content(self) -> bool: - return "title_from_content" in self.options or self.needs_config.title_from_content + return ( + "title_from_content" in self.options or self.needs_config.title_from_content + ) @property def docname(self) -> str: @@ -211,8 +230,8 @@ def _get_full_title(self) -> str: if len(self.arguments) > 0: # a title was passed if "title_from_content" in self.options: self.log.warning( - 'need "{}" has :title_from_content: set, ' - "but a title was provided. (see file {}) [needs]".format(self.arguments[0], self.docname), + f'need "{self.arguments[0]}" has :title_from_content: set, ' + f"but a title was provided. (see file {self.docname}) [needs]", type="needs", location=(self.env.docname, self.lineno), ) @@ -223,7 +242,7 @@ def _get_full_title(self) -> str: raise NeedsInvalidException( ":title_from_content: set, but " "no content provided. " - "(Line {} of file {}".format(self.lineno, self.docname) + f"(Line {self.lineno} of file {self.docname}" ) return first_sentence else: @@ -260,7 +279,9 @@ def get_sections_and_signature_and_needs( if isinstance(sibling, desc_signature): # Check the child of the found signature for the text content/node. for desc_child in sibling.children: - if isinstance(desc_child, desc_name) and isinstance(desc_child.children[0], nodes.Text): + if isinstance(desc_child, desc_name) and isinstance( + desc_child.children[0], nodes.Text + ): signature = desc_child.children[0] if signature: break @@ -324,7 +345,9 @@ def analyse_need_locations(app: Sphinx, doctree: nodes.document) -> None: # Fetch values from need # Start from the target node, which is a sibling of the current need node - sections, signature, parent_needs = get_sections_and_signature_and_needs(previous_sibling(need_node)) + sections, signature, parent_needs = get_sections_and_signature_and_needs( + previous_sibling(need_node) + ) # append / set values from need if sections: @@ -410,7 +433,12 @@ 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() @@ -423,7 +451,9 @@ def format_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, fo find_and_replace_node_content(node_need, env, need_data) for index, attribute in enumerate(node_need.attributes["classes"]): - node_need.attributes["classes"][index] = check_and_get_content(attribute, need_data, env) + node_need.attributes["classes"][index] = check_and_get_content( + attribute, need_data, env + ) layout = need_data["layout"] or NeedsSphinxConfig(app.config).default_layout @@ -441,14 +471,15 @@ def check_links(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> N report_dead_links = config.report_dead_links for need in needs.values(): for link_type in extra_links: - need_link_value = ( - [need[link_type["option"]]] if isinstance(need[link_type["option"]], str) else need[link_type["option"]] # type: ignore - ) + _value = need[link_type["option"]] # type: ignore[literal-required] + need_link_value = [_value] if isinstance(_value, str) else _value for need_id_full in need_link_value: need_id_main, need_id_part = split_need_id(need_id_full) if need_id_main not in needs or ( - need_id_main in needs and need_id_part and need_id_part not in needs[need_id_main]["parts"] + need_id_main in needs + and need_id_part + and need_id_part not in needs[need_id_main]["parts"] ): need["has_dead_links"] = True if not link_type.get("allow_dead_links", False): @@ -473,7 +504,9 @@ 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``, @@ -484,7 +517,9 @@ 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) @@ -494,9 +529,14 @@ def create_back_links(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig # Handling of links to need_parts inside a need if need_id_part and need_id_part in needs[need_id_main]["parts"]: - if option_back not in needs[need_id_main]["parts"][need_id_part].keys(): + if ( + option_back + not in needs[need_id_main]["parts"][need_id_part].keys() + ): needs[need_id_main]["parts"][need_id_part][option_back] = [] # type: ignore[literal-required] - needs[need_id_main]["parts"][need_id_part][option_back].append(key) # type: ignore[literal-required] + needs[need_id_main]["parts"][need_id_part][option_back].append( # type: ignore[literal-required] + key + ) def _fix_list_dyn_func(list: list[str]) -> list[str]: diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index 623bef414..598b425a8 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -82,7 +82,11 @@ def run(self) -> Sequence[nodes.Node]: style = self.options.get("style") matplotlib = import_matplotlib() - style = style.strip() if style else (matplotlib.style.use("default") if matplotlib else "default") + style = ( + style.strip() + if style + else (matplotlib.style.use("default") if matplotlib else "default") + ) legend = "legend" in self.options @@ -167,7 +171,12 @@ 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) @@ -221,13 +230,19 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun else: # We can only process content with the same lenght for each line if test_columns_length != len(row_data): - raise Exception(f"{error_id}: each content line must have the same length") + raise Exception( + f"{error_id}: each content line must have the same length" + ) # 3. process the labels (maybe from content) xlabels = current_needbar["xlabels"] - xlabels_in_content = bool(xlabels and len(xlabels) >= 1 and xlabels[0] == "FROM_DATA") + xlabels_in_content = bool( + xlabels and len(xlabels) >= 1 and xlabels[0] == "FROM_DATA" + ) ylabels = current_needbar["ylabels"] - ylabels_in_content = bool(ylabels and len(ylabels) >= 1 and ylabels[0] == "FROM_DATA") + ylabels_in_content = bool( + ylabels and len(ylabels) >= 1 and ylabels[0] == "FROM_DATA" + ) if xlabels_in_content: # get xlabels from content => first row in content @@ -265,14 +280,19 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun # 4. transpose the data if needed if current_needbar["transpose"]: - local_data = [[local_data[j][i] for j in range(len(local_data))] for i in range(len(local_data[0]))] + local_data = [ + [local_data[j][i] for j in range(len(local_data))] + for i in range(len(local_data[0])) + ] tmp = ylabels ylabels = xlabels xlabels = tmp # 5. process content local_data_number = [] - need_list = list(prepare_need_list(needs_data.get_or_create_needs().values())) # adds parts to need_list + need_list = list( + prepare_need_list(needs_data.get_or_create_needs().values()) + ) # adds parts to need_list for line in local_data: line_number = [] @@ -335,7 +355,9 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun colors = colors + matplotlib.rcParams["axes.prop_cycle"].by_key()["color"] multi = math.ceil(len(local_data) / len(colors)) if multi > 1: - print(f"{error_id} warning: color schema is smaller than data, double coloring is occurring") + print( + f"{error_id} warning: color schema is smaller than data, double coloring is occurring" + ) colors = colors * multi colors = colors[: len(local_data)] @@ -368,9 +390,13 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun if current_needbar["show_sum"]: try: - bar_label = axes.bar_label(bar, label_type="center") # show label in the middel of each bar + bar_label = axes.bar_label( + bar, label_type="center" + ) # show label in the middel of each bar bar_labels.append(bar_label) - except AttributeError: # bar_label is not support in older matplotlib versions + except ( + AttributeError + ): # bar_label is not support in older matplotlib versions current_needbar["show_sum"] = None current_needbar["show_top_sum"] = None @@ -381,18 +407,24 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun try: bar_label = axes.bar_label(bar) bar_labels.append(bar_label) - except AttributeError: # bar_label is not support in older matplotlib versions + except ( + AttributeError + ): # bar_label is not support in older matplotlib versions current_needbar["show_sum"] = None current_needbar["show_top_sum"] = None sum_rotation = current_needbar["sum_rotation"] - if sum_rotation and (current_needbar["show_top_sum"] or current_needbar["show_sum"]): + if sum_rotation and ( + current_needbar["show_top_sum"] or current_needbar["show_sum"] + ): sum_rotation = sum_rotation.strip() # Rotate the bar labels if sum_rotation.isdigit(): matplotlib.pyplot.setp(bar_labels, rotation=int(sum_rotation)) - centers = [(i + j) / 2.0 for i, j in zip(index[0], index[len(local_data_number) - 1])] + centers = [ + (i + j) / 2.0 for i, j in zip(index[0], index[len(local_data_number) - 1]) + ] if not current_needbar["horizontal"]: # We want to support even older version of matplotlib, which do not support axes.set_xticks(labels) axes.set_xticks(centers) @@ -408,14 +440,18 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun xlabels_rotation = xlabels_rotation.strip() # Rotate the tick labels if xlabels_rotation.isdigit(): - matplotlib.pyplot.setp(axes.get_xticklabels(), rotation=int(xlabels_rotation)) + matplotlib.pyplot.setp( + axes.get_xticklabels(), rotation=int(xlabels_rotation) + ) ylabels_rotation = current_needbar["ylabels_rotation"] if ylabels_rotation: ylabels_rotation = ylabels_rotation.strip() # Rotate the tick labels if ylabels_rotation.isdigit(): - matplotlib.pyplot.setp(axes.get_yticklabels(), rotation=int(ylabels_rotation)) + matplotlib.pyplot.setp( + axes.get_yticklabels(), rotation=int(ylabels_rotation) + ) if current_needbar["title"]: axes.set_title(current_needbar["title"].strip()) @@ -433,7 +469,9 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun # We need to calculate an unique bar-image file name hash_value = hashlib.sha256(id.encode()).hexdigest()[:5] - image_node = save_matplotlib_figure(app, figure, f"need_bar_{hash_value}", fromdocname) + image_node = save_matplotlib_figure( + app, figure, f"need_bar_{hash_value}", fromdocname + ) # Add lineno to node image_node.line = current_needbar["lineno"] diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 886aaa123..054d97f53 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -44,9 +44,13 @@ def run(self) -> Sequence[nodes.Node]: extend_filter = self.arguments[0] if self.arguments else None if not extend_filter: - raise NeedsInvalidFilter(f"Filter of needextend must be set. See {env.docname}:{self.lineno}") + raise NeedsInvalidFilter( + f"Filter of needextend must be set. See {env.docname}:{self.lineno}" + ) - strict_option = self.options.get("strict", str(NeedsSphinxConfig(self.env.app.config).needextend_strict)) + strict_option = self.options.get( + "strict", str(NeedsSphinxConfig(self.env.app.config).needextend_strict) + ) strict = True if strict_option.upper() == "TRUE": strict = True @@ -69,7 +73,9 @@ 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.""" @@ -81,7 +87,9 @@ def extend_needs_data( if need_filter in all_needs: # a single known ID found_needs = [all_needs[need_filter]] - elif need_filter is not None and re.fullmatch(needs_config.id_regex, need_filter): + elif need_filter is not None and re.fullmatch( + needs_config.id_regex, need_filter + ): # an unknown ID error = f"Provided id {need_filter} for needextend does not exist." if current_needextend["strict"]: @@ -92,7 +100,9 @@ def extend_needs_data( else: # a filter string try: - found_needs = filter_needs(all_needs.values(), needs_config, need_filter) + found_needs = filter_needs( + all_needs.values(), needs_config, need_filter + ) except NeedsInvalidFilter as e: raise NeedsInvalidFilter( f"Filter not valid for needextend on page {current_needextend['docname']}:\n{e}" @@ -108,7 +118,9 @@ def extend_needs_data( if option.startswith("+"): option_name = option[1:] if option_name in link_names: - if value.strip().startswith("[[") and value.strip().endswith("]]"): # dynamic function + if value.strip().startswith("[[") and value.strip().endswith( + "]]" + ): # dynamic function need[option_name].append(value) else: for ref_need in [i.strip() for i in re.split(";|,", value)]: @@ -116,13 +128,18 @@ def extend_needs_data( logger.warning( f"Provided link id {ref_need} for needextend does not exist. [needs]", type="needs", - location=(current_needextend["docname"], current_needextend["lineno"]), + location=( + current_needextend["docname"], + current_needextend["lineno"], + ), ) continue if ref_need not in need[option_name]: need[option_name].append(ref_need) elif option_name in list_values: - if value.strip().startswith("[[") and value.strip().endswith("]]"): # dynamic function + if value.strip().startswith("[[") and value.strip().endswith( + "]]" + ): # dynamic function need[option_name].append(value) else: for item in [i.strip() for i in re.split(";|,", value)]: @@ -145,7 +162,9 @@ def extend_needs_data( else: if option in link_names: need[option] = [] - if value.strip().startswith("[[") and value.strip().endswith("]]"): # dynamic function + if value.strip().startswith("[[") and value.strip().endswith( + "]]" + ): # dynamic function need[option].append(value) else: for ref_need in [i.strip() for i in re.split(";|,", value)]: @@ -153,12 +172,17 @@ def extend_needs_data( logger.warning( f"Provided link id {ref_need} for needextend does not exist. [needs]", type="needs", - location=(current_needextend["docname"], current_needextend["lineno"]), + location=( + current_needextend["docname"], + current_needextend["lineno"], + ), ) continue need[option].append(ref_need) elif option in list_values: - if value.strip().startswith("[[") and value.strip().endswith("]]"): # dynamic function + if value.strip().startswith("[[") and value.strip().endswith( + "]]" + ): # dynamic function need[option].append(value) else: need[option] = [i.strip() for i in re.split(";|,", value)] diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index 18f5cb060..9ceba1a09 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -43,7 +43,9 @@ class NeedextractDirective(FilterBase): def run(self) -> Sequence[nodes.Node]: env = self.env - targetid = "needextract-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needextract")) + targetid = "needextract-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needextract") + ) targetnode = nodes.target("", "", ids=[targetid]) filter_arg = self.arguments[0] if self.arguments else None @@ -67,7 +69,10 @@ 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. @@ -88,14 +93,18 @@ def process_needextract( # check if filter argument and option filter both exist need_filter_arg = current_needextract["filter_arg"] if need_filter_arg and current_needextract["filter"]: - raise NeedsInvalidFilter("Needextract can't have filter arguments and option filter at the same time.") + raise NeedsInvalidFilter( + "Needextract can't have filter arguments and option filter at the same time." + ) elif need_filter_arg: # check if given filter argument is need-id if need_filter_arg in all_needs: need_filter_arg = f'id == "{need_filter_arg}"' elif re.fullmatch(needs_config.id_regex, need_filter_arg): # check if given filter argument is need-id, but not exists - raise NeedsInvalidFilter(f"Provided id {need_filter_arg} for needextract does not exist.") + raise NeedsInvalidFilter( + f"Provided id {need_filter_arg} for needextract does not exist." + ) current_needextract["filter"] = need_filter_arg found_needs = process_filters(app, all_needs.values(), current_needextract) @@ -118,7 +127,9 @@ def process_needextract( content.append(need_extract) if len(content) == 0: - content.append(no_needs_found_paragraph(current_needextract.get("filter_warning"))) + content.append( + no_needs_found_paragraph(current_needextract.get("filter_warning")) + ) if current_needextract["show_filters"]: content.append(used_filter_paragraph(current_needextract)) diff --git a/sphinx_needs/directives/needfilter.py b/sphinx_needs/directives/needfilter.py index ef0cb3d71..0d7cd00c8 100644 --- a/sphinx_needs/directives/needfilter.py +++ b/sphinx_needs/directives/needfilter.py @@ -49,7 +49,9 @@ def layout(argument: str) -> str: def run(self) -> Sequence[nodes.Node]: env = self.env - targetid = "needfilter-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needfilter")) + targetid = "needfilter-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needfilter") + ) targetnode = nodes.target("", "", ids=[targetid]) # Add the need and all needed information @@ -72,7 +74,10 @@ 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. @@ -129,7 +134,14 @@ def process_needfilters( status_colspec = nodes.colspec(colwidth=5) links_colspec = nodes.colspec(colwidth=5) tags_colspec = nodes.colspec(colwidth=5) - tgroup += [id_colspec, title_colspec, type_colspec, status_colspec, links_colspec, tags_colspec] + tgroup += [ + id_colspec, + title_colspec, + type_colspec, + status_colspec, + links_colspec, + tags_colspec, + ] tgroup += nodes.thead( "", nodes.row( @@ -170,7 +182,9 @@ def process_needfilters( else: ref = nodes.reference("", "") ref["refdocname"] = need_info["docname"] - ref["refuri"] = builder.get_relative_uri(fromdocname, need_info["docname"]) + ref["refuri"] = builder.get_relative_uri( + fromdocname, need_info["docname"] + ) ref["refuri"] += "#" + target_id ref.append(title) line_node += ref @@ -178,11 +192,17 @@ def process_needfilters( line_block.append(line_node) elif current_needfilter["layout"] == "table": row = nodes.row() - row += row_col_maker(app, fromdocname, all_needs, need_info, "id", make_ref=True) + row += row_col_maker( + app, fromdocname, all_needs, need_info, "id", make_ref=True + ) row += row_col_maker(app, fromdocname, all_needs, need_info, "title") - row += row_col_maker(app, fromdocname, all_needs, need_info, "type_name") + row += row_col_maker( + app, fromdocname, all_needs, need_info, "type_name" + ) row += row_col_maker(app, fromdocname, all_needs, need_info, "status") - row += row_col_maker(app, fromdocname, all_needs, need_info, "links", ref_lookup=True) + row += row_col_maker( + app, fromdocname, all_needs, need_info, "links", ref_lookup=True + ) row += row_col_maker(app, fromdocname, all_needs, need_info, "tags") tbody += row elif current_needfilter["layout"] == "diagram": @@ -203,9 +223,13 @@ def process_needfilters( link = "" diagram_template = Template(needs_config.diagram_template) - node_text = diagram_template.render(**need_info, **needs_config.render_context) + node_text = diagram_template.render( + **need_info, **needs_config.render_context + ) - puml_node["uml"] += '{style} "{node_text}" as {id} [[{link}]] {color}\n'.format( + puml_node[ + "uml" + ] += '{style} "{node_text}" as {id} [[{link}]] {color}\n'.format( id=need_info["id"], node_text=node_text, link=link, @@ -213,7 +237,9 @@ def process_needfilters( style=need_info["type_style"], ) for link in need_info["links"]: - puml_connections += "{id} --> {link}\n".format(id=need_info["id"], link=link) + puml_connections += "{id} --> {link}\n".format( + id=need_info["id"], link=link + ) if current_needfilter["layout"] == "list": content.append(line_block) @@ -227,11 +253,15 @@ def process_needfilters( puml_node["uml"] += create_legend(needs_config.types) puml_node["uml"] += "@enduml" puml_node["incdir"] = os.path.dirname(current_needfilter["docname"]) - puml_node["filename"] = os.path.split(current_needfilter["docname"])[1] # Needed for plantuml >= 0.9 + puml_node["filename"] = os.path.split(current_needfilter["docname"])[ + 1 + ] # Needed for plantuml >= 0.9 content.append(puml_node) if len(content) == 0: - content.append(no_needs_found_paragraph(current_needfilter.get("filter_warning"))) + content.append( + no_needs_found_paragraph(current_needfilter.get("filter_warning")) + ) if current_needfilter["show_filters"]: para_node = nodes.paragraph() filter_text = "Used filter:" @@ -240,17 +270,25 @@ def process_needfilters( if len(current_needfilter["status"]) > 0 else "" ) - if len(current_needfilter["status"]) > 0 and len(current_needfilter["tags"]) > 0: + if ( + len(current_needfilter["status"]) > 0 + and len(current_needfilter["tags"]) > 0 + ): filter_text += " AND " filter_text += ( - " tags(%s)" % " OR ".join(current_needfilter["tags"]) if len(current_needfilter["tags"]) > 0 else "" + " tags(%s)" % " OR ".join(current_needfilter["tags"]) + if len(current_needfilter["tags"]) > 0 + else "" ) - if (len(current_needfilter["status"]) > 0 or len(current_needfilter["tags"]) > 0) and len( - current_needfilter["types"] - ) > 0: + if ( + len(current_needfilter["status"]) > 0 + or len(current_needfilter["tags"]) > 0 + ) and len(current_needfilter["types"]) > 0: filter_text += " AND " filter_text += ( - " types(%s)" % " OR ".join(current_needfilter["types"]) if len(current_needfilter["types"]) > 0 else "" + " types(%s)" % " OR ".join(current_needfilter["types"]) + if len(current_needfilter["types"]) > 0 + else "" ) filter_node = nodes.emphasis(filter_text, filter_text) diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 31572afb4..5f4df8d92 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -74,7 +74,9 @@ def run(self) -> Sequence[nodes.Node]: targetnode = nodes.target("", "", ids=[targetid]) all_link_types = ",".join(x["option"] for x in needs_config.extra_links) - link_types = split_link_types(self.options.get("link_types", all_link_types), location) + link_types = split_link_types( + self.options.get("link_types", all_link_types), location + ) config_names = self.options.get("config") configs = [] @@ -220,7 +222,12 @@ def walk_curr_need_tree( # check curr need child has children or has parts if curr_child_need["parent_needs_back"] or curr_child_need["parts"]: curr_need_tree += walk_curr_need_tree( - app, fromdocname, current_needflow, all_needs, found_needs, curr_child_need + app, + fromdocname, + current_needflow, + all_needs, + found_needs, + curr_child_need, ) # add newline for next element curr_need_tree += "\n" @@ -261,7 +268,9 @@ def cal_needs_node( top_needs = get_root_needs(found_needs) curr_need_tree = "" for top_need in top_needs: - top_need_node = get_need_node_rep_for_plantuml(app, fromdocname, current_needflow, all_needs, top_need) + top_need_node = get_need_node_rep_for_plantuml( + app, fromdocname, current_needflow, all_needs, top_need + ) curr_need_tree += ( top_need_node + walk_curr_need_tree( @@ -278,7 +287,12 @@ 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 @@ -305,7 +319,9 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou if lt not in link_type_names: logger.warning( "Unknown link type {link_type} in needflow {flow}. Allowed values: {link_types} [needs]".format( - link_type=lt, flow=current_needflow["target_id"], link_types=",".join(link_type_names) + link_type=lt, + flow=current_needflow["target_id"], + link_types=",".join(link_type_names), ), type="needs", ) @@ -342,7 +358,9 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou config = current_needflow["config"] if config and len(config) >= 3: # Remove all empty lines - config = "\n".join([line.strip() for line in config.split("\n") if line.strip()]) + config = "\n".join( + [line.strip() for line in config.split("\n") if line.strip()] + ) puml_node["uml"] += "\n' Config\n\n" puml_node["uml"] += config puml_node["uml"] += "\n\n" @@ -353,9 +371,13 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou for link_type in link_types: # Skip link-type handling, if it is not part of a specified list of allowed link_types or # if not part of the overall configuration of needs_flow_link_types - if (current_needflow["link_types"] and link_type["option"].upper() not in option_link_types) or ( + if ( + current_needflow["link_types"] + and link_type["option"].upper() not in option_link_types + ) or ( not current_needflow["link_types"] - and link_type["option"].upper() not in allowed_link_types_options + and link_type["option"].upper() + not in allowed_link_types_options ): continue @@ -367,30 +389,42 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou # If source or target of link is a need_part, a specific style is needed if "." in link or "." in need_info["id_complete"]: final_link = link - if current_needflow["show_link_names"] or needs_config.flow_show_links: + if ( + current_needflow["show_link_names"] + or needs_config.flow_show_links + ): desc = link_type["outgoing"] + "\\n" comment = f": {desc}" else: comment = "" if "style_part" in link_type and link_type["style_part"]: - link_style = "[{style}]".format(style=link_type["style_part"]) + link_style = "[{style}]".format( + style=link_type["style_part"] + ) else: link_style = "[dotted]" else: final_link = link - if current_needflow["show_link_names"] or needs_config.flow_show_links: + if ( + current_needflow["show_link_names"] + or needs_config.flow_show_links + ): comment = ": {desc}".format(desc=link_type["outgoing"]) else: comment = "" if "style" in link_type and link_type["style"]: - link_style = "[{style}]".format(style=link_type["style"]) + link_style = "[{style}]".format( + style=link_type["style"] + ) else: link_style = "" # Do not create an links, if the link target is not part of the search result. - if final_link not in [x["id"] for x in found_needs if x["is_need"]] and final_link not in [ + if final_link not in [ + x["id"] for x in found_needs if x["is_need"] + ] and final_link not in [ x["id_complete"] for x in found_needs if x["is_part"] ]: continue @@ -415,7 +449,9 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou ) # calculate needs node representation for plantuml - puml_node["uml"] += cal_needs_node(app, fromdocname, current_needflow, all_needs.values(), found_needs) + puml_node["uml"] += cal_needs_node( + app, fromdocname, current_needflow, all_needs.values(), found_needs + ) puml_node["uml"] += "\n' Connection definition \n\n" puml_node["uml"] += puml_connections @@ -426,7 +462,9 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou puml_node["uml"] += "\n@enduml" puml_node["incdir"] = os.path.dirname(current_needflow["docname"]) - puml_node["filename"] = os.path.split(current_needflow["docname"])[1] # Needed for plantuml >= 0.9 + puml_node["filename"] = os.path.split(current_needflow["docname"])[ + 1 + ] # Needed for plantuml >= 0.9 scale = int(current_needflow["scale"]) # if scale != 100: @@ -452,8 +490,14 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou gen_flow_link = generate_name(app, puml_node.children[0], file_ext) current_file_parts = fromdocname.split("/") subfolder_amount = len(current_file_parts) - 1 - img_locaton = "../" * subfolder_amount + "_images/" + gen_flow_link[0].split("/")[-1] - flow_ref = nodes.reference("t", current_needflow["caption"], refuri=img_locaton) + img_locaton = ( + "../" * subfolder_amount + + "_images/" + + gen_flow_link[0].split("/")[-1] + ) + flow_ref = nodes.reference( + "t", current_needflow["caption"], refuri=img_locaton + ) puml_node += nodes.caption("", "", flow_ref) # Add lineno to node @@ -461,25 +505,36 @@ def process_needflow(app: Sphinx, doctree: nodes.document, fromdocname: str, fou content.append(puml_node) else: # no needs found - content.append(no_needs_found_paragraph(current_needflow.get("filter_warning"))) + content.append( + no_needs_found_paragraph(current_needflow.get("filter_warning")) + ) if current_needflow["show_filters"]: para = nodes.paragraph() filter_text = "Used filter:" filter_text += ( - " status(%s)" % " OR ".join(current_needflow["status"]) if len(current_needflow["status"]) > 0 else "" + " status(%s)" % " OR ".join(current_needflow["status"]) + if len(current_needflow["status"]) > 0 + else "" ) - if len(current_needflow["status"]) > 0 and len(current_needflow["tags"]) > 0: + if ( + len(current_needflow["status"]) > 0 + and len(current_needflow["tags"]) > 0 + ): filter_text += " AND " filter_text += ( - " tags(%s)" % " OR ".join(current_needflow["tags"]) if len(current_needflow["tags"]) > 0 else "" + " tags(%s)" % " OR ".join(current_needflow["tags"]) + if len(current_needflow["tags"]) > 0 + else "" ) - if (len(current_needflow["status"]) > 0 or len(current_needflow["tags"]) > 0) and len( - current_needflow["types"] - ) > 0: + if ( + len(current_needflow["status"]) > 0 or len(current_needflow["tags"]) > 0 + ) and len(current_needflow["types"]) > 0: filter_text += " AND " filter_text += ( - " types(%s)" % " OR ".join(current_needflow["types"]) if len(current_needflow["types"]) > 0 else "" + " types(%s)" % " OR ".join(current_needflow["types"]) + if len(current_needflow["types"]) > 0 + else "" ) filter_node = nodes.emphasis(filter_text, filter_text) diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index d0f4b9c11..5f8e8f3f1 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -88,15 +88,21 @@ def run(self) -> Sequence[nodes.Node]: timeline_options = ["daily", "weekly", "monthly"] if timeline and timeline not in timeline_options: raise NeedGanttException( - "Given scale value {} is invalid. Please use: " "{}".format(timeline, ",".join(timeline_options)) + "Given scale value {} is invalid. Please use: " "{}".format( + timeline, ",".join(timeline_options) + ) ) else: timeline = None # Timeline/scale not set later no_color = "no_color" in self.options - duration_option = self.options.get("duration_option", needs_config.duration_option) - completion_option = self.options.get("completion_option", needs_config.completion_option) + duration_option = self.options.get( + "duration_option", needs_config.duration_option + ) + completion_option = self.options.get( + "completion_option", needs_config.completion_option + ) # Add the needgantt and all needed information SphinxNeedsData(env).get_or_create_gantts()[targetid] = { @@ -121,7 +127,9 @@ def run(self) -> Sequence[nodes.Node]: return [targetnode] + [Needgantt("")] 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))] + 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] @@ -131,14 +139,20 @@ def get_link_type_option(self, name: str, default: str = "") -> list[str]: continue if link_type not in conf_link_types_name: raise SphinxNeedsLinkTypeException( - link_type + "does not exist in configuration option needs_extra_links" + link_type + + "does not exist in configuration option needs_extra_links" ) final_link_types.append(link_type) 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) @@ -196,7 +210,9 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo except Exception: raise NeedGanttException( 'start_date "{}"for needgantt is invalid. ' - 'File: {}:current_needgantt["lineno"]'.format(start_date_string, current_needgantt["docname"]) + 'File: {}:current_needgantt["lineno"]'.format( + start_date_string, current_needgantt["docname"] + ) ) month = MONTH_NAMES[int(start_date.strftime("%m"))] @@ -212,12 +228,16 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo complete = None if current_needgantt["milestone_filter"]: - is_milestone = filter_single_need(need, needs_config, current_needgantt["milestone_filter"]) + is_milestone = filter_single_need( + need, needs_config, current_needgantt["milestone_filter"] + ) else: is_milestone = False if current_needgantt["milestone_filter"] and is_milestone: - gantt_element = "[{}] as [{}] lasts 0 days\n".format(need["title"], need["id"]) + 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] # type: ignore[literal-required] @@ -230,18 +250,26 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo type="needs", ) duration = 1 - gantt_element = "[{}] as [{}] lasts {} days\n".format(need["title"], need["id"], duration) + gantt_element = "[{}] as [{}] lasts {} days\n".format( + need["title"], need["id"], duration + ) if complete: complete = complete.replace("%", "") - el_completion_string += "[{}] is {}% completed\n".format(need["title"], complete) + el_completion_string += "[{}] is {}% completed\n".format( + need["title"], complete + ) - el_color_string += "[{}] is colored in {}\n".format(need["title"], need["type_color"]) + el_color_string += "[{}] is colored in {}\n".format( + need["title"], need["type_color"] + ) puml_node["uml"] += gantt_element puml_node["uml"] += "\n' Element links definition \n\n" - puml_node["uml"] += "\n' Deactivated, as currently supported by plantuml beta only" + puml_node[ + "uml" + ] += "\n' Deactivated, as currently supported by plantuml beta only" puml_node["uml"] += "\n' Element completion definition \n\n" puml_node["uml"] += el_completion_string + "\n" @@ -257,10 +285,16 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo puml_node["uml"] += "\n' Constraints definition \n\n" for need in found_needs: if current_needgantt["milestone_filter"]: - is_milestone = filter_single_need(need, needs_config, current_needgantt["milestone_filter"]) + is_milestone = filter_single_need( + need, needs_config, current_needgantt["milestone_filter"] + ) else: is_milestone = False - for con_type in ("starts_with_links", "starts_after_links", "ends_with_links"): + for con_type in ( + "starts_with_links", + "starts_after_links", + "ends_with_links", + ): if is_milestone: keyword = "happens" elif con_type in ["starts_with_links", "starts_after_links"]: @@ -288,7 +322,9 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo puml_node["uml"] += "\n@endgantt" puml_node["incdir"] = os.path.dirname(current_needgantt["docname"]) - puml_node["filename"] = os.path.split(current_needgantt["docname"])[1] # Needed for plantuml >= 0.9 + puml_node["filename"] = os.path.split(current_needgantt["docname"])[ + 1 + ] # Needed for plantuml >= 0.9 scale = int(current_needgantt["scale"]) # if scale != 100: @@ -311,14 +347,20 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo gen_flow_link = generate_name(app, puml_node.children[0], file_ext) current_file_parts = fromdocname.split("/") subfolder_amount = len(current_file_parts) - 1 - img_location = "../" * subfolder_amount + "_images/" + gen_flow_link[0].split("/")[-1] - flow_ref = nodes.reference("t", current_needgantt["caption"], refuri=img_location) + img_location = ( + "../" * subfolder_amount + "_images/" + gen_flow_link[0].split("/")[-1] + ) + flow_ref = nodes.reference( + "t", current_needgantt["caption"], refuri=img_location + ) puml_node += nodes.caption("", "", flow_ref) content.append(puml_node) if len(found_needs) == 0: - content = [no_needs_found_paragraph(current_needgantt.get("filter_warning"))] + content = [ + no_needs_found_paragraph(current_needgantt.get("filter_warning")) + ] if current_needgantt["show_filters"]: content.append(get_filter_para(current_needgantt)) diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index b66148079..cf9d26cc3 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -73,19 +73,25 @@ def run(self) -> Sequence[nodes.Node]: response.json() ) # The downloaded file MUST be json. Everything else we do not handle! except Exception as e: - raise NeedimportException(f"Getting {need_import_path} didn't work. Reason: {e}.") + raise NeedimportException( + f"Getting {need_import_path} didn't work. Reason: {e}." + ) else: logger.info(f"Importing needs from {need_import_path}") if not os.path.isabs(need_import_path): # Relative path should start from current rst file directory curr_dir = os.path.dirname(self.docname) - new_need_import_path = os.path.join(self.env.app.srcdir, curr_dir, need_import_path) + new_need_import_path = os.path.join( + self.env.app.srcdir, curr_dir, need_import_path + ) correct_need_import_path = new_need_import_path if not os.path.exists(new_need_import_path): # Check the old way that calculates relative path starting from conf.py directory - old_need_import_path = os.path.join(self.env.app.srcdir, need_import_path) + old_need_import_path = os.path.join( + self.env.app.srcdir, need_import_path + ) if os.path.exists(old_need_import_path): correct_need_import_path = old_need_import_path logger.warning( @@ -97,14 +103,20 @@ def run(self) -> Sequence[nodes.Node]: ) else: # Absolute path starts with /, based on the source directory. The / need to be striped - correct_need_import_path = os.path.join(self.env.app.srcdir, need_import_path[1:]) + correct_need_import_path = os.path.join( + self.env.app.srcdir, need_import_path[1:] + ) if not os.path.exists(correct_need_import_path): - raise ReferenceError(f"Could not load needs import file {correct_need_import_path}") + raise ReferenceError( + f"Could not load needs import file {correct_need_import_path}" + ) errors = check_needs_file(correct_need_import_path) if errors.schema: - logger.info(f"Schema validation errors detected in file {correct_need_import_path}:") + logger.info( + f"Schema validation errors detected in file {correct_need_import_path}:" + ) for error in errors.schema: logger.info(f' {error.message} -> {".".join(error.path)}') @@ -121,13 +133,19 @@ def run(self) -> Sequence[nodes.Node]: if not isinstance(version, str): raise KeyError except KeyError: - raise CorruptedNeedsFile(f"Key 'current_version' missing or corrupted in {correct_need_import_path}") + raise CorruptedNeedsFile( + f"Key 'current_version' missing or corrupted in {correct_need_import_path}" + ) if version not in needs_import_list["versions"].keys(): - raise VersionNotFound(f"Version {version} not found in needs import file {correct_need_import_path}") + raise VersionNotFound( + f"Version {version} not found in needs import file {correct_need_import_path}" + ) 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 = {} @@ -161,13 +179,20 @@ def run(self) -> Sequence[nodes.Node]: for id in needs_list: # Manipulate links in all link types for extra_link in extra_links: - if extra_link["option"] in need and id in need[extra_link["option"]]: # type: ignore[literal-required] + if ( + extra_link["option"] in need + and id in need[extra_link["option"]] # type: ignore[literal-required] + ): for n, link in enumerate(need[extra_link["option"]]): # type: ignore[literal-required] if id == link: - need[extra_link["option"]][n] = "".join([id_prefix, id]) # type: ignore[literal-required] + need[extra_link["option"]][n] = "".join( # type: ignore[literal-required] + [id_prefix, id] + ) # Manipulate descriptions # ToDo: Use regex for better matches. - need["description"] = need["description"].replace(id, "".join([id_prefix, id])) # type: ignore[typeddict-item] + need["description"] = need["description"].replace( # type: ignore[typeddict-item] + id, "".join([id_prefix, id]) + ) # tags update for need in needs_list.values(): @@ -194,8 +219,12 @@ def run(self) -> Sequence[nodes.Node]: for need in needs_list.values(): # Set some values based on given option or value from imported need. need["template"] = self.options.get("template", need.get("template")) - need["pre_template"] = self.options.get("pre_template", need.get("pre_template")) - need["post_template"] = self.options.get("post_template", need.get("post_template")) + need["pre_template"] = self.options.get( + "pre_template", need.get("pre_template") + ) + need["post_template"] = self.options.get( + "post_template", need.get("post_template") + ) need["layout"] = self.options.get("layout", need.get("layout")) need["style"] = self.options.get("style", need.get("style")) diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index 91a2950f2..d6b1a528d 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -41,7 +41,9 @@ class NeedlistDirective(FilterBase): def run(self) -> Sequence[nodes.Node]: env = self.env - targetid = "needlist-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needlist")) + targetid = "needlist-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needlist") + ) targetnode = nodes.target("", "", ids=[targetid]) # Add the need and all needed information @@ -60,7 +62,12 @@ 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. @@ -81,7 +88,7 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou all_needs = list(SphinxNeedsData(env).get_or_create_needs().values()) found_needs = process_filters(app, all_needs, current_needfilter) - if 0 < len(found_needs): + if len(found_needs) > 0: line_block = nodes.line_block() # Add lineno to node @@ -102,10 +109,14 @@ 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" + 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) + ref["refuri"] = check_and_calc_base_url_rel_path( + need_info["external_url"], fromdocname + ) ref["classes"].append(need_info["external_css"]) ref.append(title) @@ -114,7 +125,9 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou target_id = need_info["target_id"] ref = nodes.reference("", "") ref["refdocname"] = need_info["docname"] - ref["refuri"] = builder.get_relative_uri(fromdocname, need_info["docname"]) + ref["refuri"] = builder.get_relative_uri( + fromdocname, need_info["docname"] + ) ref["refuri"] += "#" + target_id ref.append(title) para += ref @@ -122,7 +135,9 @@ def process_needlist(app: Sphinx, doctree: nodes.document, fromdocname: str, fou content.append(line_block) if len(content) == 0: - content.append(no_needs_found_paragraph(current_needfilter.get("filter_warning"))) + content.append( + no_needs_found_paragraph(current_needfilter.get("filter_warning")) + ) if current_needfilter["show_filters"]: content.append(used_filter_paragraph(current_needfilter)) diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index f055dffe3..700c25247 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -106,7 +106,12 @@ 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) @@ -149,7 +154,9 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun content = current_needpie["content"] sizes = [] - need_list = list(prepare_need_list(needs_data.get_or_create_needs().values())) # adds parts to need_list + need_list = list( + prepare_need_list(needs_data.get_or_create_needs().values()) + ) # adds parts to need_list if content and not current_needpie["filter_func"]: for line in content: if line.isdigit(): @@ -160,7 +167,9 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun elif current_needpie["filter_func"] and not content: try: # check and get filter_func - filter_func, filter_args = check_and_get_external_filter_func(current_needpie.get("filter_func")) + filter_func, filter_args = check_and_get_external_filter_func( + current_needpie.get("filter_func") + ) # execute filter_func code # Provides only a copy of needs to avoid data manipulations. context = { @@ -192,7 +201,9 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun except Exception as e: raise e elif current_needpie["filter_func"] and content: - logger.error("filter_func and content can't be used at the same time for needpie.") + logger.error( + "filter_func and content can't be used at the same time for needpie." + ) else: logger.error("Both filter_func and content are not used for needpie.") @@ -215,7 +226,9 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun shadow = current_needpie["shadow"] text_color = current_needpie["text_color"] - fig, axes = matplotlib.pyplot.subplots(figsize=(8, 4), subplot_kw={"aspect": "equal"}) + fig, axes = matplotlib.pyplot.subplots( + figsize=(8, 4), subplot_kw={"aspect": "equal"} + ) pie_kwargs = { "labels": labels, @@ -229,7 +242,9 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun if text_color: pie_kwargs["textprops"] = {"color": text_color} - wedges, _texts, autotexts = axes.pie(sizes, normalize=sum(float(s) for s in sizes) >= 1, **pie_kwargs) + wedges, _texts, autotexts = axes.pie( + sizes, normalize=sum(float(s) for s in sizes) >= 1, **pie_kwargs + ) ratio = 20 # we will remove all labels with size smaller 5% legend_enforced = False @@ -248,12 +263,12 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun for i in range(len(sizes)): if sum(sizes) > 0: labels[i] = "{label} {percent:.1f}% ({size:.0f})".format( - label=labels[i], percent=100 * sizes[i] / sum(sizes), size=sizes[i] + label=labels[i], + percent=100 * sizes[i] / sum(sizes), + size=sizes[i], ) else: - labels[i] = "{label} {percent:.1f}% ({size:.0f})".format( - label=labels[i], percent=0.0, size=sizes[i] - ) + labels[i] = f"{labels[i]} {0.0:.1f}% ({sizes[i]:.0f})" if text_color: for autotext in autotexts: @@ -262,7 +277,13 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun # Legend preparation if current_needpie["legend"]: - axes.legend(wedges, labels, title="legend", loc="center left", bbox_to_anchor=(0.8, 0, 0.5, 1)) + axes.legend( + wedges, + labels, + title="legend", + loc="center left", + bbox_to_anchor=(0.8, 0, 0.5, 1), + ) matplotlib.pyplot.setp(autotexts, size=8, weight="bold") @@ -273,13 +294,17 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun # We need to calculate an unique pie-image file name hash_value = hashlib.sha256(id.encode()).hexdigest()[:5] - image_node = save_matplotlib_figure(app, fig, f"need_pie_{hash_value}", fromdocname) + image_node = save_matplotlib_figure( + app, fig, f"need_pie_{hash_value}", fromdocname + ) # Add lineno to node image_node.line = current_needpie["lineno"] if len(sizes) == 0 or all(s == 0 for s in sizes): - node.replace_self(no_needs_found_paragraph(current_needpie.get("filter_warning"))) + node.replace_self( + no_needs_found_paragraph(current_needpie.get("filter_warning")) + ) else: node.replace_self(image_node) diff --git a/sphinx_needs/directives/needreport.py b/sphinx_needs/directives/needreport.py index 97f4b5c08..516a70abe 100644 --- a/sphinx_needs/directives/needreport.py +++ b/sphinx_needs/directives/needreport.py @@ -49,12 +49,18 @@ def run(self) -> Sequence[nodes.raw]: report_info.update(**needs_config.render_context) if "template" in self.options: - need_report_template_path = Path(self.env.relfn2path(self.options["template"], self.env.docname)[1]) + need_report_template_path = Path( + self.env.relfn2path(self.options["template"], self.env.docname)[1] + ) elif needs_config.report_template: # Absolute path starts with /, based on the conf.py directory. The / need to be striped - need_report_template_path = Path(str(env.app.srcdir)) / needs_config.report_template.lstrip("/") + need_report_template_path = Path( + str(env.app.srcdir) + ) / needs_config.report_template.lstrip("/") else: - need_report_template_path = Path(__file__).parent / "needreport_template.rst" + need_report_template_path = ( + Path(__file__).parent / "needreport_template.rst" + ) if not need_report_template_path.is_file(): LOGGER.warning( @@ -65,11 +71,15 @@ def run(self) -> Sequence[nodes.raw]: ) return [] - needs_report_template_file_content = need_report_template_path.read_text(encoding="utf8") + needs_report_template_file_content = need_report_template_path.read_text( + encoding="utf8" + ) template = Template(needs_report_template_file_content, autoescape=True) text = template.render(**report_info) - self.state_machine.insert_input(text.split("\n"), self.state_machine.document.attributes["source"]) + self.state_machine.insert_input( + text.split("\n"), self.state_machine.document.attributes["source"] + ) report_node = nodes.raw() diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index 42b1e1e25..bba1269dc 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -57,7 +57,8 @@ def run(self) -> Sequence[nodes.Node]: start = self.options.get("start") if start is None or len(start.strip()) == 0: raise NeedSequenceException( - "No valid start option given for needsequence. " "See file {}:{}".format(env.docname, self.lineno) + "No valid start option given for needsequence. " + f"See file {env.docname}:{self.lineno}" ) # Add the needsequence and all needed information @@ -76,7 +77,10 @@ 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 @@ -98,13 +102,17 @@ def process_needsequence( id = node.attributes["ids"][0] current_needsequence = needs_data.get_or_create_sequences()[id] - option_link_types = [link.upper() for link in current_needsequence["link_types"]] + option_link_types = [ + link.upper() for link in current_needsequence["link_types"] + ] for lt in option_link_types: if lt not in link_type_names: logger.warning( "Unknown link type {link_type} in needsequence {flow}. Allowed values:" " {link_types} [needs]".format( - link_type=lt, flow=current_needsequence["target_id"], link_types=",".join(link_type_names) + link_type=lt, + flow=current_needsequence["target_id"], + link_types=",".join(link_type_names), ), type="needs", ) @@ -131,7 +139,9 @@ def process_needsequence( config = current_needsequence["config"] puml_node["uml"] += add_config(config) - start_needs_id = [x.strip() for x in re.split(";|,", current_needsequence["start"])] + start_needs_id = [ + x.strip() for x in re.split(";|,", current_needsequence["start"]) + ] if len(start_needs_id) == 0: # TODO this should be a warning (and not tested) raise NeedSequenceException( @@ -150,9 +160,11 @@ def process_needsequence( need = all_needs_dict[need_id.strip()] except KeyError: raise NeedSequenceException( - "Given {} in needsequence unknown." - " File {}" - ":{}".format(need_id, current_needsequence["docname"], current_needsequence["lineno"]) + "Given {} in needsequence unknown." " File {}" ":{}".format( + need_id, + current_needsequence["docname"], + current_needsequence["lineno"], + ) ) # Add children of participants @@ -177,7 +189,9 @@ def process_needsequence( puml_node["uml"] += "\n@enduml" puml_node["incdir"] = os.path.dirname(current_needsequence["docname"]) - puml_node["filename"] = os.path.split(current_needsequence["docname"])[1] # Needed for plantuml >= 0.9 + puml_node["filename"] = os.path.split(current_needsequence["docname"])[ + 1 + ] # Needed for plantuml >= 0.9 scale = int(current_needsequence["scale"]) # if scale != 100: @@ -203,8 +217,12 @@ def process_needsequence( gen_flow_link = generate_name(app, puml_node.children[0], file_ext) current_file_parts = fromdocname.split("/") subfolder_amount = len(current_file_parts) - 1 - img_locaton = "../" * subfolder_amount + "_images/" + gen_flow_link[0].split("/")[-1] - flow_ref = nodes.reference("t", current_needsequence["caption"], refuri=img_locaton) + img_locaton = ( + "../" * subfolder_amount + "_images/" + gen_flow_link[0].split("/")[-1] + ) + flow_ref = nodes.reference( + "t", current_needsequence["caption"], refuri=img_locaton + ) puml_node += nodes.caption("", "", flow_ref) # Add lineno to node @@ -212,8 +230,12 @@ def process_needsequence( content.append(puml_node) - if len(c_string) == 0 and p_string.count("participant") == 1: # no connections and just one (start) participant - content = [(no_needs_found_paragraph(current_needsequence.get("filter_warning")))] + if ( + len(c_string) == 0 and p_string.count("participant") == 1 + ): # no connections and just one (start) participant + content = [ + (no_needs_found_paragraph(current_needsequence.get("filter_warning"))) + ] if current_needsequence["show_filters"]: content.append(get_filter_para(current_needsequence)) @@ -241,7 +263,11 @@ def get_message_needs( p_string = "" c_string = "" for msg_need in msg_needs: - messages[msg_need["id"]] = {"id": msg_need["id"], "title": msg_need["title"], "receivers": {}} + messages[msg_need["id"]] = { + "id": msg_need["id"], + "title": msg_need["title"], + "receivers": {}, + } if sender["id"] not in tracked_receivers: p_string += 'participant "{}" as {}\n'.format(sender["title"], sender["id"]) tracked_receivers.append(sender["id"]) @@ -252,17 +278,31 @@ def get_message_needs( from sphinx_needs.filter_common import filter_single_need if not filter_single_need( - all_needs_dict[rec_id], NeedsSphinxConfig(app.config), filter, needs=all_needs_dict.values() + all_needs_dict[rec_id], + NeedsSphinxConfig(app.config), + filter, + needs=all_needs_dict.values(), ): continue - rec_data = {"id": rec_id, "title": all_needs_dict[rec_id]["title"], "messages": []} + rec_data = { + "id": rec_id, + "title": all_needs_dict[rec_id]["title"], + "messages": [], + } - c_string += "{} -> {}: {}\n".format(sender["id"], rec_data["id"], msg_need["title"]) + c_string += "{} -> {}: {}\n".format( + sender["id"], rec_data["id"], msg_need["title"] + ) if rec_id not in tracked_receivers: rec_messages, p_string_new, c_string_new = get_message_needs( - app, all_needs_dict[rec_id], link_types, all_needs_dict, tracked_receivers, filter=filter + app, + all_needs_dict[rec_id], + link_types, + all_needs_dict, + tracked_receivers, + filter=filter, ) p_string += p_string_new c_string += c_string_new diff --git a/sphinx_needs/directives/needservice.py b/sphinx_needs/directives/needservice.py index bf234f7fe..73f920b7d 100644 --- a/sphinx_needs/directives/needservice.py +++ b/sphinx_needs/directives/needservice.py @@ -48,7 +48,17 @@ def __init__( state: RSTState, state_machine: RSTStateMachine, ): - super().__init__(name, arguments, options, content, lineno, content_offset, block_text, state, state_machine) + super().__init__( + name, + arguments, + options, + content, + lineno, + content_offset, + block_text, + state, + state_machine, + ) self.log = get_logger(__name__) def run(self) -> Sequence[nodes.Node]: @@ -94,8 +104,12 @@ def run(self) -> Sequence[nodes.Node]: missing_options = {} for element in datum.keys(): defined_options = list(self.__class__.option_spec.keys()) - defined_options.append("content") # Add content, so that it gets not detected as missing - if element not in defined_options and element not in getattr(app.config, "needs_extra_links", []): + defined_options.append( + "content" + ) # Add content, so that it gets not detected as missing + if element not in defined_options and element not in getattr( + app.config, "needs_extra_links", [] + ): missing_options[element] = datum[element] # Finally delete not found options @@ -112,13 +126,25 @@ def run(self) -> Sequence[nodes.Node]: datum.update(options) # ToDo: Tags and Status are not set (but exist in data) - section += add_need(self.env.app, self.state, docname, self.lineno, need_type, need_title, **datum) + section += add_need( + self.env.app, + self.state, + docname, + self.lineno, + need_type, + need_title, + **datum, + ) else: try: service_debug_data = service.debug(self.options) except NotImplementedError: - service_debug_data = {"error": f'Service {service_name} does not support "debug" output.'} - viewer_node = get_data_viewer_node(title="Debug data", data=service_debug_data) + service_debug_data = { + "error": f'Service {service_name} does not support "debug" output.' + } + viewer_node = get_data_viewer_node( + title="Debug data", data=service_debug_data + ) section.append(viewer_node) add_doc(self.env, self.env.docname) diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index c05dfb61b..e6c492d79 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -52,7 +52,9 @@ class NeedtableDirective(FilterBase): def run(self) -> Sequence[nodes.Node]: env = self.env - targetid = "needtable-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needtable")) + targetid = "needtable-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needtable") + ) targetnode = nodes.target("", "", ids=[targetid]) columns_str = str(self.options.get("columns", "")) @@ -68,7 +70,9 @@ def run(self) -> Sequence[nodes.Node]: colwidths = str(self.options.get("colwidths", "")) colwidths_list = [] if colwidths: - colwidths_list = [int(width.strip()) for width in re.split(";|,", colwidths)] + colwidths_list = [ + int(width.strip()) for width in re.split(";|,", colwidths) + ] if len(columns) != len(colwidths_list): raise NeedsInvalidException( f"Amount of elements in colwidths and columns do not match: " @@ -115,7 +119,10 @@ 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. @@ -149,7 +156,9 @@ def process_needtables( id = node.attributes["ids"][0] current_needtable = needs_data.get_or_create_tables()[id] - if current_needtable["style"] == "" or current_needtable["style"].upper() not in ["TABLE", "DATATABLES"]: + if current_needtable["style"] == "" or current_needtable[ + "style" + ].upper() not in ["TABLE", "DATATABLES"]: if needs_config.table_style == "": style = "DATATABLES" else: @@ -199,7 +208,9 @@ def process_needtables( # Perform filtering of needs try: - filtered_needs = process_filters(app, list(all_needs.values()), current_needtable) + filtered_needs = process_filters( + app, list(all_needs.values()), current_needtable + ) except Exception as e: raise e @@ -228,8 +239,12 @@ def sort(need: NeedsInfoType) -> Any: filtered_needs.sort(key=get_sorter(current_needtable["sort"])) for need_info in filtered_needs: - style_row = check_and_get_content(current_needtable["style_row"], need_info, env) - style_row = style_row.replace(" ", "_") # Replace whitespaces with _ to get valid css name + style_row = check_and_get_content( + current_needtable["style_row"], need_info, env + ) + style_row = style_row.replace( + " ", "_" + ) # Replace whitespaces with _ to get valid css name temp_need = need_info.copy() if temp_need["is_need"]: @@ -243,12 +258,26 @@ def sort(need: NeedsInfoType) -> Any: for option, _title in current_needtable["columns"]: if option == "ID": - row += row_col_maker(app, fromdocname, all_needs, temp_need, "id", make_ref=True, prefix=prefix) + row += row_col_maker( + app, + fromdocname, + all_needs, + temp_need, + "id", + make_ref=True, + prefix=prefix, + ) elif option == "TITLE": - row += row_col_maker(app, fromdocname, all_needs, temp_need, "title", prefix=prefix) + row += row_col_maker( + app, fromdocname, all_needs, temp_need, "title", prefix=prefix + ) elif option in link_type_list: link_type = link_type_list[option] - if option in ["INCOMING", link_type["option"].upper() + "_BACK", link_type["incoming"].upper()]: + if option in [ + "INCOMING", + link_type["option"].upper() + "_BACK", + link_type["incoming"].upper(), + ]: row += row_col_maker( app, fromdocname, @@ -259,10 +288,17 @@ def sort(need: NeedsInfoType) -> Any: ) else: row += row_col_maker( - app, fromdocname, all_needs, temp_need, link_type["option"], ref_lookup=True + app, + fromdocname, + all_needs, + temp_need, + link_type["option"], + ref_lookup=True, ) else: - row += row_col_maker(app, fromdocname, all_needs, temp_need, option.lower()) + row += row_col_maker( + app, fromdocname, all_needs, temp_need, option.lower() + ) tbody += row # Need part rows @@ -315,7 +351,9 @@ def sort(need: NeedsInfoType) -> Any: ref_lookup=True, ) else: - row += row_col_maker(app, fromdocname, all_needs, temp_part, option.lower()) + row += row_col_maker( + app, fromdocname, all_needs, temp_part, option.lower() + ) tbody += row diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index 0b2efb482..3fd8f3b41 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -45,10 +45,14 @@ def run(self) -> Sequence[nodes.Node]: env = self.env if self.name == "needarch": - targetid = "needarch-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needarch")) + targetid = "needarch-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needarch") + ) is_arch = True else: - targetid = "needuml-{docname}-{id}".format(docname=env.docname, id=env.new_serialno("needuml")) + targetid = "needuml-{docname}-{id}".format( + docname=env.docname, id=env.new_serialno("needuml") + ) is_arch = False targetnode = nodes.target("", "", ids=[targetid]) @@ -87,7 +91,9 @@ def run(self) -> Sequence[nodes.Node]: plantuml_code_out_path = None if save_path: if os.path.isabs(save_path): - raise NeedumlException(f"Given save path: {save_path}, is not a relative path.") + raise NeedumlException( + f"Given save path: {save_path}, is not a relative path." + ) else: plantuml_code_out_path = save_path @@ -123,7 +129,9 @@ def run(self) -> Sequence[nodes.Node]: return NeedumlDirective.run(self) -def transform_uml_to_plantuml_node(app, uml_content: str, parent_need_id: str, key: str, kwargs: dict, config: str): +def transform_uml_to_plantuml_node( + app, uml_content: str, parent_need_id: str, key: str, kwargs: dict, config: str +): try: if "sphinxcontrib.plantuml" not in app.config.extensions: raise ImportError @@ -178,7 +186,13 @@ def get_debug_node_from_puml_node(puml_node): def jinja2uml( - app, fromdocname, uml_content: str, parent_need_id: str, key: str, processed_need_ids: {}, kwargs: dict + app, + fromdocname, + uml_content: str, + parent_need_id: str, + key: str, + processed_need_ids: {}, + kwargs: dict, ) -> (str, {}): # Let's render jinja templates with uml content template to 'plantuml syntax' uml # 1. Remove @startuml and @enduml @@ -192,7 +206,9 @@ def jinja2uml( # 4. Append need_id to processed_need_ids, so it will not been processed again if parent_need_id: - jinja_utils.append_need_to_processed_needs(need_id=parent_need_id, art="uml", key=key, kwargs=kwargs) + jinja_utils.append_need_to_processed_needs( + need_id=parent_need_id, art="uml", key=key, kwargs=kwargs + ) # 5. Get data for the jinja processing data = {} @@ -229,13 +245,17 @@ class JinjaFunctions: Provides access to sphinx-app and all Needs objects. """ - def __init__(self, app: Sphinx, fromdocname, parent_need_id: str, processed_need_ids: dict): + def __init__( + self, app: Sphinx, fromdocname, parent_need_id: str, processed_need_ids: dict + ): self.needs = SphinxNeedsData(app.env).get_or_create_needs() self.app = app self.fromdocname = fromdocname self.parent_need_id = parent_need_id if parent_need_id and parent_need_id not in self.needs: - raise NeedumlException(f"JinjaFunctions initialized with undefined parent_need_id: '{parent_need_id}'") + raise NeedumlException( + f"JinjaFunctions initialized with undefined parent_need_id: '{parent_need_id}'" + ) self.processed_need_ids = processed_need_ids def need_to_processed_data(self, art: str, key: str, kwargs: dict) -> {}: @@ -246,7 +266,9 @@ def need_to_processed_data(self, art: str, key: str, kwargs: dict) -> {}: } return d - def append_need_to_processed_needs(self, need_id: str, art: str, key: str, kwargs: dict) -> None: + def append_need_to_processed_needs( + self, need_id: str, art: str, key: str, kwargs: dict + ) -> None: data = self.need_to_processed_data(art=art, key=key, kwargs=kwargs) if need_id not in self.processed_need_ids: self.processed_need_ids[need_id] = [] @@ -261,18 +283,26 @@ def append_needs_to_processed_needs(self, processed_needs_data: dict) -> None: if d not in self.processed_need_ids[k]: self.processed_need_ids[k].append(d) - def data_in_processed_data(self, need_id: str, art: str, key: str, kwargs: dict) -> bool: + def data_in_processed_data( + self, need_id: str, art: str, key: str, kwargs: dict + ) -> bool: data = self.need_to_processed_data(art=art, key=key, kwargs=kwargs) - return (need_id in self.processed_need_ids) and (data in self.processed_need_ids[need_id]) + return (need_id in self.processed_need_ids) and ( + data in self.processed_need_ids[need_id] + ) def get_processed_need_ids(self) -> {}: return self.processed_need_ids def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs) -> str: if need_id not in self.needs: - raise NeedumlException(f"Jinja function uml() is called with undefined need_id: '{need_id}'.") + raise NeedumlException( + f"Jinja function uml() is called with undefined need_id: '{need_id}'." + ) - if self.data_in_processed_data(need_id=need_id, art="uml", key=key, kwargs=kwargs): + if self.data_in_processed_data( + need_id=need_id, art="uml", key=key, kwargs=kwargs + ): return "" need_info = self.needs[need_id] @@ -281,7 +311,9 @@ def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs) -> str: if need_info["arch"][key]: uml_content = need_info["arch"][key] else: - raise NeedumlException(f"Option key name: {key} does not exist in need {need_id}.") + raise NeedumlException( + f"Option key name: {key} does not exist in need {need_id}." + ) else: if "diagram" in need_info["arch"] and need_info["arch"]["diagram"]: uml_content = need_info["arch"]["diagram"] @@ -307,13 +339,17 @@ def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs) -> str: def flow(self, need_id) -> str: if need_id not in self.needs: - raise NeedumlException(f"Jinja function flow is called with undefined need_id: '{need_id}'.") + raise NeedumlException( + f"Jinja function flow is called with undefined need_id: '{need_id}'." + ) if self.data_in_processed_data(need_id=need_id, art="flow", key="", kwargs={}): return "" # append need_id to processed_need_ids, so it will not been processed again - self.append_need_to_processed_needs(need_id=need_id, art="flow", key="", kwargs={}) + self.append_need_to_processed_needs( + need_id=need_id, art="flow", key="", kwargs={} + ) need_info = self.needs[need_id] link = calculate_link(self.app, need_info, self.fromdocname) @@ -334,9 +370,13 @@ def flow(self, need_id) -> str: def ref(self, need_id: str, option: str = None, text: str = None) -> str: if need_id not in self.needs: - raise NeedumlException(f"Jinja function ref is called with undefined need_id: '{need_id}'.") + raise NeedumlException( + f"Jinja function ref is called with undefined need_id: '{need_id}'." + ) if (option and text) and (not option and not text): - raise NeedumlException("Jinja function ref requires exactly one entry 'option' or 'text'") + raise NeedumlException( + "Jinja function ref requires exactly one entry 'option' or 'text'" + ) need_info = self.needs[need_id] link = calculate_link(self.app, need_info, self.fromdocname) @@ -354,11 +394,15 @@ def filter(self, filter_string): """ needs_config = NeedsSphinxConfig(self.app.config) - return filter_needs(list(self.needs.values()), needs_config, filter_string=filter_string) + return filter_needs( + list(self.needs.values()), needs_config, filter_string=filter_string + ) def imports(self, *args): if not self.parent_need_id: - raise NeedumlException("Jinja function 'import()' is not supported in needuml directive.") + raise NeedumlException( + "Jinja function 'import()' is not supported in needuml directive." + ) # gets all need ids from need links/extra_links options and wrap into jinja function uml() need_info = self.needs[self.parent_need_id] uml_ids = [] @@ -378,7 +422,9 @@ def imports(self, *args): def need(self): if not self.parent_need_id: - raise NeedumlException("Jinja function 'need()' is not supported in needuml directive.") + raise NeedumlException( + "Jinja function 'need()' is not supported in needuml directive." + ) return self.needs[self.parent_need_id] @@ -407,7 +453,12 @@ 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): @@ -421,14 +472,18 @@ def process_needuml(app: Sphinx, doctree: nodes.document, fromdocname: str, foun # Check if needarch is only used inside a need parent_need_id = is_element_of_need(node) if not parent_need_id: - raise NeedArchException("Directive needarch can only be used inside a need.") + raise NeedArchException( + "Directive needarch can only be used inside a need." + ) content = [] # Adding config config = current_needuml["config"] if config and len(config) >= 3: # Remove all empty lines - config = "\n".join([line.strip() for line in config.split("\n") if line.strip()]) + config = "\n".join( + [line.strip() for line in config.split("\n") if line.strip()] + ) puml_node = transform_uml_to_plantuml_node( app=app, @@ -459,7 +514,9 @@ def process_needuml(app: Sphinx, doctree: nodes.document, fromdocname: str, foun puml_node["align"] = "center" puml_node["incdir"] = os.path.dirname(current_needuml["docname"]) - puml_node["filename"] = os.path.split(current_needuml["docname"])[1] # Needed for plantuml >= 0.9 + puml_node["filename"] = os.path.split(current_needuml["docname"])[ + 1 + ] # Needed for plantuml >= 0.9 content.append(puml_node) diff --git a/sphinx_needs/directives/utils.py b/sphinx_needs/directives/utils.py index 378342909..abb3b891b 100644 --- a/sphinx_needs/directives/utils.py +++ b/sphinx_needs/directives/utils.py @@ -24,17 +24,25 @@ def used_filter_paragraph(current_needfilter: NeedsFilteredBaseType) -> nodes.pa para = nodes.paragraph() filter_text = "Used filter:" filter_text += ( - " status(%s)" % " OR ".join(current_needfilter["status"]) if len(current_needfilter["status"]) > 0 else "" + " status(%s)" % " OR ".join(current_needfilter["status"]) + if len(current_needfilter["status"]) > 0 + else "" ) if len(current_needfilter["status"]) > 0 and len(current_needfilter["tags"]) > 0: filter_text += " AND " - filter_text += " tags(%s)" % " OR ".join(current_needfilter["tags"]) if len(current_needfilter["tags"]) > 0 else "" - if (len(current_needfilter["status"]) > 0 or len(current_needfilter["tags"]) > 0) and len( - current_needfilter["types"] - ) > 0: + filter_text += ( + " tags(%s)" % " OR ".join(current_needfilter["tags"]) + if len(current_needfilter["tags"]) > 0 + else "" + ) + if ( + len(current_needfilter["status"]) > 0 or len(current_needfilter["tags"]) > 0 + ) and len(current_needfilter["types"]) > 0: filter_text += " AND " filter_text += ( - " types(%s)" % " OR ".join(current_needfilter["types"]) if len(current_needfilter["types"]) > 0 else "" + " types(%s)" % " OR ".join(current_needfilter["types"]) + if len(current_needfilter["types"]) > 0 + else "" ) filter_node = nodes.emphasis(filter_text, filter_text) @@ -91,7 +99,9 @@ def analyse_needs_metrics(env: BuildEnvironment) -> dict[str, Any]: if i["type"] in needs_types: needs_types[i["type"]] += 1 - metric_data["needs_types"] = {i[0]: i[1] for i in sorted(needs_types.items(), key=lambda x: x[0])} + metric_data["needs_types"] = { + i[0]: i[1] for i in sorted(needs_types.items(), key=lambda x: x[0]) + } return metric_data diff --git a/sphinx_needs/environment.py b/sphinx_needs/environment.py index 61dbf95dc..786ea2666 100644 --- a/sphinx_needs/environment.py +++ b/sphinx_needs/environment.py @@ -40,13 +40,21 @@ def safe_add_file(filename: Path, app: Sphinx) -> None: if pure_path.suffix == ".js": # Make sure the calculated (posix)-path is not already registered as "web"-path - if hasattr(builder, "script_files") and str(static_data_file) not in builder.script_files: + if ( + hasattr(builder, "script_files") + and str(static_data_file) not in builder.script_files + ): app.add_js_file(str(pure_path)) elif pure_path.suffix == ".css": - if hasattr(builder, "css_files") and str(static_data_file) not in builder.css_files: + if ( + hasattr(builder, "css_files") + and str(static_data_file) not in builder.css_files + ): app.add_css_file(str(pure_path)) else: - raise NotImplementedError(f"File type {pure_path.suffix} not support by save_add_file") + raise NotImplementedError( + f"File type {pure_path.suffix} not support by save_add_file" + ) def safe_remove_file(filename: Path, app: Sphinx) -> None: @@ -120,7 +128,10 @@ def _find_css_files() -> Iterable[Path]: if not source_file_path.exists(): source_file_path = css_root / "blank" / "blank.css" - logger.warning(f"{source_file_path} not found. Copying sphinx-internal blank.css [needs]", type="needs") + logger.warning( + f"{source_file_path} not found. Copying sphinx-internal blank.css [needs]", + type="needs", + ) dest_file = dest_dir / source_file_path.name dest_dir.mkdir(exist_ok=True) @@ -215,7 +226,9 @@ def install_permalink_file(app: Sphinx, env: BuildEnvironment) -> None: return # load jinja template - jinja_env = Environment(loader=PackageLoader("sphinx_needs"), autoescape=select_autoescape()) + jinja_env = Environment( + loader=PackageLoader("sphinx_needs"), autoescape=select_autoescape() + ) template = jinja_env.get_template("permalink.html") # save file to build dir diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index fc8003648..331038996 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -47,18 +47,28 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No ) ) elif not (source.get("json_url", False) or source.get("json_path", False)): - raise NeedsExternalException("json_path or json_url must be configured to use external_needs.") + raise NeedsExternalException( + "json_path or json_url must be configured to use external_needs." + ) if source.get("json_url", False): - log.info(clean_log(f"Loading external needs from url {source['json_url']}.")) + log.info( + clean_log(f"Loading external needs from url {source['json_url']}.") + ) s = requests.Session() s.mount("file://", FileAdapter()) try: response = s.get(source["json_url"]) - needs_json = response.json() # The downloaded file MUST be json. Everything else we do not handle! + needs_json = ( + response.json() + ) # The downloaded file MUST be json. Everything else we do not handle! except Exception as e: raise NeedsExternalException( - clean_log("Getting {} didn't work. Reason: {}".format(source["json_url"], e)) + clean_log( + "Getting {} didn't work. Reason: {}".format( + source["json_url"], e + ) + ) ) if source.get("json_path", False): @@ -68,7 +78,9 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No json_path = os.path.join(app.srcdir, source["json_path"]) if not os.path.exists(json_path): - raise NeedsExternalException(f"Given json_path {json_path} does not exist.") + raise NeedsExternalException( + f"Given json_path {json_path} does not exist." + ) with open(json_path) as json_file: needs_json = json.load(json_file) @@ -83,7 +95,9 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No needs = needs_json["versions"][version]["needs"] except KeyError: raise NeedsExternalException( - clean_log(f"Version {version} not found in json file from {source['json_url']}") + clean_log( + f"Version {version} not found in json file from {source['json_url']}" + ) ) log.debug(f"Loading {len(needs)} needs.") @@ -98,7 +112,16 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No if ( key not in needs_config.extra_options and key not in extra_links - and key not in ["title", "type", "id", "description", "tags", "docname", "status"] + and key + not in [ + "title", + "type", + "id", + "description", + "tags", + "docname", + "status", + ] ): del need_params[key] @@ -112,9 +135,9 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No cal_target_url = mem_template.render(**{"need": need}) need_params["external_url"] = f'{source["base_url"]}/{cal_target_url}' else: - need_params["external_url"] = ( - f'{source["base_url"]}/{need.get("docname", "__error__")}.html#{need["id"]}' - ) + need_params[ + "external_url" + ] = f'{source["base_url"]}/{need.get("docname", "__error__")}.html#{need["id"]}' need_params["content"] = need["description"] need_params["links"] = need.get("links", []) diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 896256860..0ba740372 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -55,7 +55,11 @@ class FilterBase(SphinxDirective): def collect_filter_attributes(self) -> FilterAttributesType: _tags = str(self.options.get("tags", "")) - tags = [tag.strip() for tag in re.split(";|,", _tags) if len(tag) > 0] if _tags else [] + tags = ( + [tag.strip() for tag in re.split(";|,", _tags) if len(tag) > 0] + if _tags + else [] + ) status = self.options.get("status") if status: @@ -92,7 +96,10 @@ def collect_filter_attributes(self) -> FilterAttributesType: def process_filters( - app: Sphinx, all_needs: Iterable[NeedsInfoType], filter_data: NeedsFilteredBaseType, include_external: bool = True + app: Sphinx, + all_needs: Iterable[NeedsInfoType], + filter_data: NeedsFilteredBaseType, + include_external: bool = True, ) -> list[NeedsPartsInfoType]: """ Filters all needs with given configuration. @@ -112,7 +119,10 @@ def process_filters( try: all_needs = sorted(all_needs, key=lambda node: node[sort_key] or "") # type: ignore[literal-required] except KeyError as e: - log.warning(f"Sorting parameter {sort_key} not valid: Error: {e} [needs]", type="needs") + log.warning( + f"Sorting parameter {sort_key} not valid: Error: {e} [needs]", + type="needs", + ) # check if include external needs checked_all_needs: Iterable[NeedsInfoType] @@ -130,7 +140,9 @@ def process_filters( all_needs_incl_parts = prepare_need_list(checked_all_needs) # Check if external filter code is defined - filter_func, filter_args = check_and_get_external_filter_func(filter_data.get("filter_func")) + filter_func, filter_args = check_and_get_external_filter_func( + filter_data.get("filter_func") + ) filter_code = None # Get filter_code from @@ -141,12 +153,19 @@ def process_filters( if bool(filter_data["status"] or filter_data["tags"] or filter_data["types"]): for need_info in all_needs_incl_parts: status_filter_passed = False - if not filter_data["status"] or need_info["status"] and need_info["status"] in filter_data["status"]: + if ( + not filter_data["status"] + or need_info["status"] + and need_info["status"] in filter_data["status"] + ): # Filtering for status was not requested or match was found status_filter_passed = True tags_filter_passed = False - if len(set(need_info["tags"]) & set(filter_data["tags"])) > 0 or len(filter_data["tags"]) == 0: + if ( + len(set(need_info["tags"]) & set(filter_data["tags"])) > 0 + or len(filter_data["tags"]) == 0 + ): tags_filter_passed = True type_filter_passed = False @@ -160,13 +179,19 @@ def process_filters( if status_filter_passed and tags_filter_passed and type_filter_passed: found_needs_by_options.append(need_info) # Get need by filter string - found_needs_by_string = filter_needs(all_needs_incl_parts, needs_config, filter_data["filter"]) + found_needs_by_string = filter_needs( + all_needs_incl_parts, needs_config, filter_data["filter"] + ) # Make an intersection of both lists - found_needs = intersection_of_need_results(found_needs_by_options, found_needs_by_string) + found_needs = intersection_of_need_results( + found_needs_by_options, found_needs_by_string + ) else: # There is no other config as the one for filter string. # So we only need this result. - found_needs = filter_needs(all_needs_incl_parts, needs_config, filter_data["filter"]) + found_needs = filter_needs( + all_needs_incl_parts, needs_config, filter_data["filter"] + ) else: # Provides only a copy of needs to avoid data manipulations. context = { @@ -185,7 +210,9 @@ def process_filters( context[f"arg{index+1}"] = arg # Decorate function to allow time measurments - filter_func = measure_time_func(filter_func, category="filter_func", source="user") + filter_func = measure_time_func( + filter_func, category="filter_func", source="user" + ) filter_func(**context) else: log.warning("Something went wrong running filter [needs]", type="needs") @@ -237,7 +264,11 @@ def prepare_need_list(need_list: Iterable[NeedsInfoType]) -> list[NeedsPartsInfo for need in need_list: for part in need["parts"].values(): id_complete = ".".join([need["id"], part["id"]]) - filter_part: NeedsPartsInfoType = {**need, **part, **{"id_parent": need["id"], "id_complete": id_complete}} # type: ignore[typeddict-item] + filter_part: NeedsPartsInfoType = { + **need, + **part, + **{"id_parent": need["id"], "id_complete": id_complete}, # type: ignore[typeddict-item] + } all_needs_incl_parts.append(filter_part) # Be sure extra attributes, which makes only sense for need_parts, are also available on @@ -288,12 +319,21 @@ def filter_needs( for filter_need in needs: try: if filter_single_need( - filter_need, config, filter_string, needs, current_need, filter_compiled=filter_compiled + filter_need, + config, + filter_string, + needs, + current_need, + filter_compiled=filter_compiled, ): found_needs.append(filter_need) except Exception as e: if not error_reported: # Let's report a filter-problem only onces - location = (current_need["docname"], current_need["lineno"]) if current_need else None + location = ( + (current_need["docname"], current_need["lineno"]) + if current_need + else None + ) log.warning(str(e) + " [needs]", type="needs", location=location) error_reported = True diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index f5d96bf06..467743294 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -19,7 +19,13 @@ 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. @@ -41,7 +47,12 @@ 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 @@ -154,7 +165,9 @@ def copy( need = needs[need_id] if filter: - result = filter_needs(needs.values(), NeedsSphinxConfig(app.config), filter, need) + result = filter_needs( + needs.values(), NeedsSphinxConfig(app.config), filter, need + ) if result: need = result[0] @@ -323,7 +336,10 @@ def check_linked_values( if not filter_single_need(need, needs_config, filter_string): continue except Exception as e: - logger.warning(f"CheckLinkedValues: Filter {filter_string} not valid: Error: {e} [needs]", type="needs") + logger.warning( + f"CheckLinkedValues: Filter {filter_string} not valid: Error: {e} [needs]", + type="needs", + ) need_value = need[search_option] # type: ignore[literal-required] if not one_hit and need_value not in search_value: @@ -422,7 +438,9 @@ def calc_sum( :return: A float number """ needs_config = NeedsSphinxConfig(app.config) - check_needs = [needs[link] for link in need["links"]] if links_only else needs.values() + check_needs = ( + [needs[link] for link in need["links"]] if links_only else needs.values() + ) calculated_sum = 0.0 @@ -434,7 +452,9 @@ def calc_sum( except ValueError: pass except NeedsInvalidFilter as ex: - logger.warning(f"Given filter is not valid. Error: {ex} [needs]", type="needs") + logger.warning( + f"Given filter is not valid. Error: {ex} [needs]", type="needs" + ) with contextlib.suppress(ValueError): calculated_sum += float(check_need[option]) # type: ignore[literal-required] @@ -514,7 +534,9 @@ def links_from_content( needs_config = NeedsSphinxConfig(app.config) filtered_links = [] for link in raw_links: - if link not in filtered_links and filter_single_need(needs[link], needs_config, filter): + if link not in filtered_links and filter_single_need( + needs[link], needs_config, filter + ): filtered_links.append(link) return filtered_links diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 694435d17..32ead7438 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -29,7 +29,8 @@ # TODO these functions also take optional *args and **kwargs DynamicFunction = Callable[ - [Sphinx, NeedsInfoType, Dict[str, NeedsInfoType]], Union[str, int, float, List[Union[str, int, float]]] + [Sphinx, NeedsInfoType, Dict[str, NeedsInfoType]], + Union[str, int, float, List[Union[str, int, float]]], ] @@ -55,7 +56,9 @@ def register_func(need_function: DynamicFunction, name: str | None = None) -> No # We can not throw an exception here, as using sphinx-needs in different sphinx-projects with the # same python interpreter session does not clean NEEDS_FUNCTIONS. # This is mostly the case during tet runs. - logger.info(f"sphinx-needs: Function name {func_name} already registered. Ignoring the new one!") + logger.info( + f"sphinx-needs: Function name {func_name} already registered. Ignoring the new one!" + ) NEEDS_FUNCTIONS[func_name] = {"name": func_name, "function": need_function} @@ -72,10 +75,22 @@ def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str) -> Any: func_name, func_args, func_kwargs = _analyze_func_string(func_string, need) if func_name not in NEEDS_FUNCTIONS: - raise SphinxError("Unknown dynamic sphinx-needs function: {}. Found in need: {}".format(func_name, need["id"])) + raise SphinxError( + "Unknown dynamic sphinx-needs function: {}. Found in need: {}".format( + func_name, need["id"] + ) + ) - func = measure_time_func(NEEDS_FUNCTIONS[func_name]["function"], category="dyn_func", source="user") - func_return = func(app, need, SphinxNeedsData(app.env).get_or_create_needs(), *func_args, **func_kwargs) + func = measure_time_func( + NEEDS_FUNCTIONS[func_name]["function"], category="dyn_func", source="user" + ) + func_return = func( + app, + need, + SphinxNeedsData(app.env).get_or_create_needs(), + *func_args, + **func_kwargs, + ) if not isinstance(func_return, (str, int, float, list, unicode)) and func_return: raise SphinxError( @@ -97,7 +112,9 @@ def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str) -> Any: func_pattern = re.compile(r"\[\[(.*?)\]\]") # RegEx to detect function strings -def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: NeedsInfoType) -> nodes.Node: +def find_and_replace_node_content( + node: nodes.Node, env: BuildEnvironment, need: NeedsInfoType +) -> nodes.Node: """ Search inside a given node and its children for nodes of type Text, if found, check if it contains a function string and run/replace it. @@ -106,7 +123,11 @@ def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: :return: None """ new_children = [] - if not node.children and isinstance(node, nodes.Text) or isinstance(node, nodes.reference): + if ( + not node.children + and isinstance(node, nodes.Text) + or isinstance(node, nodes.reference) + ): if isinstance(node, nodes.reference): try: new_text = node.attributes["refuri"] @@ -127,8 +148,8 @@ def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: func_string = func_string.replace("”", '"') func_string = func_string.replace("”", '"') - func_string = func_string.replace("‘", "'") - func_string = func_string.replace("’", "'") + func_string = func_string.replace("‘", "'") # noqa: RUF001 + func_string = func_string.replace("’", "'") # noqa: RUF001 func_return = execute_func(env.app, need, func_string) # This should never happen, but we can not be sure. @@ -176,20 +197,30 @@ def resolve_dynamic_values(needs: dict[str, NeedsInfoType], app: Sphinx) -> None """ for need in needs.values(): for need_option in need: - if need_option in ["docname", "lineno", "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)): func_call: str | None = "init" while func_call: try: - func_call, func_return = _detect_and_execute(need[need_option], need, app) + func_call, func_return = _detect_and_execute( + need[need_option], need, app + ) except FunctionParsingException: raise SphinxError( "Function definition of {option} in file {file}:{line} has " "unsupported parameters. " "supported are str, int/float, list".format( - option=need_option, file=need["docname"], line=need["lineno"] + option=need_option, + file=need["docname"], + line=need["lineno"], ) ) @@ -197,9 +228,13 @@ def resolve_dynamic_values(needs: dict[str, NeedsInfoType], app: Sphinx) -> None continue # Replace original function string with return value of function call if func_return is None: - need[need_option] = need[need_option].replace(f"[[{func_call}]]", "") + need[need_option] = need[need_option].replace( + f"[[{func_call}]]", "" + ) else: - need[need_option] = need[need_option].replace(f"[[{func_call}]]", str(func_return)) + need[need_option] = need[need_option].replace( + f"[[{func_call}]]", str(func_return) + ) if need[need_option] == "": need[need_option] = None @@ -213,7 +248,9 @@ def resolve_dynamic_values(needs: dict[str, NeedsInfoType], app: Sphinx) -> None "Function definition of {option} in file {file}:{line} has " "unsupported parameters. " "supported are str, int/float, list".format( - option=need_option, file=need["docname"], line=need["lineno"] + option=need_option, + file=need["docname"], + line=need["lineno"], ) ) if func_call is None: @@ -221,7 +258,9 @@ def resolve_dynamic_values(needs: dict[str, NeedsInfoType], app: Sphinx) -> None else: # Replace original function string with return value of function call if isinstance(need[need_option], (str, int, float)): - new_values.append(element.replace(f"[[{func_call}]]", str(func_return))) + new_values.append( + element.replace(f"[[{func_call}]]", str(func_return)) + ) else: if isinstance(need[need_option], (list, set)): if isinstance(func_return, (list, set)): @@ -233,7 +272,9 @@ 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. @@ -255,20 +296,28 @@ def resolve_variants_options( for need in needs.values(): # Data to use as filter context. need_context: dict[str, Any] = {**need} - need_context.update(**needs_config.filter_data) # Add needs_filter_data to filter context + need_context.update( + **needs_config.filter_data + ) # Add needs_filter_data to filter context need_context.update(**tags) # Add sphinx tags to filter context for var_option in variants_options: if var_option in need and need[var_option] not in (None, "", []): if not isinstance(need[var_option], (list, set, tuple)): option_value: str = need[var_option] - need[var_option] = match_variants(option_value, need_context, needs_config.variants) + need[var_option] = match_variants( + option_value, need_context, needs_config.variants + ) else: option_value = need[var_option] - need[var_option] = match_variants(option_value, need_context, needs_config.variants) + need[var_option] = match_variants( + option_value, need_context, needs_config.variants + ) -def check_and_get_content(content: str, need: NeedsInfoType, env: BuildEnvironment) -> str: +def check_and_get_content( + content: str, need: NeedsInfoType, env: BuildEnvironment +) -> str: """ Checks if the given content is a function call. If not, content is returned. @@ -290,14 +339,18 @@ def check_and_get_content(content: str, need: NeedsInfoType, env: BuildEnvironme return content func_call = func_match.group(1) # Extract function call - func_return = execute_func(env.app, need, func_call) # Execute function call and get return value + func_return = execute_func( + env.app, need, func_call + ) # Execute function call and get return value # Replace the function_call with the calculated value content = content.replace(f"[[{func_call}]]", func_return) return content -def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> tuple[str | None, 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) @@ -309,12 +362,16 @@ def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> tuple return None, None func_call = func_match.group(1) # Extract function call - func_return = execute_func(app, need, func_call) # Execute function call and get return value + func_return = execute_func( + app, need, func_call + ) # Execute function call and get return value return func_call, func_return -def _analyze_func_string(func_string: str, need: NeedsInfoType | None) -> 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: @@ -331,12 +388,16 @@ def _analyze_func_string(func_string: str, need: NeedsInfoType | None) -> tuple[ func = ast.parse(func_string) except SyntaxError as e: need_id = need["id"] if need else "UNKNOWN" - raise SphinxError(f"Parsing function string failed for need {need_id}: {func_string}. {e}") + raise SphinxError( + f"Parsing function string failed for need {need_id}: {func_string}. {e}" + ) try: func_call = func.body[0].value # type: ignore func_name = func_call.func.id except AttributeError: - raise SphinxError(f"Given dynamic function string is not a valid python call. Got: {func_string}") + raise SphinxError( + f"Given dynamic function string is not a valid python call. Got: {func_string}" + ) func_args: list[Any] = [] for arg in func_call.args: @@ -366,8 +427,8 @@ def _analyze_func_string(func_string: str, need: NeedsInfoType | None) -> tuple[ ) else: raise FunctionParsingException( - "Unsupported type found in function definition: {}. " - "Supported are numbers, strings, bool and list".format(func_string) + f"Unsupported type found in function definition: {func_string}. " + "Supported are numbers, strings, bool and list" ) func_kargs: dict[str, Any] = {} for keyword in func_call.keywords: diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index d9c573945..e7cc60e35 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -33,7 +33,11 @@ @measure_time("need") def create_need( - need_id: str, app: Sphinx, layout: str | None = None, style: str | None = None, docname: str | None = 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. @@ -78,7 +82,9 @@ def create_need( # Overwrite the docname, which must be the original one from the reused need, as all used paths are relative # to the original location, not to the current document. - env.temp_data["docname"] = need_data["docname"] # Dirty, as in this phase normally no docname is set anymore in env + env.temp_data["docname"] = need_data[ + "docname" + ] # Dirty, as in this phase normally no docname is set anymore in env ImageCollector().process_doc(app, node_inner) # type: ignore[arg-type] DownloadFileCollector().process_doc(app, node_inner) # type: ignore[arg-type] @@ -102,7 +108,9 @@ def create_need( # set the layout and style for the new need node_container[0].attributes = node_container.parent.children[0].attributes # type: ignore - node_container[0].children[0].attributes = node_container.parent.children[0].children[0].attributes # type: ignore + node_container[0].children[0].attributes = ( # type: ignore + node_container.parent.children[0].children[0].attributes # type: ignore + ) node_container.attributes["ids"] = [] @@ -129,7 +137,11 @@ 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: str | None = None, fromdocname: str | None = 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. @@ -221,7 +233,11 @@ def __init__( self.fromdocname = fromdocname # For ReadTheDocs Theme we need to add 'rtd-exclude-wy-table'. - classes = ["need", "needs_grid_" + self.layout["grid"], "needs_layout_" + self.layout_name] + classes = [ + "need", + "needs_grid_" + self.layout["grid"], + "needs_layout_" + self.layout_name, + ] classes.extend(self.needs_config.table_classes) self.style = style or self.need["style"] or self.needs_config.default_style @@ -241,59 +257,121 @@ def __init__( self.grids = { "simple": { "func": self._grid_simple, - "configs": {"colwidths": [100], "side_left": False, "side_right": False, "footer": False}, + "configs": { + "colwidths": [100], + "side_left": False, + "side_right": False, + "footer": False, + }, }, "simple_footer": { "func": self._grid_simple, - "configs": {"colwidths": [100], "side_left": False, "side_right": False, "footer": True}, + "configs": { + "colwidths": [100], + "side_left": False, + "side_right": False, + "footer": True, + }, }, "simple_side_left": { "func": self._grid_simple, - "configs": {"colwidths": [30, 70], "side_left": "full", "side_right": False, "footer": False}, + "configs": { + "colwidths": [30, 70], + "side_left": "full", + "side_right": False, + "footer": False, + }, }, "simple_side_right": { "func": self._grid_simple, - "configs": {"colwidths": [70, 30], "side_left": False, "side_right": "full", "footer": False}, + "configs": { + "colwidths": [70, 30], + "side_left": False, + "side_right": "full", + "footer": False, + }, }, "simple_side_left_partial": { "func": self._grid_simple, - "configs": {"colwidths": [20, 80], "side_left": "part", "side_right": False, "footer": False}, + "configs": { + "colwidths": [20, 80], + "side_left": "part", + "side_right": False, + "footer": False, + }, }, "simple_side_right_partial": { "func": self._grid_simple, - "configs": {"colwidths": [80, 20], "side_left": False, "side_right": "part", "footer": False}, + "configs": { + "colwidths": [80, 20], + "side_left": False, + "side_right": "part", + "footer": False, + }, }, "complex": self._grid_complex, "content": { "func": self._grid_content, - "configs": {"colwidths": [100], "side_left": False, "side_right": False, "footer": False}, + "configs": { + "colwidths": [100], + "side_left": False, + "side_right": False, + "footer": False, + }, }, "content_footer": { "func": self._grid_content, - "configs": {"colwidths": [100], "side_left": False, "side_right": False, "footer": True}, + "configs": { + "colwidths": [100], + "side_left": False, + "side_right": False, + "footer": True, + }, }, "content_side_left": { "func": self._grid_content, - "configs": {"colwidths": [5, 95], "side_left": True, "side_right": False, "footer": False}, + "configs": { + "colwidths": [5, 95], + "side_left": True, + "side_right": False, + "footer": False, + }, }, "content_side_right": { "func": self._grid_content, - "configs": {"colwidths": [95, 5], "side_left": False, "side_right": True, "footer": False}, + "configs": { + "colwidths": [95, 5], + "side_left": False, + "side_right": True, + "footer": False, + }, }, "content_footer_side_left": { "func": self._grid_content, - "configs": {"colwidths": [5, 95], "side_left": True, "side_right": False, "footer": True}, + "configs": { + "colwidths": [5, 95], + "side_left": True, + "side_right": False, + "footer": True, + }, }, "content_footer_side_right": { "func": self._grid_content, - "configs": {"colwidths": [95, 5], "side_left": False, "side_right": True, "footer": True}, + "configs": { + "colwidths": [95, 5], + "side_left": False, + "side_right": True, + "footer": True, + }, }, } # Dummy Document setup self.doc_settings, self.inline_parser = _generate_inline_parser() self.dummy_doc = new_document("dummy", self.doc_settings) - self.doc_language = languages.get_language(self.dummy_doc.settings.language_code) + self.doc_language = languages.get_language( + self.dummy_doc.settings.language_code + ) self.doc_memo = Struct( document=self.dummy_doc, reporter=self.dummy_doc.reporter, @@ -304,7 +382,9 @@ def __init__( inliner=None, ) - self.functions: dict[str, Callable[..., 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, @@ -334,7 +414,9 @@ def __init__( def get_need_table(self) -> nodes.table: if self.layout["grid"] not in self.grids.keys(): raise SphinxNeedLayoutException( - "Unknown layout-grid: {}. Supported are {}".format(self.layout["grid"], ", ".join(self.grids.keys())) + "Unknown layout-grid: {}. Supported are {}".format( + self.layout["grid"], ", ".join(self.grids.keys()) + ) ) func = self.grids[self.layout["grid"]] @@ -376,7 +458,9 @@ def _parse(self, line: str) -> list[nodes.Node]: :param line: string to parse :return: nodes """ - result, message = self.inline_parser.parse(line, 0, self.doc_memo, self.dummy_doc) # type: ignore + result, message = self.inline_parser.parse( # type: ignore + line, 0, self.doc_memo, self.dummy_doc + ) if message: raise SphinxNeedLayoutException(message) return result # type: ignore[no-any-return] @@ -420,7 +504,9 @@ def _func_replace(self, section_nodes: list[nodes.Node]) -> list[nodes.Node]: ) func_def_clean = func_def.replace("<<", "").replace(">>", "") - func_name, func_args, func_kargs = _analyze_func_string(func_def_clean, None) + func_name, func_args, func_kargs = _analyze_func_string( + func_def_clean, None + ) # Replace place holders # Looks for {{name}}, where name must be an option of need, and replaces it with the @@ -552,7 +638,9 @@ def meta( ref_item = nodes.Text(datum) data_node += ref_item - if (name in needs_string_links_option and index + 1 < len(data)) or index + 1 < len([data]): + if ( + name in needs_string_links_option and index + 1 < len(data) + ) or index + 1 < len([data]): data_node += nodes.emphasis("; ", "; ") data_container.append(data_node) @@ -703,7 +791,9 @@ 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: list[str] | None = 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. @@ -718,7 +808,9 @@ def meta_links_all(self, prefix: str = "", postfix: str = "", exclude: list[str] type_key = link_type["option"] if self.need[type_key] and type_key not in exclude: # type: ignore[literal-required] outgoing_line = nodes.line() - outgoing_label = prefix + "{}:".format(link_type["outgoing"]) + postfix + " " + outgoing_label = ( + prefix + "{}:".format(link_type["outgoing"]) + postfix + " " + ) outgoing_line += self._parse(outgoing_label) outgoing_line += self.meta_links(link_type["option"], incoming=False) data_container.append(outgoing_line) @@ -726,7 +818,9 @@ def meta_links_all(self, prefix: str = "", postfix: str = "", exclude: list[str] type_key = link_type["option"] + "_back" if self.need[type_key] and type_key not in exclude: # type: ignore[literal-required] incoming_line = nodes.line() - incoming_label = prefix + "{}:".format(link_type["incoming"]) + postfix + " " + incoming_label = ( + prefix + "{}:".format(link_type["incoming"]) + postfix + " " + ) incoming_line += self._parse(incoming_label) incoming_line += self.meta_links(link_type["option"], incoming=True) data_container.append(incoming_line) @@ -791,7 +885,9 @@ def image( options["align"] = align if url is None or not isinstance(url, str): - raise SphinxNeedLayoutException("not valid url given for image function in layout") + raise SphinxNeedLayoutException( + "not valid url given for image function in layout" + ) if url.startswith("icon:"): if any(x in builder.name.upper() for x in ["PDF", "LATEX"]): @@ -926,7 +1022,11 @@ def link( return data_container def collapse_button( - self, target: str = "meta", collapsed: str = "Show", visible: str = "Close", initial: bool = False + self, + target: str = "meta", + collapsed: str = "Show", + visible: str = "Close", + initial: bool = False, ) -> nodes.inline | None: """ To show icons instead of text on the button, use collapse_button() like this:: @@ -952,16 +1052,28 @@ def collapse_button( if collapsed.startswith("image:") or collapsed.startswith("icon:"): coll_node_collapsed.append( - self.image(collapsed.replace("image:", ""), width="17px", no_link=True, img_class="sn_collapse_img") + self.image( + collapsed.replace("image:", ""), + width="17px", + no_link=True, + img_class="sn_collapse_img", + ) ) elif collapsed.startswith("Debug view"): - coll_node_collapsed.append(nodes.container(classes=["debug_on_layout_btn"])) # For debug layout + coll_node_collapsed.append( + nodes.container(classes=["debug_on_layout_btn"]) + ) # For debug layout else: coll_node_collapsed.append(nodes.Text(collapsed)) if visible.startswith("image:") or visible.startswith("icon:"): coll_node_visible.append( - self.image(visible.replace("image:", ""), width="17px", no_link=True, img_class="sn_collapse_img") + self.image( + visible.replace("image:", ""), + width="17px", + no_link=True, + img_class="sn_collapse_img", + ) ) elif visible.startswith("Debug view"): coll_node_visible.append(nodes.container(classes=["debug_off_layout_btn"])) @@ -972,7 +1084,9 @@ def collapse_button( # docutils doesn't allow has to add any html-attributes beside class and id to nodes. # So we misused "id" for this and use "__" (2x _) as separator for row-target names - if (not self.need["collapse"]) or (self.need["collapse"] is None and not initial): + if (not self.need["collapse"]) or ( + self.need["collapse"] is None and not initial + ): status = "show" if (self.need["collapse"]) or (not self.need["collapse"] and initial): @@ -1034,7 +1148,13 @@ def permalink( prefix=prefix, ) - def _grid_simple(self, colwidths: list[int], side_left: bool | str, side_right: 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. @@ -1104,7 +1224,9 @@ def _grid_simple(self, colwidths: list[int], side_left: bool | str, side_right: head_row = nodes.row(classes=["need", "head"]) if side_left: - side_entry = nodes.entry(classes=["need", "side"], morerows=side_left_morerows) + side_entry = nodes.entry( + classes=["need", "side"], morerows=side_left_morerows + ) side_entry += self.get_section("side") head_row += side_entry @@ -1113,7 +1235,9 @@ def _grid_simple(self, colwidths: list[int], side_left: bool | str, side_right: head_row += head_entry if side_right: - side_entry = nodes.entry(classes=["need", "side"], morerows=side_right_morerows) + side_entry = nodes.entry( + classes=["need", "side"], morerows=side_right_morerows + ) side_entry += self.get_section("side") head_row += side_entry @@ -1125,14 +1249,18 @@ def _grid_simple(self, colwidths: list[int], side_left: bool | str, side_right: # CONTENT row content_row = nodes.row(classes=["need", "content"]) - content_entry = nodes.entry(classes=["need", "content"], morecols=common_more_cols) + content_entry = nodes.entry( + classes=["need", "content"], morecols=common_more_cols + ) content_entry.insert(0, self.node.children) content_row += content_entry # FOOTER row if footer: footer_row = nodes.row(classes=["need", "footer"]) - footer_entry = nodes.entry(classes=["need", "footer"], morecols=common_more_cols) + footer_entry = nodes.entry( + classes=["need", "footer"], morecols=common_more_cols + ) footer_entry += self.get_section("footer") footer_row += footer_entry @@ -1207,7 +1335,9 @@ 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/need_constraints.py b/sphinx_needs/need_constraints.py index e068a6bdf..b1925ab8a 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -11,7 +11,9 @@ 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``. @@ -56,7 +58,9 @@ def process_constraints(needs: dict[str, NeedsInfoType], config: NeedsSphinxConf if "error_message" in executable_constraints: msg = str(executable_constraints["error_message"]) - template = error_templates_cache.setdefault(msg, jinja2.Template(msg)) + template = error_templates_cache.setdefault( + msg, jinja2.Template(msg) + ) need["constraints_error"] = template.render(**need) if "severity" not in executable_constraints: @@ -88,10 +92,14 @@ def process_constraints(needs: dict[str, NeedsInfoType], config: NeedsSphinxConf # set styles old_style = need["style"] if old_style and len(old_style) > 0: - new_styles = "".join(", " + x for x in failed_options.get("style", [])) + new_styles = "".join( + ", " + x for x in failed_options.get("style", []) + ) else: old_style = "" - new_styles = "".join(x + "," for x in failed_options.get("style", [])) + new_styles = "".join( + x + "," for x in failed_options.get("style", []) + ) if failed_options.get("force_style", False): need["style"] = new_styles.strip(", ") diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index e252dfc7f..4d99df291 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -109,7 +109,10 @@ __version__ = VERSION = "2.0.0" NEEDS_FUNCTIONS.clear() -_NODE_TYPES_T = Dict[Type[nodes.Element], Callable[[Sphinx, nodes.document, str, List[nodes.Element]], None]] +_NODE_TYPES_T = Dict[ + Type[nodes.Element], + Callable[[Sphinx, nodes.document, str, List[nodes.Element]], None], +] NODE_TYPES_PRIO: _NODE_TYPES_T = { # Node types to be checked before most others Needextract: process_needextract, @@ -150,7 +153,9 @@ def setup(app: Sphinx) -> dict[str, Any]: NeedsSphinxConfig.add_config_values(app) # Define nodes - app.add_node(Need, html=(html_visit, html_depart), latex=(latex_visit, latex_depart)) + app.add_node( + Need, html=(html_visit, html_depart), latex=(latex_visit, latex_depart) + ) app.add_node( Needfilter, ) @@ -167,7 +172,11 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_node(Needextend) app.add_node(Needuml) app.add_node(List2Need) - app.add_node(NeedPart, html=(visitor_dummy, visitor_dummy), latex=(visitor_dummy, visitor_dummy)) + app.add_node( + NeedPart, + html=(visitor_dummy, visitor_dummy), + latex=(visitor_dummy, visitor_dummy), + ) ######################################################################## # DIRECTIVES @@ -195,23 +204,54 @@ def setup(app: Sphinx) -> dict[str, Any]: # ROLES ######################################################################## # Provides :need:`ABC_123` for inline links. - app.add_role("need", NeedsXRefRole(nodeclass=NeedRef, innernodeclass=nodes.emphasis, warn_dangling=True)) + app.add_role( + "need", + NeedsXRefRole( + nodeclass=NeedRef, innernodeclass=nodes.emphasis, warn_dangling=True + ), + ) app.add_role( - "need_incoming", NeedsXRefRole(nodeclass=NeedIncoming, innernodeclass=nodes.emphasis, warn_dangling=True) + "need_incoming", + NeedsXRefRole( + nodeclass=NeedIncoming, innernodeclass=nodes.emphasis, warn_dangling=True + ), ) app.add_role( - "need_outgoing", NeedsXRefRole(nodeclass=NeedOutgoing, innernodeclass=nodes.emphasis, warn_dangling=True) + "need_outgoing", + NeedsXRefRole( + nodeclass=NeedOutgoing, innernodeclass=nodes.emphasis, warn_dangling=True + ), ) - app.add_role("need_part", NeedsXRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) + app.add_role( + "need_part", + NeedsXRefRole( + nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True + ), + ) # Shortcut for need_part - app.add_role("np", NeedsXRefRole(nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True)) + app.add_role( + "np", + NeedsXRefRole( + nodeclass=NeedPart, innernodeclass=nodes.inline, warn_dangling=True + ), + ) - app.add_role("need_count", NeedsXRefRole(nodeclass=NeedCount, innernodeclass=nodes.inline, warn_dangling=True)) + app.add_role( + "need_count", + NeedsXRefRole( + nodeclass=NeedCount, innernodeclass=nodes.inline, warn_dangling=True + ), + ) - app.add_role("need_func", NeedsXRefRole(nodeclass=NeedFunc, innernodeclass=nodes.inline, warn_dangling=True)) + app.add_role( + "need_func", + NeedsXRefRole( + nodeclass=NeedFunc, innernodeclass=nodes.inline, warn_dangling=True + ), + ) ######################################################################## # EVENTS @@ -241,7 +281,11 @@ def setup(app: Sphinx) -> dict[str, Any]: # doctree-read. So manipulating the doctree may result in conflicts, as e.g. images get not # registered for sphinx. So some sphinx-internal tasks/functions may be called by hand again... # See also https://github.com/sphinx-doc/sphinx/issues/7054#issuecomment-578019701 for an example - app.connect("doctree-resolved", process_creator(NODE_TYPES_PRIO, "needextract"), priority=100) + app.connect( + "doctree-resolved", + process_creator(NODE_TYPES_PRIO, "needextract"), + priority=100, + ) app.connect("doctree-resolved", process_need_nodes) app.connect("doctree-resolved", process_creator(NODE_TYPES)) @@ -280,7 +324,8 @@ def process_caller(app: Sphinx, doctree: nodes.document, fromdocname: str) -> No """ # We only need to analyse docs, which have Sphinx-Needs directives in it. if ( - fromdocname not in SphinxNeedsData(app.env).get_or_create_docs().get(doc_category, []) + fromdocname + not in SphinxNeedsData(app.env).get_or_create_docs().get(doc_category, []) and fromdocname != f"{app.config.root_doc}" ): return @@ -297,7 +342,11 @@ def process_caller(app: Sphinx, doctree: nodes.document, fromdocname: str) -> No # Let's call the handlers for check_node, check_func in node_list.items(): # Call the handler only, if it defined, and we found some nodes for it - if check_node in current_nodes and check_func is not None and current_nodes[check_node]: + if ( + check_node in current_nodes + and check_func is not None + and current_nodes[check_node] + ): check_func(app, doctree, fromdocname, current_nodes[check_node]) return process_caller @@ -319,7 +368,9 @@ def load_config(app: Sphinx, *_args: Any) -> None: for option in needs_config.extra_options: if option in extra_options: LOGGER.warning( - f'extra_option "{option}" already registered. [needs.config]', type="needs", subtype="config" + f'extra_option "{option}" already registered. [needs.config]', + type="needs", + subtype="config", ) NEEDS_CONFIG.extra_options[option] = directives.unchanged @@ -397,7 +448,9 @@ def load_config(app: Sphinx, *_args: Any) -> None: NEEDS_CONFIG.warnings[name] = check else: LOGGER.warning( - f"{name!r} in 'needs_warnings' is already registered. [needs.config]", type="needs", subtype="config" + f"{name!r} in 'needs_warnings' is already registered. [needs.config]", + type="needs", + subtype="config", ) if needs_config.constraints_failed_color: @@ -441,7 +494,11 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: # Register user defined services for name, service in needs_config.services.items(): - if name not in services.services and "class" in service and "class_init" in service: + if ( + name not in services.services + and "class" in service + and "class_init" in service + ): # We found a not yet registered service # But only register, if service-config contains class and class_init. # Otherwise, the service may get registered later by an external sphinx-needs extension @@ -456,7 +513,14 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: register_func(needs_func) # Own extra options - for option in ["hidden", "duration", "completion", "has_dead_links", "has_forbidden_dead_links", "constraints"]: + for option in [ + "hidden", + "duration", + "completion", + "has_dead_links", + "has_forbidden_dead_links", + "constraints", + ]: # Check if not already set by user if option not in NEEDS_CONFIG.extra_options: NEEDS_CONFIG.extra_options[option] = directives.unchanged @@ -525,25 +589,29 @@ def check_configuration(_app: Sphinx, config: Config) -> None: # Check if needs external filter and extra option are using the same name if extern_filter in extra_options: raise NeedsConfigException( - "Same name for external filter and extra option: {}." " This is not allowed.".format(extern_filter) + f"Same name for external filter and extra option: {extern_filter}." + " This is not allowed." ) # Check for usage of internal names for internal in INTERNALS: if internal in extra_options: raise NeedsConfigException( - 'Extra option "{}" already used internally. ' " Please use another name.".format(internal) + f'Extra option "{internal}" already used internally. ' + " Please use another name." ) if internal in link_types: raise NeedsConfigException( - 'Link type name "{}" already used internally. ' " Please use another name.".format(internal) + f'Link type name "{internal}" already used internally. ' + " Please use another name." ) # Check if option and link are using the same name for link in link_types: if link in extra_options: raise NeedsConfigException( - "Same name for link type and extra option: {}." " This is not allowed.".format(link) + f"Same name for link type and extra option: {link}." + " This is not allowed." ) if link + "_back" in extra_options: raise NeedsConfigException( @@ -562,7 +630,11 @@ def check_configuration(_app: Sphinx, config: Config) -> None: for option in external_variant_options: # Check variant option is added in either extra options or extra links or NEED_DEFAULT_OPTIONS - if option not in extra_options and option not in link_types and option not in NEED_DEFAULT_OPTIONS.keys(): + if ( + option not in extra_options + and option not in link_types + and option not in NEED_DEFAULT_OPTIONS.keys() + ): raise NeedsConfigException( "Variant option `{}` is not added in either extra options or extra links. " "This is not allowed.".format(option) diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index ece2bbd66..cf1184fc8 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -94,16 +94,30 @@ def update_or_add_version(self, version: str) -> None: def add_need(self, version: str, need_info: NeedsInfoType) -> None: self.update_or_add_version(version) - writable_needs = {key: need_info[key] for key in need_info if key not in self._exclude_need_keys} # type: ignore[literal-required] + writable_needs = { + key: need_info[key] # type: ignore[literal-required] + for key in need_info + if key not in self._exclude_need_keys + } writable_needs["description"] = need_info["content"] self.needs_list["versions"][version]["needs"][need_info["id"]] = writable_needs - self.needs_list["versions"][version]["needs_amount"] = len(self.needs_list["versions"][version]["needs"]) + self.needs_list["versions"][version]["needs_amount"] = len( + self.needs_list["versions"][version]["needs"] + ) def add_filter(self, version: str, need_filter: NeedsFilterType) -> None: self.update_or_add_version(version) - writable_filters = {key: need_filter[key] for key in need_filter if key not in self._exclude_filter_keys} # type: ignore[literal-required] - self.needs_list["versions"][version]["filters"][need_filter["export_id"].upper()] = writable_filters - self.needs_list["versions"][version]["filters_amount"] = len(self.needs_list["versions"][version]["filters"]) + writable_filters = { + key: need_filter[key] # type: ignore[literal-required] + for key in need_filter + if key not in self._exclude_filter_keys + } + self.needs_list["versions"][version]["filters"][ + need_filter["export_id"].upper() + ] = writable_filters + self.needs_list["versions"][version]["filters_amount"] = len( + self.needs_list["versions"][version]["filters"] + ) def wipe_version(self, version: str) -> None: if version in self.needs_list["versions"]: @@ -130,7 +144,9 @@ def load_json(self, file: str) -> None: file = os.path.join(self.confdir, file) if not os.path.exists(file): - self.log.warning(f"Could not load needs json file {file} [needs]", type="needs") + self.log.warning( + f"Could not load needs json file {file} [needs]", type="needs" + ) else: errors = check_needs_file(file) # We only care for schema errors here, all other possible errors @@ -144,7 +160,9 @@ def load_json(self, file: str) -> None: try: needs_list = json.load(needs_file) except json.JSONDecodeError: - self.log.warning(f"Could not decode json file {file} [needs]", type="needs") + self.log.warning( + f"Could not decode json file {file} [needs]", type="needs" + ) else: self.needs_list = needs_list diff --git a/sphinx_needs/roles/need_count.py b/sphinx_needs/roles/need_count.py index 8ff1299a8..15275a090 100644 --- a/sphinx_needs/roles/need_count.py +++ b/sphinx_needs/roles/need_count.py @@ -23,7 +23,10 @@ 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 23daee4ac..3497f395b 100644 --- a/sphinx_needs/roles/need_func.py +++ b/sphinx_needs/roles/need_func.py @@ -18,11 +18,18 @@ 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): for node_need_func in found_nodes: - result = check_and_get_content(node_need_func.attributes["reftarget"], {"id": "need_func_dummy"}, env) # type: ignore + result = check_and_get_content( + node_need_func.attributes["reftarget"], + {"id": "need_func_dummy"}, # type: ignore + env, + ) new_node_func = nodes.Text(str(result)) node_need_func.replace_self(new_node_func) diff --git a/sphinx_needs/roles/need_incoming.py b/sphinx_needs/roles/need_incoming.py index fcdf5251b..2aa939035 100644 --- a/sphinx_needs/roles/need_incoming.py +++ b/sphinx_needs/roles/need_incoming.py @@ -15,7 +15,10 @@ 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 @@ -64,8 +67,12 @@ def process_need_incoming( node_need_backref["reftarget"], ) else: - assert target_need["external_url"] is not None, "External URL must not be set" - new_node_ref = nodes.reference(target_need["id"], target_need["id"]) + assert ( + target_need["external_url"] is not None + ), "External URL must not be set" + new_node_ref = nodes.reference( + target_need["id"], target_need["id"] + ) new_node_ref["refuri"] = check_and_calc_base_url_rel_path( target_need["external_url"], fromdocname ) @@ -82,7 +89,10 @@ def process_need_incoming( pass else: - logger.warning(f"need {node_need_backref['reftarget']} not found [needs]", location=node_need_backref) + logger.warning( + f"need {node_need_backref['reftarget']} not found [needs]", + location=node_need_backref, + ) if len(node_link_container.children) == 0: node_link_container += nodes.Text("None") diff --git a/sphinx_needs/roles/need_outgoing.py b/sphinx_needs/roles/need_outgoing.py index 1939b7e4e..c6391129d 100644 --- a/sphinx_needs/roles/need_outgoing.py +++ b/sphinx_needs/roles/need_outgoing.py @@ -18,7 +18,10 @@ 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 @@ -55,7 +58,11 @@ def process_need_outgoing( target_need = needs_all_needs[need_id_main] if need_id_part and need_id_part in target_need["parts"]: part_content = target_need["parts"][need_id_part]["content"] - target_title = part_content if len(part_content) < 30 else part_content[:27] + "..." + target_title = ( + part_content + if len(part_content) < 30 + else part_content[:27] + "..." + ) target_id = ".".join([need_id_main, need_id_part]) else: target_title = target_need["title"] @@ -84,8 +91,12 @@ def process_need_outgoing( node_need_ref["reftarget"], ) else: - assert target_need["external_url"] is not None, "External URL must be set" - new_node_ref = nodes.reference(target_need["id"], target_need["id"]) + assert ( + target_need["external_url"] is not None + ), "External URL must be set" + new_node_ref = nodes.reference( + target_need["id"], target_need["id"] + ) new_node_ref["refuri"] = check_and_calc_base_url_rel_path( target_need["external_url"], fromdocname ) diff --git a/sphinx_needs/roles/need_part.py b/sphinx_needs/roles/need_part.py index 242d197d4..ae2b1eb89 100644 --- a/sphinx_needs/roles/need_part.py +++ b/sphinx_needs/roles/need_part.py @@ -27,14 +27,21 @@ 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: @@ -45,7 +52,9 @@ def update_need_with_parts(env: BuildEnvironment, need: NeedsInfoType, part_node part_content = result.group(2) else: part_content = content - inline_id = hashlib.sha1(part_content.encode("UTF-8")).hexdigest().upper()[:3] + inline_id = ( + hashlib.sha1(part_content.encode("UTF-8")).hexdigest().upper()[:3] + ) if "parts" not in need: need["parts"] = {} @@ -78,7 +87,9 @@ def update_need_with_parts(env: BuildEnvironment, need: NeedsInfoType, part_node from sphinx.util.nodes import make_refnode - part_ref_node = make_refnode(builder, need["docname"], need["docname"], part_id_ref, part_link_node) + part_ref_node = make_refnode( + builder, need["docname"], need["docname"], part_id_ref, part_link_node + ) part_ref_node["classes"] += ["needs-id"] part_node.children = [] diff --git a/sphinx_needs/roles/need_ref.py b/sphinx_needs/roles/need_ref.py index d817b9274..231d3c627 100644 --- a/sphinx_needs/roles/need_ref.py +++ b/sphinx_needs/roles/need_ref.py @@ -51,7 +51,12 @@ 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) @@ -78,7 +83,9 @@ def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, fou if need_id_main in all_needs: target_need = all_needs[need_id_main] - dict_need = transform_need_to_dict(target_need) # Transform a dict in a dict of {str, str} + dict_need = transform_need_to_dict( + target_need + ) # Transform a dict in a dict of {str, str} # We set the id to the complete id maintained in node_need_ref["reftarget"] dict_need["id"] = need_id_full @@ -118,7 +125,8 @@ def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, fou link_text = needs_config.role_need_template.format(**dict_need) except KeyError as e: link_text = ( - '"the config parameter needs_role_need_template uses not supported placeholders: %s "' % e + '"the config parameter needs_role_need_template uses not supported placeholders: %s "' + % e ) log.warning(link_text + " [needs]", type="needs") @@ -135,9 +143,13 @@ def process_need_ref(app: Sphinx, doctree: nodes.document, fromdocname: str, fou node_need_ref["reftarget"], ) else: - assert target_need["external_url"] is not None, "external_url must be set for external needs" + assert ( + target_need["external_url"] is not None + ), "external_url must be set for external needs" new_node_ref = nodes.reference(target_need["id"], target_need["id"]) - new_node_ref["refuri"] = check_and_calc_base_url_rel_path(target_need["external_url"], fromdocname) + new_node_ref["refuri"] = check_and_calc_base_url_rel_path( + target_need["external_url"], fromdocname + ) new_node_ref["classes"].append(target_need["external_css"]) else: diff --git a/sphinx_needs/services/config/github.py b/sphinx_needs/services/config/github.py index ed2c2a6e2..65f40708c 100644 --- a/sphinx_needs/services/config/github.py +++ b/sphinx_needs/services/config/github.py @@ -3,9 +3,22 @@ EXTRA_DATA_OPTIONS = ["user", "created_at", "updated_at", "closed_at", "service"] EXTRA_LINK_OPTIONS = ["url"] EXTRA_IMAGE_OPTIONS = ["avatar"] -CONFIG_OPTIONS = ["type", "query", "specific", "max_amount", "max_content_lines", "id_prefix"] -GITHUB_DATA = ["status", "tags"] + EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS -GITHUB_DATA_STR = '"' + '","'.join(EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS) + '"' +CONFIG_OPTIONS = [ + "type", + "query", + "specific", + "max_amount", + "max_content_lines", + "id_prefix", +] +GITHUB_DATA = ( + ["status", "tags"] + EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS +) +GITHUB_DATA_STR = ( + '"' + + '","'.join(EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS) + + '"' +) CONFIG_DATA_STR = '"' + '","'.join(CONFIG_OPTIONS) + '"' GITHUB_LAYOUT = { "grid": "complex", @@ -18,14 +31,20 @@ "head": [ '**<>** (' + ", ".join( - ['<>'.format(value=x) for x in EXTRA_LINK_OPTIONS] + [ + f'<>' + for x in EXTRA_LINK_OPTIONS + ] ) + ")" ], - "head_right": ['<>', '<>'], - "meta_left": ['<>'.format(value=x) for x in EXTRA_DATA_OPTIONS] + "head_right": [ + '<>', + '<>', + ], + "meta_left": [f'<>' for x in EXTRA_DATA_OPTIONS] + [ - '<>'.format(value=x) + f'<>' for x in EXTRA_LINK_OPTIONS ], "meta_right": [ diff --git a/sphinx_needs/services/github.py b/sphinx_needs/services/github.py index b2fc6da72..4e5c54c84 100644 --- a/sphinx_needs/services/github.py +++ b/sphinx_needs/services/github.py @@ -25,9 +25,13 @@ class GithubService(BaseService): - options = CONFIG_OPTIONS + EXTRA_DATA_OPTIONS + EXTRA_LINK_OPTIONS + EXTRA_IMAGE_OPTIONS + 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 @@ -50,7 +54,11 @@ def __init__(self, app: Sphinx, name: str, config: dict[str, Any], **kwargs: Any layouts["github"] = GITHUB_LAYOUT self.gh_type_config = { - "issue": {"url": "search/issues", "query": "is:issue", "need_type": "issue"}, + "issue": { + "url": "search/issues", + "query": "is:issue", + "need_type": "issue", + }, "pr": {"url": "search/issues", "query": "is:pr", "need_type": "pr"}, "commit": {"url": "search/commits", "query": "", "need_type": "commit"}, } @@ -68,15 +76,21 @@ def __init__(self, app: Sphinx, name: str, config: dict[str, Any], **kwargs: Any if self.gh_type not in self.gh_type_config.keys(): raise KeyError( - 'github type "{}" not supported. Use: {}'.format(self.gh_type, ", ".join(self.gh_type_config.keys())) + 'github type "{}" not supported. Use: {}'.format( + self.gh_type, ", ".join(self.gh_type_config.keys()) + ) ) # Set need_type to use by default - self.need_type = self.config.get("need_type", self.gh_type_config[self.gh_type]["need_type"]) + self.need_type = self.config.get( + "need_type", self.gh_type_config[self.gh_type]["need_type"] + ) 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" @@ -93,17 +107,20 @@ def _send(self, query: str, options: dict[str, Any], specific: bool = False) -> single_type = "pulls" else: single_type = "commits" - url = self.url + "repos/{owner}/{repo}/{single_type}/{number}".format( - owner=owner, repo=repo, single_type=single_type, number=number - ) + url = self.url + f"repos/{owner}/{repo}/{single_type}/{number}" except IndexError: - raise NeedGithubServiceException('Single option ot valid, must follow "owner/repo/number"') + raise NeedGithubServiceException( + 'Single option ot valid, must follow "owner/repo/number"' + ) params = {} else: url = self.url + self.gh_type_config[self.gh_type]["url"] query = "{} {}".format(query, self.gh_type_config[self.gh_type]["query"]) - params = {"q": query, "per_page": options.get("max_amount", self.max_amount)} + params = { + "q": query, + "per_page": options.get("max_amount", self.max_amount), + } self.log.info(f"Service {self.name} requesting data for query: {query}") @@ -122,26 +139,30 @@ def _send(self, query: str, options: dict[str, Any], specific: bool = False) -> if "rate limit" in resp.json()["message"]: resp_limit = requests.get(self.url + "rate_limit", auth=auth) extra_info = resp_limit.json() - self.log.info("GitHub: API rate limit exceeded. We need to wait 60 secs...") + self.log.info( + "GitHub: API rate limit exceeded. We need to wait 60 secs..." + ) self.log.info(extra_info) time.sleep(61) resp = requests.get(url, params=params, auth=auth, headers=headers) if resp.status_code > 299: if "rate limit" in resp.json()["message"]: - raise NeedGithubServiceException("GitHub: API rate limit exceeded (twice). Stop here.") + raise NeedGithubServiceException( + "GitHub: API rate limit exceeded (twice). Stop here." + ) else: raise NeedGithubServiceException( "Github service error during request.\n" - "Status code: {}\n" - "Error: {}\n" - "{}".format(resp.status_code, resp.text, extra_info) + f"Status code: {resp.status_code}\n" + f"Error: {resp.text}\n" + f"{extra_info}" ) else: raise NeedGithubServiceException( "Github service error during request.\n" - "Status code: {}\n" - "Error: {}\n" - "{}".format(resp.status_code, resp.text, extra_info) + f"Status code: {resp.status_code}\n" + f"Error: {resp.text}\n" + f"{extra_info}" ) if specific: @@ -154,9 +175,13 @@ def request(self, options: dict[str, Any] | None = None) -> list[dict[str, Any]] self.log.debug(f"Requesting data for service {self.name}") if "query" not in options and "specific" not in options: - raise NeedGithubServiceException('"query" or "specific" missing as option for github service.') + raise NeedGithubServiceException( + '"query" or "specific" missing as option for github service.' + ) elif "query" in options and "specific" in options: - raise NeedGithubServiceException('Only "query" or "specific" allowed for github service. Not both!') + raise NeedGithubServiceException( + 'Only "query" or "specific" allowed for github service. Not both!' + ) elif "query" in options: query = options["query"] specific = False @@ -168,7 +193,9 @@ def request(self, options: dict[str, Any] | None = None) -> list[dict[str, Any]] if "items" not in response: if "errors" in response: raise NeedGithubServiceException( - "GitHub service query error: {}\n" "Used query: {}".format(response["errors"][0]["message"], query) + "GitHub service query error: {}\n" "Used query: {}".format( + response["errors"][0]["message"], query + ) ) else: raise NeedGithubServiceException("Github service: Unknown error.") @@ -182,7 +209,9 @@ def request(self, options: dict[str, Any] | None = None) -> list[dict[str, Any]] 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() @@ -191,7 +220,11 @@ def prepare_issue_data(self, items: list[dict[str, Any]], options: dict[str, Any # wraps content lines, if they are too long. Respects already existing newlines. content_lines = [ - "\n ".join(textwrap.wrap(line, 60, break_long_words=True, replace_whitespace=False)) + "\n ".join( + textwrap.wrap( + line, 60, break_long_words=True, replace_whitespace=False + ) + ) for line in item["body"].splitlines() # type: ignore if line.strip() ] @@ -199,7 +232,9 @@ def prepare_issue_data(self, items: list[dict[str, Any]], options: dict[str, Any content = "\n\n ".join(content_lines) # Reduce content length, if requested by config if self.max_content_lines > 0: - max_lines = int(options.get("max_content_lines", self.max_content_lines)) + max_lines = int( + options.get("max_content_lines", self.max_content_lines) + ) content_lines = content.splitlines() if len(content_lines) > max_lines: content_lines = content_lines[0:max_lines] @@ -242,7 +277,9 @@ 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: @@ -253,7 +290,9 @@ def prepare_commit_data(self, items: list[dict[str, Any]], options: dict[str, An "type": options.get("type", self.need_type), "layout": options.get("layout", self.layout), "id": self.id_prefix + item["sha"][:6], - "title": item["commit"]["message"].split("\n")[0][:60], # 1. line, max length 60 chars + "title": item["commit"]["message"].split("\n")[0][ + :60 + ], # 1. line, max length 60 chars "content": item["commit"]["message"], "user": item["author"]["login"], "url": item["html_url"], @@ -278,7 +317,9 @@ def _get_avatar(self, avatar_url: str) -> str: avatar_file_path = os.path.join(path, filename) # Placeholder avatar, if things go wrong or avatar download is deactivated - default_avatar_file_path = os.path.join(os.path.dirname(__file__), "../images/avatar.png") + default_avatar_file_path = os.path.join( + os.path.dirname(__file__), "../images/avatar.png" + ) if self.download_avatars: # Download only, if file not downloaded yet if not os.path.exists(avatar_file_path): @@ -294,20 +335,20 @@ def _get_avatar(self, avatar_url: str) -> str: f.write(response.content) elif response.status_code == 302: self.log.warning( - "GitHub service {} could not download avatar image " - "from {}.\n" - " Status code: {}\n" + f"GitHub service {self.name} could not download avatar image " + f"from {avatar_url}.\n" + f" Status code: {response.status_code}\n" " Reason: Looks like the authentication provider tries to redirect you." " This is not supported and is a common problem, " - "if you use GitHub Enterprise. [needs]".format(self.name, avatar_url, response.status_code), + "if you use GitHub Enterprise. [needs]", type="needs", ) avatar_file_path = default_avatar_file_path else: self.log.warning( - "GitHub service {} could not download avatar image " - "from {}.\n" - " Status code: {} [needs]".format(self.name, avatar_url, response.status_code), + f"GitHub service {self.name} could not download avatar image " + f"from {avatar_url}.\n" + f" Status code: {response.status_code} [needs]", type="needs", ) avatar_file_path = default_avatar_file_path @@ -316,7 +357,9 @@ 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 ecdacadaf..b95962a22 100644 --- a/sphinx_needs/services/manager.py +++ b/sphinx_needs/services/manager.py @@ -23,7 +23,9 @@ def register(self, name: str, klass: type[BaseService], **kwargs: Any) -> None: try: config = NeedsSphinxConfig(self.app.config).services[name] except KeyError: - self.log.debug(f"No service config found for {name}. Add it in your conf.py to needs_services dictionary.") + self.log.debug( + f"No service config found for {name}. Add it in your conf.py to needs_services dictionary." + ) config = {} # Register options from service class @@ -43,7 +45,9 @@ def get(self, name: str) -> BaseService: return self.services[name] else: raise NeedsServiceException( - "Service {} could not be found. " "Available services are {}".format(name, ", ".join(self.services)) + "Service {} could not be found. " "Available services are {}".format( + name, ", ".join(self.services) + ) ) diff --git a/sphinx_needs/services/open_needs.py b/sphinx_needs/services/open_needs.py index 6a9ea2195..d11e99b30 100644 --- a/sphinx_needs/services/open_needs.py +++ b/sphinx_needs/services/open_needs.py @@ -24,7 +24,9 @@ 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 @@ -41,7 +43,9 @@ def __init__(self, app: Sphinx, name: str, config: dict[str, Any], **kwargs: Any self.query = self.config.get("query", "") self.content = self.config.get("content", DEFAULT_CONTENT) self.mappings: dict[str, Any] = self.config.get("mappings", {}) - self.mapping_replaces = self.config.get("mappings_replaces", MAPPINGS_REPLACES_DEFAULT) + self.mapping_replaces = self.config.get( + "mappings_replaces", MAPPINGS_REPLACES_DEFAULT + ) self.extra_data: dict[str, Any] = self.config.get("extra_data", {}) self.params = self.config.get("params", "skip=0,limit=100") @@ -59,8 +63,8 @@ def _oauthorization(self) -> None: if login_resp.status_code != 200: raise OpenNeedsServiceException( "ONS service error during request.\n" - "Status code: {}\n" - "Error: {}\n".format(login_resp.status_code, login_resp.text) + f"Status code: {login_resp.status_code}\n" + f"Error: {login_resp.text}\n" ) oauth_credentials = dict(**login_resp.json()) self.token_type = oauth_credentials.get("token_type") @@ -72,8 +76,13 @@ 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}" @@ -93,10 +102,14 @@ def _send_request(request: Any) -> Any: result: Any = requests.get(**request) if result.status_code >= 300: - raise OpenNeedsServiceException(f"Problem accessing {result.url}.\nReason: {result.text}") + 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 @@ -176,7 +189,10 @@ def _extract_data(self, data: list[dict[str, Any]], options: dict[str, Any]) -> if name == "links": # Add a prefix to the referenced link if it is an ID of a need object in # the data retrieved from the Open Needs Server or don't add prefix - value = [(prefix + link if link in ids_of_needs_data else link) for link in value] + value = [ + (prefix + link if link in ids_of_needs_data else link) + for link in value + ] value = ";".join(value) # Ensures mapping option with value == None is not implemented. E.g. the links option # can't be == None since there will be nothing to link to and that will raise a warning @@ -184,7 +200,9 @@ def _extract_data(self, data: list[dict[str, Any]], options: dict[str, Any]) -> need_values[name] = value for regex, new_str in self.mapping_replaces.items(): - need_values[name] = re.sub(regex, new_str, need_values.get(name, "")) + need_values[name] = re.sub( + regex, new_str, need_values.get(name, "") + ) if name == "id": need_values[name] = str(prefix) + str(need_values.get(name, "")) diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index eb0fcccb7..4476c9bb9 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -173,8 +173,12 @@ def row_col_maker( link_string_list = {} for link_name, link_conf in needs_config.string_links.items(): link_string_list[link_name] = { - "url_template": Environment(autoescape=True).from_string(link_conf["link_url"]), - "name_template": Environment(autoescape=True).from_string(link_conf["link_name"]), + "url_template": Environment(autoescape=True).from_string( + link_conf["link_url"] + ), + "name_template": Environment(autoescape=True).from_string( + link_conf["link_name"] + ), "regex_compiled": re.compile(link_conf["regex"]), "options": link_conf["options"], "name": link_name, @@ -203,22 +207,34 @@ def row_col_maker( if make_ref: if need_info["is_external"]: - assert need_info["external_url"] is not None, "external_url must be set for external needs" - ref_col["refuri"] = check_and_calc_base_url_rel_path(need_info["external_url"], fromdocname) + assert ( + need_info["external_url"] is not None + ), "external_url must be set for external needs" + ref_col["refuri"] = check_and_calc_base_url_rel_path( + need_info["external_url"], fromdocname + ) ref_col["classes"].append(need_info["external_css"]) row_col["classes"].append(need_info["external_css"]) else: - ref_col["refuri"] = builder.get_relative_uri(fromdocname, need_info["docname"]) + ref_col["refuri"] = builder.get_relative_uri( + fromdocname, need_info["docname"] + ) ref_col["refuri"] += "#" + datum elif ref_lookup: temp_need = all_needs[link_id] if temp_need["is_external"]: - assert temp_need["external_url"] is not None, "external_url must be set for external needs" - ref_col["refuri"] = check_and_calc_base_url_rel_path(temp_need["external_url"], fromdocname) + assert ( + temp_need["external_url"] is not None + ), "external_url must be set for external needs" + ref_col["refuri"] = check_and_calc_base_url_rel_path( + temp_need["external_url"], fromdocname + ) ref_col["classes"].append(temp_need["external_css"]) row_col["classes"].append(temp_need["external_css"]) else: - ref_col["refuri"] = builder.get_relative_uri(fromdocname, temp_need["docname"]) + ref_col["refuri"] = builder.get_relative_uri( + fromdocname, temp_need["docname"] + ) ref_col["refuri"] += "#" + temp_need["id"] if link_part: ref_col["refuri"] += "." + link_part @@ -230,7 +246,11 @@ def row_col_maker( para_col += ref_col elif matching_link_confs: para_col += match_string_link( - datum_text, datum, need_key, matching_link_confs, render_context=needs_config.render_context + datum_text, + datum, + need_key, + matching_link_confs, + render_context=needs_config.render_context, ) else: para_col += text_col @@ -258,7 +278,9 @@ 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). @@ -285,7 +307,9 @@ def import_prefix_link_edit(needs: dict[str, Any], id_prefix: str, needs_extra_l need[extra_link["option"]][n] = f"{id_prefix}{id}" # Manipulate descriptions # ToDo: Use regex for better matches. - need["description"] = need["description"].replace(id, "".join([id_prefix, id])) + need["description"] = need["description"].replace( + id, "".join([id_prefix, id]) + ) FuncT = TypeVar("FuncT") @@ -332,7 +356,11 @@ def check_and_calc_base_url_rel_path(external_url: str, fromdocname: str) -> str # get path sep considering plattform dependency, '\' for Windows, '/' fro Unix curr_path_sep = os.path.sep # check / or \ to determine the relative path to conf.py directory - if not parsed_url.scheme and not os.path.isabs(external_url) and curr_path_sep in fromdocname: + if ( + not parsed_url.scheme + and not os.path.isabs(external_url) + and curr_path_sep in fromdocname + ): sub_level = len(fromdocname.split(curr_path_sep)) - 1 ref_uri = os.path.join(sub_level * (".." + curr_path_sep), external_url) @@ -350,7 +378,8 @@ def check_and_get_external_filter_func(filter_func_ref: str | None) -> tuple[Any filter_module, filter_function = filter_func_ref.rsplit(".") except ValueError: logger.warning( - f'Filter function not valid "{filter_func_ref}". Example: my_module:my_func [needs]', type="needs" + f'Filter function not valid "{filter_func_ref}". Example: my_module:my_func [needs]', + type="needs", ) return filter_func, filter_args @@ -364,7 +393,10 @@ def check_and_get_external_filter_func(filter_func_ref: str | None) -> tuple[Any final_module = importlib.import_module(filter_module) filter_func = getattr(final_module, filter_function) except Exception: - logger.warning(f"Could not import filter function: {filter_func_ref} [needs]", type="needs") + logger.warning( + f"Could not import filter function: {filter_func_ref} [needs]", + type="needs", + ) return filter_func, filter_args return filter_func, filter_args @@ -385,7 +417,10 @@ def jinja_parse(context: dict[str, Any], jinja_string: str) -> str: try: content_template = Template(jinja_string, autoescape=True) except Exception as e: - raise ReferenceError(f'There was an error in the jinja statement: "{jinja_string}". ' f"Error Msg: {e}") + raise ReferenceError( + f'There was an error in the jinja statement: "{jinja_string}". ' + f"Error Msg: {e}" + ) content = content_template.render(**context) return content @@ -407,7 +442,9 @@ def import_matplotlib() -> matplotlib | None: 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 @@ -464,20 +501,34 @@ 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 link_url = None - link_conf = matching_link_confs[0] # We only handle the first matching string_link + link_conf = matching_link_confs[ + 0 + ] # We only handle the first matching string_link match = link_conf["regex_compiled"].search(data) if match: render_content = match.groupdict() - link_url = link_conf["url_template"].render(**render_content, **render_context) - link_name = link_conf["name_template"].render(**render_content, **render_context) + link_url = link_conf["url_template"].render( + **render_content, **render_context + ) + link_name = link_conf["name_template"].render( + **render_content, **render_context + ) # if no string_link match was made, we handle it as normal string value - ref_item = nodes.reference(link_name, link_name, refuri=link_url) if link_name else nodes.Text(text_item) + ref_item = ( + nodes.reference(link_name, link_name, refuri=link_url) + if link_name + else nodes.Text(text_item) + ) except Exception as e: logger.warning( @@ -490,7 +541,9 @@ def match_string_link( def match_variants( - option_value: str | list[str], keywords: dict[str, Any], needs_variants: dict[str, 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. @@ -503,7 +556,9 @@ def match_variants( """ def variant_handling( - variant_definitions: list[str], variant_data: dict[str, Any], variant_pattern: Pattern # type: ignore[type-arg] + variant_definitions: list[str], + variant_data: dict[str, Any], + variant_pattern: Pattern, # type: ignore[type-arg] ) -> str | None: filter_context = variant_data # filter_result = [] @@ -515,9 +570,15 @@ def variant_handling( if check_definition: variants_in_option = True # Separate variant definition from value to use for the option - filter_string, output, _ = re.split(r"(:[\w':.\-\" ]+)$", variant_definition) + filter_string, output, _ = re.split( + r"(:[\w':.\-\" ]+)$", variant_definition + ) filter_string = re.sub(r"^\[|[:\]]$", "", filter_string) - filter_string = needs_variants[filter_string] if filter_string in needs_variants else filter_string + filter_string = ( + needs_variants[filter_string] + if filter_string in needs_variants + else filter_string + ) try: # https://docs.python.org/3/library/functions.html?highlight=compile#compile filter_compiled = compile(filter_string, "", "eval") @@ -533,7 +594,8 @@ def variant_handling( return output.lstrip(":") except Exception as e: logger.warning( - f'There was an error in the filter statement: "{filter_string}". ' f"Error Msg: {e} [needs]", + f'There was an error in the filter statement: "{filter_string}". ' + f"Error Msg: {e} [needs]", type="needs", ) else: @@ -557,11 +619,17 @@ def variant_handling( if isinstance(option_value, str): 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, ";", "", " ") + re.sub(r"^([;, ]+)|([;, ]+$)", "", i) + for i in multiple_variants + if i not in (None, ";", "", " ") ] - if len(multiple_variants) == 1 and not variant_rule_matching.search(multiple_variants[0]): + if len(multiple_variants) == 1 and not variant_rule_matching.search( + multiple_variants[0] + ): return option_value - new_option_value = variant_handling(multiple_variants, keywords, variant_rule_matching) + new_option_value = variant_handling( + multiple_variants, keywords, variant_rule_matching + ) if new_option_value is None: return option_value return new_option_value @@ -570,10 +638,14 @@ def variant_handling( # In case an option value is a list (:tags: open; close), and does not contain any variant definition, # then return the unmodified value # options = all([bool(not variant_rule_matching.search(i)) for i in multiple_variants]) - options = all(bool(not variant_rule_matching.search(i)) for i in multiple_variants) + options = all( + bool(not variant_rule_matching.search(i)) for i in multiple_variants + ) if options: return option_value - new_option_value = variant_handling(multiple_variants, keywords, variant_rule_matching) + new_option_value = variant_handling( + multiple_variants, keywords, variant_rule_matching + ) return new_option_value else: return option_value @@ -596,7 +668,9 @@ def clean_log(data: str) -> str: return clean_credentials -def node_match(node_types: 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() @@ -618,7 +692,9 @@ def node_match(node_types: type[nodes.Element] | list[type[nodes.Element]]) -> C """ 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 diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index c110e1496..1f9a47c2c 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -61,7 +61,9 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: for warning_name, warning_filter in NEEDS_CONFIG.warnings.items(): if isinstance(warning_filter, str): # filter string used - result = filter_needs(checked_needs.values(), needs_config, warning_filter) + result = filter_needs( + checked_needs.values(), needs_config, warning_filter + ) elif callable(warning_filter): # custom defined filter code used from conf.py result = [] @@ -69,7 +71,10 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: if warning_filter(need, logger): result.append(need) else: - logger.warning(f"Unknown needs warnings filter {warning_filter}! [needs]", type="needs") + logger.warning( + f"Unknown needs warnings filter {warning_filter}! [needs]", + type="needs", + ) if len(result) == 0: logger.info(f"{warning_name}: passed") @@ -94,17 +99,26 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: if warnings_always_warn: logger.warning( "{}: failed\n\t\tfailed needs: {} ({})\n\t\tused filter: {} [needs]".format( - warning_name, len(need_ids), ", ".join(need_ids), warning_text + warning_name, + len(need_ids), + ", ".join(need_ids), + warning_text, ), type="needs", ) else: logger.info( "{}: failed\n\t\tfailed needs: {} ({})\n\t\tused filter: {}".format( - warning_name, len(need_ids), ", ".join(need_ids), warning_text + warning_name, + len(need_ids), + ", ".join(need_ids), + warning_text, ) ) warning_raised = True if warning_raised: - logger.warning("warnings were raised. See console / log output for details. [needs]", type="needs") + logger.warning( + "warnings were raised. See console / log output for details. [needs]", + type="needs", + ) diff --git a/tests/benchmarks/test_basic.py b/tests/benchmarks/test_basic.py index 02c329101..a45133136 100644 --- a/tests/benchmarks/test_basic.py +++ b/tests/benchmarks/test_basic.py @@ -8,7 +8,9 @@ @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True +) def test_basic_time(test_app, benchmark): responses.add_callback( responses.GET, @@ -16,7 +18,9 @@ def test_basic_time(test_app, benchmark): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app benchmark.pedantic(app.builder.build_all, rounds=1, iterations=1) diff --git a/tests/benchmarks/test_official.py b/tests/benchmarks/test_official.py index b2f3ba6a8..663a3ea86 100644 --- a/tests/benchmarks/test_official.py +++ b/tests/benchmarks/test_official.py @@ -9,7 +9,9 @@ @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "../docs"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "../docs"}], indirect=True +) def test_official_time(test_app, benchmark): responses.add_callback( responses.GET, @@ -17,7 +19,9 @@ def test_official_time(test_app, benchmark): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app benchmark.pedantic(app.builder.build_all, rounds=1, iterations=1) @@ -29,7 +33,11 @@ def test_official_time(test_app, benchmark): @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "../docs", "parallel": 1}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "../docs", "parallel": 1}], + indirect=True, +) def test_official_memory(test_app): responses.add_callback( responses.GET, @@ -37,7 +45,9 @@ def test_official_memory(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app diff --git a/tests/conftest.py b/tests/conftest.py index c631b27fc..e39cd8524 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -83,7 +83,16 @@ class Starter(ProcessStarter): pattern = "Serving HTTP on [0-9.]+ port 62343|Address already in use" timeout = 20 terminate_on_interrupt = True - args = ["python3", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", addr, port] + args = [ + "python3", + "-m", + "http.server", + "--directory", + sphinx_test_tempdir, + "--bind", + addr, + port, + ] env = {"PYTHONUNBUFFERED": "1"} def check_server_connection(log_path: str): @@ -98,13 +107,18 @@ def check_server_connection(log_path: str): sock.close() if result == 0: with open(str(log_path), "wb", 0) as stdout: - stdout.write(bytes("Serving HTTP on 127.0.0.1 port 62343 (http://127.0.0.1:62343/) ...\n", "utf8")) + stdout.write( + bytes( + "Serving HTTP on 127.0.0.1 port 62343 (http://127.0.0.1:62343/) ...\n", + "utf8", + ) + ) return True return False if not check_server_connection(log_path=xprocess.getinfo("http_server").logpath): # Start the process and ensure it is running - _, logfile = xprocess.ensure("http_server", Starter, persist_logs=False) # noqa:F841 + _, logfile = xprocess.ensure("http_server", Starter, persist_logs=False) http_server_process = xprocess.getinfo("http_server") server_url = f"http://{addr}:{port}" @@ -129,7 +143,9 @@ def test_js(self) -> Dict[str, Any]: """ cypress_testpath = get_abspath(self.spec_pattern) - if not cypress_testpath or not (os.path.isabs(cypress_testpath) and os.path.exists(cypress_testpath)): + if not cypress_testpath or not ( + os.path.isabs(cypress_testpath) and os.path.exists(cypress_testpath) + ): return { "returncode": 1, "stdout": None, @@ -179,7 +195,12 @@ def test_js(self) -> Dict[str, Any]: def pytest_addoption(parser): - parser.addoption("--sn-build-dir", action="store", default=None, help="Base directory for sphinx-needs builds") + parser.addoption( + "--sn-build-dir", + action="store", + default=None, + help="Base directory for sphinx-needs builds", + ) @pytest.fixture(scope="session") @@ -195,7 +216,9 @@ def sphinx_test_tempdir(request) -> path: # We create a temp-folder on our own, as the util-functions from sphinx and pytest make troubles. # It seems like they reuse certain-temp names - temp_base = os.path.abspath(request.config.getoption("--sn-build-dir") or tempfile.gettempdir()) + temp_base = os.path.abspath( + request.config.getoption("--sn-build-dir") or tempfile.gettempdir() + ) sphinx_test_tempdir = path(temp_base).joinpath("sn_test_build_data") utils_dir = sphinx_test_tempdir.joinpath("utils") @@ -235,7 +258,9 @@ def test_app(make_app, sphinx_test_tempdir, request): if not builder_params.get("no_plantuml", False): # Since we don't want copy the plantuml.jar file for each test function, # we need to override the plantuml conf variable and set it to what we have already - plantuml = "java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") + plantuml = "java -Djava.awt.headless=true -jar %s" % os.path.join( + sphinx_test_tempdir, "utils", "plantuml.jar" + ) sphinx_conf_overrides.update(plantuml=plantuml) # copy test srcdir to test temporary directory sphinx_test_tempdir diff --git a/tests/data/service_github.py b/tests/data/service_github.py index fb24ab4d8..9151feb32 100644 --- a/tests/data/service_github.py +++ b/tests/data/service_github.py @@ -108,7 +108,8 @@ GITHUB_SPECIFIC_ISSUE_ANSWER = { "url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/141", "repository_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs", - "labels_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/" "141/labels{/name}", + "labels_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/" + "141/labels{/name}", "comments_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/141/comments", "events_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/141/events", "html_url": "https://github.com/useblocks/sphinxcontrib-needs/issues/141", @@ -140,7 +141,8 @@ { "id": 491973814, "node_id": "MDU6TGFiZWw0OTE5NzM4MTQ=", - "url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs" "/labels/bug", + "url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs" + "/labels/bug", "name": "bug", "color": "ee0701", "default": True, @@ -318,7 +320,8 @@ "/collaborators{/collaborator}", "teams_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/teams", "hooks_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/hooks", - "issue_events_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/events{" "/number}", + "issue_events_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues/events{" + "/number}", "events_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/events", "assignees_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/assignees{/user}", "branches_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/branches{/branch}", @@ -334,17 +337,22 @@ "subscribers_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/subscribers", "subscription_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/subscription", "commits_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/git" "/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/git" + "/commits{/sha}", "comments_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues" "/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues" + "/comments{/number}", "contents_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/contents/{+path}", - "compare_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/compare/{base}...{" "head}", + "compare_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/compare/{base}...{" + "head}", "merges_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/merges", - "archive_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/{archive_format}{" "/ref}", + "archive_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/{archive_format}{" + "/ref}", "downloads_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/downloads", "issues_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/issues{/number}", "pulls_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/pulls{/number}", - "milestones_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/milestones{" "/number}", + "milestones_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/milestones{" + "/number}", "notifications_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs" "/notifications{?since,all,participating}", "labels_url": "https://api.github.com/repos/useblocks/sphinxcontrib-needs/labels{/name}", diff --git a/tests/doc_test/api_doc/conf.py b/tests/doc_test/api_doc/conf.py index 7832f346d..df50838b8 100644 --- a/tests/doc_test/api_doc/conf.py +++ b/tests/doc_test/api_doc/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs", "dummy_extension.dummy"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/api_doc_awesome/conf.py b/tests/doc_test/api_doc_awesome/conf.py index 7832f346d..df50838b8 100644 --- a/tests/doc_test/api_doc_awesome/conf.py +++ b/tests/doc_test/api_doc_awesome/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs", "dummy_extension.dummy"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/arch_doc/conf.py b/tests/doc_test/arch_doc/conf.py index 1571c763f..70ce823f5 100644 --- a/tests/doc_test/arch_doc/conf.py +++ b/tests/doc_test/arch_doc/conf.py @@ -3,8 +3,32 @@ plantuml_output_format = "svg" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/broken_doc/conf.py b/tests/doc_test/broken_doc/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/broken_doc/conf.py +++ b/tests/doc_test/broken_doc/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/broken_links/conf.py b/tests/doc_test/broken_links/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/broken_links/conf.py +++ b/tests/doc_test/broken_links/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/broken_statuses/conf.py b/tests/doc_test/broken_statuses/conf.py index 3706ca5ff..6e013eae5 100644 --- a/tests/doc_test/broken_statuses/conf.py +++ b/tests/doc_test/broken_statuses/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_statuses = [ diff --git a/tests/doc_test/broken_syntax_doc/conf.py b/tests/doc_test/broken_syntax_doc/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/broken_syntax_doc/conf.py +++ b/tests/doc_test/broken_syntax_doc/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/broken_tags/conf.py b/tests/doc_test/broken_tags/conf.py index 7c04d916c..6559d7fe0 100644 --- a/tests/doc_test/broken_tags/conf.py +++ b/tests/doc_test/broken_tags/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_tags = [ diff --git a/tests/doc_test/broken_tags_2/conf.py b/tests/doc_test/broken_tags_2/conf.py index 7c04d916c..6559d7fe0 100644 --- a/tests/doc_test/broken_tags_2/conf.py +++ b/tests/doc_test/broken_tags_2/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_tags = [ diff --git a/tests/doc_test/doc_basic/conf.py b/tests/doc_test/doc_basic/conf.py index 0a5cb1761..2db347716 100644 --- a/tests/doc_test/doc_basic/conf.py +++ b/tests/doc_test/doc_basic/conf.py @@ -6,8 +6,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_basic_latex/conf.py b/tests/doc_test/doc_basic_latex/conf.py index 32f8e8de5..1f9bcc6ed 100644 --- a/tests/doc_test/doc_basic_latex/conf.py +++ b/tests/doc_test/doc_basic_latex/conf.py @@ -8,8 +8,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_build_latex/conf.py b/tests/doc_test/doc_build_latex/conf.py index 6a81ad4bb..df70aa270 100644 --- a/tests/doc_test/doc_build_latex/conf.py +++ b/tests/doc_test/doc_build_latex/conf.py @@ -13,8 +13,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_df_calc_sum/conf.py b/tests/doc_test/doc_df_calc_sum/conf.py index 2e1bb2cf3..ee4795ac0 100644 --- a/tests/doc_test/doc_df_calc_sum/conf.py +++ b/tests/doc_test/doc_df_calc_sum/conf.py @@ -3,10 +3,38 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] -needs_extra_options = {"test_func": directives.unchanged, "hours": directives.unchanged, "amount": directives.unchanged} +needs_extra_options = { + "test_func": directives.unchanged, + "hours": directives.unchanged, + "amount": directives.unchanged, +} diff --git a/tests/doc_test/doc_df_check_linked_values/conf.py b/tests/doc_test/doc_df_check_linked_values/conf.py index 707f3afad..2a93cda81 100644 --- a/tests/doc_test/doc_df_check_linked_values/conf.py +++ b/tests/doc_test/doc_df_check_linked_values/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = {"test_func": directives.unchanged, "hours": directives.unchanged} diff --git a/tests/doc_test/doc_df_user_functions/conf.py b/tests/doc_test/doc_df_user_functions/conf.py index 07958c0c0..14945940d 100644 --- a/tests/doc_test/doc_df_user_functions/conf.py +++ b/tests/doc_test/doc_df_user_functions/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = {"test_func": directives.unchanged, "hours": directives.unchanged} diff --git a/tests/doc_test/doc_dynamic_functions/conf.py b/tests/doc_test/doc_dynamic_functions/conf.py index 5097fc12d..e474af668 100644 --- a/tests/doc_test/doc_dynamic_functions/conf.py +++ b/tests/doc_test/doc_dynamic_functions/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = {"test_func": directives.unchanged} diff --git a/tests/doc_test/doc_export_id/conf.py b/tests/doc_test/doc_export_id/conf.py index 34095785f..d2a706459 100644 --- a/tests/doc_test/doc_export_id/conf.py +++ b/tests/doc_test/doc_export_id/conf.py @@ -6,10 +6,34 @@ plantuml_output_format = "svg" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ diff --git a/tests/doc_test/doc_extra_links/conf.py b/tests/doc_test/doc_extra_links/conf.py index ad8a7f24e..863b56681 100644 --- a/tests/doc_test/doc_extra_links/conf.py +++ b/tests/doc_test/doc_extra_links/conf.py @@ -6,10 +6,34 @@ plantuml_output_format = "svg" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ diff --git a/tests/doc_test/doc_github_issue_21/conf.py b/tests/doc_test/doc_github_issue_21/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/doc_github_issue_21/conf.py +++ b/tests/doc_test/doc_github_issue_21/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_github_issue_44/conf.py b/tests/doc_test/doc_github_issue_44/conf.py index df3fe57f8..7556c0c4e 100644 --- a/tests/doc_test/doc_github_issue_44/conf.py +++ b/tests/doc_test/doc_github_issue_44/conf.py @@ -3,8 +3,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_global_options/conf.py b/tests/doc_test/doc_global_options/conf.py index 46408adc5..f22187cad 100644 --- a/tests/doc_test/doc_global_options/conf.py +++ b/tests/doc_test/doc_global_options/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_global_options = { diff --git a/tests/doc_test/doc_layout/conf.py b/tests/doc_test/doc_layout/conf.py index 6969f1c23..899d6306c 100644 --- a/tests/doc_test/doc_layout/conf.py +++ b/tests/doc_test/doc_layout/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = { @@ -19,7 +43,10 @@ "grid": "simple_side_right_partial", "layout": { "head": ['**<>** for *<>*'], - "meta": ['**status**: <>', '**author**: <>'], + "meta": [ + '**status**: <>', + '**author**: <>', + ], "side": ['<>'], }, }, @@ -27,14 +54,20 @@ "grid": "simple", "layout": { "head": ['**<>**'], - "meta": ['**status**: <>', r'<>'], + "meta": [ + '**status**: <>', + r'<>', + ], }, }, "footer_grid": { "grid": "simple_footer", "layout": { "head": ['**<>** for *<>*'], - "meta": ['**status**: <>', '**author**: <>'], + "meta": [ + '**status**: <>', + '**author**: <>', + ], "footer": ['**custom footer for <>**'], }, }, diff --git a/tests/doc_test/doc_list2need/conf.py b/tests/doc_test/doc_list2need/conf.py index 009477a1b..43d93e5f4 100644 --- a/tests/doc_test/doc_list2need/conf.py +++ b/tests/doc_test/doc_list2need/conf.py @@ -8,10 +8,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ diff --git a/tests/doc_test/doc_measure_time/conf.py b/tests/doc_test/doc_measure_time/conf.py index 74f164462..a4b1d4e5f 100644 --- a/tests/doc_test/doc_measure_time/conf.py +++ b/tests/doc_test/doc_measure_time/conf.py @@ -17,10 +17,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_need_count/conf.py b/tests/doc_test/doc_need_count/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/doc_need_count/conf.py +++ b/tests/doc_test/doc_need_count/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_need_delete/conf.py b/tests/doc_test/doc_need_delete/conf.py index 104609b54..3badaf917 100644 --- a/tests/doc_test/doc_need_delete/conf.py +++ b/tests/doc_test/doc_need_delete/conf.py @@ -6,8 +6,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "R_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "R_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_need_id_from_title/conf.py b/tests/doc_test/doc_need_id_from_title/conf.py index e3a53e73b..d7e949123 100644 --- a/tests/doc_test/doc_need_id_from_title/conf.py +++ b/tests/doc_test/doc_need_id_from_title/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_id_length = 20 diff --git a/tests/doc_test/doc_need_jinja_content/conf.py b/tests/doc_test/doc_need_jinja_content/conf.py index 3f7d3cdd7..6081c7b09 100644 --- a/tests/doc_test/doc_need_jinja_content/conf.py +++ b/tests/doc_test/doc_need_jinja_content/conf.py @@ -6,10 +6,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "R_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "R_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_need_parts/conf.py b/tests/doc_test/doc_need_parts/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/doc_need_parts/conf.py +++ b/tests/doc_test/doc_need_parts/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needarch/conf.py b/tests/doc_test/doc_needarch/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needarch/conf.py +++ b/tests/doc_test/doc_needarch/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needarch_jinja_func_import/conf.py b/tests/doc_test/doc_needarch_jinja_func_import/conf.py index dfccf97c2..f20c68b8c 100644 --- a/tests/doc_test/doc_needarch_jinja_func_import/conf.py +++ b/tests/doc_test/doc_needarch_jinja_func_import/conf.py @@ -20,7 +20,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -29,10 +36,34 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ @@ -41,5 +72,10 @@ "incoming": "is used by", "outgoing": "uses", }, - {"option": "tests", "incoming": "is tested by", "outgoing": "tests", "style": "#00AA00"}, + { + "option": "tests", + "incoming": "is tested by", + "outgoing": "tests", + "style": "#00AA00", + }, ] diff --git a/tests/doc_test/doc_needarch_jinja_func_need/conf.py b/tests/doc_test/doc_needarch_jinja_func_need/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needarch_jinja_func_need/conf.py +++ b/tests/doc_test/doc_needarch_jinja_func_need/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needarch_negative_tests/conf.py b/tests/doc_test/doc_needarch_negative_tests/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needarch_negative_tests/conf.py +++ b/tests/doc_test/doc_needarch_negative_tests/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needbar/conf.py b/tests/doc_test/doc_needbar/conf.py index 0d9b66fe3..c0dc66839 100644 --- a/tests/doc_test/doc_needbar/conf.py +++ b/tests/doc_test/doc_needbar/conf.py @@ -3,6 +3,18 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RQ_", "color": "#FEDCD2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RQ_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needextend/conf.py b/tests/doc_test/doc_needextend/conf.py index 08df9747f..4c07658ee 100644 --- a/tests/doc_test/doc_needextend/conf.py +++ b/tests/doc_test/doc_needextend/conf.py @@ -3,8 +3,32 @@ needs_build_json = True needs_id_regex = "^[A-Za-z0-9_]*" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needextend_strict/conf.py b/tests/doc_test/doc_needextend_strict/conf.py index 6b4b9fa9d..99f842750 100644 --- a/tests/doc_test/doc_needextend_strict/conf.py +++ b/tests/doc_test/doc_needextend_strict/conf.py @@ -2,8 +2,32 @@ needs_id_regex = "^[A-Za-z0-9_]*" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needextract/conf.py b/tests/doc_test/doc_needextract/conf.py index 47badfa7d..87cb4ac0e 100644 --- a/tests/doc_test/doc_needextract/conf.py +++ b/tests/doc_test/doc_needextract/conf.py @@ -8,8 +8,32 @@ needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needflow/conf.py b/tests/doc_test/doc_needflow/conf.py index aa6219420..aab24df0d 100644 --- a/tests/doc_test/doc_needflow/conf.py +++ b/tests/doc_test/doc_needflow/conf.py @@ -8,8 +8,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needflow_incl_child_needs/conf.py b/tests/doc_test/doc_needflow_incl_child_needs/conf.py index 24b1cbd1b..96753e0bd 100644 --- a/tests/doc_test/doc_needflow_incl_child_needs/conf.py +++ b/tests/doc_test/doc_needflow_incl_child_needs/conf.py @@ -8,10 +8,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needimport_download_needs_json/conf.py b/tests/doc_test/doc_needimport_download_needs_json/conf.py index 885bd47d6..814c929f3 100644 --- a/tests/doc_test/doc_needimport_download_needs_json/conf.py +++ b/tests/doc_test/doc_needimport_download_needs_json/conf.py @@ -3,8 +3,32 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needimport_download_needs_json_negative/conf.py b/tests/doc_test/doc_needimport_download_needs_json_negative/conf.py index 885bd47d6..814c929f3 100644 --- a/tests/doc_test/doc_needimport_download_needs_json_negative/conf.py +++ b/tests/doc_test/doc_needimport_download_needs_json_negative/conf.py @@ -3,8 +3,32 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needlist/conf.py b/tests/doc_test/doc_needlist/conf.py index 885bd47d6..814c929f3 100644 --- a/tests/doc_test/doc_needlist/conf.py +++ b/tests/doc_test/doc_needlist/conf.py @@ -3,8 +3,32 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needpie/conf.py b/tests/doc_test/doc_needpie/conf.py index 86ef3ff45..2c635d301 100644 --- a/tests/doc_test/doc_needpie/conf.py +++ b/tests/doc_test/doc_needpie/conf.py @@ -8,10 +8,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = ["author"] diff --git a/tests/doc_test/doc_needs_builder/conf.py b/tests/doc_test/doc_needs_builder/conf.py index ce1cbf2cf..0e12a0867 100644 --- a/tests/doc_test/doc_needs_builder/conf.py +++ b/tests/doc_test/doc_needs_builder/conf.py @@ -5,10 +5,34 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_file = "custom_needs_test.json" diff --git a/tests/doc_test/doc_needs_builder_negative_tests/conf.py b/tests/doc_test/doc_needs_builder_negative_tests/conf.py index 885bd47d6..814c929f3 100644 --- a/tests/doc_test/doc_needs_builder_negative_tests/conf.py +++ b/tests/doc_test/doc_needs_builder_negative_tests/conf.py @@ -3,8 +3,32 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needs_builder_parallel/conf.py b/tests/doc_test/doc_needs_builder_parallel/conf.py index 87abb2f42..dabfbcfcf 100644 --- a/tests/doc_test/doc_needs_builder_parallel/conf.py +++ b/tests/doc_test/doc_needs_builder_parallel/conf.py @@ -7,10 +7,34 @@ needs_build_json = True needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_file = "custom_needs_test.json" diff --git a/tests/doc_test/doc_needs_external_needs/conf.py b/tests/doc_test/doc_needs_external_needs/conf.py index 95e5cee80..2d787751d 100644 --- a/tests/doc_test/doc_needs_external_needs/conf.py +++ b/tests/doc_test/doc_needs_external_needs/conf.py @@ -6,13 +6,45 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "ext_"}, - {"base_url": "../../_build/html", "json_path": "needs_test_small.json", "id_prefix": "ext_rel_path_"}, + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "ext_", + }, + { + "base_url": "../../_build/html", + "json_path": "needs_test_small.json", + "id_prefix": "ext_rel_path_", + }, ] diff --git a/tests/doc_test/doc_needs_external_needs_remote/conf.py b/tests/doc_test/doc_needs_external_needs_remote/conf.py index b63ea602c..460476c0b 100644 --- a/tests/doc_test/doc_needs_external_needs_remote/conf.py +++ b/tests/doc_test/doc_needs_external_needs_remote/conf.py @@ -6,10 +6,34 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ diff --git a/tests/doc_test/doc_needs_external_needs_with_target_url/conf.py b/tests/doc_test/doc_needs_external_needs_with_target_url/conf.py index d4c198d2d..6a45a59f8 100644 --- a/tests/doc_test/doc_needs_external_needs_with_target_url/conf.py +++ b/tests/doc_test/doc_needs_external_needs_with_target_url/conf.py @@ -6,10 +6,34 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ @@ -31,5 +55,9 @@ "json_path": "needs_test_small.json", "id_prefix": "ext_need_type_", }, - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "ext_default_"}, + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "ext_default_", + }, ] diff --git a/tests/doc_test/doc_needs_filter_data/conf.py b/tests/doc_test/doc_needs_filter_data/conf.py index 23aa68579..8c62e9c97 100644 --- a/tests/doc_test/doc_needs_filter_data/conf.py +++ b/tests/doc_test/doc_needs_filter_data/conf.py @@ -12,10 +12,34 @@ needs_id_regex = "^[A-Za-z0-9_]*" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needs_filter_func_allow_dirty_filter/conf.py b/tests/doc_test/doc_needs_filter_func_allow_dirty_filter/conf.py index 6d7baed7d..3361ea436 100644 --- a/tests/doc_test/doc_needs_filter_func_allow_dirty_filter/conf.py +++ b/tests/doc_test/doc_needs_filter_func_allow_dirty_filter/conf.py @@ -8,8 +8,20 @@ needs_id_regex = "^[A-Za-z0-9_]*" needs_types = [ - {"directive": "feature", "title": "Feature", "prefix": "FE_", "color": "#FEDCD2", "style": "node"}, - {"directive": "usecase", "title": "Use Case", "prefix": "USE_", "color": "#DF744A", "style": "node"}, + { + "directive": "feature", + "title": "Feature", + "prefix": "FE_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "usecase", + "title": "Use Case", + "prefix": "USE_", + "color": "#DF744A", + "style": "node", + }, ] needs_extra_options = ["ti", "tcl"] diff --git a/tests/doc_test/doc_needs_warnings/conf.py b/tests/doc_test/doc_needs_warnings/conf.py index f6b991773..cfc630c88 100644 --- a/tests/doc_test/doc_needs_warnings/conf.py +++ b/tests/doc_test/doc_needs_warnings/conf.py @@ -3,14 +3,42 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "ext_"} + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "ext_", + } ] @@ -38,7 +66,11 @@ def setup(app): add_warning(app, "api_warning_filter", filter_string="status == 'example_2'") add_warning(app, "api_warning_func", custom_warning_func) - add_warning(app, "invalid_status", "status not in ['open', 'closed', 'done', 'example_2', 'example_3']") + add_warning( + app, + "invalid_status", + "status not in ['open', 'closed', 'done', 'example_2', 'example_3']", + ) # Needs option to set True or False to raise sphinx-warning for each not passed warning check diff --git a/tests/doc_test/doc_needs_warnings_return_status_code/conf.py b/tests/doc_test/doc_needs_warnings_return_status_code/conf.py index 490cb7534..47d7db034 100644 --- a/tests/doc_test/doc_needs_warnings_return_status_code/conf.py +++ b/tests/doc_test/doc_needs_warnings_return_status_code/conf.py @@ -3,10 +3,34 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needsfile/conf.py b/tests/doc_test/doc_needsfile/conf.py index e607e1f34..457adbf2a 100644 --- a/tests/doc_test/doc_needsfile/conf.py +++ b/tests/doc_test/doc_needsfile/conf.py @@ -3,8 +3,32 @@ needs_file = "needs_errors.json" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needtable/conf.py b/tests/doc_test/doc_needtable/conf.py index 73fd61077..0aef9a7d0 100644 --- a/tests/doc_test/doc_needtable/conf.py +++ b/tests/doc_test/doc_needtable/conf.py @@ -5,10 +5,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_options = [ diff --git a/tests/doc_test/doc_needuml/conf.py b/tests/doc_test/doc_needuml/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml/conf.py +++ b/tests/doc_test/doc_needuml/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_diagram_allowmixing/conf.py b/tests/doc_test/doc_needuml_diagram_allowmixing/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_diagram_allowmixing/conf.py +++ b/tests/doc_test/doc_needuml_diagram_allowmixing/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_duplicate_key/conf.py b/tests/doc_test/doc_needuml_duplicate_key/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_duplicate_key/conf.py +++ b/tests/doc_test/doc_needuml_duplicate_key/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_filter/conf.py b/tests/doc_test/doc_needuml_filter/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_filter/conf.py +++ b/tests/doc_test/doc_needuml_filter/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_jinja_func_flow/conf.py b/tests/doc_test/doc_needuml_jinja_func_flow/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_jinja_func_flow/conf.py +++ b/tests/doc_test/doc_needuml_jinja_func_flow/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_jinja_func_import_negative_tests/conf.py b/tests/doc_test/doc_needuml_jinja_func_import_negative_tests/conf.py index dfccf97c2..f20c68b8c 100644 --- a/tests/doc_test/doc_needuml_jinja_func_import_negative_tests/conf.py +++ b/tests/doc_test/doc_needuml_jinja_func_import_negative_tests/conf.py @@ -20,7 +20,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -29,10 +36,34 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ @@ -41,5 +72,10 @@ "incoming": "is used by", "outgoing": "uses", }, - {"option": "tests", "incoming": "is tested by", "outgoing": "tests", "style": "#00AA00"}, + { + "option": "tests", + "incoming": "is tested by", + "outgoing": "tests", + "style": "#00AA00", + }, ] diff --git a/tests/doc_test/doc_needuml_jinja_func_need_removed/conf.py b/tests/doc_test/doc_needuml_jinja_func_need_removed/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_jinja_func_need_removed/conf.py +++ b/tests/doc_test/doc_needuml_jinja_func_need_removed/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_jinja_func_ref/conf.py b/tests/doc_test/doc_needuml_jinja_func_ref/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_jinja_func_ref/conf.py +++ b/tests/doc_test/doc_needuml_jinja_func_ref/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_key_name_diagram/conf.py b/tests/doc_test/doc_needuml_key_name_diagram/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_key_name_diagram/conf.py +++ b/tests/doc_test/doc_needuml_key_name_diagram/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_needuml_save/conf.py b/tests/doc_test/doc_needuml_save/conf.py index e4c6437fd..ad06ccaad 100644 --- a/tests/doc_test/doc_needuml_save/conf.py +++ b/tests/doc_test/doc_needuml_save/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,10 +38,34 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_build_needumls = "my_needumls" diff --git a/tests/doc_test/doc_needuml_save_with_abs_path/conf.py b/tests/doc_test/doc_needuml_save_with_abs_path/conf.py index 1666acffc..208a58691 100644 --- a/tests/doc_test/doc_needuml_save_with_abs_path/conf.py +++ b/tests/doc_test/doc_needuml_save_with_abs_path/conf.py @@ -22,7 +22,14 @@ "color": "#BFD8D2", "style": "card", }, - {"directive": "sys", "content": "plantuml", "title": "System", "prefix": "S_", "color": "#FF68D2", "style": "node"}, + { + "directive": "sys", + "content": "plantuml", + "title": "System", + "prefix": "S_", + "color": "#FF68D2", + "style": "node", + }, { "directive": "prod", "content": "plantuml", @@ -31,8 +38,32 @@ "color": "#FF68D2", "style": "node", }, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/doc_open_needs_service/conf.py b/tests/doc_test/doc_open_needs_service/conf.py index be86e0012..50d1c74d3 100644 --- a/tests/doc_test/doc_open_needs_service/conf.py +++ b/tests/doc_test/doc_open_needs_service/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "R_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "task", "title": "Task", "prefix": "T_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "R_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "task", + "title": "Task", + "prefix": "T_", + "color": "#DCB239", + "style": "node", + }, ] needs_services = { diff --git a/tests/doc_test/doc_report_dead_links_false/conf.py b/tests/doc_test/doc_report_dead_links_false/conf.py index 813f445a4..ae1448dee 100644 --- a/tests/doc_test/doc_report_dead_links_false/conf.py +++ b/tests/doc_test/doc_report_dead_links_false/conf.py @@ -4,10 +4,34 @@ plantuml_output_format = "svg" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] suppress_warnings = ["needs.link_outgoing"] diff --git a/tests/doc_test/doc_report_dead_links_true/conf.py b/tests/doc_test/doc_report_dead_links_true/conf.py index 91467f963..e114d9518 100644 --- a/tests/doc_test/doc_report_dead_links_true/conf.py +++ b/tests/doc_test/doc_report_dead_links_true/conf.py @@ -4,10 +4,34 @@ plantuml_output_format = "svg" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_extra_links = [ diff --git a/tests/doc_test/doc_role_need_max_title_length/conf.py b/tests/doc_test/doc_role_need_max_title_length/conf.py index 46b0b1685..11f882b66 100644 --- a/tests/doc_test/doc_role_need_max_title_length/conf.py +++ b/tests/doc_test/doc_role_need_max_title_length/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_role_need_template = "[{id}] {title} ({status}) {type_name}/{type} - {tags} - {links} - {links_back} - {content}" diff --git a/tests/doc_test/doc_role_need_max_title_length_unlimited/conf.py b/tests/doc_test/doc_role_need_max_title_length_unlimited/conf.py index 23dec017c..0a85db025 100644 --- a/tests/doc_test/doc_role_need_max_title_length_unlimited/conf.py +++ b/tests/doc_test/doc_role_need_max_title_length_unlimited/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_role_need_template = "[{id}] {title} ({status}) {type_name}/{type} - {tags} - {links} - {links_back} - {content}" diff --git a/tests/doc_test/doc_role_need_template/conf.py b/tests/doc_test/doc_role_need_template/conf.py index 4d15bdb0d..fa86a8027 100644 --- a/tests/doc_test/doc_role_need_template/conf.py +++ b/tests/doc_test/doc_role_need_template/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_role_need_template = "[{id}] {title} ({status}) {type_name}/{type} - {tags} - {links} - {links_back} - {content}" diff --git a/tests/doc_test/doc_style_blank/conf.py b/tests/doc_test/doc_style_blank/conf.py index ef8a46842..959f7f07b 100644 --- a/tests/doc_test/doc_style_blank/conf.py +++ b/tests/doc_test/doc_style_blank/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_css = "blank.css" diff --git a/tests/doc_test/doc_style_custom/conf.py b/tests/doc_test/doc_style_custom/conf.py index 80485dc17..ed2820a9b 100644 --- a/tests/doc_test/doc_style_custom/conf.py +++ b/tests/doc_test/doc_style_custom/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_css = os.path.join(os.path.dirname(__file__), "my_custom.css") diff --git a/tests/doc_test/doc_style_modern/conf.py b/tests/doc_test/doc_style_modern/conf.py index 68f00c54c..51fd8c41c 100644 --- a/tests/doc_test/doc_style_modern/conf.py +++ b/tests/doc_test/doc_style_modern/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_css = "modern.css" diff --git a/tests/doc_test/doc_style_unknown/conf.py b/tests/doc_test/doc_style_unknown/conf.py index ec0cfafe9..8ee6fe502 100644 --- a/tests/doc_test/doc_style_unknown/conf.py +++ b/tests/doc_test/doc_style_unknown/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_css = "UNKNOWN.css" diff --git a/tests/doc_test/external_doc/conf.py b/tests/doc_test/external_doc/conf.py index e38367471..c884b37f9 100644 --- a/tests/doc_test/external_doc/conf.py +++ b/tests/doc_test/external_doc/conf.py @@ -10,11 +10,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] test_dir = os.path.dirname(__file__) @@ -23,7 +53,11 @@ # needs_external_needs = [{"base_url": f"file://{test_dir}", "json_url": f"file://{test_json}", "id_prefix": "ext_"}] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "EXT_"} + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "EXT_", + } ] # Needed to export really ALL needs. The default entry would filter out all needs coming from external diff --git a/tests/doc_test/filter_doc/conf.py b/tests/doc_test/filter_doc/conf.py index 717640d49..787367095 100644 --- a/tests/doc_test/filter_doc/conf.py +++ b/tests/doc_test/filter_doc/conf.py @@ -8,13 +8,55 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, - {"directive": "user", "title": "User", "prefix": "U_", "color": "#777777", "style": "node"}, - {"directive": "action", "title": "Action", "prefix": "A_", "color": "#FFCC00", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, + { + "directive": "user", + "title": "User", + "prefix": "U_", + "color": "#777777", + "style": "node", + }, + { + "directive": "action", + "title": "Action", + "prefix": "A_", + "color": "#FFCC00", + "style": "node", + }, ] needs_extra_links = [ diff --git a/tests/doc_test/generic_doc/conf.py b/tests/doc_test/generic_doc/conf.py index 21eab6d52..1b74e151a 100644 --- a/tests/doc_test/generic_doc/conf.py +++ b/tests/doc_test/generic_doc/conf.py @@ -1,8 +1,32 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/import_doc/conf.py b/tests/doc_test/import_doc/conf.py index fcbde4dd9..5ddaeb329 100644 --- a/tests/doc_test/import_doc/conf.py +++ b/tests/doc_test/import_doc/conf.py @@ -5,11 +5,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_template = """ diff --git a/tests/doc_test/import_doc_empty/conf.py b/tests/doc_test/import_doc_empty/conf.py index 45270f660..7e12482c6 100644 --- a/tests/doc_test/import_doc_empty/conf.py +++ b/tests/doc_test/import_doc_empty/conf.py @@ -3,11 +3,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_template = """ diff --git a/tests/doc_test/import_doc_invalid/conf.py b/tests/doc_test/import_doc_invalid/conf.py index 45270f660..7e12482c6 100644 --- a/tests/doc_test/import_doc_invalid/conf.py +++ b/tests/doc_test/import_doc_invalid/conf.py @@ -3,11 +3,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_template = """ diff --git a/tests/doc_test/need_constraints/conf.py b/tests/doc_test/need_constraints/conf.py index b33483a78..ab7f10295 100644 --- a/tests/doc_test/need_constraints/conf.py +++ b/tests/doc_test/need_constraints/conf.py @@ -4,14 +4,42 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "ext_"} + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "ext_", + } ] @@ -39,7 +67,11 @@ def setup(app): add_warning(app, "api_warning_filter", filter_string="status == 'example_2'") add_warning(app, "api_warning_func", custom_warning_func) - add_warning(app, "invalid_status", "status not in ['open', 'closed', 'done', 'example_2', 'example_3']") + add_warning( + app, + "invalid_status", + "status not in ['open', 'closed', 'done', 'example_2', 'example_3']", + ) # Needs option to set True or False to raise sphinx-warning for each not passed warning check diff --git a/tests/doc_test/need_constraints_failed/conf.py b/tests/doc_test/need_constraints_failed/conf.py index 31228134f..7430e40f2 100644 --- a/tests/doc_test/need_constraints_failed/conf.py +++ b/tests/doc_test/need_constraints_failed/conf.py @@ -3,14 +3,42 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "ext_"} + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "ext_", + } ] @@ -38,7 +66,11 @@ def setup(app): add_warning(app, "api_warning_filter", filter_string="status == 'example_2'") add_warning(app, "api_warning_func", custom_warning_func) - add_warning(app, "invalid_status", "status not in ['open', 'closed', 'done', 'example_2', 'example_3']") + add_warning( + app, + "invalid_status", + "status not in ['open', 'closed', 'done', 'example_2', 'example_3']", + ) # Needs option to set True or False to raise sphinx-warning for each not passed warning check diff --git a/tests/doc_test/needextract_with_nested_needs/conf.py b/tests/doc_test/needextract_with_nested_needs/conf.py index df3fe57f8..7556c0c4e 100644 --- a/tests/doc_test/needextract_with_nested_needs/conf.py +++ b/tests/doc_test/needextract_with_nested_needs/conf.py @@ -3,8 +3,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/needpie_with_zero_needs/conf.py b/tests/doc_test/needpie_with_zero_needs/conf.py index df3fe57f8..7556c0c4e 100644 --- a/tests/doc_test/needpie_with_zero_needs/conf.py +++ b/tests/doc_test/needpie_with_zero_needs/conf.py @@ -3,8 +3,32 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/non_exists_file_import/conf.py b/tests/doc_test/non_exists_file_import/conf.py index 45270f660..7e12482c6 100644 --- a/tests/doc_test/non_exists_file_import/conf.py +++ b/tests/doc_test/non_exists_file_import/conf.py @@ -3,11 +3,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_template = """ diff --git a/tests/doc_test/parallel_doc/conf.py b/tests/doc_test/parallel_doc/conf.py index 03f708826..f6f03d869 100644 --- a/tests/doc_test/parallel_doc/conf.py +++ b/tests/doc_test/parallel_doc/conf.py @@ -1,10 +1,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_variants = {"change_author": "assignee == 'Randy Duodu'"} needs_variant_options = ["status", "author"] diff --git a/tests/doc_test/role_need_doc/conf.py b/tests/doc_test/role_need_doc/conf.py index a3d0a4e67..b64a2a601 100644 --- a/tests/doc_test/role_need_doc/conf.py +++ b/tests/doc_test/role_need_doc/conf.py @@ -8,11 +8,41 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "req", "title": "Requirement", "prefix": "RE_", "color": "#BFD8D2", "style": "node"}, - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "req", + "title": "Requirement", + "prefix": "RE_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] test_dir = os.path.dirname(__file__) @@ -21,7 +51,11 @@ # needs_external_needs = [{"base_url": f"file://{test_dir}", "json_url": f"file://{test_json}", "id_prefix": "ext_"}] needs_external_needs = [ - {"base_url": "http://my_company.com/docs/v1/", "json_path": "needs_test_small.json", "id_prefix": "EXT_"} + { + "base_url": "http://my_company.com/docs/v1/", + "json_path": "needs_test_small.json", + "id_prefix": "EXT_", + } ] # Needed to export really ALL needs. The default entry would filter out all needs coming from external diff --git a/tests/doc_test/service_doc/conf.py b/tests/doc_test/service_doc/conf.py index b2a23705d..b931c94b1 100644 --- a/tests/doc_test/service_doc/conf.py +++ b/tests/doc_test/service_doc/conf.py @@ -3,10 +3,34 @@ extensions = ["sphinx_needs"] needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/unicode_support/conf.py b/tests/doc_test/unicode_support/conf.py index 885bd47d6..814c929f3 100644 --- a/tests/doc_test/unicode_support/conf.py +++ b/tests/doc_test/unicode_support/conf.py @@ -3,8 +3,32 @@ needs_table_style = "TABLE" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] diff --git a/tests/doc_test/variant_doc/conf.py b/tests/doc_test/variant_doc/conf.py index d94dd50ed..d78cd7913 100644 --- a/tests/doc_test/variant_doc/conf.py +++ b/tests/doc_test/variant_doc/conf.py @@ -8,10 +8,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_variants = {"change_author": "assignee == 'Randy Duodu'"} needs_variant_options = ["status", "author", "links"] diff --git a/tests/doc_test/variant_options/conf.py b/tests/doc_test/variant_options/conf.py index 42108d4a1..1902a3923 100644 --- a/tests/doc_test/variant_options/conf.py +++ b/tests/doc_test/variant_options/conf.py @@ -8,10 +8,34 @@ needs_id_regex = "^[A-Za-z0-9_]" needs_types = [ - {"directive": "story", "title": "User Story", "prefix": "US_", "color": "#BFD8D2", "style": "node"}, - {"directive": "spec", "title": "Specification", "prefix": "SP_", "color": "#FEDCD2", "style": "node"}, - {"directive": "impl", "title": "Implementation", "prefix": "IM_", "color": "#DF744A", "style": "node"}, - {"directive": "test", "title": "Test Case", "prefix": "TC_", "color": "#DCB239", "style": "node"}, + { + "directive": "story", + "title": "User Story", + "prefix": "US_", + "color": "#BFD8D2", + "style": "node", + }, + { + "directive": "spec", + "title": "Specification", + "prefix": "SP_", + "color": "#FEDCD2", + "style": "node", + }, + { + "directive": "impl", + "title": "Implementation", + "prefix": "IM_", + "color": "#DF744A", + "style": "node", + }, + { + "directive": "test", + "title": "Test Case", + "prefix": "TC_", + "color": "#DCB239", + "style": "node", + }, ] needs_variants = {"change_author": "assignee == 'Randy Duodu'"} needs_variant_options = [] diff --git a/tests/no_mpl_tests.py b/tests/no_mpl_tests.py index 95aaf0747..dfaeb30ef 100644 --- a/tests/no_mpl_tests.py +++ b/tests/no_mpl_tests.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needbar"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needbar"}], + indirect=True, +) def test_needbar(test_app): """Test the build fails correctly, if matplotlib is not installed.""" test_app.build() @@ -11,7 +15,11 @@ def test_needbar(test_app): assert expected in test_app._warning.getvalue() -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needpie"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needpie"}], + indirect=True, +) def test_needpie(test_app): """Test the build fails correctly, if matplotlib is not installed.""" test_app.build() diff --git a/tests/test_add_sections.py b/tests/test_add_sections.py index 77028bf84..4658beef8 100644 --- a/tests/test_add_sections.py +++ b/tests/test_add_sections.py @@ -4,7 +4,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/add_sections"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/add_sections"}], + indirect=True, +) def test_section_is_usable_in_filters(test_app): app = test_app app.builder.build_all() diff --git a/tests/test_api_configuration.py b/tests/test_api_configuration.py index 556c98887..c031f8e6e 100644 --- a/tests/test_api_configuration.py +++ b/tests/test_api_configuration.py @@ -14,7 +14,9 @@ def setup(app): sys.modules["dummy_extension.dummy"] = dummy_extension -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/api_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/api_doc"}], indirect=True +) def test_api_get_types(test_app): from sphinx_needs.api import get_need_types @@ -24,7 +26,11 @@ def test_api_get_types(test_app): assert set(need_types) == {"story", "spec", "impl", "test"} -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/api_doc_awesome"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/api_doc_awesome"}], + indirect=True, +) def test_api_add_type(test_app, snapshot): from sphinx_needs.api import add_need_type diff --git a/tests/test_api_usage_in_extension.py b/tests/test_api_usage_in_extension.py index 1ff0dd801..2a434e8df 100644 --- a/tests/test_api_usage_in_extension.py +++ b/tests/test_api_usage_in_extension.py @@ -22,7 +22,9 @@ def after_config(app, config): sys.modules["dummy_extension.dummy"] = dummy_extension -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/api_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/api_doc"}], indirect=True +) def test_api_configuration(test_app): app = test_app diff --git a/tests/test_arch.py b/tests/test_arch.py index f17ec8887..e58389a30 100644 --- a/tests/test_arch.py +++ b/tests/test_arch.py @@ -3,7 +3,9 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/arch_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/arch_doc"}], indirect=True +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_basic_doc.py b/tests/test_basic_doc.py index 3c12b8ac7..806d29147 100644 --- a/tests/test_basic_doc.py +++ b/tests/test_basic_doc.py @@ -31,7 +31,9 @@ def random_data_callback(request): if re.match(r"/search/issues", request.path_url): data = GITHUB_ISSUE_SEARCH_ANSWER data["items"][0]["number"] = randrange(10000) - elif re.match(r"/.+/issue/.+", request.path_url) or re.match(r"/.+/pulls/.+", request.path_url): + elif re.match(r"/.+/issue/.+", request.path_url) or re.match( + r"/.+/pulls/.+", request.path_url + ): data = GITHUB_SPECIFIC_ISSUE_ANSWER data["number"] = randrange(10000) elif re.match(r"/search/commits", request.path_url): @@ -49,7 +51,9 @@ def random_data_callback(request): @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True +) def test_build_html(test_app): responses.add_callback( responses.GET, @@ -57,7 +61,9 @@ def test_build_html(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @@ -71,7 +77,11 @@ def test_build_html(test_app): @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], + indirect=True, +) def test_build_html_parallel(test_app: Sphinx, snapshot_doctree): responses.add_callback( responses.GET, @@ -79,7 +89,9 @@ def test_build_html_parallel(test_app: Sphinx, snapshot_doctree): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @@ -94,8 +106,14 @@ def test_build_html_parallel(test_app: Sphinx, snapshot_doctree): assert app.env.get_doctree("index") == snapshot_doctree -@pytest.mark.skipif(sys.platform == "win32", reason="assert fails on windows, need to fix later.") -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], indirect=True) +@pytest.mark.skipif( + sys.platform == "win32", reason="assert fails on windows, need to fix later." +) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], + indirect=True, +) def test_html_head_files(test_app): app = test_app app.builder.build_all() @@ -120,7 +138,11 @@ def test_html_head_files(test_app): @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "singlehtml", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "singlehtml", "srcdir": "doc_test/doc_basic"}], + indirect=True, +) def test_build_singlehtml(test_app): responses.add_callback( responses.GET, @@ -128,14 +150,20 @@ def test_build_singlehtml(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "latex", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "latex", "srcdir": "doc_test/doc_basic"}], + indirect=True, +) def test_build_latex(test_app): responses.add_callback( responses.GET, @@ -143,14 +171,18 @@ def test_build_latex(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "epub", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "epub", "srcdir": "doc_test/doc_basic"}], indirect=True +) def test_build_epub(test_app): responses.add_callback( responses.GET, @@ -158,14 +190,18 @@ def test_build_epub(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "json", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", [{"buildername": "json", "srcdir": "doc_test/doc_basic"}], indirect=True +) def test_build_json(test_app): responses.add_callback( responses.GET, @@ -173,14 +209,20 @@ def test_build_json(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @responses.activate -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_basic"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_basic"}], + indirect=True, +) def test_build_needs(test_app, snapshot): responses.add_callback( responses.GET, @@ -188,7 +230,9 @@ def test_build_needs(test_app, snapshot): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) app = test_app app.builder.build_all() @@ -202,7 +246,13 @@ def test_build_needs(test_app, snapshot): @responses.activate @pytest.mark.parametrize( "test_app", - [{"buildername": "html", "srcdir": "doc_test/doc_basic", "confoverrides": {"needs_id_required": True}}], + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_basic", + "confoverrides": {"needs_id_required": True}, + } + ], indirect=True, ) def test_id_required_build_html(test_app): @@ -213,7 +263,11 @@ def test_id_required_build_html(test_app): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, + re.compile(r"https://avatars.githubusercontent.com/.*"), + body="", + ) app = test_app app.builder.build_all() @@ -231,7 +285,9 @@ def test_sphinx_api_build(): callback=random_data_callback, content_type="application/json", ) - responses.add(responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="") + responses.add( + responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" + ) temp_dir = tempfile.mkdtemp() src_dir = os.path.join(os.path.dirname(__file__), "doc_test", "doc_basic") diff --git a/tests/test_broken_doc.py b/tests/test_broken_doc.py index 4458d69ce..d203541e0 100644 --- a/tests/test_broken_doc.py +++ b/tests/test_broken_doc.py @@ -3,7 +3,11 @@ from sphinx_needs.api.need import NeedsDuplicatedId -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_doc"}], + indirect=True, +) def test_doc_build_html(test_app): with pytest.raises(NeedsDuplicatedId): app = test_app diff --git a/tests/test_broken_links.py b/tests/test_broken_links.py index 0bc1613b9..37944db51 100644 --- a/tests/test_broken_links.py +++ b/tests/test_broken_links.py @@ -3,7 +3,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/broken_links", "no_plantuml": True}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_links", "no_plantuml": True}], + indirect=True, ) def test_doc_build_html(test_app): app = test_app diff --git a/tests/test_broken_statuses.py b/tests/test_broken_statuses.py index dcb09237d..4efc7abff 100644 --- a/tests/test_broken_statuses.py +++ b/tests/test_broken_statuses.py @@ -3,7 +3,11 @@ from sphinx_needs.api.need import NeedsStatusNotAllowed -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_statuses"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_statuses"}], + indirect=True, +) def test_doc_build_html(test_app): with pytest.raises(NeedsStatusNotAllowed): app = test_app diff --git a/tests/test_broken_syntax_doc.py b/tests/test_broken_syntax_doc.py index 162da581a..33552793f 100644 --- a/tests/test_broken_syntax_doc.py +++ b/tests/test_broken_syntax_doc.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_syntax_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_syntax_doc"}], + indirect=True, +) def test_doc_broken_syntax(test_app): app = test_app diff --git a/tests/test_broken_tags.py b/tests/test_broken_tags.py index 8a1d6896f..631c0b623 100644 --- a/tests/test_broken_tags.py +++ b/tests/test_broken_tags.py @@ -5,7 +5,11 @@ from sphinx_needs.api.need import NeedsTagNotAllowed -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_tags"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_tags"}], + indirect=True, +) def test_doc_build_html(test_app): with pytest.raises(NeedsTagNotAllowed): app = test_app @@ -15,7 +19,11 @@ def test_doc_build_html(test_app): assert "SP_TOO_003" in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/broken_tags_2"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/broken_tags_2"}], + indirect=True, +) def test_doc_build_html_unneeded_chars(test_app): """ Test for https://github.com/useblocks/sphinxcontrib-needs/issues/36 diff --git a/tests/test_clean_log.py b/tests/test_clean_log.py index 327e8adcf..af6f25036 100644 --- a/tests/test_clean_log.py +++ b/tests/test_clean_log.py @@ -5,9 +5,13 @@ class CleanLogTestCase(unittest.TestCase): def test_external_needs_clean_log(self): - self.assertEqual(clean_log("http://user:password@host.url/"), "http://****:****@host.url/") self.assertEqual( - clean_log("Downloading file from https://daniel:my_password@server.com now"), + clean_log("http://user:password@host.url/"), "http://****:****@host.url/" + ) + self.assertEqual( + clean_log( + "Downloading file from https://daniel:my_password@server.com now" + ), "Downloading file from https://****:****@server.com now", ) self.assertEqual( diff --git a/tests/test_complex_builders.py b/tests/test_complex_builders.py index 3c913b2c7..c937df6d4 100644 --- a/tests/test_complex_builders.py +++ b/tests/test_complex_builders.py @@ -10,7 +10,14 @@ @pytest.mark.parametrize( "test_app", - [{"buildername": "latex", "srcdir": "doc_test/doc_basic_latex", "warning": True, "parallel": 2}], + [ + { + "buildername": "latex", + "srcdir": "doc_test/doc_basic_latex", + "warning": True, + "parallel": 2, + } + ], indirect=True, ) def test_doc_complex_latex(test_app): @@ -23,7 +30,14 @@ def test_doc_complex_latex(test_app): @pytest.mark.parametrize( "test_app", - [{"buildername": "singlehtml", "srcdir": "doc_test/doc_basic_latex", "warning": True, "parallel": 2}], + [ + { + "buildername": "singlehtml", + "srcdir": "doc_test/doc_basic_latex", + "warning": True, + "parallel": 2, + } + ], indirect=True, ) def test_doc_complex_singlehtml(test_app): diff --git a/tests/test_doc_build_latex.py b/tests/test_doc_build_latex.py index a8e763ee0..11b3a89e8 100644 --- a/tests/test_doc_build_latex.py +++ b/tests/test_doc_build_latex.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "latex", "srcdir": "doc_test/doc_build_latex"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "latex", "srcdir": "doc_test/doc_build_latex"}], + indirect=True, +) def test_doc_build_latex(test_app): app = test_app diff --git a/tests/test_dynamic_functions.py b/tests/test_dynamic_functions.py index 181cd2256..59d0a7978 100644 --- a/tests/test_dynamic_functions.py +++ b/tests/test_dynamic_functions.py @@ -5,7 +5,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_dynamic_functions"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_dynamic_functions"}], + indirect=True, ) def test_doc_dynamic_functions(test_app): app = test_app @@ -13,22 +15,42 @@ def test_doc_dynamic_functions(test_app): html = Path(app.outdir, "index.html").read_text() assert "This is id SP_TOO_001" in html - assert sum(1 for _ in re.finditer('test2', html)) == 2 - assert sum(1 for _ in re.finditer('test', html)) == 2 - assert sum(1 for _ in re.finditer('my_tag', html)) == 1 + assert ( + sum(1 for _ in re.finditer('test2', html)) == 2 + ) + assert ( + sum(1 for _ in re.finditer('test', html)) == 2 + ) + assert ( + sum(1 for _ in re.finditer('my_tag', html)) == 1 + ) - assert sum(1 for _ in re.finditer('test_4a', html)) == 1 - assert sum(1 for _ in re.finditer('test_4b', html)) == 1 - assert sum(1 for _ in re.finditer('TEST_4', html)) == 2 + assert ( + sum(1 for _ in re.finditer('test_4a', html)) + == 1 + ) + assert ( + sum(1 for _ in re.finditer('test_4b', html)) + == 1 + ) + assert ( + sum(1 for _ in re.finditer('TEST_4', html)) == 2 + ) - assert sum(1 for _ in re.finditer('TEST_5', html)) == 2 + assert ( + sum(1 for _ in re.finditer('TEST_5', html)) == 2 + ) assert "Test output of need TEST_3. args:" in html assert 'link' in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_df_calc_sum"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_df_calc_sum"}], + indirect=True, +) def test_doc_df_calc_sum(test_app): app = test_app app.build() @@ -39,7 +61,9 @@ def test_doc_df_calc_sum(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_df_check_linked_values"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_df_check_linked_values"}], + indirect=True, ) def test_doc_df_linked_values(test_app): app = test_app @@ -51,7 +75,9 @@ def test_doc_df_linked_values(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_df_user_functions"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_df_user_functions"}], + indirect=True, ) def test_doc_df_user_functions(test_app): app = test_app diff --git a/tests/test_export_id.py b/tests/test_export_id.py index dcdb7152e..19f4beccc 100644 --- a/tests/test_export_id.py +++ b/tests/test_export_id.py @@ -6,7 +6,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_export_id"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_export_id"}], + indirect=True, +) def test_export_id(test_app, snapshot): app = test_app app.build() @@ -14,7 +18,11 @@ def test_export_id(test_app, snapshot): assert needs_data == snapshot(exclude=props("created")) -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_export_id"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_export_id"}], + indirect=True, +) def test_export_id_html(test_app): app = test_app app.build() diff --git a/tests/test_external.py b/tests/test_external.py index 9169cf431..de705d8e2 100644 --- a/tests/test_external.py +++ b/tests/test_external.py @@ -5,7 +5,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/external_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/external_doc"}], + indirect=True, +) def test_external_html(test_app): app = test_app app.build() @@ -17,11 +21,16 @@ def test_external_html(test_app): assert ( '

Test need ref: EXT_TEST_01

' in html + ' href="http://my_company.com/docs/v1/index.html#TEST_01">EXT_TEST_01

' + in html ) -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/external_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/external_doc"}], + indirect=True, +) def test_external_json(test_app, snapshot): app = test_app app.build() @@ -30,7 +39,11 @@ def test_external_json(test_app, snapshot): assert needs == snapshot(exclude=props("created")) -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/external_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/external_doc"}], + indirect=True, +) def test_external_needs_warnings(test_app): import os import subprocess @@ -40,8 +53,11 @@ def test_external_needs_warnings(test_app): srcdir = Path(app.srcdir) out_dir = os.path.join(srcdir, "_build") - out = subprocess.run(["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True + ) assert ( "WARNING: Couldn't create need EXT_TEST_03. Reason: The need-type (i.e. `ask`) is not" - " set in the project's 'need_types' configuration in conf.py." in out.stderr.decode("utf-8") + " set in the project's 'need_types' configuration in conf.py." + in out.stderr.decode("utf-8") ) diff --git a/tests/test_extra_links.py b/tests/test_extra_links.py index b57088989..e089d38f0 100644 --- a/tests/test_extra_links.py +++ b/tests/test_extra_links.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_extra_links"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_extra_links"}], + indirect=True, +) def test_extra_links_html(test_app): app = test_app app.build() @@ -16,11 +20,17 @@ def test_extra_links_html(test_app): # Check for correct dead_links handling assert 'DEAD_LINK_ALLOWED' in html - assert 'DEAD_LINK_NOT_ALLOWED' in html + assert ( + 'DEAD_LINK_NOT_ALLOWED' in html + ) assert 'REQ_005.invalid' in html -@pytest.mark.parametrize("test_app", [{"buildername": "latex", "srcdir": "doc_test/doc_extra_links"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "latex", "srcdir": "doc_test/doc_extra_links"}], + indirect=True, +) def test_extra_links_latex(test_app): app = test_app app.build() diff --git a/tests/test_extra_options.py b/tests/test_extra_options.py index 4d00b6ecc..4e6921887 100644 --- a/tests/test_extra_options.py +++ b/tests/test_extra_options.py @@ -4,7 +4,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/extra_options"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/extra_options"}], + indirect=True, +) def test_custom_attributes_appear(test_app): app = test_app app.build() @@ -37,6 +41,8 @@ def test_custom_attributes_appear(test_app): assert "R_12346" not in tables[3] # Need list should only have component B requirements - items = re.findall('(
child needs: CHILD_1_STORY
' in html_5 + 'href="#CHILD_1_STORY" title="STORY_PARENT">CHILD_1_STORY' + in html_5 ) assert ( '
parent needs: CHILD_1_STORY
' in html_5 + 'href="#CHILD_1_STORY" title="CHILD_2_STORY">CHILD_1_STORY' + in html_5 ) html_6 = Path(app.outdir, "filter_no_needs.html").read_text() diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 717f37e5f..4c8862136 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -5,7 +5,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_44"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_44"}], + indirect=True, +) def test_doc_github_44(test_app): """ https://github.com/useblocks/sphinxcontrib-needs/issues/44 @@ -17,7 +21,9 @@ def test_doc_github_44(test_app): app = test_app output = subprocess.run( - ["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], check=True, capture_output=True + ["sphinx-build", "-a", "-E", "-b", "html", app.srcdir, app.outdir], + check=True, + capture_output=True, ) # app.build() Uncomment, if build should stop on breakpoints @@ -34,7 +40,11 @@ def test_doc_github_44(test_app): ] -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_61"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_61"}], + indirect=True, +) def test_doc_github_61(test_app): """ Test for https://github.com/useblocks/sphinxcontrib-needs/issues/61 @@ -58,7 +68,9 @@ def test_doc_github_61(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_160"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_github_issue_160"}], + indirect=True, ) def test_doc_github_160(test_app): app = test_app diff --git a/tests/test_global_options.py b/tests/test_global_options.py index cf45c5ab8..9829f1a7c 100644 --- a/tests/test_global_options.py +++ b/tests/test_global_options.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_global_options"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_global_options"}], + indirect=True, +) def test_doc_global_option(test_app): app = test_app app.build() diff --git a/tests/test_import.py b/tests/test_import.py index e5bd1b760..0fb73e9ad 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -6,7 +6,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/import_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/import_doc"}], + indirect=True, +) def test_import_json(test_app): app = test_app app.build() @@ -40,7 +44,9 @@ def test_import_json(test_app): assert "small_rel_path_TEST_01" in rel_path_import_html # Check deprecated relative path import based on conf.py - deprec_rel_path_import_html = Path(app.outdir, "subdoc/deprecated_rel_path_import.html").read_text() + deprec_rel_path_import_html = Path( + app.outdir, "subdoc/deprecated_rel_path_import.html" + ).read_text() assert "small_depr_rel_path_TEST_01" in deprec_rel_path_import_html warning = app._warning @@ -48,7 +54,11 @@ def test_import_json(test_app): assert "Deprecation warning:" in warnings -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/import_doc_invalid"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/import_doc_invalid"}], + indirect=True, +) def test_json_schema_console_check(test_app): """Checks the console output for hints about json schema validation errors""" import os @@ -58,12 +68,18 @@ def test_json_schema_console_check(test_app): srcdir = Path(app.srcdir) out_dir = os.path.join(srcdir, "_build") - out = subprocess.run(["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True + ) assert "Schema validation errors detected" in str(out.stdout) -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/import_doc_invalid"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/import_doc_invalid"}], + indirect=True, +) def test_json_schema_file_check(test_app): """Checks that an invalid json-file gets normally still imported and is used as normal (if possible)""" app = test_app @@ -74,7 +90,11 @@ def test_json_schema_file_check(test_app): assert "new_tag" in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/import_doc_empty"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/import_doc_empty"}], + indirect=True, +) def test_empty_file_check(test_app): """Checks that an empty needs.json throws an exception""" app = test_app @@ -85,7 +105,9 @@ def test_empty_file_check(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/non_exists_file_import"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/non_exists_file_import"}], + indirect=True, ) def test_import_non_exists_json(test_app): # Check non exists file import @@ -97,7 +119,11 @@ def test_import_non_exists_json(test_app): assert "non_exists_file_import" in err.args[0] -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/import_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/import_doc"}], + indirect=True, +) def test_import_builder(test_app, snapshot): app = test_app app.build() @@ -107,7 +133,9 @@ def test_import_builder(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_needimport_download_needs_json"}], indirect=True + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_needimport_download_needs_json"}], + indirect=True, ) def test_needimport_needs_json_download(test_app, snapshot): app = test_app @@ -175,7 +203,12 @@ def test_needimport_needs_json_download(test_app, snapshot): @pytest.mark.parametrize( "test_app", - [{"buildername": "needs", "srcdir": "doc_test/doc_needimport_download_needs_json_negative"}], + [ + { + "buildername": "needs", + "srcdir": "doc_test/doc_needimport_download_needs_json_negative", + } + ], indirect=True, ) def test_needimport_needs_json_download_negative(test_app): @@ -210,11 +243,16 @@ def test_needimport_needs_json_download_negative(test_app): with requests_mock.Mocker() as m: # test with invalid url - m.get("http://my_wrong_name_company.com/docs/v1/remote-needs.json", json=remote_json) + m.get( + "http://my_wrong_name_company.com/docs/v1/remote-needs.json", + json=remote_json, + ) src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) + output = subprocess.run( + ["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True + ) assert ( "NeedimportException: Getting http://my_wrong_name_company.com/docs/v1/remote-needs.json didn't work." in output.stderr.decode("utf-8") diff --git a/tests/test_jinja_content_option.py b/tests/test_jinja_content_option.py index f9cd5f508..dbc03913e 100644 --- a/tests/test_jinja_content_option.py +++ b/tests/test_jinja_content_option.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_need_jinja_content"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_need_jinja_content"}], + indirect=True, ) def test_doc_need_jinja_content(test_app): app = test_app diff --git a/tests/test_layouts.py b/tests/test_layouts.py index 89097855f..bed530dca 100644 --- a/tests/test_layouts.py +++ b/tests/test_layouts.py @@ -3,7 +3,11 @@ from tests.util import extract_needs_from_html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_layout"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_layout"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() @@ -17,7 +21,8 @@ def test_doc_build_html(test_app): assert len(needs) == 6 assert ( - 'author: some author' in html + 'author: some author' + in html ) assert '' in html diff --git a/tests/test_list2need.py b/tests/test_list2need.py index 0569c32a9..605f4331f 100644 --- a/tests/test_list2need.py +++ b/tests/test_list2need.py @@ -3,14 +3,21 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_list2need"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_list2need"}], + indirect=True, +) def test_doc_list2need_html(test_app): app = test_app app.build() index_html = Path(app.outdir, "index.html").read_text() assert "NEED-002" in index_html assert "Sub-Need on level 3" in index_html - assert 'Test chapter' in index_html + assert ( + 'Test chapter' + in index_html + ) # Check parent-child linking (nested) assert ( @@ -51,5 +58,6 @@ def test_doc_list2need_html(test_app): assert ( '
is triggered by: NEED-B
' in links_down_html + 'href="#NEED-B" title="NEED-C">NEED-B' + in links_down_html ) diff --git a/tests/test_multiple_link_backs.py b/tests/test_multiple_link_backs.py index 10516e948..2c9407918 100644 --- a/tests/test_multiple_link_backs.py +++ b/tests/test_multiple_link_backs.py @@ -4,7 +4,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/multiple_link_backs"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/multiple_link_backs"}], + indirect=True, +) def test_multiple_link_backs(test_app): app = test_app diff --git a/tests/test_need_constraints.py b/tests/test_need_constraints.py index 72e869dfa..029d2cc4f 100644 --- a/tests/test_need_constraints.py +++ b/tests/test_need_constraints.py @@ -8,7 +8,11 @@ from sphinx_needs.api.exceptions import NeedsConstraintNotAllowed -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/need_constraints"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/need_constraints"}], + indirect=True, +) def test_need_constraints(test_app, snapshot): app = test_app app.build() @@ -21,16 +25,23 @@ def test_need_constraints(test_app, snapshot): out_dir = srcdir / "_build" # Check return code when "-W --keep-going" not used - out_normal = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out_normal = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out_normal.returncode == 0 # Check return code when only "-W" is used - out_w = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir, "-W"], capture_output=True) + out_w = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir, "-W"], capture_output=True + ) assert out_w.returncode >= 1 # test if constraints_results / constraints_passed is properly set html = Path(app.outdir, "index.html").read_text() - assert "constraints_results: {'critical': {'check_0': False}}" in html + assert ( + "constraints_results: {'critical': {'check_0': False}}" + in html + ) assert 'constraints_passed: False' in html # test force_style @@ -43,7 +54,9 @@ def test_need_constraints(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/need_constraints_failed"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/need_constraints_failed"}], + indirect=True, ) def test_need_constraints_config(test_app): app = test_app diff --git a/tests/test_need_count.py b/tests/test_need_count.py index c032460b6..7d0883d4f 100644 --- a/tests/test_need_count.py +++ b/tests/test_need_count.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_need_count"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_need_count"}], + indirect=True, +) def test_doc_need_count(test_app): app = test_app app.build() diff --git a/tests/test_need_delete_option.py b/tests/test_need_delete_option.py index 33766632a..d61ae0f09 100644 --- a/tests/test_need_delete_option.py +++ b/tests/test_need_delete_option.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_need_delete"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_need_delete"}], + indirect=True, +) def test_doc_need_delete(test_app): app = test_app app.build() diff --git a/tests/test_need_id_from_title.py b/tests/test_need_id_from_title.py index e03d20f63..077e0814b 100644 --- a/tests/test_need_id_from_title.py +++ b/tests/test_need_id_from_title.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_need_id_from_title"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_need_id_from_title"}], + indirect=True, ) def test_doc_build_html(test_app): app = test_app @@ -27,5 +29,9 @@ def test_doc_build_html(test_app): if need_type["directive"] == "story": need_directive_story_prefix = need_type["prefix"] - assert len("US_A_STORY_TITLE_2FD447") == need_id_length + len(need_directive_story_prefix) - assert len("US_CONTENT_ID_TEST_A313") == need_id_length + len(need_directive_story_prefix) + assert len("US_A_STORY_TITLE_2FD447") == need_id_length + len( + need_directive_story_prefix + ) + assert len("US_CONTENT_ID_TEST_A313") == need_id_length + len( + need_directive_story_prefix + ) diff --git a/tests/test_need_parts.py b/tests/test_need_parts.py index 09057c00a..e8a3c8c69 100644 --- a/tests/test_need_parts.py +++ b/tests/test_need_parts.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_need_parts"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_need_parts"}], + indirect=True, +) def test_doc_need_parts(test_app): app = test_app app.build() @@ -16,5 +20,7 @@ def test_doc_need_parts(test_app): assert 'exit() (SP_TOO_001.1)' in html assert 'start() (SP_TOO_001.2)' in html assert 'blub() (SP_TOO_001.awesome_id)' in html - assert 'My custom link name (SP_TOO_001.awesome_id)' in html + assert ( + 'My custom link name (SP_TOO_001.awesome_id)' in html + ) assert "SP_TOO_001" in html diff --git a/tests/test_needarch.py b/tests/test_needarch.py index b75dfab13..0d68df362 100644 --- a/tests/test_needarch.py +++ b/tests/test_needarch.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needarch"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needarch"}], + indirect=True, +) def test_doc_needarch(test_app): app = test_app app.build() @@ -12,7 +16,9 @@ def test_doc_needarch(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needarch_negative_tests"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needarch_negative_tests"}], + indirect=True, ) def test_doc_needarch_negative(test_app): import subprocess @@ -22,7 +28,9 @@ def test_doc_needarch_negative(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( @@ -32,7 +40,9 @@ def test_doc_needarch_negative(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needarch_jinja_func_import"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needarch_jinja_func_import"}], + indirect=True, ) def test_doc_needarch_jinja_import(test_app, snapshot): app = test_app @@ -46,7 +56,9 @@ def test_doc_needarch_jinja_import(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needarch_jinja_func_need"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needarch_jinja_func_need"}], + indirect=True, ) def test_needarch_jinja_func_need(test_app, snapshot): app = test_app @@ -63,5 +75,7 @@ def test_needarch_jinja_func_need(test_app, snapshot): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 diff --git a/tests/test_needbar.py b/tests/test_needbar.py index 776b7a758..d677e7b74 100644 --- a/tests/test_needbar.py +++ b/tests/test_needbar.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needbar"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needbar"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_needextend.py b/tests/test_needextend.py index c4095446f..266655c1d 100644 --- a/tests/test_needextend.py +++ b/tests/test_needextend.py @@ -7,7 +7,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needextend"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needextend"}], + indirect=True, +) def test_doc_needextend_html(test_app: Sphinx, snapshot): app = test_app app.build() @@ -20,13 +24,15 @@ def test_doc_needextend_html(test_app: Sphinx, snapshot): assert ( '
links outgoing: extend_test_004
' in index_html + 'test_004" title="extend_test_003">extend_test_004' + in index_html ) assert ( '' in index_html + 'test_004" title="extend_test_006">extend_test_004' + in index_html ) page_1__html = Path(app.outdir, "page_1.html").read_text() @@ -38,7 +44,9 @@ def test_doc_needextend_html(test_app: Sphinx, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needextend_strict"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needextend_strict"}], + indirect=True, ) def test_doc_needextend_strict(test_app): import os @@ -49,10 +57,15 @@ def test_doc_needextend_strict(test_app): srcdir = Path(app.srcdir) out_dir = os.path.join(srcdir, "_build") - out = subprocess.run(["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-b", "html", srcdir, out_dir], capture_output=True + ) # Strict option is set to false on needextend. Log info-level message - assert "Provided id strict_disable_extend_test for needextend does not exist." in out.stdout.decode("utf-8") + assert ( + "Provided id strict_disable_extend_test for needextend does not exist." + in out.stdout.decode("utf-8") + ) # Strict option is set to true on needextend. Raise Exception if sys.platform == "win32": assert ( @@ -67,7 +80,9 @@ def test_doc_needextend_strict(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needextend_dynamic"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needextend_dynamic"}], + indirect=True, ) def test_doc_needextend_dynamic(test_app, snapshot): app = test_app diff --git a/tests/test_needextract.py b/tests/test_needextract.py index 678d6f533..e0d1b40cb 100644 --- a/tests/test_needextract.py +++ b/tests/test_needextract.py @@ -4,7 +4,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needextract"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needextract"}], + indirect=True, +) def test_needextract_filter_options(test_app): import subprocess @@ -13,11 +17,17 @@ def test_needextract_filter_options(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needextract"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needextract"}], + indirect=True, +) def test_needextract_basic_run(test_app): app = test_app app.build() @@ -28,9 +38,13 @@ def run_checks(checks, html_path): html_path = str(Path(app.outdir, html_path)) tree = html_parser.parse(html_path) for check in checks: - img_src = tree.xpath(f"//table[@id='{check[0]}']//td[@class='need content']//img/@src")[0] + img_src = tree.xpath( + f"//table[@id='{check[0]}']//td[@class='need content']//img/@src" + )[0] assert img_src == check[1] - assert os.path.exists(str(Path(app.outdir, os.path.dirname(html_path), img_src))) + assert os.path.exists( + str(Path(app.outdir, os.path.dirname(html_path), img_src)) + ) checks = [ ("US_SUB_001", "_images/smile.png"), diff --git a/tests/test_needextract_with_nested_needs.py b/tests/test_needextract_with_nested_needs.py index 5e660129c..696ef4396 100644 --- a/tests/test_needextract_with_nested_needs.py +++ b/tests/test_needextract_with_nested_needs.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/needextract_with_nested_needs"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/needextract_with_nested_needs"}], + indirect=True, ) def test_needextract_with_nested_needs(test_app): app = test_app @@ -22,9 +24,11 @@ def test_needextract_with_nested_needs(test_app): ) assert ( 'SPEC_1_1_1' in needextract_html + 'href="index.html#SPEC_1_1_1" title="SPEC_1_1_1">SPEC_1_1_1' + in needextract_html ) assert ( 'SPEC_1_1_2' in needextract_html + 'href="index.html#SPEC_1_1_2" title="SPEC_1_1_2">SPEC_1_1_2' + in needextract_html ) diff --git a/tests/test_needflow.py b/tests/test_needflow.py index 6c9ae191e..05278fe72 100644 --- a/tests/test_needflow.py +++ b/tests/test_needflow.py @@ -4,7 +4,11 @@ from docutils import __version__ as doc_ver -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needflow"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needflow"}], + indirect=True, +) def test_doc_build_html(test_app): import sphinx @@ -47,12 +51,16 @@ def test_doc_build_html(test_app): assert "STORY_2 [[../index.html#STORY_2]]" in page_html assert "STORY_2.another_one [[../index.html#STORY_2.another_one]]" in page_html - empty_needflow_with_debug = Path(app.outdir, "empty_needflow_with_debug.html").read_text() + empty_needflow_with_debug = Path( + app.outdir, "empty_needflow_with_debug.html" + ).read_text() assert "No needs passed the filters" in empty_needflow_with_debug @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needflow_incl_child_needs"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needflow_incl_child_needs"}], + indirect=True, ) def test_doc_build_needflow_incl_child_needs(test_app): app = test_app @@ -80,7 +88,9 @@ def test_doc_build_needflow_incl_child_needs(test_app): assert index_html.count("[[../index.html#SPEC_5]]") == 2 assert index_html.count("@enduml") == 1 - single_parent_need_filer_html = Path(app.outdir, "single_parent_need_filer.html").read_text() + single_parent_need_filer_html = Path( + app.outdir, "single_parent_need_filer.html" + ).read_text() assert single_parent_need_filer_html assert single_parent_need_filer_html.count("@startuml") == 1 assert single_parent_need_filer_html.count("[[../index.html#STORY_3]]") == 2 @@ -96,10 +106,14 @@ def test_doc_build_needflow_incl_child_needs(test_app): assert "[[../index.html#SPEC_4]]" not in single_parent_need_filer_html assert "[[../index.html#SPEC_5]]" not in single_parent_need_filer_html - single_child_with_child_need_filter_html = Path(app.outdir, "single_child_with_child_need_filter.html").read_text() + single_child_with_child_need_filter_html = Path( + app.outdir, "single_child_with_child_need_filter.html" + ).read_text() assert single_child_with_child_need_filter_html assert single_child_with_child_need_filter_html.count("@startuml") == 1 - assert single_child_with_child_need_filter_html.count("[[../index.html#STORY_2]]") == 2 + assert ( + single_child_with_child_need_filter_html.count("[[../index.html#STORY_2]]") == 2 + ) assert single_child_with_child_need_filter_html.count("@enduml") == 1 assert "[[../index.html#STORY_1]]" not in single_child_with_child_need_filter_html assert "[[../index.html#STORY_1.1]]" not in single_child_with_child_need_filter_html @@ -112,7 +126,9 @@ def test_doc_build_needflow_incl_child_needs(test_app): assert "[[../index.html#STORY_3]]" not in single_child_with_child_need_filter_html assert "[[../index.html#SPEC_5]]" not in single_child_with_child_need_filter_html - single_child_need_filter_html = Path(app.outdir, "single_child_need_filter.html").read_text() + single_child_need_filter_html = Path( + app.outdir, "single_child_need_filter.html" + ).read_text() assert single_child_need_filter_html assert single_child_need_filter_html.count("@startuml") == 1 assert single_child_need_filter_html.count("[[../index.html#SPEC_1]]") == 2 diff --git a/tests/test_needimport_noindex.py b/tests/test_needimport_noindex.py index dd1312af5..b6aaacf87 100644 --- a/tests/test_needimport_noindex.py +++ b/tests/test_needimport_noindex.py @@ -6,7 +6,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "latex", "srcdir": "doc_test/doc_needimport_noindex"}], indirect=True + "test_app", + [{"buildername": "latex", "srcdir": "doc_test/doc_needimport_noindex"}], + indirect=True, ) def test_doc_needimport_noindex(test_app): app = test_app @@ -17,5 +19,5 @@ def test_doc_needimport_noindex(test_app): print(f"in path {app.outdir}", sys.stderr) assert os.path.exists(latex_path) - assert 0 < len(latex) + assert len(latex) > 0 assert "AAA" in latex diff --git a/tests/test_needlist.py b/tests/test_needlist.py index 1436bf118..eafec6308 100644 --- a/tests/test_needlist.py +++ b/tests/test_needlist.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needlist"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needlist"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_needpie.py b/tests/test_needpie.py index 3e33ad8dc..4bcaa20c4 100644 --- a/tests/test_needpie.py +++ b/tests/test_needpie.py @@ -7,7 +7,11 @@ import sphinx -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needpie"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needpie"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_needpie_with_zero_needs.py b/tests/test_needpie_with_zero_needs.py index 44e9a012b..b775237ed 100644 --- a/tests/test_needpie_with_zero_needs.py +++ b/tests/test_needpie_with_zero_needs.py @@ -2,7 +2,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/needpie_with_zero_needs"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/needpie_with_zero_needs"}], + indirect=True, ) def test_needpie_with_zero_needs(test_app): app = test_app diff --git a/tests/test_needreport.py b/tests/test_needreport.py index 73c474fec..8efef1a84 100644 --- a/tests/test_needreport.py +++ b/tests/test_needreport.py @@ -3,13 +3,20 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needreport"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needreport"}], + indirect=True, +) def test_doc_needarch(test_app): app = test_app app.build() # check for warning about missing options warnings = app._warning.getvalue() - assert "index.rst:6: WARNING: No options specified to generate need report [needs.report]" in warnings + assert ( + "index.rst:6: WARNING: No options specified to generate need report [needs.report]" + in warnings + ) assert "index.rst:8: WARNING: Could not load needs report template file" in warnings html = Path(app.outdir, "index.html").read_text(encoding="utf8") assert "Need Types" in html diff --git a/tests/test_needs_builder.py b/tests/test_needs_builder.py index 06e9dbbca..61fa2c1d5 100644 --- a/tests/test_needs_builder.py +++ b/tests/test_needs_builder.py @@ -7,7 +7,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_needs_builder"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_needs_builder"}], + indirect=True, +) def test_doc_needs_builder(test_app, snapshot): app = test_app app.build() @@ -36,7 +40,9 @@ def test_doc_needs_builder_reproducible(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_needs_builder_negative_tests"}], indirect=True + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_needs_builder_negative_tests"}], + indirect=True, ) def test_doc_needs_build_without_needs_file(test_app): app = test_app @@ -44,13 +50,20 @@ def test_doc_needs_build_without_needs_file(test_app): srcdir = Path(app.srcdir) out_dir = os.path.join(srcdir, "_build") - out = subprocess.run(["sphinx-build", "-b", "needs", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-b", "needs", srcdir, out_dir], capture_output=True + ) assert not out.stderr - assert "needs.json found, but will not be used because needs_file not configured." in out.stdout.decode("utf-8") + assert ( + "needs.json found, but will not be used because needs_file not configured." + in out.stdout.decode("utf-8") + ) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_builder_parallel"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_builder_parallel"}], + indirect=True, ) def test_needs_html_and_json(test_app): """ @@ -64,7 +77,9 @@ def test_needs_html_and_json(test_app): srcdir = app.srcdir build_dir = os.path.join(app.outdir, "../needs") - subprocess.run(["sphinx-build", "-b", "needs", srcdir, build_dir], capture_output=True) + subprocess.run( + ["sphinx-build", "-b", "needs", srcdir, build_dir], capture_output=True + ) needs_json_path_2 = os.path.join(build_dir, "needs.json") assert os.path.exists(needs_json_path_2) diff --git a/tests/test_needs_external_needs_build.py b/tests/test_needs_external_needs_build.py index a5a4ad0a8..0ba7c4d34 100644 --- a/tests/test_needs_external_needs_build.py +++ b/tests/test_needs_external_needs_build.py @@ -9,7 +9,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs"}], + indirect=True, ) def test_doc_build_html(test_app, sphinx_test_tempdir): import subprocess @@ -18,9 +20,12 @@ def test_doc_build_html(test_app, sphinx_test_tempdir): src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - plantuml = r"java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") + plantuml = r"java -Djava.awt.headless=true -jar %s" % os.path.join( + sphinx_test_tempdir, "utils", "plantuml.jar" + ) output = subprocess.run( - ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True + ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], + capture_output=True, ) assert output.stderr.decode("utf-8").splitlines() == [ "WARNING: http://my_company.com/docs/v1/index.html#TEST_01: Need 'EXT_TEST_01' has unknown outgoing link 'SPEC_1' in field 'links' [needs.external_link_outgoing]", @@ -29,24 +34,36 @@ def test_doc_build_html(test_app, sphinx_test_tempdir): # run second time and check output_second = subprocess.run( - ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True + ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], + capture_output=True, ) assert not output_second.stderr # check if incremental build used # first build output - assert "updating environment: [new config] 3 added, 0 changed, 0 removed" in output.stdout.decode("utf-8") + assert ( + "updating environment: [new config] 3 added, 0 changed, 0 removed" + in output.stdout.decode("utf-8") + ) # second build output assert "loading pickled environment" in output_second.stdout.decode("utf-8") - assert "updating environment: [new config] 3 added, 0 changed, 0 removed" not in output_second.stdout.decode( - "utf-8" + assert ( + "updating environment: [new config] 3 added, 0 changed, 0 removed" + not in output_second.stdout.decode("utf-8") + ) + assert ( + "updating environment: 0 added, 0 changed, 0 removed" + in output_second.stdout.decode("utf-8") ) - assert "updating environment: 0 added, 0 changed, 0 removed" in output_second.stdout.decode("utf-8") -@pytest.mark.skipif(sys.platform == "win32", reason="assert fails on windows, need to fix later.") +@pytest.mark.skipif( + sys.platform == "win32", reason="assert fails on windows, need to fix later." +) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs"}], + indirect=True, ) def test_external_needs_base_url_relative_path(test_app): app = test_app @@ -75,7 +92,10 @@ def test_external_needs_base_url_relative_path(test_app): assert root_list_hrefs[4].attrib["href"] == "../../_build/html/index.html#TEST_02" assert root_list_hrefs[4].text == "EXT_REL_PATH_TEST_02: TEST_02 DESCRIPTION" # check base_url url - assert root_list_hrefs[1].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_02" + assert ( + root_list_hrefs[1].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_02" + ) assert root_list_hrefs[1].text == "EXT_TEST_02: TEST_02 DESCRIPTION" # check needtable usage for base_url in root level @@ -87,30 +107,48 @@ def test_external_needs_base_url_relative_path(test_app): assert root_table_hrefs[0].attrib["href"] == "../../_build/html/index.html#TEST_01" assert root_table_hrefs[0].text == "EXT_REL_PATH_TEST_01" # check base_url url in root level - assert root_table_hrefs[4].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + root_table_hrefs[4].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) assert root_table_hrefs[4].text == "EXT_TEST_01" # check needflow usage for base_url in root level if not sphinx.__version__.startswith("3.5"): if int(doc_ver.split(".")[1]) >= 18: root_flow_hrefs = root_tree.xpath("//figure/p/object/a/img") - assert root_tree.xpath("//figure/figcaption/p/span/a")[0].text == "My needflow" + assert ( + root_tree.xpath("//figure/figcaption/p/span/a")[0].text == "My needflow" + ) else: - root_flow_hrefs = root_tree.xpath("//div[@class='figure align-center']/p/object/a/img") + root_flow_hrefs = root_tree.xpath( + "//div[@class='figure align-center']/p/object/a/img" + ) assert ( - root_tree.xpath("//div[@class='figure align-center']/p[@class='caption']/span/a")[0].text + root_tree.xpath( + "//div[@class='figure align-center']/p[@class='caption']/span/a" + )[0].text == "My needflow" ) # check base_url url in root level - assert "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" in root_flow_hrefs[0].attrib["alt"] + assert ( + "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" + in root_flow_hrefs[0].attrib["alt"] + ) # check base_url relative path in root level - assert "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" in root_flow_hrefs[0].attrib["alt"] + assert ( + "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" + in root_flow_hrefs[0].attrib["alt"] + ) # check role need_outgoing and need_incoming for base_url in root level for element in root_tree.xpath("//p/span/a"): # check link for need_outgoing if element.text == "EXT_TEST_01": - assert element.attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + element.attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) # check link for need_incoming if element.text == "EXT_REL_PATH_TEST_02": assert element.attrib["href"] == "../../_build/html/index.html#TEST_02" @@ -128,7 +166,10 @@ def test_external_needs_base_url_relative_path(test_app): assert sub_list_hrefs[3].attrib["href"] == "../../../_build/html/index.html#TEST_01" assert sub_list_hrefs[3].text == "EXT_REL_PATH_TEST_01: TEST_01 DESCRIPTION" # check base_url url in subfolder level - assert sub_list_hrefs[0].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + sub_list_hrefs[0].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) assert sub_list_hrefs[0].text == "EXT_TEST_01: TEST_01 DESCRIPTION" # check needtable usage for base_url in subfolder level @@ -137,39 +178,61 @@ def test_external_needs_base_url_relative_path(test_app): if "class" in external_link.attrib: assert external_link.attrib["class"] == "external_link reference external" # check base_url relative path in subfolder level, one level deeper than base_url - assert sub_table_hrefs[0].attrib["href"] == "../../../_build/html/index.html#TEST_01" + assert ( + sub_table_hrefs[0].attrib["href"] == "../../../_build/html/index.html#TEST_01" + ) assert sub_table_hrefs[0].text == "EXT_REL_PATH_TEST_01" # check base_url url in subfolder level - assert sub_table_hrefs[4].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + sub_table_hrefs[4].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) assert sub_table_hrefs[4].text == "EXT_TEST_01" # check needflow usage for base_url in subfolder level if not sphinx.__version__.startswith("3.5"): if int(doc_ver.split(".")[1]) >= 18: sub_flow_hrefs = sub_tree.xpath("//figure/p/object/a/img") - assert sub_tree.xpath("//figure/figcaption/p/span/a")[0].text == "My needflow" + assert ( + sub_tree.xpath("//figure/figcaption/p/span/a")[0].text == "My needflow" + ) else: - sub_flow_hrefs = sub_tree.xpath("//div[@class='figure align-center']/p/object/a/img") + sub_flow_hrefs = sub_tree.xpath( + "//div[@class='figure align-center']/p/object/a/img" + ) assert ( - sub_tree.xpath("//div[@class='figure align-center']/p[@class='caption']/span/a")[0].text + sub_tree.xpath( + "//div[@class='figure align-center']/p[@class='caption']/span/a" + )[0].text == "My needflow" ) # check base_url url in root level - assert "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" in sub_flow_hrefs[0].attrib["alt"] + assert ( + "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" + in sub_flow_hrefs[0].attrib["alt"] + ) # check base_url relative path in subfolder level, one level deeper than base_url - assert "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" in sub_flow_hrefs[0].attrib["alt"] + assert ( + "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" + in sub_flow_hrefs[0].attrib["alt"] + ) # check role need_outgoing and need_incoming for base_url in subfolder level for element in sub_tree.xpath("//p/span/a"): # check link for need_outgoing if element.text == "EXT_TEST_01": - assert element.attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + element.attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) # check link for need_incoming if element.text == "EXT_REL_PATH_TEST_02": assert element.attrib["href"] == "../../../_build/html/index.html#TEST_02" # check usage in sub subfolder level - sub_sub_html_path = str(Path(app.outdir, "subfolder_b", "subfolder_c", "index.html")) + sub_sub_html_path = str( + Path(app.outdir, "subfolder_b", "subfolder_c", "index.html") + ) sub_sub_tree = html_parser.parse(sub_sub_html_path) # check needlist usage for base_url in subsubfolder level @@ -178,10 +241,16 @@ def test_external_needs_base_url_relative_path(test_app): if "class" in ext_link.attrib: assert ext_link.attrib["class"] == "external_link reference external" # check base_url relative path in subsubfolder level, two level deeper than base_url - assert sub_sub_list_hrefs[3].attrib["href"] == "../../../../_build/html/index.html#TEST_01" + assert ( + sub_sub_list_hrefs[3].attrib["href"] + == "../../../../_build/html/index.html#TEST_01" + ) assert sub_sub_list_hrefs[3].text == "EXT_REL_PATH_TEST_01: TEST_01 DESCRIPTION" # check base_url url in subsubfolder level - assert sub_sub_list_hrefs[0].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + sub_sub_list_hrefs[0].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) assert sub_sub_list_hrefs[0].text == "EXT_TEST_01: TEST_01 DESCRIPTION" # check needtable usage for base_url in subsubfolder level @@ -190,45 +259,67 @@ def test_external_needs_base_url_relative_path(test_app): if "class" in external_link.attrib: assert external_link.attrib["class"] == "external_link reference external" # check base_url relative path in subsubfolder level, two level deeper than base_url - assert sub_sub_table_hrefs[0].attrib["href"] == "../../../../_build/html/index.html#TEST_01" + assert ( + sub_sub_table_hrefs[0].attrib["href"] + == "../../../../_build/html/index.html#TEST_01" + ) assert sub_sub_table_hrefs[0].text == "EXT_REL_PATH_TEST_01" # check base_url url in subsubfolder level - assert sub_sub_table_hrefs[4].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + sub_sub_table_hrefs[4].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) assert sub_sub_table_hrefs[4].text == "EXT_TEST_01" # check needflow usage for base_url in subsubfolder level if not sphinx.__version__.startswith("3.5"): if int(doc_ver.split(".")[1]) >= 18: sub_sub_flow_hrefs = sub_sub_tree.xpath("//figure/p/object/a/img") - assert sub_sub_tree.xpath("//figure/figcaption/p/span/a")[0].text == "My needflow" + assert ( + sub_sub_tree.xpath("//figure/figcaption/p/span/a")[0].text + == "My needflow" + ) else: - sub_sub_flow_hrefs = sub_tree.xpath("//div[@class='figure align-center']/p/object/a/img") + sub_sub_flow_hrefs = sub_tree.xpath( + "//div[@class='figure align-center']/p/object/a/img" + ) assert ( - sub_sub_tree.xpath("//div[@class='figure align-center']/p[@class='caption']/span/a")[0].text + sub_sub_tree.xpath( + "//div[@class='figure align-center']/p[@class='caption']/span/a" + )[0].text == "My needflow" ) # check base_url url in subsubfolder level assert ( - "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" in sub_sub_flow_hrefs[0].attrib["alt"] + "as EXT_TEST_01 [[http://my_company.com/docs/v1/index.html#TEST_01]]" + in sub_sub_flow_hrefs[0].attrib["alt"] ) # check base_url relative path in subsubfolder level, two level deeper than base_url assert ( - "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" in sub_sub_flow_hrefs[0].attrib["alt"] + "as EXT_REL_PATH_TEST_01 [[../../../_build/html/index.html#TEST_01]]" + in sub_sub_flow_hrefs[0].attrib["alt"] ) # check role need_outgoing and need_incoming for base_url in subsubfolder level for element in sub_sub_tree.xpath("//p/span/a"): # check link for need_outgoing if element.text == "EXT_TEST_01": - assert element.attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_01" + assert ( + element.attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_01" + ) # check link for need_incoming if element.text == "EXT_REL_PATH_TEST_02": - assert element.attrib["href"] == "../../../../_build/html/index.html#TEST_02" + assert ( + element.attrib["href"] == "../../../../_build/html/index.html#TEST_02" + ) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs_remote"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs_remote"}], + indirect=True, ) def test_external_needs_json_url(test_app): app = test_app @@ -283,7 +374,10 @@ def test_external_needs_json_url(test_app): if "class" in ext_link.attrib: assert ext_link.attrib["class"] == "external_link reference external" # check usage from remote URL - assert root_list_hrefs[0].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_101" + assert ( + root_list_hrefs[0].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_101" + ) assert root_list_hrefs[0].text == "EXT_REMOTE_TEST_101: TEST_101 TITLE" # check needtable usage for base_url in root level @@ -292,12 +386,22 @@ def test_external_needs_json_url(test_app): if "class" in external_link.attrib: assert external_link.attrib["class"] == "external_link reference external" # check for remote url - assert root_table_hrefs[0].attrib["href"] == "http://my_company.com/docs/v1/index.html#TEST_101" + assert ( + root_table_hrefs[0].attrib["href"] + == "http://my_company.com/docs/v1/index.html#TEST_101" + ) assert root_table_hrefs[0].text == "EXT_REMOTE_TEST_101" @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs_with_target_url"}], indirect=True + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_needs_external_needs_with_target_url", + } + ], + indirect=True, ) def test_external_needs_target_url(test_app): app = test_app diff --git a/tests/test_needs_filter_data.py b/tests/test_needs_filter_data.py index ff1c4f710..bf1fee904 100644 --- a/tests/test_needs_filter_data.py +++ b/tests/test_needs_filter_data.py @@ -5,7 +5,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_filter_data"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_filter_data"}], + indirect=True, ) def test_doc_needs_filter_data_html(test_app): app = test_app @@ -29,7 +31,10 @@ def test_doc_needs_filter_data_html(test_app): # Check needtable works assert 'Example table' in index_html assert '

Test Example 3

' in index_html - assert '

my_tag; current_variant

' in index_html + assert ( + '

my_tag; current_variant

' + in index_html + ) # check needflow works if int(doc_ver.split(".")[1]) >= 18: @@ -48,7 +53,8 @@ def test_doc_needs_filter_data_html(test_app): assert ( 'tags: ' 'test_tag_001' - ', current_variant' in index_html + ', current_variant' + in index_html ) # check needs_warnings works @@ -62,7 +68,9 @@ def test_doc_needs_filter_data_html(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_filter_data"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_filter_data"}], + indirect=True, ) def test_doc_needs_filter_code(test_app): app = test_app diff --git a/tests/test_needs_id_builder.py b/tests/test_needs_id_builder.py index 6c647242b..cca0c9ef7 100644 --- a/tests/test_needs_id_builder.py +++ b/tests/test_needs_id_builder.py @@ -9,7 +9,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "needs_id", "srcdir": "doc_test/doc_needs_builder"}], indirect=True + "test_app", + [{"buildername": "needs_id", "srcdir": "doc_test/doc_needs_builder"}], + indirect=True, ) def test_doc_needs_id_builder(test_app, snapshot): app = test_app @@ -17,5 +19,7 @@ def test_doc_needs_id_builder(test_app, snapshot): data = SphinxNeedsData(app.env) needs_config = NeedsSphinxConfig(app.config) needs_id_path = Path(app.outdir, needs_config.build_json_per_id_path) - data = {path.name: json.loads(path.read_text()) for path in needs_id_path.glob("*.json")} + data = { + path.name: json.loads(path.read_text()) for path in needs_id_path.glob("*.json") + } assert data == snapshot(exclude=props("created")) diff --git a/tests/test_needs_warning.py b/tests/test_needs_warning.py index 3a4f03411..866d9bab1 100644 --- a/tests/test_needs_warning.py +++ b/tests/test_needs_warning.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_warnings"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needs_warnings"}], + indirect=True, +) def test_needs_warnings(test_app): app = test_app app.build() @@ -18,7 +22,10 @@ def test_needs_warnings(test_app): # check warnings contents assert "WARNING: invalid_status: failed" in warnings assert "failed needs: 2 (SP_TOO_001, US_63252)" in warnings - assert "used filter: status not in ['open', 'closed', 'done', 'example_2', 'example_3']" in warnings + assert ( + "used filter: status not in ['open', 'closed', 'done', 'example_2', 'example_3']" + in warnings + ) # check needs warning from custom defined filter code assert "failed needs: 1 (TC_001)" in warnings @@ -36,7 +43,14 @@ def test_needs_warnings(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_warnings_return_status_code"}], indirect=True + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_needs_warnings_return_status_code", + } + ], + indirect=True, ) def test_needs_warnings_return_status_code(test_app): import subprocess @@ -47,22 +61,28 @@ def test_needs_warnings_return_status_code(test_app): out_dir = srcdir / "_build" # Check return code when "-W --keep-going" not used - out_normal = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out_normal = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out_normal.returncode == 0 # Check return code when only "-W" is used - out_w = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir, "-W"], capture_output=True) + out_w = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir, "-W"], capture_output=True + ) assert out_w.returncode >= 1 # Check return code when only "--keep-going" is used out_keep_going = subprocess.run( - ["sphinx-build", "-M", "html", srcdir, out_dir, "--keep-going"], capture_output=True + ["sphinx-build", "-M", "html", srcdir, out_dir, "--keep-going"], + capture_output=True, ) assert out_keep_going.returncode == 0 # Check return code when "-W --keep-going" is used out_w_keep_going = subprocess.run( - ["sphinx-build", "-M", "html", srcdir, out_dir, "-W", "--keep-going"], capture_output=True + ["sphinx-build", "-M", "html", srcdir, out_dir, "-W", "--keep-going"], + capture_output=True, ) assert out_w_keep_going.returncode == 1 @@ -77,7 +97,10 @@ def test_needs_warnings_return_status_code(test_app): # Check warnings contents assert "WARNING: invalid_status: failed" in warnings assert "failed needs: 2 (SP_TOO_001, US_63252)" in warnings - assert "used filter: status not in ['open', 'closed', 'done', 'example_2', 'example_3']" in warnings + assert ( + "used filter: status not in ['open', 'closed', 'done', 'example_2', 'example_3']" + in warnings + ) # Check needs warning from custom defined filter code assert "WARNING: type_match: failed" in warnings diff --git a/tests/test_needsfile.py b/tests/test_needsfile.py index 5bcae3149..d5a24f35c 100644 --- a/tests/test_needsfile.py +++ b/tests/test_needsfile.py @@ -1,7 +1,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_needsfile"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_needsfile"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_needtable.py b/tests/test_needtable.py index 211be333e..cdbed2296 100644 --- a/tests/test_needtable.py +++ b/tests/test_needtable.py @@ -4,7 +4,11 @@ from docutils import __version__ as doc_ver -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() @@ -59,7 +63,11 @@ def test_doc_build_html(test_app): assert '' in colwidths_html_path -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], + indirect=True, +) def test_doc_needtable_options(test_app): import sphinx @@ -121,7 +129,11 @@ def test_doc_needtable_options(test_app): assert string_column_order in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], + indirect=True, +) def test_doc_needtable_styles(test_app): app = test_app app.build() @@ -131,7 +143,11 @@ def test_doc_needtable_styles(test_app): assert "NEEDS_DATATABLES" in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], + indirect=True, +) def test_doc_needtable_parts(test_app): app = test_app app.build() @@ -142,7 +158,11 @@ def test_doc_needtable_parts(test_app): assert 'class="need_part' in html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needtable"}], + indirect=True, +) def test_doc_needtable_titles(test_app): app = test_app app.build() diff --git a/tests/test_needuml.py b/tests/test_needuml.py index 45a0f518b..4beb42f9a 100644 --- a/tests/test_needuml.py +++ b/tests/test_needuml.py @@ -5,7 +5,11 @@ from syrupy.filters import props -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml"}], + indirect=True, +) def test_doc_build_html(test_app, snapshot): app = test_app app.build() @@ -20,7 +24,9 @@ def test_doc_build_html(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_duplicate_key"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_duplicate_key"}], + indirect=True, ) def test_needuml_option_key_duplicate(test_app): app = test_app @@ -28,17 +34,22 @@ def test_needuml_option_key_duplicate(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( "sphinx_needs.directives.needuml.NeedumlException: Inside need: INT_001, " - "found duplicate Needuml option key name: sequence" in out.stderr.decode("utf-8") + "found duplicate Needuml option key name: sequence" + in out.stderr.decode("utf-8") ) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_key_name_diagram"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_key_name_diagram"}], + indirect=True, ) def test_needuml_option_key_forbidden(test_app): app = test_app @@ -46,7 +57,9 @@ def test_needuml_option_key_forbidden(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( @@ -56,7 +69,9 @@ def test_needuml_option_key_forbidden(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_diagram_allowmixing"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_diagram_allowmixing"}], + indirect=True, ) def test_needuml_diagram_allowmixing(test_app): app = test_app @@ -64,11 +79,17 @@ def test_needuml_diagram_allowmixing(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_save"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_save"}], + indirect=True, +) def test_needuml_save(test_app, snapshot): app = test_app app.build() @@ -91,7 +112,9 @@ def test_needuml_save(test_app, snapshot): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_save_with_abs_path"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_save_with_abs_path"}], + indirect=True, ) def test_needuml_save_with_abs_path(test_app): app = test_app @@ -99,17 +122,22 @@ def test_needuml_save_with_abs_path(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( "sphinx_needs.directives.needuml.NeedumlException: " - "Given save path: /_out/my_needuml.puml, is not a relative path." in out.stderr.decode("utf-8") + "Given save path: /_out/my_needuml.puml, is not a relative path." + in out.stderr.decode("utf-8") ) @pytest.mark.parametrize( - "test_app", [{"buildername": "needumls", "srcdir": "doc_test/doc_needuml_save"}], indirect=True + "test_app", + [{"buildername": "needumls", "srcdir": "doc_test/doc_needuml_save"}], + indirect=True, ) def test_needumls_builder(test_app, snapshot): app = test_app @@ -131,7 +159,11 @@ def test_needumls_builder(test_app, snapshot): assert umls == snapshot -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_filter"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_filter"}], + indirect=True, +) def test_needuml_filter(test_app, snapshot): app = test_app app.build() @@ -145,12 +177,16 @@ def test_needuml_filter(test_app, snapshot): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_flow"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_flow"}], + indirect=True, ) def test_needuml_jinja_func_flow(test_app, snapshot): app = test_app @@ -165,12 +201,16 @@ def test_needuml_jinja_func_flow(test_app, snapshot): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_need_removed"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_need_removed"}], + indirect=True, ) def test_needuml_jinja_func_need_removed(test_app): app = test_app @@ -178,17 +218,25 @@ def test_needuml_jinja_func_need_removed(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( "sphinx_needs.directives.needuml.NeedumlException: " - "Jinja function 'need()' is not supported in needuml directive." in out.stderr.decode("utf-8") + "Jinja function 'need()' is not supported in needuml directive." + in out.stderr.decode("utf-8") ) @pytest.mark.parametrize( "test_app", - [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_import_negative_tests"}], + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_needuml_jinja_func_import_negative_tests", + } + ], indirect=True, ) def test_doc_needarch_jinja_import_negative(test_app): @@ -197,17 +245,22 @@ def test_doc_needarch_jinja_import_negative(test_app): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 1 assert ( "sphinx_needs.directives.needuml.NeedumlException: " - "Jinja function 'import()' is not supported in needuml directive." in out.stderr.decode("utf-8") + "Jinja function 'import()' is not supported in needuml directive." + in out.stderr.decode("utf-8") ) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_ref"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_needuml_jinja_func_ref"}], + indirect=True, ) def test_needuml_jinja_func_ref(test_app, snapshot): app = test_app @@ -223,5 +276,7 @@ def test_needuml_jinja_func_ref(test_app, snapshot): srcdir = Path(app.srcdir) out_dir = srcdir / "_build" - out = subprocess.run(["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True) + out = subprocess.run( + ["sphinx-build", "-M", "html", srcdir, out_dir], capture_output=True + ) assert out.returncode == 0 diff --git a/tests/test_open_needs_service.py b/tests/test_open_needs_service.py index da2cd1564..e88a2e1da 100644 --- a/tests/test_open_needs_service.py +++ b/tests/test_open_needs_service.py @@ -22,7 +22,12 @@ def json(): "description": "We finally need to build our Neptune3000 rocket.", "format": "txt", "project_id": 1, - "options": {"status": "done", "priority": "high", "costs": 3500000, "approved": 1}, + "options": { + "status": "done", + "priority": "high", + "costs": 3500000, + "approved": 1, + }, "references": {}, }, { @@ -32,7 +37,12 @@ def json(): "description": "Lets test the rocket on a test bench", "format": "txt", "project_id": 1, - "options": {"status": "open", "priority": "high", "costs": 500000, "approved": 0}, + "options": { + "status": "open", + "priority": "high", + "costs": 500000, + "approved": 0, + }, "references": {}, }, { @@ -42,7 +52,12 @@ def json(): "description": "Red and blue. No other colors please.", "format": "txt", "project_id": 1, - "options": {"status": "open", "priority": "low", "costs": 20000, "approved": 1}, + "options": { + "status": "open", + "priority": "low", + "costs": 20000, + "approved": 1, + }, "references": {"links": ["NEP_001", "NEP_002"]}, }, { @@ -66,11 +81,16 @@ def __init__(self): @staticmethod def json(): - return {"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9", "token_type": "bearer"} + return { + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9", + "token_type": "bearer", + } @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_open_needs_service"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_open_needs_service"}], + indirect=True, ) def test_ons_service(test_app, monkeypatch): def mock_get(*args, **kwargs): diff --git a/tests/test_parallel_execution.py b/tests/test_parallel_execution.py index 542ba1c3f..44164ffc3 100644 --- a/tests/test_parallel_execution.py +++ b/tests/test_parallel_execution.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/parallel_doc", "parallel": 4}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/parallel_doc", "parallel": 4}], + indirect=True, ) def test_doc_build_html(test_app): app = test_app @@ -16,7 +18,9 @@ def test_doc_build_html(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/parallel_doc", "parallel": 4}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/parallel_doc", "parallel": 4}], + indirect=True, ) def test_doc_parallel_build_html(test_app): app = test_app diff --git a/tests/test_report_dead_links.py b/tests/test_report_dead_links.py index fd6f88aa0..b7c69d3bf 100644 --- a/tests/test_report_dead_links.py +++ b/tests/test_report_dead_links.py @@ -5,14 +5,18 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_true"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_true"}], + indirect=True, ) def test_needs_dead_links_warnings(test_app): app = test_app src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) + output = subprocess.run( + ["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True + ) # check there are expected warnings stderr = output.stderr.decode("utf-8") @@ -25,14 +29,18 @@ def test_needs_dead_links_warnings(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_report_dead_links_true"}], indirect=True + "test_app", + [{"buildername": "needs", "srcdir": "doc_test/doc_report_dead_links_true"}], + indirect=True, ) def test_needs_dead_links_warnings_needs_builder(test_app): app = test_app src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - output = subprocess.run(["sphinx-build", "-M", "needs", src_dir, out_dir], capture_output=True) + output = subprocess.run( + ["sphinx-build", "-M", "needs", src_dir, out_dir], capture_output=True + ) # check there are expected warnings stderr = output.stderr.decode("utf-8") @@ -45,14 +53,18 @@ def test_needs_dead_links_warnings_needs_builder(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_false"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_report_dead_links_false"}], + indirect=True, ) def test_needs_dead_links_suppress_warnings(test_app): app = test_app src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) + output = subprocess.run( + ["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True + ) # check there are no warnings assert not output.stderr diff --git a/tests/test_role_need.py b/tests/test_role_need.py index a8119c001..7f1fc0dae 100644 --- a/tests/test_role_need.py +++ b/tests/test_role_need.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/role_need_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/role_need_doc"}], + indirect=True, +) def test_role_need(test_app): app = test_app app.build() @@ -24,5 +28,6 @@ def test_role_need(test_app): # External need ref assert ( 'EXT_TEST_01' in html + 'href="http://my_company.com/docs/v1/index.html#TEST_01">EXT_TEST_01' + in html ) diff --git a/tests/test_role_need_max_title_length.py b/tests/test_role_need_max_title_length.py index c8a9c1616..e6ab88963 100644 --- a/tests/test_role_need_max_title_length.py +++ b/tests/test_role_need_max_title_length.py @@ -6,7 +6,12 @@ @pytest.mark.parametrize( "test_app", - [{"buildername": "html", "srcdir": "doc_test/doc_role_need_max_title_length_unlimited"}], + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_role_need_max_title_length_unlimited", + } + ], indirect=True, ) def test_max_title_length_unlimited(test_app): @@ -23,7 +28,9 @@ def test_max_title_length_unlimited(test_app): @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_role_need_max_title_length"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_role_need_max_title_length"}], + indirect=True, ) def test_max_title_length_10(test_app): os.environ["MAX_TITLE_LENGTH"] = "10" diff --git a/tests/test_role_need_template.py b/tests/test_role_need_template.py index 2a15a740d..8e2aa4e88 100644 --- a/tests/test_role_need_template.py +++ b/tests/test_role_need_template.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_role_need_template"}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_role_need_template"}], + indirect=True, ) def test_doc_build_html(test_app): app = test_app diff --git a/tests/test_services/test_service_basics.py b/tests/test_services/test_service_basics.py index af961353a..f0b3254f0 100644 --- a/tests/test_services/test_service_basics.py +++ b/tests/test_services/test_service_basics.py @@ -34,7 +34,10 @@ def debug(self, options): "url": "http://dummy.company.com/my/service", "user": "my_user", }, - "answer": {"status_code": 200, "body": {"item_amount": 2, "items": ["item_1", "item_2"]}}, + "answer": { + "status_code": 200, + "body": {"item_amount": 2, "items": ["item_1", "item_2"]}, + }, } return debug_data @@ -50,7 +53,11 @@ def request(self, _options): return [] -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/service_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/service_doc"}], + indirect=True, +) def test_service_creation(test_app): app = test_app app.build() diff --git a/tests/test_styles/test_style_blank.py b/tests/test_styles/test_style_blank.py index ae34cf0b3..f4934b42b 100644 --- a/tests/test_styles/test_style_blank.py +++ b/tests/test_styles/test_style_blank.py @@ -5,7 +5,11 @@ from sphinx.builders.html import StandaloneHTMLBuilder -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_style_blank"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_style_blank"}], + indirect=True, +) def test_doc_style_blank(test_app): # css_files is not cleared between test runs so css files get # progressively added. This forces it to clear before re-building diff --git a/tests/test_styles/test_style_css_js_registration.py b/tests/test_styles/test_style_css_js_registration.py index a12b0b9f6..31866c334 100644 --- a/tests/test_styles/test_style_css_js_registration.py +++ b/tests/test_styles/test_style_css_js_registration.py @@ -26,8 +26,16 @@ def test_file_registration(): sphinx_app.build() # Only check Sphinx-Needs files - css_files = [x for x in sphinx_app.builder.css_files if x.startswith("_static/sphinx-needs")] - script_files = [x for x in sphinx_app.builder.script_files if x.startswith("_static/sphinx-needs")] + css_files = [ + x + for x in sphinx_app.builder.css_files + if x.startswith("_static/sphinx-needs") + ] + script_files = [ + x + for x in sphinx_app.builder.script_files + if x.startswith("_static/sphinx-needs") + ] css_run_1 = len(css_files) script_run_1 = len(script_files) @@ -41,8 +49,16 @@ def test_file_registration(): sphinx_app.build() # Only check Sphinx-Needs files - css_files = [x for x in sphinx_app.builder.css_files if x.startswith("_static/sphinx-needs")] - script_files = [x for x in sphinx_app.builder.script_files if x.startswith("_static/sphinx-needs")] + css_files = [ + x + for x in sphinx_app.builder.css_files + if x.startswith("_static/sphinx-needs") + ] + script_files = [ + x + for x in sphinx_app.builder.script_files + if x.startswith("_static/sphinx-needs") + ] css_run_2 = len(css_files) script_run_2 = len(script_files) diff --git a/tests/test_styles/test_style_custom.py b/tests/test_styles/test_style_custom.py index b76fa309b..e2f392fd0 100644 --- a/tests/test_styles/test_style_custom.py +++ b/tests/test_styles/test_style_custom.py @@ -5,7 +5,11 @@ from sphinx.builders.html import StandaloneHTMLBuilder -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_style_custom"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_style_custom"}], + indirect=True, +) def test_doc_style_custom(test_app): # css_files is not cleared between test runs so css files get # progressively added. This forces it to clear before re-building diff --git a/tests/test_styles/test_style_modern.py b/tests/test_styles/test_style_modern.py index 2e8fa8134..6c9956216 100644 --- a/tests/test_styles/test_style_modern.py +++ b/tests/test_styles/test_style_modern.py @@ -5,7 +5,11 @@ from sphinx.builders.html import StandaloneHTMLBuilder -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_style_modern"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_style_modern"}], + indirect=True, +) def test_doc_style_modern(test_app): # css_files is not cleared between test runs so css files get # progressively added. This forces it to clear before re-building diff --git a/tests/test_styles/test_style_unknown.py b/tests/test_styles/test_style_unknown.py index 6278ce31d..c934525a0 100644 --- a/tests/test_styles/test_style_unknown.py +++ b/tests/test_styles/test_style_unknown.py @@ -5,7 +5,11 @@ from sphinx.builders.html import StandaloneHTMLBuilder -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/doc_style_unknown"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/doc_style_unknown"}], + indirect=True, +) def test_doc_style_unknown(test_app): # css_files is not cleared between test runs so css files get # progressively added. This forces it to clear before re-building diff --git a/tests/test_test_doc.py b/tests/test_test_doc.py index 188971f07..8dbb8792d 100644 --- a/tests/test_test_doc.py +++ b/tests/test_test_doc.py @@ -3,7 +3,11 @@ import pytest -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], + indirect=True, +) def test_doc_build_html(test_app): app = test_app app.build() diff --git a/tests/test_title_from_content.py b/tests/test_title_from_content.py index 8bc99e7fd..28154d883 100644 --- a/tests/test_title_from_content.py +++ b/tests/test_title_from_content.py @@ -5,7 +5,11 @@ from tests.util import extract_needs_from_html -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/title_from_content"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/title_from_content"}], + indirect=True, +) def test_title_from_content_scenarios(test_app): app = test_app app.build() diff --git a/tests/test_title_optional.py b/tests/test_title_optional.py index b8333e960..972eb5e9f 100644 --- a/tests/test_title_optional.py +++ b/tests/test_title_optional.py @@ -21,10 +21,16 @@ def id(self): @property def title(self): title = self.need.find(".//html:span[@class='needs_title']", NS) - return title[0].text if title is not None else None # title[0] aims to the span_data element + return ( + title[0].text if title is not None else None + ) # title[0] aims to the span_data element -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/title_optional"}], indirect=True) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/title_optional"}], + indirect=True, +) def test_title_optional_scenarios(test_app): app = test_app app.build() diff --git a/tests/test_unicode.py b/tests/test_unicode.py index a43fddc64..d2148d204 100644 --- a/tests/test_unicode.py +++ b/tests/test_unicode.py @@ -4,8 +4,14 @@ import pytest -@pytest.mark.skipif(sys.platform == "win32", reason="assert fails on windows, need to fix later.") -@pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/unicode_support"}], indirect=True) +@pytest.mark.skipif( + sys.platform == "win32", reason="assert fails on windows, need to fix later." +) +@pytest.mark.parametrize( + "test_app", + [{"buildername": "html", "srcdir": "doc_test/unicode_support"}], + indirect=True, +) def test_unicode_html(test_app): app = test_app app.build() diff --git a/tests/test_unsafe_filter_for_filter_func.py b/tests/test_unsafe_filter_for_filter_func.py index 006784f09..23d9381ac 100644 --- a/tests/test_unsafe_filter_for_filter_func.py +++ b/tests/test_unsafe_filter_for_filter_func.py @@ -4,7 +4,14 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_filter_func_allow_dirty_filter"}], indirect=True + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_needs_filter_func_allow_dirty_filter", + } + ], + indirect=True, ) def test_doc_allow_unsafe_filter_for_filter_func(test_app): app = test_app diff --git a/tests/test_variants.py b/tests/test_variants.py index b91c5cba4..b9c3479a1 100644 --- a/tests/test_variants.py +++ b/tests/test_variants.py @@ -4,7 +4,9 @@ @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/variant_doc", "tags": ["tag_a"]}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/variant_doc", "tags": ["tag_a"]}], + indirect=True, ) def test_variant_options_html(test_app): app = test_app @@ -32,12 +34,15 @@ def test_variant_options_html(test_app): # Check if referenced link exists in html assert ( '
links outgoing: VA_003
' in html + 'internal" href="#VA_003" title="SPEC_003">VA_003
' + in html ) @pytest.mark.parametrize( - "test_app", [{"buildername": "html", "srcdir": "doc_test/variant_options", "tags": ["tag_a"]}], indirect=True + "test_app", + [{"buildername": "html", "srcdir": "doc_test/variant_options", "tags": ["tag_a"]}], + indirect=True, ) def test_empty_variant_options_html(test_app): app = test_app diff --git a/tests/util.py b/tests/util.py index 7c04aa80c..cb36b7f30 100644 --- a/tests/util.py +++ b/tests/util.py @@ -14,15 +14,21 @@ def __init__(self, need): def id(self): found_id = self.need.find(".//html:a[@class='reference internal']", NS) if found_id is None: - found_id = self.need.find(".//html:a[@class='reference internal']", {"html": ""}) + found_id = self.need.find( + ".//html:a[@class='reference internal']", {"html": ""} + ) return found_id.text @property def title(self): found_title = self.need.find(".//html:span[@class='needs_title']", NS) if found_title is None: - found_title = self.need.find(".//html:span[@class='needs_title']", {"html": ""}) - return found_title[0].text if found_title else None # title[0] aims to the span_data element + found_title = self.need.find( + ".//html:span[@class='needs_title']", {"html": ""} + ) + return ( + found_title[0].text if found_title else None + ) # title[0] aims to the span_data element def extract_needs_from_html(html): From 9a626fa1cabaa0b9cf993b18ee18fcfc02634e65 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 16 Feb 2024 12:34:16 +0000 Subject: [PATCH 12/24] =?UTF-8?q?=F0=9F=91=8C=20Improve=20and=20test=20git?= =?UTF-8?q?hub=20`needservice`=20directive=20(#1113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This directive was untested in the current test suite, so this commit adds a test and also makes some improvements to the testing and directive functionality: - remove duplication of request mocking solutions in the test-suite (removing https://github.com/jamielennox/requests-mock in favor of (https://github.com/getsentry/responses) - remove superfluous requests mocking in basic docs tests (that do not make any requests) - add test for github `needservice` directives - replace raising`NeedGithubServiceException` with emitting `needs.github` warnings - pass the directive to the `service.request` method (in a non-breaking manner) so that it can be used to add the directive source location to the warnings - Replace remaining tests that used `Sphinx` with `SphinxTestApp`, which can be "cleaned" after running (otherwise later test were emitting warnings caused by global variables previously mutated by these earlier tests) --- poetry.lock | 1228 +++++++++-------- pyproject.toml | 2 - sphinx_needs/directives/needservice.py | 6 +- sphinx_needs/services/base.py | 13 +- sphinx_needs/services/github.py | 126 +- sphinx_needs/services/open_needs.py | 9 +- tests/__snapshots__/test_service_github.ambr | 342 +++++ tests/benchmarks/test_basic.py | 15 - tests/benchmarks/test_official.py | 34 +- tests/doc_test/doc_service_github/conf.py | 8 + tests/doc_test/doc_service_github/index.rst | 23 + tests/test_basic_doc.py | 138 -- tests/test_import.py | 58 +- tests/test_measure_time.py | 2 - tests/test_needpie.py | 41 +- tests/test_needs_external_needs_build.py | 4 +- tests/test_open_needs_service.py | 174 ++- tests/test_service_github.py | 546 ++++++++ .../test_style_css_js_registration.py | 103 +- 19 files changed, 1853 insertions(+), 1019 deletions(-) create mode 100644 tests/__snapshots__/test_service_github.ambr create mode 100644 tests/doc_test/doc_service_github/conf.py create mode 100644 tests/doc_test/doc_service_github/index.rst create mode 100644 tests/test_service_github.py diff --git a/poetry.lock b/poetry.lock index b5317979b..90255facb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -38,49 +38,49 @@ files = [ [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "babel" -version = "2.13.1" +version = "2.14.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"}, - {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"}, + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} -setuptools = {version = "*", markers = "python_version >= \"3.12\""} [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "certifi" -version = "2023.7.22" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -228,7 +228,6 @@ files = [ {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, - {file = "contourpy-1.1.0-cp310-cp310-win32.whl", hash = "sha256:9b2dd2ca3ac561aceef4c7c13ba654aaa404cf885b187427760d7f7d4c57cff8"}, {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, @@ -237,7 +236,6 @@ files = [ {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, - {file = "contourpy-1.1.0-cp311-cp311-win32.whl", hash = "sha256:edb989d31065b1acef3828a3688f88b2abb799a7db891c9e282df5ec7e46221b"}, {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, @@ -246,7 +244,6 @@ files = [ {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, - {file = "contourpy-1.1.0-cp38-cp38-win32.whl", hash = "sha256:108dfb5b3e731046a96c60bdc46a1a0ebee0760418951abecbe0fc07b5b93b27"}, {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, @@ -255,7 +252,6 @@ files = [ {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, - {file = "contourpy-1.1.0-cp39-cp39-win32.whl", hash = "sha256:71551f9520f008b2950bef5f16b0e3587506ef4f23c734b71ffb7b89f8721999"}, {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, @@ -349,63 +345,63 @@ test-no-images = ["pytest", "pytest-cov", "wurlitzer"] [[package]] name = "coverage" -version = "7.3.2" +version = "7.4.1" description = "Code coverage measurement for Python" optional = true python-versions = ">=3.8" files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.dependencies] @@ -431,13 +427,13 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] @@ -453,13 +449,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.3" +version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = true python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] @@ -497,60 +493,60 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "fonttools" -version = "4.44.0" +version = "4.48.1" description = "Tools to manipulate font files" optional = true python-versions = ">=3.8" files = [ - {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1cd1c6bb097e774d68402499ff66185190baaa2629ae2f18515a2c50b93db0c"}, - {file = "fonttools-4.44.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9eab7f9837fdaa2a10a524fbcc2ec24bf60637c044b6e4a59c3f835b90f0fae"}, - {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f412954275e594f7a51c16f3b3edd850acb0d842fefc33856b63a17e18499a5"}, - {file = "fonttools-4.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d25893885e80a5955186791eed5579f1e75921751539cc1dc3ffd1160b48cf"}, - {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:22ea8aa7b3712450b42b044702bd3a64fd118006bad09a6f94bd1b227088492e"}, - {file = "fonttools-4.44.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df40daa6c03b98652ffe8110ae014fe695437f6e1cb5a07e16ea37f40e73ac86"}, - {file = "fonttools-4.44.0-cp310-cp310-win32.whl", hash = "sha256:bca49da868e8bde569ef36f0cc1b6de21d56bf9c3be185c503b629c19a185287"}, - {file = "fonttools-4.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:dbac86d83d96099890e731cc2af97976ff2c98f4ba432fccde657c5653a32f1c"}, - {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e8ff7d19a6804bfd561cfcec9b4200dd1788e28f7de4be70189801530c47c1b3"}, - {file = "fonttools-4.44.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8a1fa9a718de0bc026979c93e1e9b55c5efde60d76f91561fd713387573817d"}, - {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05064f95aacdfc06f21e55096c964b2228d942b8675fa26995a2551f6329d2d"}, - {file = "fonttools-4.44.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31b38528f25bc662401e6ffae14b3eb7f1e820892fd80369a37155e3b636a2f4"}, - {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:05d7c4d2c95b9490e669f3cb83918799bf1c838619ac6d3bad9ea017cfc63f2e"}, - {file = "fonttools-4.44.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6999e80a125b0cd8e068d0210b63323f17338038c2ecd2e11b9209ec430fe7f2"}, - {file = "fonttools-4.44.0-cp311-cp311-win32.whl", hash = "sha256:a7aec7f5d14dfcd71fb3ebc299b3f000c21fdc4043079101777ed2042ba5b7c5"}, - {file = "fonttools-4.44.0-cp311-cp311-win_amd64.whl", hash = "sha256:518a945dbfe337744bfff31423c1430303b8813c5275dffb0f2577f0734a1189"}, - {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:59b6ad83cce067d10f4790c037a5904424f45bebb5e7be2eb2db90402f288267"}, - {file = "fonttools-4.44.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c2de1fb18198acd400c45ffe2aef5420c8d55fde903e91cba705596099550f3b"}, - {file = "fonttools-4.44.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f308b7a8d28208d54315d11d35f9888d6d607673dd4d42d60b463682ee0400"}, - {file = "fonttools-4.44.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66bc6efd829382f7a7e6cf33c2fb32b13edc8a239eb15f32acbf197dce7a0165"}, - {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a8b99713d3a0d0e876b6aecfaada5e7dc9fe979fcd90ef9fa0ba1d9b9aed03f2"}, - {file = "fonttools-4.44.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b63da598d9cbc52e2381f922da0e94d60c0429f92207bd3fb04d112fc82ea7cb"}, - {file = "fonttools-4.44.0-cp312-cp312-win32.whl", hash = "sha256:f611c97678604e302b725f71626edea113a5745a7fb557c958b39edb6add87d5"}, - {file = "fonttools-4.44.0-cp312-cp312-win_amd64.whl", hash = "sha256:58af428746fa73a2edcbf26aff33ac4ef3c11c8d75bb200eaea2f7e888d2de4e"}, - {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9ee8692e23028564c13d924004495f284df8ac016a19f17a87251210e1f1f928"}, - {file = "fonttools-4.44.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dab3d00d27b1a79ae4d4a240e8ceea8af0ff049fd45f05adb4f860d93744110d"}, - {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f53526668beccdb3409c6055a4ffe50987a7f05af6436fa55d61f5e7bd450219"}, - {file = "fonttools-4.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3da036b016c975c2d8c69005bdc4d5d16266f948a7fab950244e0f58301996a"}, - {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b99fe8ef4093f672d00841569d2d05691e50334d79f4d9c15c1265d76d5580d2"}, - {file = "fonttools-4.44.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d16d9634ff1e5cea2cf4a8cbda9026f766e4b5f30b48f8180f0e99133d3abfc"}, - {file = "fonttools-4.44.0-cp38-cp38-win32.whl", hash = "sha256:3d29509f6e05e8d725db59c2d8c076223d793e4e35773040be6632a0349f2f97"}, - {file = "fonttools-4.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4fa4f4bc8fd86579b8cdbe5e948f35d82c0eda0091c399d009b2a5a6b61c040"}, - {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c794de4086f06ae609b71ac944ec7deb09f34ecf73316fddc041087dd24bba39"}, - {file = "fonttools-4.44.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2db63941fee3122e31a21dd0f5b2138ce9906b661a85b63622421d3654a74ae2"}, - {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb01c49c8aa035d5346f46630209923d4927ed15c2493db38d31da9f811eb70d"}, - {file = "fonttools-4.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c79af80a835410874683b5779b6c1ec1d5a285e11c45b5193e79dd691eb111"}, - {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b6e6aa2d066f8dafd06d8d0799b4944b5d5a1f015dd52ac01bdf2895ebe169a0"}, - {file = "fonttools-4.44.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63a3112f753baef8c6ac2f5f574bb9ac8001b86c8c0c0380039db47a7f512d20"}, - {file = "fonttools-4.44.0-cp39-cp39-win32.whl", hash = "sha256:54efed22b2799a85475e6840e907c402ba49892c614565dc770aa97a53621b2b"}, - {file = "fonttools-4.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:2e91e19b583961979e2e5a701269d3cfc07418963bee717f8160b0a24332826b"}, - {file = "fonttools-4.44.0-py3-none-any.whl", hash = "sha256:b9beb0fa6ff3ea808ad4a6962d68ac0f140ddab080957b20d9e268e4d67fb335"}, - {file = "fonttools-4.44.0.tar.gz", hash = "sha256:4e90dd81b6e0d97ebfe52c0d12a17a9ef7f305d6bfbb93081265057d6092f252"}, + {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:702ae93058c81f46461dc4b2c79f11d3c3d8fd7296eaf8f75b4ba5bbf813cd5f"}, + {file = "fonttools-4.48.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97f0a49fa6aa2d6205c6f72f4f98b74ef4b9bfdcb06fd78e6fe6c7af4989b63e"}, + {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3260db55f1843e57115256e91247ad9f68cb02a434b51262fe0019e95a98738"}, + {file = "fonttools-4.48.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e740a7602c2bb71e1091269b5dbe89549749a8817dc294b34628ffd8b2bf7124"}, + {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4108b1d247953dd7c90ec8f457a2dec5fceb373485973cc852b14200118a51ee"}, + {file = "fonttools-4.48.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56339ec557f0c342bddd7c175f5e41c45fc21282bee58a86bd9aa322bec715f2"}, + {file = "fonttools-4.48.1-cp310-cp310-win32.whl", hash = "sha256:bff5b38d0e76eb18e0b8abbf35d384e60b3371be92f7be36128ee3e67483b3ec"}, + {file = "fonttools-4.48.1-cp310-cp310-win_amd64.whl", hash = "sha256:f7449493886da6a17472004d3818cc050ba3f4a0aa03fb47972e4fa5578e6703"}, + {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18b35fd1a850ed7233a99bbd6774485271756f717dac8b594958224b54118b61"}, + {file = "fonttools-4.48.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cad5cfd044ea2e306fda44482b3dd32ee47830fa82dfa4679374b41baa294f5f"}, + {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f30e605c7565d0da6f0aec75a30ec372072d016957cd8fc4469721a36ea59b7"}, + {file = "fonttools-4.48.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee76fd81a8571c68841d6ef0da750d5ff08ff2c5f025576473016f16ac3bcf7"}, + {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5057ade278e67923000041e2b195c9ea53e87f227690d499b6a4edd3702f7f01"}, + {file = "fonttools-4.48.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b10633aafc5932995a391ec07eba5e79f52af0003a1735b2306b3dab8a056d48"}, + {file = "fonttools-4.48.1-cp311-cp311-win32.whl", hash = "sha256:0d533f89819f9b3ee2dbedf0fed3825c425850e32bdda24c558563c71be0064e"}, + {file = "fonttools-4.48.1-cp311-cp311-win_amd64.whl", hash = "sha256:d20588466367f05025bb1efdf4e5d498ca6d14bde07b6928b79199c588800f0a"}, + {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0a2417547462e468edf35b32e3dd06a6215ac26aa6316b41e03b8eeaf9f079ea"}, + {file = "fonttools-4.48.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cf5a0cd974f85a80b74785db2d5c3c1fd6cc09a2ba3c837359b2b5da629ee1b0"}, + {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0452fcbfbce752ba596737a7c5ec5cf76bc5f83847ce1781f4f90eab14ece252"}, + {file = "fonttools-4.48.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578c00f93868f64a4102ecc5aa600a03b49162c654676c3fadc33de2ddb88a81"}, + {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:63dc592a16cd08388d8c4c7502b59ac74190b23e16dfc863c69fe1ea74605b68"}, + {file = "fonttools-4.48.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9b58638d8a85e3a1b32ec0a91d9f8171a877b4b81c408d4cb3257d0dee63e092"}, + {file = "fonttools-4.48.1-cp312-cp312-win32.whl", hash = "sha256:d10979ef14a8beaaa32f613bb698743f7241d92f437a3b5e32356dfb9769c65d"}, + {file = "fonttools-4.48.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdfd7557d1bd294a200bd211aa665ca3b02998dcc18f8211a5532da5b8fad5c5"}, + {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3cdb9a92521b81bf717ebccf592bd0292e853244d84115bfb4db0c426de58348"}, + {file = "fonttools-4.48.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b4ec6d42a7555f5ae35f3b805482f0aad0f1baeeef54859492ea3b782959d4a"}, + {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902e9c4e9928301912f34a6638741b8ae0b64824112b42aaf240e06b735774b1"}, + {file = "fonttools-4.48.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8c8b54bd1420c184a995f980f1a8076f87363e2bb24239ef8c171a369d85a31"}, + {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:12ee86abca46193359ea69216b3a724e90c66ab05ab220d39e3fc068c1eb72ac"}, + {file = "fonttools-4.48.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6978bade7b6c0335095bdd0bd97f8f3d590d2877b370f17e03e0865241694eb5"}, + {file = "fonttools-4.48.1-cp38-cp38-win32.whl", hash = "sha256:bcd77f89fc1a6b18428e7a55dde8ef56dae95640293bfb8f4e929929eba5e2a2"}, + {file = "fonttools-4.48.1-cp38-cp38-win_amd64.whl", hash = "sha256:f40441437b039930428e04fb05ac3a132e77458fb57666c808d74a556779e784"}, + {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0d2b01428f7da26f229a5656defc824427b741e454b4e210ad2b25ed6ea2aed4"}, + {file = "fonttools-4.48.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:df48798f9a4fc4c315ab46e17873436c8746f5df6eddd02fad91299b2af7af95"}, + {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2eb4167bde04e172a93cf22c875d8b0cff76a2491f67f5eb069566215302d45d"}, + {file = "fonttools-4.48.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c900508c46274d32d308ae8e82335117f11aaee1f7d369ac16502c9a78930b0a"}, + {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:594206b31c95fcfa65f484385171fabb4ec69f7d2d7f56d27f17db26b7a31814"}, + {file = "fonttools-4.48.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:292922dc356d7f11f5063b4111a8b719efb8faea92a2a88ed296408d449d8c2e"}, + {file = "fonttools-4.48.1-cp39-cp39-win32.whl", hash = "sha256:4709c5bf123ba10eac210d2d5c9027d3f472591d9f1a04262122710fa3d23199"}, + {file = "fonttools-4.48.1-cp39-cp39-win_amd64.whl", hash = "sha256:63c73b9dd56a94a3cbd2f90544b5fca83666948a9e03370888994143b8d7c070"}, + {file = "fonttools-4.48.1-py3-none-any.whl", hash = "sha256:e3e33862fc5261d46d9aae3544acb36203b1a337d00bdb5d3753aae50dac860e"}, + {file = "fonttools-4.48.1.tar.gz", hash = "sha256:8b8a45254218679c7f1127812761e7854ed5c8e34349aebf581e8c9204e7495a"}, ] [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "scipy"] -lxml = ["lxml (>=4.0,<5)"] +interpolatable = ["munkres", "pycairo", "scipy"] +lxml = ["lxml (>=4.0)"] pathops = ["skia-pathops (>=0.5.0)"] plot = ["matplotlib"] repacker = ["uharfbuzz (>=0.23.0)"] @@ -562,13 +558,13 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "identify" -version = "2.5.31" +version = "2.5.34" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"}, - {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"}, + {file = "identify-2.5.34-py2.py3-none-any.whl", hash = "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed"}, + {file = "identify-2.5.34.tar.gz", hash = "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d"}, ] [package.extras] @@ -576,13 +572,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] @@ -598,20 +594,20 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.8.0" +version = "7.0.1" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] @@ -663,13 +659,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.19.2" +version = "4.21.1" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema-4.19.2-py3-none-any.whl", hash = "sha256:eee9e502c788e89cb166d4d37f43084e3b64ab405c795c03d343a4dbc2c810fc"}, - {file = "jsonschema-4.19.2.tar.gz", hash = "sha256:c9ff4d7447eed9592c23a12ccee508baf0dd0d59650615e847feb6cdca74f392"}, + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, ] [package.dependencies] @@ -686,18 +682,18 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.7.1" +version = "2023.12.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.8" files = [ - {file = "jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1"}, - {file = "jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb"}, + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, ] [package.dependencies] importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} -referencing = ">=0.28.0" +referencing = ">=0.31.0" [[package]] name = "kiwisolver" @@ -812,112 +808,133 @@ files = [ {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, ] +[[package]] +name = "linkify-it-py" +version = "2.0.3" +description = "Links recognition library with FULL unicode support." +optional = true +python-versions = ">=3.7" +files = [ + {file = "linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048"}, + {file = "linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79"}, +] + +[package.dependencies] +uc-micro-py = "*" + +[package.extras] +benchmark = ["pytest", "pytest-benchmark"] +dev = ["black", "flake8", "isort", "pre-commit", "pyproject-flake8"] +doc = ["myst-parser", "sphinx", "sphinx-book-theme"] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "lxml" -version = "4.9.3" +version = "4.9.4" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ - {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, - {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, - {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, - {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, - {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, - {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e214025e23db238805a600f1f37bf9f9a15413c7bf5f9d6ae194f84980c78722"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec53a09aee61d45e7dbe7e91252ff0491b6b5fee3d85b2d45b173d8ab453efc1"}, + {file = "lxml-4.9.4-cp27-cp27m-win32.whl", hash = "sha256:7d1d6c9e74c70ddf524e3c09d9dc0522aba9370708c2cb58680ea40174800013"}, + {file = "lxml-4.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:cb53669442895763e61df5c995f0e8361b61662f26c1b04ee82899c2789c8f69"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:647bfe88b1997d7ae8d45dabc7c868d8cb0c8412a6e730a7651050b8c7289cf2"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d973729ce04784906a19108054e1fd476bc85279a403ea1a72fdb051c76fa48"}, + {file = "lxml-4.9.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:056a17eaaf3da87a05523472ae84246f87ac2f29a53306466c22e60282e54ff8"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aaa5c173a26960fe67daa69aa93d6d6a1cd714a6eb13802d4e4bd1d24a530644"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:647459b23594f370c1c01768edaa0ba0959afc39caeeb793b43158bb9bb6a663"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bdd9abccd0927673cffe601d2c6cdad1c9321bf3437a2f507d6b037ef91ea307"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:00e91573183ad273e242db5585b52670eddf92bacad095ce25c1e682da14ed91"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a602ed9bd2c7d85bd58592c28e101bd9ff9c718fbde06545a70945ffd5d11868"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de362ac8bc962408ad8fae28f3967ce1a262b5d63ab8cefb42662566737f1dc7"}, + {file = "lxml-4.9.4-cp310-cp310-win32.whl", hash = "sha256:33714fcf5af4ff7e70a49731a7cc8fd9ce910b9ac194f66eaa18c3cc0a4c02be"}, + {file = "lxml-4.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:d3caa09e613ece43ac292fbed513a4bce170681a447d25ffcbc1b647d45a39c5"}, + {file = "lxml-4.9.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:359a8b09d712df27849e0bcb62c6a3404e780b274b0b7e4c39a88826d1926c28"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:43498ea734ccdfb92e1886dfedaebeb81178a241d39a79d5351ba2b671bff2b2"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4855161013dfb2b762e02b3f4d4a21cc7c6aec13c69e3bffbf5022b3e708dd97"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c71b5b860c5215fdbaa56f715bc218e45a98477f816b46cfde4a84d25b13274e"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9a2b5915c333e4364367140443b59f09feae42184459b913f0f41b9fed55794a"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d82411dbf4d3127b6cde7da0f9373e37ad3a43e89ef374965465928f01c2b979"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:273473d34462ae6e97c0f4e517bd1bf9588aa67a1d47d93f760a1282640e24ac"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:389d2b2e543b27962990ab529ac6720c3dded588cc6d0f6557eec153305a3622"}, + {file = "lxml-4.9.4-cp311-cp311-win32.whl", hash = "sha256:8aecb5a7f6f7f8fe9cac0bcadd39efaca8bbf8d1bf242e9f175cbe4c925116c3"}, + {file = "lxml-4.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:c7721a3ef41591341388bb2265395ce522aba52f969d33dacd822da8f018aff8"}, + {file = "lxml-4.9.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:dbcb2dc07308453db428a95a4d03259bd8caea97d7f0776842299f2d00c72fc8"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:01bf1df1db327e748dcb152d17389cf6d0a8c5d533ef9bab781e9d5037619229"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, + {file = "lxml-4.9.4-cp312-cp312-win32.whl", hash = "sha256:266f655d1baff9c47b52f529b5f6bec33f66042f65f7c56adde3fcf2ed62ae8b"}, + {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, + {file = "lxml-4.9.4-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:5557461f83bb7cc718bc9ee1f7156d50e31747e5b38d79cf40f79ab1447afd2d"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:fdb325b7fba1e2c40b9b1db407f85642e32404131c08480dd652110fc908561b"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d74d4a3c4b8f7a1f676cedf8e84bcc57705a6d7925e6daef7a1e54ae543a197"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ac7674d1638df129d9cb4503d20ffc3922bd463c865ef3cb412f2c926108e9a4"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:ddd92e18b783aeb86ad2132d84a4b795fc5ec612e3545c1b687e7747e66e2b53"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bd9ac6e44f2db368ef8986f3989a4cad3de4cd55dbdda536e253000c801bcc7"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bc354b1393dce46026ab13075f77b30e40b61b1a53e852e99d3cc5dd1af4bc85"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f836f39678cb47c9541f04d8ed4545719dc31ad850bf1832d6b4171e30d65d23"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9c131447768ed7bc05a02553d939e7f0e807e533441901dd504e217b76307745"}, + {file = "lxml-4.9.4-cp36-cp36m-win32.whl", hash = "sha256:bafa65e3acae612a7799ada439bd202403414ebe23f52e5b17f6ffc2eb98c2be"}, + {file = "lxml-4.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6197c3f3c0b960ad033b9b7d611db11285bb461fc6b802c1dd50d04ad715c225"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:7b378847a09d6bd46047f5f3599cdc64fcb4cc5a5a2dd0a2af610361fbe77b16"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:1343df4e2e6e51182aad12162b23b0a4b3fd77f17527a78c53f0f23573663545"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6dbdacf5752fbd78ccdb434698230c4f0f95df7dd956d5f205b5ed6911a1367c"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:506becdf2ecaebaf7f7995f776394fcc8bd8a78022772de66677c84fb02dd33d"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca8e44b5ba3edb682ea4e6185b49661fc22b230cf811b9c13963c9f982d1d964"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9d9d5726474cbbef279fd709008f91a49c4f758bec9c062dfbba88eab00e3ff9"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bbdd69e20fe2943b51e2841fc1e6a3c1de460d630f65bde12452d8c97209464d"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8671622256a0859f5089cbe0ce4693c2af407bc053dcc99aadff7f5310b4aa02"}, + {file = "lxml-4.9.4-cp37-cp37m-win32.whl", hash = "sha256:dd4fda67f5faaef4f9ee5383435048ee3e11ad996901225ad7615bc92245bc8e"}, + {file = "lxml-4.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6bee9c2e501d835f91460b2c904bc359f8433e96799f5c2ff20feebd9bb1e590"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:1f10f250430a4caf84115b1e0f23f3615566ca2369d1962f82bef40dd99cd81a"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b505f2bbff50d261176e67be24e8909e54b5d9d08b12d4946344066d66b3e43"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1449f9451cd53e0fd0a7ec2ff5ede4686add13ac7a7bfa6988ff6d75cff3ebe2"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4ece9cca4cd1c8ba889bfa67eae7f21d0d1a2e715b4d5045395113361e8c533d"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59bb5979f9941c61e907ee571732219fa4774d5a18f3fa5ff2df963f5dfaa6bc"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b1980dbcaad634fe78e710c8587383e6e3f61dbe146bcbfd13a9c8ab2d7b1192"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9ae6c3363261021144121427b1552b29e7b59de9d6a75bf51e03bc072efb3c37"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bcee502c649fa6351b44bb014b98c09cb00982a475a1912a9881ca28ab4f9cd9"}, + {file = "lxml-4.9.4-cp38-cp38-win32.whl", hash = "sha256:a8edae5253efa75c2fc79a90068fe540b197d1c7ab5803b800fccfe240eed33c"}, + {file = "lxml-4.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:701847a7aaefef121c5c0d855b2affa5f9bd45196ef00266724a80e439220e46"}, + {file = "lxml-4.9.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:f610d980e3fccf4394ab3806de6065682982f3d27c12d4ce3ee46a8183d64a6a"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aa9b5abd07f71b081a33115d9758ef6077924082055005808f68feccb27616bd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:365005e8b0718ea6d64b374423e870648ab47c3a905356ab6e5a5ff03962b9a9"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:16b9ec51cc2feab009e800f2c6327338d6ee4e752c76e95a35c4465e80390ccd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a905affe76f1802edcac554e3ccf68188bea16546071d7583fb1b693f9cf756b"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd814847901df6e8de13ce69b84c31fc9b3fb591224d6762d0b256d510cbf382"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91bbf398ac8bb7d65a5a52127407c05f75a18d7015a270fdd94bbcb04e65d573"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f99768232f036b4776ce419d3244a04fe83784bce871b16d2c2e984c7fcea847"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bb5bd6212eb0edfd1e8f254585290ea1dadc3687dd8fd5e2fd9a87c31915cdab"}, + {file = "lxml-4.9.4-cp39-cp39-win32.whl", hash = "sha256:88f7c383071981c74ec1998ba9b437659e4fd02a3c4a4d3efc16774eb108d0ec"}, + {file = "lxml-4.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:936e8880cc00f839aa4173f94466a8406a96ddce814651075f95837316369899"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:f6c35b2f87c004270fa2e703b872fcc984d714d430b305145c39d53074e1ffe0"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:606d445feeb0856c2b424405236a01c71af7c97e5fe42fbc778634faef2b47e4"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1bdcbebd4e13446a14de4dd1825f1e778e099f17f79718b4aeaf2403624b0f7"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0a08c89b23117049ba171bf51d2f9c5f3abf507d65d016d6e0fa2f37e18c0fc5"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:232fd30903d3123be4c435fb5159938c6225ee8607b635a4d3fca847003134ba"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:231142459d32779b209aa4b4d460b175cadd604fed856f25c1571a9d78114771"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:520486f27f1d4ce9654154b4494cf9307b495527f3a2908ad4cb48e4f7ed7ef7"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:562778586949be7e0d7435fcb24aca4810913771f845d99145a6cee64d5b67ca"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a9e7c6d89c77bb2770c9491d988f26a4b161d05c8ca58f63fb1f1b6b9a74be45"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:786d6b57026e7e04d184313c1359ac3d68002c33e4b1042ca58c362f1d09ff58"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95ae6c5a196e2f239150aa4a479967351df7f44800c93e5a975ec726fef005e2"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:9b556596c49fa1232b0fff4b0e69b9d4083a502e60e404b44341e2f8fb7187f5"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cc02c06e9e320869d7d1bd323df6dd4281e78ac2e7f8526835d3d48c69060683"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:857d6565f9aa3464764c2cb6a2e3c2e75e1970e877c188f4aeae45954a314e0c"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c42ae7e010d7d6bc51875d768110c10e8a59494855c3d4c348b068f5fb81fdcd"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f10250bb190fb0742e3e1958dd5c100524c2cc5096c67c8da51233f7448dc137"}, + {file = "lxml-4.9.4.tar.gz", hash = "sha256:b1541e50b78e15fa06a2670157a1962ef06591d4c998b998047fff5e3236880e"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.35)"] +source = ["Cython (==0.29.37)"] [[package]] name = "markdown-it-py" @@ -931,6 +948,8 @@ files = [ ] [package.dependencies] +linkify-it-py = {version = ">=1,<3", optional = true, markers = "extra == \"linkify\""} +mdit-py-plugins = {version = "*", optional = true, markers = "extra == \"plugins\""} mdurl = ">=0.1,<1.0" [package.extras] @@ -945,127 +964,127 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" -version = "2.1.3" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, - {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, - {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, - {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, - {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, - {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, - {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "matplotlib" -version = "3.7.3" +version = "3.7.4" description = "Python plotting package" optional = true python-versions = ">=3.8" files = [ - {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:085c33b27561d9c04386789d5aa5eb4a932ddef43cfcdd0e01735f9a6e85ce0c"}, - {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c568e80e1c17f68a727f30f591926751b97b98314d8e59804f54f86ae6fa6a22"}, - {file = "matplotlib-3.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7baf98c5ad59c5c4743ea884bb025cbffa52dacdfdac0da3e6021a285a90377e"}, - {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236024f582e40dac39bca592258888b38ae47a9fed7b8de652d68d3d02d47d2b"}, - {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12b4f6795efea037ce2d41e7c417ad8bd02d5719c6ad4a8450a0708f4a1cfb89"}, - {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b2136cc6c5415b78977e0e8c608647d597204b05b1d9089ccf513c7d913733"}, - {file = "matplotlib-3.7.3-cp310-cp310-win32.whl", hash = "sha256:122dcbf9be0086e2a95d9e5e0632dbf3bd5b65eaa68c369363310a6c87753059"}, - {file = "matplotlib-3.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4aab27d9e33293389e3c1d7c881d414a72bdfda0fedc3a6bf46c6fa88d9b8015"}, - {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:d5adc743de91e8e0b13df60deb1b1c285b8effea3d66223afceb14b63c9b05de"}, - {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:55de4cf7cd0071b8ebf203981b53ab64f988a0a1f897a2dff300a1124e8bcd8b"}, - {file = "matplotlib-3.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac03377fd908aaee2312d0b11735753e907adb6f4d1d102de5e2425249693f6c"}, - {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:755bafc10a46918ce9a39980009b54b02dd249594e5adf52f9c56acfddb5d0b7"}, - {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a6094c6f8e8d18db631754df4fe9a34dec3caf074f6869a7db09f18f9b1d6b2"}, - {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:272dba2f1b107790ed78ebf5385b8d14b27ad9e90419de340364b49fe549a993"}, - {file = "matplotlib-3.7.3-cp311-cp311-win32.whl", hash = "sha256:591c123bed1cb4b9996fb60b41a6d89c2ec4943244540776c5f1283fb6960a53"}, - {file = "matplotlib-3.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:3bf3a178c6504694cee8b88b353df0051583f2f6f8faa146f67115c27c856881"}, - {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:edf54cac8ee3603f3093616b40a931e8c063969756a4d78a86e82c2fea9659f7"}, - {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:91e36a85ea639a1ba9f91427041eac064b04829945fe331a92617b6cb21d27e5"}, - {file = "matplotlib-3.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:caf5eaaf7c68f8d7df269dfbcaf46f48a70ff482bfcebdcc97519671023f2a7d"}, - {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74bf57f505efea376097e948b7cdd87191a7ce8180616390aef496639edf601f"}, - {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee152a88a0da527840a426535514b6ed8ac4240eb856b1da92cf48124320e346"}, - {file = "matplotlib-3.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:67a410a9c9e07cbc83581eeea144bbe298870bf0ac0ee2f2e10a015ab7efee19"}, - {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:259999c05285cb993d7f2a419cea547863fa215379eda81f7254c9e932963729"}, - {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3f4e7fd5a6157e1d018ce2166ec8e531a481dd4a36f035b5c23edfe05a25419a"}, - {file = "matplotlib-3.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:faa3d12d8811d08d14080a8b7b9caea9a457dc495350166b56df0db4b9909ef5"}, - {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:336e88900c11441e458da01c8414fc57e04e17f9d3bb94958a76faa2652bcf6b"}, - {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:12f4c0dd8aa280d796c8772ea8265a14f11a04319baa3a16daa5556065e8baea"}, - {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1990955b11e7918d256cf3b956b10997f405b7917a3f1c7d8e69c1d15c7b1930"}, - {file = "matplotlib-3.7.3-cp38-cp38-win32.whl", hash = "sha256:e78707b751260b42b721507ad7aa60fe4026d7f51c74cca6b9cd8b123ebb633a"}, - {file = "matplotlib-3.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:e594ee43c59ea39ca5c6244667cac9d017a3527febc31f5532ad9135cf7469ec"}, - {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6eaa1cf0e94c936a26b78f6d756c5fbc12e0a58c8a68b7248a2a31456ce4e234"}, - {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0a97af9d22e8ebedc9f00b043d9bbd29a375e9e10b656982012dded44c10fd77"}, - {file = "matplotlib-3.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f9c6c16597af660433ab330b59ee2934b832ee1fabcaf5cbde7b2add840f31e"}, - {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7240259b4b9cbc62381f6378cff4d57af539162a18e832c1e48042fabc40b6b"}, - {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747c6191d2e88ae854809e69aa358dbf852ff1a5738401b85c1cc9012309897a"}, - {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec726b08a5275d827aa91bb951e68234a4423adb91cf65bc0fcdc0f2777663f7"}, - {file = "matplotlib-3.7.3-cp39-cp39-win32.whl", hash = "sha256:40e3b9b450c6534f07278310c4e34caff41c2a42377e4b9d47b0f8d3ac1083a2"}, - {file = "matplotlib-3.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfc118642903a23e309b1da32886bb39a4314147d013e820c86b5fb4cb2e36d0"}, - {file = "matplotlib-3.7.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:165c8082bf8fc0360c24aa4724a22eaadbfd8c28bf1ccf7e94d685cad48261e4"}, - {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebd8470cc2a3594746ff0513aecbfa2c55ff6f58e6cef2efb1a54eb87c88ffa2"}, - {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7153453669c9672b52095119fd21dd032d19225d48413a2871519b17db4b0fde"}, - {file = "matplotlib-3.7.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:498a08267dc69dd8f24c4b5d7423fa584d7ce0027ba71f7881df05fc09b89bb7"}, - {file = "matplotlib-3.7.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48999c4b19b5a0c058c9cd828ff6fc7748390679f6cf9a2ad653a3e802c87d3"}, - {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22d65d18b4ee8070a5fea5761d59293f1f9e2fac37ec9ce090463b0e629432fd"}, - {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c40cde976c36693cc0767e27cf5f443f91c23520060bd9496678364adfafe9c"}, - {file = "matplotlib-3.7.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:39018a2b17592448fbfdf4b8352955e6c3905359939791d4ff429296494d1a0c"}, - {file = "matplotlib-3.7.3.tar.gz", hash = "sha256:f09b3dd6bdeb588de91f853bbb2d6f0ff8ab693485b0c49035eaa510cb4f142e"}, + {file = "matplotlib-3.7.4-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:b71079239bd866bf56df023e5146de159cb0c7294e508830901f4d79e2d89385"}, + {file = "matplotlib-3.7.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:bf91a42f6274a64cb41189120b620c02e574535ff6671fa836cade7701b06fbd"}, + {file = "matplotlib-3.7.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f757e8b42841d6add0cb69b42497667f0d25a404dcd50bd923ec9904e38414c4"}, + {file = "matplotlib-3.7.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dfee00aa4bd291e08bb9461831c26ce0da85ca9781bb8794f2025c6e925281"}, + {file = "matplotlib-3.7.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3640f33632beb3993b698b1be9d1c262b742761d6101f3c27b87b2185d25c875"}, + {file = "matplotlib-3.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff539c4a17ecdf076ed808ee271ffae4a30dcb7e157b99ccae2c837262c07db6"}, + {file = "matplotlib-3.7.4-cp310-cp310-win32.whl", hash = "sha256:24b8f28af3e766195c09b780b15aa9f6710192b415ae7866b9c03dee7ec86370"}, + {file = "matplotlib-3.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fa193286712c3b6c3cfa5fe8a6bb563f8c52cc750006c782296e0807ce5e799"}, + {file = "matplotlib-3.7.4-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:b167f54cb4654b210c9624ec7b54e2b3b8de68c93a14668937e7e53df60770ec"}, + {file = "matplotlib-3.7.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7dfe6821f1944cb35603ff22e21510941bbcce7ccf96095beffaac890d39ce77"}, + {file = "matplotlib-3.7.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3c557d9165320dff3c5f2bb99bfa0b6813d3e626423ff71c40d6bc23b83c3339"}, + {file = "matplotlib-3.7.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08372696b3bb45c563472a552a705bfa0942f0a8ffe084db8a4e8f9153fbdf9d"}, + {file = "matplotlib-3.7.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81e1a7ac818000e8ac3ca696c3fdc501bc2d3adc89005e7b4e22ee5e9d51de98"}, + {file = "matplotlib-3.7.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:390920a3949906bc4b0216198d378f2a640c36c622e3584dd0c79a7c59ae9f50"}, + {file = "matplotlib-3.7.4-cp311-cp311-win32.whl", hash = "sha256:62e094d8da26294634da9e7f1856beee3978752b1b530c8e1763d2faed60cc10"}, + {file = "matplotlib-3.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:f8fc2df756105784e650605e024d36dc2d048d68e5c1b26df97ee25d1bd41f9f"}, + {file = "matplotlib-3.7.4-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:568574756127791903604e315c11aef9f255151e4cfe20ec603a70f9dda8e259"}, + {file = "matplotlib-3.7.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7d479aac338195e2199a8cfc03c4f2f55914e6a120177edae79e0340a6406457"}, + {file = "matplotlib-3.7.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:32183d4be84189a4c52b4b8861434d427d9118db2cec32986f98ed6c02dcfbb6"}, + {file = "matplotlib-3.7.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0037d066cca1f4bda626c507cddeb6f7da8283bc6a214da2db13ff2162933c52"}, + {file = "matplotlib-3.7.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44856632ebce88abd8efdc0a0dceec600418dcac06b72ae77af0019d260aa243"}, + {file = "matplotlib-3.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:632fc938c22117d4241411191cfb88ac264a4c0a9ac702244641ddf30f0d739c"}, + {file = "matplotlib-3.7.4-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:ce163be048613b9d1962273708cc97e09ca05d37312e670d166cf332b80bbaff"}, + {file = "matplotlib-3.7.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:e680f49bb8052ba3b2698e370155d2b4afb49f9af1cc611a26579d5981e2852a"}, + {file = "matplotlib-3.7.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0604880e4327114054199108b7390f987f4f40ee5ce728985836889e11a780ba"}, + {file = "matplotlib-3.7.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1e6abcde6fc52475f9d6a12b9f1792aee171ce7818ef6df5d61cb0b82816e6e8"}, + {file = "matplotlib-3.7.4-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f59a70e2ec3212033ef6633ed07682da03f5249379722512a3a2a26a7d9a738e"}, + {file = "matplotlib-3.7.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a9981b2a2dd9da06eca4ab5855d09b54b8ce7377c3e0e3957767b83219d652d"}, + {file = "matplotlib-3.7.4-cp38-cp38-win32.whl", hash = "sha256:83859ac26839660ecd164ee8311272074250b915ac300f9b2eccc84410f8953b"}, + {file = "matplotlib-3.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:7a7709796ac59fe8debde68272388be6ed449c8971362eb5b60d280eac8dadde"}, + {file = "matplotlib-3.7.4-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:b1d70bc1ea1bf110bec64f4578de3e14947909a8887df4c1fd44492eca487955"}, + {file = "matplotlib-3.7.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c83f49e795a5de6c168876eea723f5b88355202f9603c55977f5356213aa8280"}, + {file = "matplotlib-3.7.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c9133f230945fe10652eb33e43642e933896194ef6a4f8d5e79bb722bdb2000"}, + {file = "matplotlib-3.7.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:798ff59022eeb276380ce9a73ba35d13c3d1499ab9b73d194fd07f1b0a41c304"}, + {file = "matplotlib-3.7.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1707b20b25e90538c2ce8d4409e30f0ef1df4017cc65ad0439633492a973635b"}, + {file = "matplotlib-3.7.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e6227ca8492baeef873cdd8e169a318efb5c3a25ce94e69727e7f964995b0b1"}, + {file = "matplotlib-3.7.4-cp39-cp39-win32.whl", hash = "sha256:5661c8639aded7d1bbf781373a359011cb1dd09199dee49043e9e68dd16f07ba"}, + {file = "matplotlib-3.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:55eec941a4743f0bd3e5b8ee180e36b7ea8e62f867bf2613937c9f01b9ac06a2"}, + {file = "matplotlib-3.7.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ab16868714e5cc90ec8f7ff5d83d23bcd6559224d8e9cb5227c9f58748889fe8"}, + {file = "matplotlib-3.7.4-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c698b33f9a3f0b127a8e614c8fb4087563bb3caa9c9d95298722fa2400cdd3f"}, + {file = "matplotlib-3.7.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be3493bbcb4d255cb71de1f9050ac71682fce21a56089eadbcc8e21784cb12ee"}, + {file = "matplotlib-3.7.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f8c725d1dd2901b2e7ec6cd64165e00da2978cc23d4143cb9ef745bec88e6b04"}, + {file = "matplotlib-3.7.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:286332f8f45f8ffde2d2119b9fdd42153dccd5025fa9f451b4a3b5c086e26da5"}, + {file = "matplotlib-3.7.4-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:116ef0b43aa00ff69260b4cce39c571e4b8c6f893795b708303fa27d9b9d7548"}, + {file = "matplotlib-3.7.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c90590d4b46458677d80bc3218f3f1ac11fc122baa9134e0cb5b3e8fc3714052"}, + {file = "matplotlib-3.7.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:de7c07069687be64fd9d119da3122ba13a8d399eccd3f844815f0dc78a870b2c"}, + {file = "matplotlib-3.7.4.tar.gz", hash = "sha256:7cd4fef8187d1dd0d9dcfdbaa06ac326d396fb8c71c647129f0bf56835d77026"}, ] [package.dependencies] @@ -1079,7 +1098,25 @@ packaging = ">=20.0" pillow = ">=6.2.0" pyparsing = ">=2.3.1" python-dateutil = ">=2.7" -setuptools_scm = ">=7" + +[[package]] +name = "mdit-py-plugins" +version = "0.4.0" +description = "Collection of plugins for markdown-it-py" +optional = true +python-versions = ">=3.8" +files = [ + {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, + {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, +] + +[package.dependencies] +markdown-it-py = ">=1.0.0,<4.0.0" + +[package.extras] +code-style = ["pre-commit"] +rtd = ["myst-parser", "sphinx-book-theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mdurl" @@ -1094,58 +1131,61 @@ files = [ [[package]] name = "memray" -version = "1.10.0" +version = "1.11.0" description = "A memory profiler for Python applications" optional = true python-versions = ">=3.7.0" files = [ - {file = "memray-1.10.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:843a688877691746f9d1835cfa8a65139948471bdd78720435808d20bc30a1cc"}, - {file = "memray-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6937d7ef67d18ccc01c3250cdf3b4ef1445b859ee8756f09e3d11bd3ff0c7d67"}, - {file = "memray-1.10.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:23e8c402625cfb32d0e9edb5ec0945f3e5e54bc6b0c5699f6284302082b80bd4"}, - {file = "memray-1.10.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f16c5c8730b616613dc8bafe32649ca6bd7252606251eb00148582011758d0b5"}, - {file = "memray-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7aeb47174c42e99740a8e2b3b6fe0932c95d987258d48a746974ead19176c26"}, - {file = "memray-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2ce59ef485db3634de98b3a026d2450fc0a875e3a58a9ea85f7a89098841defe"}, - {file = "memray-1.10.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:53a8f66af18b1f3bcf5c9f3c95ae4134dd675903a38f9d0e6341b7bca01b63d0"}, - {file = "memray-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9627184c926252c8f719c301f1fefe970f0d033c643a6448b93fed2889d1ea94"}, - {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3a14960838d89a91747885897d34134afb65883cc3b0ed7ff30fe1af00f9fe6"}, - {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f2a47871c172a0539bd72737bb6b294fc10c510464066b825d90fcd3bb4916"}, - {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c401c57f49c4c5f1fecaee1e746f537cdc6680da05fb963dc143bd08ee109bf"}, - {file = "memray-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ce22a887a585ef5020896de89ffc793e531b65ccc81fbafcc7886010c2c562b3"}, - {file = "memray-1.10.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:b75040f28e8678d0e9c4907d55c95cf26db8ef5adc9941a228f1b280a9efd9c0"}, - {file = "memray-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:95e563d9c976e429ad597ad2720d95cebbe8bac891a3082465439143e2740772"}, - {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:663d463e89a64bae4a6b2f8c837d11a3d094834442d536a4165e1d31899a3500"}, - {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a21745fb516b7a6efcd40aa7487c59e9313fcfc782d0193fcfcf00b48426874"}, - {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6d683c4f8d25c6ad06ae18715f218983c5eb86803953615e902d632fdf6ec1"}, - {file = "memray-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b311e91203be71e1a0ce5e4f978137765bcb1045f3bf5646129c83c5b96ab3c"}, - {file = "memray-1.10.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:68bd8df023c8a32f44c11d997e5c536837e27c0955daf557d3a377edd55a1dd3"}, - {file = "memray-1.10.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:322ed0b69014a0969b777768d461a785203f81f9864386b666b5b26645d9c294"}, - {file = "memray-1.10.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e985fb7646b0475c303919d19211d2aa54e5a9e2cd2a102472299be5dbebd3"}, - {file = "memray-1.10.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4eba29179772b4a2e440a065b320b03bc2e73fe2648bdf7936aa3b9a086fab4a"}, - {file = "memray-1.10.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:b681519357d94f5f0857fbc6029e7c44d3f41436109e955a14fd312d8317bc35"}, - {file = "memray-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8196c684f1be8fe423e5cdd2356d4255a2cb482a1f3e89612b70d2a2862cf5bb"}, - {file = "memray-1.10.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:898acd60f57a10dc5aaf1fd64aa2f821f0420114f3f60c3058083788603f173a"}, - {file = "memray-1.10.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fd13ef666c7fced9768d1cfabf71dc6dfa6724935a8dff463495ac2dc5e13a4"}, - {file = "memray-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e356af93e3b031c83957e9ac1a653f5aaba5df1e357dd17142f5ed19bb3dc660"}, - {file = "memray-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92c372cb262eddd23049f945ca9527f0e4cc7c40a070aade1802d066f680885b"}, - {file = "memray-1.10.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:38393c86ce6d0a08e6ec0eb1401d49803b7c0c950c2565386751cdc81568cba8"}, - {file = "memray-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a8bb7fbd8303c4f0017ba7faef6b88f904cda2931ed667cbf3b98f024b3bc44"}, - {file = "memray-1.10.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d56f37a34125684746c13d24bd7a3fb17549b0bb355eb50969eb11e05e3ba62"}, - {file = "memray-1.10.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:85c32d6613d81b075f740e398c4d653e0803cd48e82c33dcd584c109d6782666"}, - {file = "memray-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566602b2143e06b3d592901d98c52ce4599e71aa2555146eeb5cec03506f9498"}, - {file = "memray-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:391aac6c9f744528d3186bc82d708a1acc83525778f804045d7c96f860f8ec98"}, - {file = "memray-1.10.0.tar.gz", hash = "sha256:38322e052b882790993412f1840517a51818aa55c47037f69915b2007f2c4cee"}, + {file = "memray-1.11.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:dd5a91fc0632896b524ad7b121146e991176252cd072bb06ea2596042232a04f"}, + {file = "memray-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:72df1994a39018c4687a75c1750b7be3bfcd5c0c5e79e9ed73b552d4d5077588"}, + {file = "memray-1.11.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:266736c1471ddfb59d03e6d78f93f55fd0ab2fe800b9929fc5256d9208efc4a2"}, + {file = "memray-1.11.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6bf07fef1a66b96126bc0f398e01c3860e59f01eb89b244cfdcc36e70b68edad"}, + {file = "memray-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e9a74eaa673cf4c87302bd0845586a072dba7fc172a3960af64b1ad5cedf00f"}, + {file = "memray-1.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eedea42d13b3630faa5591e298659f34e6ead06aa867050def12de6cc03e1a97"}, + {file = "memray-1.11.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:9fbb2a1a82e24f0f90a9bb4ca7af6174ce91c5f3b3ce58e0b16361e989ea7cc1"}, + {file = "memray-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f46e00d4a10a7fb73b560e57689a68ca3661bf969e228093d20fc1313c42f0b"}, + {file = "memray-1.11.0-cp311-cp311-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:016a68de76fc800554fcc7dc473e48092d749b3b4302a6babd2e592a5fe8ae5e"}, + {file = "memray-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7824202d23e3060c7a0380e1a9bb6f131f47ee29c6f30b322e844648ea3aa9da"}, + {file = "memray-1.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5b8860e3cc7df4f7f451e043aabe60a3812f99c3e308f0c4c0e7a03d72c1563"}, + {file = "memray-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fc83741aedd6daa9c49ecee0a8e0048f278b6eb1ae22bdcf9b4523be7ba7106"}, + {file = "memray-1.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:39bbf9e74c3933a84c22e047920a0f6e2d491ba943a39f4aa041f1c0422c8403"}, + {file = "memray-1.11.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0ed869e4a82722a4558da749f39d6079f2ef5e767d1399d2d090b04742e2b3f2"}, + {file = "memray-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad1f2bb1223759e6b9755b6139087f6bcbaca1718cfed70c31aba0943542b431"}, + {file = "memray-1.11.0-cp312-cp312-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9c577e81f8f7cd1418c7bfae4651d9bb3f16b72200e4b8d9b80c71aeeab64bb8"}, + {file = "memray-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1534520c3c3e6b8234fe13c6d36bd74ab855dc19cef6e9d190a2a0b48fd2d83d"}, + {file = "memray-1.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3dfb2c20fbbb128489f7b9f5bd2704bae6f77dba11c253cccf8eb8299697fe4"}, + {file = "memray-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8e02e8bbe03826c5e65c2cc28760b1d0bc59f9bee6d6769c01e800b50542f5b"}, + {file = "memray-1.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0814a234cceaa664184ede2ebada2923e89c6b358b5bb9d71409a35ecae1623b"}, + {file = "memray-1.11.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:db4ebee46c24212a357641fe9fb893c842bfc66bee25546ff2efe9350e850138"}, + {file = "memray-1.11.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a5b31192d8a8d44d12320873f231c22e6ea5aed079b880cf21556ab34b3f526"}, + {file = "memray-1.11.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24894d1f5c63cdaba137199ad989d8882485ecb4190d1ff7dc5214ac84484a06"}, + {file = "memray-1.11.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1dbb599c66ffaf1467c4f96aabbecbf30b58963366f17e07bea869c95bec7f72"}, + {file = "memray-1.11.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:50889d09343993513113b21ad86a7d56e128abdb9a526c4fd394df7a3a7bda78"}, + {file = "memray-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fc9372c1f0161b245a235b12ff3d5dc1a05216ad3fde158e62d1143b7f3b99cc"}, + {file = "memray-1.11.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad1aeec47f1abb37ca6bd4a5a8be8c556e7456fe2e4a5c79b7bc32eaac916b24"}, + {file = "memray-1.11.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0ea78c073e8c5c408d4034f2da04031d0dfa8e1eface5512b350d81766aebb25"}, + {file = "memray-1.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fb0ae51e06e90336573ed9454cc05541075756e633023550086f8f1882bd38b"}, + {file = "memray-1.11.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9076942a66a03a7a3e668314cd00f720db31116df7e8626808150e4e22a079cd"}, + {file = "memray-1.11.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a4f012204aaeb233c5414e776d04d468d7a542da259811b059a89a519032e2ec"}, + {file = "memray-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83b34f92781f22ef6a7b7f4a67258deb516a06f86c713da33211a6db4fc9ea6"}, + {file = "memray-1.11.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8b2819a6612b771ffab2d80f518cf602aeec7bacee9659c6f7af40596fbfe9f6"}, + {file = "memray-1.11.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:68f15ff78a6f44344599209bc0d1e5e5d608e81bd2c9b5f02824d08751cf07d9"}, + {file = "memray-1.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89fdfbdd8ec5d9fad75b7ee487de6b2394856235511b1950c3505e78afbc8170"}, + {file = "memray-1.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:16a6ce38566029433323f7c0dfc76732a47158eee3de4c1734f00ad36d953181"}, + {file = "memray-1.11.0.tar.gz", hash = "sha256:f72c111a4868d0f2b4e4fb9ba4da736db8c73b6fb0ac6e6f2deca8ee540eb688"}, ] [package.dependencies] jinja2 = ">=2.9" rich = ">=11.2.0" +textual = ">=0.34.0" [package.extras] benchmark = ["asv"] -dev = ["Cython", "IPython", "asv", "black", "bump2version", "check-manifest", "flake8", "furo", "greenlet", "ipython", "isort", "mypy", "pytest", "pytest-cov", "setuptools", "sphinx", "sphinx-argparse", "towncrier"] +dev = ["Cython", "IPython", "asv", "black", "bump2version", "check-manifest", "flake8", "furo", "greenlet", "ipython", "isort", "mypy", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools", "sphinx", "sphinx-argparse", "towncrier"] docs = ["IPython", "bump2version", "furo", "sphinx", "sphinx-argparse", "towncrier"] lint = ["black", "check-manifest", "flake8", "isort", "mypy"] -test = ["Cython", "greenlet", "ipython", "pytest", "pytest-cov", "setuptools"] +test = ["Cython", "greenlet", "ipython", "pytest", "pytest-cov", "pytest-textual-snapshot", "setuptools"] [[package]] name = "nodeenv" @@ -1213,7 +1253,7 @@ files = [ name = "pillow" version = "10.2.0" description = "Python Imaging Library (Fork)" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, @@ -1307,28 +1347,28 @@ files = [ [[package]] name = "platformdirs" -version = "3.11.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = true python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1355,27 +1395,27 @@ virtualenv = ">=20.10.0" [[package]] name = "psutil" -version = "5.9.6" +version = "5.9.8" description = "Cross-platform lib for process and system monitoring in Python." optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ - {file = "psutil-5.9.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d"}, - {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c"}, - {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28"}, - {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017"}, - {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c"}, - {file = "psutil-5.9.6-cp27-none-win32.whl", hash = "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9"}, - {file = "psutil-5.9.6-cp27-none-win_amd64.whl", hash = "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac"}, - {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, - {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, - {file = "psutil-5.9.6-cp36-cp36m-win32.whl", hash = "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602"}, - {file = "psutil-5.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa"}, - {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, - {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, - {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, - {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, ] [package.extras] @@ -1542,34 +1582,35 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-extra-types" -version = "2.1.0" +version = "2.2.0" description = "Extra Pydantic types." optional = true python-versions = ">=3.7" files = [ - {file = "pydantic_extra_types-2.1.0-py3-none-any.whl", hash = "sha256:1b8aa83a2986b0bc6a7179834fdb423c5e0bcef6b2b4cd9261bf753ad7dcc483"}, - {file = "pydantic_extra_types-2.1.0.tar.gz", hash = "sha256:d07b869e733d33712b07d6b8cd7b0223077c23ae5a1e23bd0699a00401259ec7"}, + {file = "pydantic_extra_types-2.2.0-py3-none-any.whl", hash = "sha256:d4c6bcab0ea4b5300075ae7a664c134d923ac35496ad8caa0feae30234f14756"}, + {file = "pydantic_extra_types-2.2.0.tar.gz", hash = "sha256:7f2fdd5251729745983cf8cdc5e431ffee2362f283fff54f68122625cef82c11"}, ] [package.dependencies] pydantic = ">=2.0.3" [package.extras] -all = ["phonenumbers (>=8,<9)", "pycountry (>=22,<23)"] +all = ["phonenumbers (>=8,<9)", "pycountry (>=22,<23)", "python-ulid (>=1,<2)"] [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyparsing" @@ -1587,13 +1628,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = true python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -1647,13 +1688,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-xdist" -version = "3.4.0" +version = "3.5.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = true python-versions = ">=3.7" files = [ - {file = "pytest-xdist-3.4.0.tar.gz", hash = "sha256:3a94a931dd9e268e0b871a877d09fe2efb6175c2c23d60d56a6001359002b832"}, - {file = "pytest_xdist-3.4.0-py3-none-any.whl", hash = "sha256:e513118bf787677a427e025606f55e95937565e06dfaac8d87f55301e57ae607"}, + {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"}, + {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"}, ] [package.dependencies] @@ -1697,13 +1738,13 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] @@ -1718,7 +1759,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1726,16 +1766,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1752,7 +1784,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1760,7 +1791,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1768,13 +1798,13 @@ files = [ [[package]] name = "referencing" -version = "0.30.2" +version = "0.33.0" description = "JSON Referencing + Python" optional = false python-versions = ">=3.8" files = [ - {file = "referencing-0.30.2-py3-none-any.whl", hash = "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf"}, - {file = "referencing-0.30.2.tar.gz", hash = "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0"}, + {file = "referencing-0.33.0-py3-none-any.whl", hash = "sha256:39240f2ecc770258f28b642dd47fd74bc8b02484de54e1882b74b35ebd779bd5"}, + {file = "referencing-0.33.0.tar.gz", hash = "sha256:c775fedf74bc0f9189c2a3be1c12fd03e8c23f4d371dce795df44e06c5b412f7"}, ] [package.dependencies] @@ -1817,25 +1847,6 @@ files = [ requests = ">=1.0.0" six = "*" -[[package]] -name = "requests-mock" -version = "1.11.0" -description = "Mock out responses from the requests package" -optional = true -python-versions = "*" -files = [ - {file = "requests-mock-1.11.0.tar.gz", hash = "sha256:ef10b572b489a5f28e09b708697208c4a3b2b89ef80a9f01584340ea357ec3c4"}, - {file = "requests_mock-1.11.0-py2.py3-none-any.whl", hash = "sha256:f7fae383f228633f6bececebdab236c478ace2284d6292c6e7e2867b9ab74d15"}, -] - -[package.dependencies] -requests = ">=2.3,<3" -six = "*" - -[package.extras] -fixture = ["fixtures"] -test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "testtools"] - [[package]] name = "responses" version = "0.22.0" @@ -1858,13 +1869,13 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rich" -version = "13.6.0" +version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = true python-versions = ">=3.7.0" files = [ - {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, ] [package.dependencies] @@ -1877,150 +1888,128 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.12.0" +version = "0.18.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.12.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:c694bee70ece3b232df4678448fdda245fd3b1bb4ba481fb6cd20e13bb784c46"}, - {file = "rpds_py-0.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30e5ce9f501fb1f970e4a59098028cf20676dee64fc496d55c33e04bbbee097d"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d72a4315514e5a0b9837a086cb433b004eea630afb0cc129de76d77654a9606f"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eebaf8c76c39604d52852366249ab807fe6f7a3ffb0dd5484b9944917244cdbe"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a239303acb0315091d54c7ff36712dba24554993b9a93941cf301391d8a997ee"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ced40cdbb6dd47a032725a038896cceae9ce267d340f59508b23537f05455431"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c8c0226c71bd0ce9892eaf6afa77ae8f43a3d9313124a03df0b389c01f832de"}, - {file = "rpds_py-0.12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8e11715178f3608874508f08e990d3771e0b8c66c73eb4e183038d600a9b274"}, - {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5210a0018c7e09c75fa788648617ebba861ae242944111d3079034e14498223f"}, - {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:171d9a159f1b2f42a42a64a985e4ba46fc7268c78299272ceba970743a67ee50"}, - {file = "rpds_py-0.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:57ec6baec231bb19bb5fd5fc7bae21231860a1605174b11585660236627e390e"}, - {file = "rpds_py-0.12.0-cp310-none-win32.whl", hash = "sha256:7188ddc1a8887194f984fa4110d5a3d5b9b5cd35f6bafdff1b649049cbc0ce29"}, - {file = "rpds_py-0.12.0-cp310-none-win_amd64.whl", hash = "sha256:1e04581c6117ad9479b6cfae313e212fe0dfa226ac727755f0d539cd54792963"}, - {file = "rpds_py-0.12.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:0a38612d07a36138507d69646c470aedbfe2b75b43a4643f7bd8e51e52779624"}, - {file = "rpds_py-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f12d69d568f5647ec503b64932874dade5a20255736c89936bf690951a5e79f5"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8a1d990dc198a6c68ec3d9a637ba1ce489b38cbfb65440a27901afbc5df575"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c567c664fc2f44130a20edac73e0a867f8e012bf7370276f15c6adc3586c37c"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e9e976e0dbed4f51c56db10831c9623d0fd67aac02853fe5476262e5a22acb7"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efddca2d02254a52078c35cadad34762adbae3ff01c6b0c7787b59d038b63e0d"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9e7f29c00577aff6b318681e730a519b235af292732a149337f6aaa4d1c5e31"}, - {file = "rpds_py-0.12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:389c0e38358fdc4e38e9995e7291269a3aead7acfcf8942010ee7bc5baee091c"}, - {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33ab498f9ac30598b6406e2be1b45fd231195b83d948ebd4bd77f337cb6a2bff"}, - {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d56b1cd606ba4cedd64bb43479d56580e147c6ef3f5d1c5e64203a1adab784a2"}, - {file = "rpds_py-0.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1fa73ed22c40a1bec98d7c93b5659cd35abcfa5a0a95ce876b91adbda170537c"}, - {file = "rpds_py-0.12.0-cp311-none-win32.whl", hash = "sha256:dbc25baa6abb205766fb8606f8263b02c3503a55957fcb4576a6bb0a59d37d10"}, - {file = "rpds_py-0.12.0-cp311-none-win_amd64.whl", hash = "sha256:c6b52b7028b547866c2413f614ee306c2d4eafdd444b1ff656bf3295bf1484aa"}, - {file = "rpds_py-0.12.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:9620650c364c01ed5b497dcae7c3d4b948daeae6e1883ae185fef1c927b6b534"}, - {file = "rpds_py-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2124f9e645a94ab7c853bc0a3644e0ca8ffbe5bb2d72db49aef8f9ec1c285733"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281c8b219d4f4b3581b918b816764098d04964915b2f272d1476654143801aa2"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27ccc93c7457ef890b0dd31564d2a05e1aca330623c942b7e818e9e7c2669ee4"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1c562a9bb72244fa767d1c1ab55ca1d92dd5f7c4d77878fee5483a22ffac808"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e57919c32ee295a2fca458bb73e4b20b05c115627f96f95a10f9f5acbd61172d"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa35ad36440aaf1ac8332b4a4a433d4acd28f1613f0d480995f5cfd3580e90b7"}, - {file = "rpds_py-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e6aea5c0eb5b0faf52c7b5c4a47c8bb64437173be97227c819ffa31801fa4e34"}, - {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:81cf9d306c04df1b45971c13167dc3bad625808aa01281d55f3cf852dde0e206"}, - {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08e6e7ff286254016b945e1ab632ee843e43d45e40683b66dd12b73791366dd1"}, - {file = "rpds_py-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d0a675a7acbbc16179188d8c6d0afb8628604fc1241faf41007255957335a0b"}, - {file = "rpds_py-0.12.0-cp312-none-win32.whl", hash = "sha256:b2287c09482949e0ca0c0eb68b2aca6cf57f8af8c6dfd29dcd3bc45f17b57978"}, - {file = "rpds_py-0.12.0-cp312-none-win_amd64.whl", hash = "sha256:8015835494b21aa7abd3b43fdea0614ee35ef6b03db7ecba9beb58eadf01c24f"}, - {file = "rpds_py-0.12.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6174d6ad6b58a6bcf67afbbf1723420a53d06c4b89f4c50763d6fa0a6ac9afd2"}, - {file = "rpds_py-0.12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a689e1ded7137552bea36305a7a16ad2b40be511740b80748d3140614993db98"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45321224144c25a62052035ce96cbcf264667bcb0d81823b1bbc22c4addd194"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aa32205358a76bf578854bf31698a86dc8b2cb591fd1d79a833283f4a403f04b"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91bd2b7cf0f4d252eec8b7046fa6a43cee17e8acdfc00eaa8b3dbf2f9a59d061"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3acadbab8b59f63b87b518e09c4c64b142e7286b9ca7a208107d6f9f4c393c5c"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:429349a510da82c85431f0f3e66212d83efe9fd2850f50f339341b6532c62fe4"}, - {file = "rpds_py-0.12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05942656cb2cb4989cd50ced52df16be94d344eae5097e8583966a1d27da73a5"}, - {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0c5441b7626c29dbd54a3f6f3713ec8e956b009f419ffdaaa3c80eaf98ddb523"}, - {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b6b0e17d39d21698185097652c611f9cf30f7c56ccec189789920e3e7f1cee56"}, - {file = "rpds_py-0.12.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3b7a64d43e2a1fa2dd46b678e00cabd9a49ebb123b339ce799204c44a593ae1c"}, - {file = "rpds_py-0.12.0-cp38-none-win32.whl", hash = "sha256:e5bbe011a2cea9060fef1bb3d668a2fd8432b8888e6d92e74c9c794d3c101595"}, - {file = "rpds_py-0.12.0-cp38-none-win_amd64.whl", hash = "sha256:bec29b801b4adbf388314c0d050e851d53762ab424af22657021ce4b6eb41543"}, - {file = "rpds_py-0.12.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:1096ca0bf2d3426cbe79d4ccc91dc5aaa73629b08ea2d8467375fad8447ce11a"}, - {file = "rpds_py-0.12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48aa98987d54a46e13e6954880056c204700c65616af4395d1f0639eba11764b"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7979d90ee2190d000129598c2b0c82f13053dba432b94e45e68253b09bb1f0f6"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:88857060b690a57d2ea8569bca58758143c8faa4639fb17d745ce60ff84c867e"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4eb74d44776b0fb0782560ea84d986dffec8ddd94947f383eba2284b0f32e35e"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f62581d7e884dd01ee1707b7c21148f61f2febb7de092ae2f108743fcbef5985"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f5dcb658d597410bb7c967c1d24eaf9377b0d621358cbe9d2ff804e5dd12e81"}, - {file = "rpds_py-0.12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bf9acce44e967a5103fcd820fc7580c7b0ab8583eec4e2051aec560f7b31a63"}, - {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:240687b5be0f91fbde4936a329c9b7589d9259742766f74de575e1b2046575e4"}, - {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:25740fb56e8bd37692ed380e15ec734be44d7c71974d8993f452b4527814601e"}, - {file = "rpds_py-0.12.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a54917b7e9cd3a67e429a630e237a90b096e0ba18897bfb99ee8bd1068a5fea0"}, - {file = "rpds_py-0.12.0-cp39-none-win32.whl", hash = "sha256:b92aafcfab3d41580d54aca35a8057341f1cfc7c9af9e8bdfc652f83a20ced31"}, - {file = "rpds_py-0.12.0-cp39-none-win_amd64.whl", hash = "sha256:cd316dbcc74c76266ba94eb021b0cc090b97cca122f50bd7a845f587ff4bf03f"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0853da3d5e9bc6a07b2486054a410b7b03f34046c123c6561b535bb48cc509e1"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cb41ad20064e18a900dd427d7cf41cfaec83bcd1184001f3d91a1f76b3fcea4e"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b710bf7e7ae61957d5c4026b486be593ed3ec3dca3e5be15e0f6d8cf5d0a4990"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a952ae3eb460c6712388ac2ec706d24b0e651b9396d90c9a9e0a69eb27737fdc"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0bedd91ae1dd142a4dc15970ed2c729ff6c73f33a40fa84ed0cdbf55de87c777"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:761531076df51309075133a6bc1db02d98ec7f66e22b064b1d513bc909f29743"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2baa6be130e8a00b6cbb9f18a33611ec150b4537f8563bddadb54c1b74b8193"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f05450fa1cd7c525c0b9d1a7916e595d3041ac0afbed2ff6926e5afb6a781b7f"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:81c4d1a3a564775c44732b94135d06e33417e829ff25226c164664f4a1046213"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e888be685fa42d8b8a3d3911d5604d14db87538aa7d0b29b1a7ea80d354c732d"}, - {file = "rpds_py-0.12.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6f8d7fe73d1816eeb5378409adc658f9525ecbfaf9e1ede1e2d67a338b0c7348"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:0831d3ecdea22e4559cc1793f22e77067c9d8c451d55ae6a75bf1d116a8e7f42"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:513ccbf7420c30e283c25c82d5a8f439d625a838d3ba69e79a110c260c46813f"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:301bd744a1adaa2f6a5e06c98f1ac2b6f8dc31a5c23b838f862d65e32fca0d4b"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8832a4f83d4782a8f5a7b831c47e8ffe164e43c2c148c8160ed9a6d630bc02a"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2416ed743ec5debcf61e1242e012652a4348de14ecc7df3512da072b074440"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35585a8cb5917161f42c2104567bb83a1d96194095fc54a543113ed5df9fa436"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d389ff1e95b6e46ebedccf7fd1fadd10559add595ac6a7c2ea730268325f832c"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b007c2444705a2dc4a525964fd4dd28c3320b19b3410da6517cab28716f27d3"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:188912b22b6c8225f4c4ffa020a2baa6ad8fabb3c141a12dbe6edbb34e7f1425"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b4cf9ab9a0ae0cb122685209806d3f1dcb63b9fccdf1424fb42a129dc8c2faa"}, - {file = "rpds_py-0.12.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2d34a5450a402b00d20aeb7632489ffa2556ca7b26f4a63c35f6fccae1977427"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:466030a42724780794dea71eb32db83cc51214d66ab3fb3156edd88b9c8f0d78"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:68172622a5a57deb079a2c78511c40f91193548e8ab342c31e8cb0764d362459"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54cdfcda59251b9c2f87a05d038c2ae02121219a04d4a1e6fc345794295bdc07"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b75b912a0baa033350367a8a07a8b2d44fd5b90c890bfbd063a8a5f945f644b"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47aeceb4363851d17f63069318ba5721ae695d9da55d599b4d6fb31508595278"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0525847f83f506aa1e28eb2057b696fe38217e12931c8b1b02198cfe6975e142"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efbe0b5e0fd078ed7b005faa0170da4f72666360f66f0bb2d7f73526ecfd99f9"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0fadfdda275c838cba5102c7f90a20f2abd7727bf8f4a2b654a5b617529c5c18"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:56dd500411d03c5e9927a1eb55621e906837a83b02350a9dc401247d0353717c"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:6915fc9fa6b3ec3569566832e1bb03bd801c12cea030200e68663b9a87974e76"}, - {file = "rpds_py-0.12.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5f1519b080d8ce0a814f17ad9fb49fb3a1d4d7ce5891f5c85fc38631ca3a8dc4"}, - {file = "rpds_py-0.12.0.tar.gz", hash = "sha256:7036316cc26b93e401cedd781a579be606dad174829e6ad9e9c5a0da6e036f80"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, ] [[package]] name = "setuptools" -version = "68.2.2" +version = "69.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "setuptools-scm" -version = "8.0.4" -description = "the blessed package to manage your versions by scm tags" -optional = true -python-versions = ">=3.8" -files = [ - {file = "setuptools-scm-8.0.4.tar.gz", hash = "sha256:b5f43ff6800669595193fd09891564ee9d1d7dcb196cab4b2506d53a2e1c95c7"}, - {file = "setuptools_scm-8.0.4-py3-none-any.whl", hash = "sha256:b47844cd2a84b83b3187a5782c71128c28b4c94cad8bfb871da2784a5cb54c4f"}, -] - -[package.dependencies] -packaging = ">=20" -setuptools = "*" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} -typing-extensions = "*" - -[package.extras] -docs = ["entangled-cli[rich]", "mkdocs", "mkdocs-entangled-plugin", "mkdocs-material", "mkdocstrings[python]", "pygments"] -rich = ["rich"] -test = ["build", "pytest", "rich", "wheel"] - [[package]] name = "six" version = "1.16.0" @@ -2098,16 +2087,17 @@ rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] [[package]] name = "sphinx-data-viewer" -version = "0.1.2" -description = "Sphinx extension to show dta in an interacitve list view" +version = "0.1.4" +description = "Sphinx extension to show data in an interactive list view" optional = false python-versions = ">=3.6,<4.0" files = [ - {file = "sphinx-data-viewer-0.1.2.tar.gz", hash = "sha256:f99a4bf2c09b59e2e302ff51198c62acd5df83317f0226c170e47980b4445094"}, + {file = "sphinx_data_viewer-0.1.4-py3-none-any.whl", hash = "sha256:f83bccfc90a1e15303cb0e8159e36ecb17b5c7f71fcd5479ac4259f147417450"}, + {file = "sphinx_data_viewer-0.1.4.tar.gz", hash = "sha256:9b82cf55a4c0358a4176e98c8a91e7a9b26a326930e18b10e53f0d393ebe5416"}, ] [package.extras] -docs = ["sphinx (>=4,<5)"] +docs = ["sphinx (>=4)"] [[package]] name = "sphinx-design" @@ -2307,6 +2297,25 @@ files = [ colored = ">=1.3.92,<2.0.0" pytest = ">=5.1.0,<8.0.0" +[[package]] +name = "textual" +version = "0.51.0" +description = "Modern Text User Interface framework" +optional = true +python-versions = ">=3.8,<4.0" +files = [ + {file = "textual-0.51.0-py3-none-any.whl", hash = "sha256:c25c8d5f462ca169fa50add10f4d3604d98409b6a9f8dadff6a269cc7027516c"}, + {file = "textual-0.51.0.tar.gz", hash = "sha256:ca3d58c00a360ef1988a9be2dbb34d8a8526f2b9fe40c2ed7ac6687875422efd"}, +] + +[package.dependencies] +markdown-it-py = {version = ">=2.1.0", extras = ["linkify", "plugins"]} +rich = ">=13.3.3" +typing-extensions = ">=4.4.0,<5.0.0" + +[package.extras] +syntax = ["tree-sitter (>=0.20.1,<0.21.0)", "tree_sitter_languages (>=1.7.0)"] + [[package]] name = "toml" version = "0.10.2" @@ -2342,46 +2351,61 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = true python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "uc-micro-py" +version = "1.0.3" +description = "Micro subset of unicode data files for linkify-it-py projects." +optional = true +python-versions = ">=3.7" +files = [ + {file = "uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a"}, + {file = "uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5"}, ] +[package.extras] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.24.6" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, - {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] @@ -2406,10 +2430,10 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p benchmark = ["memray", "pytest-benchmark"] docs = ["matplotlib", "pydantic", "sphinx-copybutton", "sphinx-design", "sphinx-immaterial", "sphinxcontrib-plantuml", "sphinxcontrib-programoutput"] plotting = ["matplotlib"] -test = ["lxml", "matplotlib", "pytest", "pytest-cov", "pytest-xprocess", "requests-mock", "responses", "sphinxcontrib-plantuml", "syrupy"] +test = ["lxml", "matplotlib", "pytest", "pytest-cov", "pytest-xprocess", "responses", "sphinxcontrib-plantuml", "syrupy"] test-parallel = ["pytest-xdist"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4" -content-hash = "62a51b434da5c5837ddf0aaba96b8c40df03bd1d8e5d771f0848192024aebd51" +content-hash = "4dbc7ef92df5d050d2c0deada02216ddb9036bd5d1957d3445bfdfa426c5df74" diff --git a/pyproject.toml b/pyproject.toml index 23fe436da..26a3957bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ matplotlib = { version = ">=3.3.0", optional = true } pytest = { version = "^7", optional = true } pytest-cov = { version = "^4", optional = true } lxml = { version = "^4.6.5", optional = true } -requests-mock = { version = ">=1.9.3", optional = true } responses = { version = "^0.22.0", optional = true } sphinxcontrib-plantuml = { version = "^0", optional = true } syrupy = { version = "^3", optional = true } @@ -76,7 +75,6 @@ test = [ "pytest-cov", "syrupy", "sphinxcontrib-plantuml", - "requests-mock", "lxml", "responses", "pytest-xprocess" diff --git a/sphinx_needs/directives/needservice.py b/sphinx_needs/directives/needservice.py index 73f920b7d..47f6360ba 100644 --- a/sphinx_needs/directives/needservice.py +++ b/sphinx_needs/directives/needservice.py @@ -11,9 +11,9 @@ from sphinx_needs.api import add_need from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.data import SphinxNeedsData from sphinx_needs.directives.need import NeedDirective from sphinx_needs.logging import get_logger -from sphinx_needs.services.base import BaseService from sphinx_needs.utils import add_doc @@ -67,7 +67,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 = SphinxNeedsData(self.env).get_or_create_services() service_name = self.arguments[0] service = needs_services.get(service_name) @@ -75,7 +75,7 @@ def run(self) -> Sequence[nodes.Node]: section = [] if "debug" not in self.options: - service_data = service.request(self.options) + service_data = service.request_from_directive(self) for datum in service_data: options = {} diff --git a/sphinx_needs/services/base.py b/sphinx_needs/services/base.py index 7189c115d..269c6f78d 100644 --- a/sphinx_needs/services/base.py +++ b/sphinx_needs/services/base.py @@ -2,6 +2,8 @@ from typing import Any, ClassVar +from sphinx.util.docutils import SphinxDirective + from sphinx_needs.logging import get_logger @@ -11,8 +13,17 @@ class BaseService: def __init__(self, *args: Any, **kwargs: Any) -> None: self.log = get_logger(__name__) - def request(self, *args: Any, **kwargs: Any) -> Any: + # note the public API originally just had the `request` method that parsed options as the first argument, + # and so to not break existing subclasses, we keep that method signature, + # then also introduce `request_from_directive`, which is what is now called by the directive + + def request(self, *args: Any, **kwargs: Any) -> list[dict[str, Any]]: raise NotImplementedError("Must be implemented by the service!") + def request_from_directive( + self, directive: SphinxDirective, / + ) -> list[dict[str, Any]]: + return self.request(directive.options) + def debug(self, *args: Any, **kwargs: Any) -> Any: raise NotImplementedError("Must be implemented by the service!") diff --git a/sphinx_needs/services/github.py b/sphinx_needs/services/github.py index 4e5c54c84..6a14dda12 100644 --- a/sphinx_needs/services/github.py +++ b/sphinx_needs/services/github.py @@ -9,6 +9,8 @@ import requests from sphinx.application import Sphinx +from sphinx.util.docutils import SphinxDirective +from sphinx.util.logging import getLogger from sphinx_needs.api import add_need_type from sphinx_needs.api.exceptions import NeedsApiConfigException @@ -23,6 +25,8 @@ GITHUB_LAYOUT, ) +LOGGER = getLogger(__name__) + class GithubService(BaseService): options = ( @@ -39,6 +43,7 @@ def __init__( self.url = self.config.get("url", "https://api.github.com/") if not self.url.endswith("/"): self.url = f"{self.url}/" + self.retry_delay = self.config.get("retry_delay", 61) self.max_amount = self.config.get("max_amount", -1) self.max_content_lines = self.config.get("max_content_lines", -1) self.id_prefix = self.config.get("id_prefix", "GITHUB_") @@ -109,7 +114,7 @@ def _send( single_type = "commits" url = self.url + f"repos/{owner}/{repo}/{single_type}/{number}" except IndexError: - raise NeedGithubServiceException( + raise _SendException( 'Single option ot valid, must follow "owner/repo/number"' ) @@ -140,25 +145,25 @@ def _send( resp_limit = requests.get(self.url + "rate_limit", auth=auth) extra_info = resp_limit.json() self.log.info( - "GitHub: API rate limit exceeded. We need to wait 60 secs..." + f"GitHub: API rate limit exceeded. trying again in {self.retry_delay} seconds..." ) self.log.info(extra_info) - time.sleep(61) + time.sleep(self.retry_delay) resp = requests.get(url, params=params, auth=auth, headers=headers) if resp.status_code > 299: if "rate limit" in resp.json()["message"]: - raise NeedGithubServiceException( + raise _SendException( "GitHub: API rate limit exceeded (twice). Stop here." ) else: - raise NeedGithubServiceException( + raise _SendException( "Github service error during request.\n" f"Status code: {resp.status_code}\n" f"Error: {resp.text}\n" f"{extra_info}" ) else: - raise NeedGithubServiceException( + raise _SendException( "Github service error during request.\n" f"Status code: {resp.status_code}\n" f"Error: {resp.text}\n" @@ -169,48 +174,63 @@ def _send( return {"items": [resp.json()]} return resp.json() # type: ignore - def request(self, options: dict[str, Any] | None = None) -> list[dict[str, Any]]: - if options is None: - options = {} + def request_from_directive( + self, directive: SphinxDirective, / + ) -> list[dict[str, Any]]: self.log.debug(f"Requesting data for service {self.name}") + options = directive.options if "query" not in options and "specific" not in options: - raise NeedGithubServiceException( - '"query" or "specific" missing as option for github service.' + create_warning( + directive, + '"query" or "specific" missing as option for github service.', ) - elif "query" in options and "specific" in options: - raise NeedGithubServiceException( - 'Only "query" or "specific" allowed for github service. Not both!' + return [] + + if "query" in options and "specific" in options: + create_warning( + directive, + 'Only "query" or "specific" allowed for github service. Not both!', ) - elif "query" in options: + return [] + + if "query" in options: query = options["query"] specific = False else: query = options["specific"] specific = True - response = self._send(query, options, specific=specific) + try: + response = self._send(query, options, specific=specific) + except _SendException as e: + create_warning(directive, str(e)) + return [] if "items" not in response: if "errors" in response: - raise NeedGithubServiceException( + create_warning( + directive, "GitHub service query error: {}\n" "Used query: {}".format( response["errors"][0]["message"], query - ) + ), ) + return [] else: - raise NeedGithubServiceException("Github service: Unknown error.") + create_warning(directive, "Github service: Unknown error") + return [] if self.gh_type == "issue" or self.gh_type == "pr": - data = self.prepare_issue_data(response["items"], options) + data = self.prepare_issue_data(response["items"], directive) elif self.gh_type == "commit": - data = self.prepare_commit_data(response["items"], options) + data = self.prepare_commit_data(response["items"], directive) else: - raise NeedGithubServiceException("Github service failed. Wrong gh_type...") + create_warning(directive, "Github service failed. Wrong gh_type...") + return [] return data def prepare_issue_data( - self, items: list[dict[str, Any]], options: dict[str, Any] + self, items: list[dict[str, Any]], directive: SphinxDirective ) -> list[dict[str, Any]]: data = [] for item in items: @@ -231,9 +251,11 @@ def prepare_issue_data( content = "\n\n ".join(content_lines) # Reduce content length, if requested by config - if self.max_content_lines > 0: + if (self.max_content_lines > 0) or ( + "max_content_lines" in directive.options + ): max_lines = int( - options.get("max_content_lines", self.max_content_lines) + directive.options.get("max_content_lines", self.max_content_lines) ) content_lines = content.splitlines() if len(content_lines) > max_lines: @@ -245,21 +267,21 @@ def prepare_issue_data( # everything in a safe code-block content = ".. code-block:: text\n\n " + content - prefix = options.get("id_prefix", self.id_prefix) + prefix = directive.options.get("id_prefix", self.id_prefix) need_id = prefix + str(item["number"]) - given_tags = options.get("tags", False) + given_tags = directive.options.get("tags", False) github_tags = ",".join([x["name"] for x in item["labels"]]) if given_tags: tags = str(given_tags) + ", " + str(github_tags) else: tags = github_tags - avatar_file_path = self._get_avatar(item["user"]["avatar_url"]) + avatar_file_path = self._get_avatar(item["user"]["avatar_url"], directive) element_data = { "service": self.name, - "type": options.get("type", self.need_type), - "layout": options.get("layout", self.layout), + "type": directive.options.get("type", self.need_type), + "layout": directive.options.get("layout", self.layout), "id": need_id, "title": item["title"], "content": content, @@ -272,23 +294,23 @@ def prepare_issue_data( "updated_at": item["updated_at"], "closed_at": item["closed_at"], } - self._add_given_options(options, element_data) + self._add_given_options(directive.options, element_data) data.append(element_data) return data def prepare_commit_data( - self, items: list[dict[str, Any]], options: dict[str, Any] + self, items: list[dict[str, Any]], directive: SphinxDirective ) -> list[dict[str, Any]]: data = [] for item in items: - avatar_file_path = self._get_avatar(item["author"]["avatar_url"]) + avatar_file_path = self._get_avatar(item["author"]["avatar_url"], directive) element_data = { "service": self.name, - "type": options.get("type", self.need_type), - "layout": options.get("layout", self.layout), + "type": directive.options.get("type", self.need_type), + "layout": directive.options.get("layout", self.layout), "id": self.id_prefix + item["sha"][:6], "title": item["commit"]["message"].split("\n")[0][ :60 @@ -299,18 +321,13 @@ def prepare_commit_data( "avatar": avatar_file_path, "created_at": item["commit"]["author"]["date"], } - self._add_given_options(options, element_data) + self._add_given_options(directive.options, element_data) data.append(element_data) return data - def _get_avatar(self, avatar_url: str) -> str: - """ - Download and store avatar image - - :param avatar_url: - :return: - """ + def _get_avatar(self, avatar_url: str, directive: SphinxDirective) -> str: + """Download and store avatar image""" url_parsed = urlparse(avatar_url) filename = os.path.basename(url_parsed.path) + ".png" path = os.path.join(self.app.srcdir, self.download_folder) @@ -334,22 +351,22 @@ def _get_avatar(self, avatar_url: str) -> str: with open(avatar_file_path, "wb") as f: f.write(response.content) elif response.status_code == 302: - self.log.warning( + create_warning( + directive, f"GitHub service {self.name} could not download avatar image " f"from {avatar_url}.\n" f" Status code: {response.status_code}\n" " Reason: Looks like the authentication provider tries to redirect you." " This is not supported and is a common problem, " - "if you use GitHub Enterprise. [needs]", - type="needs", + "if you use GitHub Enterprise.", ) avatar_file_path = default_avatar_file_path else: - self.log.warning( + create_warning( + directive, f"GitHub service {self.name} could not download avatar image " f"from {avatar_url}.\n" - f" Status code: {response.status_code} [needs]", - type="needs", + f" Status code: {response.status_code}", ) avatar_file_path = default_avatar_file_path else: @@ -373,5 +390,14 @@ def _add_given_options( element_data[key] = value -class NeedGithubServiceException(BaseException): +class _SendException(Exception): pass + + +def create_warning(directive: SphinxDirective, message: str) -> None: + LOGGER.warning( + message + " [needs.github]", + type="needs", + subtype="github", + location=directive.get_location(), + ) diff --git a/sphinx_needs/services/open_needs.py b/sphinx_needs/services/open_needs.py index d11e99b30..7c312413e 100644 --- a/sphinx_needs/services/open_needs.py +++ b/sphinx_needs/services/open_needs.py @@ -7,6 +7,7 @@ import requests from jinja2 import Template from sphinx.application import Sphinx +from sphinx.util.docutils import SphinxDirective from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.utils import dict_get, jinja_parse @@ -213,10 +214,12 @@ def _extract_data( need_data.append(finale_data) return need_data - def request(self, options: Any = None, *args: Any, **kwargs: Any) -> Any: + def request_from_directive( + self, directive: SphinxDirective, / + ) -> list[dict[str, Any]]: self.log.info(f"Requesting data for service {self.name}") self._oauthorization() # Get authorization token - params = self._prepare_request(options) + params = self._prepare_request(directive.options) request_params = { "url": params["url"], @@ -230,7 +233,7 @@ def request(self, options: Any = None, *args: Any, **kwargs: Any) -> Any: if "description" not in datum or datum["description"] is None: datum["description"] = "" - need_data = self._extract_data(data, options) + need_data = self._extract_data(data, directive.options) return need_data diff --git a/tests/__snapshots__/test_service_github.ambr b/tests/__snapshots__/test_service_github.ambr new file mode 100644 index 000000000..afb6ca7a0 --- /dev/null +++ b/tests/__snapshots__/test_service_github.ambr @@ -0,0 +1,342 @@ +# name: test_build[test_app0] + dict({ + 'current_version': '1', + 'project': 'Python', + 'versions': dict({ + '1': dict({ + 'filters': dict({ + }), + 'filters_amount': 0, + 'needs': dict({ + 'GITHUB_050bec': dict({ + 'arch': dict({ + }), + 'closed_at': '', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'GITHUB_050bec', + 'created_at': '2024-02-15T14:04:06Z', + 'delete': False, + 'description': ''' + Bump actions/cache from 3 to 4 (#1092) + + Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> + ''', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'Bump actions/cache from 3 to 4 (#1092)', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'GITHUB_050bec', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': False, + 'layout': 'github', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'Title', + 'sections': list([ + 'Title', + ]), + 'service': 'github-commits', + 'signature': '', + 'specific': 'useblocks/sphinx-needs/050bec750ff2c5acf881415fa2b5efb5fcce8414', + 'status': None, + 'style': None, + 'tags': list([ + ]), + 'target_id': 'GITHUB_050bec', + 'template': None, + 'title': 'Bump actions/cache from 3 to 4 (#1092)', + 'type': 'commit', + 'type_name': 'Commit', + 'updated_at': '', + 'url': 'https://github.com/useblocks/sphinx-needs/commit/050bec750ff2c5acf881415fa2b5efb5fcce8414', + 'url_postfix': '', + 'user': 'dependabot[bot]', + }), + 'GITHUB_1110': dict({ + 'arch': dict({ + }), + 'closed_at': 'None', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'GITHUB_1110', + 'created_at': '2024-02-15T12:19:11Z', + 'delete': False, + 'description': ''' + .. code-block:: text + + I've also just realised there is a bug in this directive: + + + [...] + + ''', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'needreport usage should count needs in post-process', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'GITHUB_1110', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': False, + 'layout': 'github', + 'links': list([ + ]), + 'max_amount': '1', + 'max_content_lines': '2', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': 'repo:useblocks/sphinx-needs', + 'section_name': 'Title', + 'sections': list([ + 'Title', + ]), + 'service': 'github-issues', + 'signature': '', + 'specific': '', + 'status': 'open', + 'style': None, + 'tags': list([ + 'bug', + ]), + 'target_id': 'GITHUB_1110', + 'template': None, + 'title': 'needreport usage should count needs in post-process', + 'type': 'issue', + 'type_name': 'Issue', + 'updated_at': '2024-02-15T12:19:30Z', + 'url': 'https://github.com/useblocks/sphinx-needs/issues/1110', + 'url_postfix': '', + 'user': 'chrisjsewell', + }), + 'GITHUB_1112': dict({ + 'arch': dict({ + }), + 'closed_at': 'None', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'GITHUB_1112', + 'created_at': '2024-02-15T20:45:12Z', + 'delete': False, + 'description': ''' + .. code-block:: text + + @David-Le-Nir and @danwos, as I explained in + https://github.com/useblocks/sphinx- + needs/issues/1103#issuecomment-1936305902, I think this is a + better solution for handling need defined within `only` + directives. + + In this first "read" phase, we simply just note all the + parent `only` expressions of the need, storing them on an + (optional) `only_expressions` field of the need data item. + + If desired, in a subsequent "build" post-processing phase, + called from the cached data (once per build), you could then + evaluate the `only_expressions` and remove needs as a + necessary (or do whatever). + + This logic would go here: + https://github.com/useblocks/sphinx-needs/blob/84a5f72f2e72a + b1471ab2d1bb5c570d6115ef199/sphinx_needs/directives/need.py# + L385 + + this is more in-line with the logic of the `only` directive, + where everything is cached and parts of the doctree are only + removed during the build phase. + + would supercede #1106 + + closes #1103 + ''', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': '👌 Capture `only` expressions for each need', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'GITHUB_1112', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': False, + 'layout': 'github', + 'links': list([ + ]), + 'max_amount': '1', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': 'repo:useblocks/sphinx-needs', + 'section_name': 'Title', + 'sections': list([ + 'Title', + ]), + 'service': 'github-prs', + 'signature': '', + 'specific': '', + 'status': 'open', + 'style': None, + 'tags': list([ + ]), + 'target_id': 'GITHUB_1112', + 'template': None, + 'title': '👌 Capture `only` expressions for each need', + 'type': 'pr', + 'type_name': 'PullRequest', + 'updated_at': '2024-02-15T20:55:43Z', + 'url': 'https://github.com/useblocks/sphinx-needs/pull/1112', + 'url_postfix': '', + 'user': 'chrisjsewell', + }), + 'GITHUB_6abd38': dict({ + 'arch': dict({ + }), + 'closed_at': '', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'GITHUB_6abd38', + 'created_at': '2024-02-12T08:36:07.000Z', + 'delete': False, + 'description': ''' + 🧪 Add test for `needreport` directive (#1105) + + Currently there is no test for this directive, this PR adds one. + + This PR also fixes the directive: + + - Make the options flags + - Change errors in the directive to emit warnings, rather than excepting + the whole build + - Allow for `template` to be specified as a directive option + - Allow the the `dropdown` directive used in the default template, which + requires an external sphinx extension, to be overriden using + `needs_render_context = {"report_directive": "admonition"}` (I left the + default as `dropdown`, so as not to introduce a breaking change) + ''', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': '🧪 Add test for `needreport` directive (#1105)', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'GITHUB_6abd38', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': False, + 'layout': 'github', + 'links': list([ + ]), + 'max_amount': '1', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': 'repo:useblocks/sphinx-needs error', + 'section_name': 'Title', + 'sections': list([ + 'Title', + ]), + 'service': 'github-commits', + 'signature': '', + 'specific': '', + 'status': None, + 'style': None, + 'tags': list([ + ]), + 'target_id': 'GITHUB_6abd38', + 'template': None, + 'title': '🧪 Add test for `needreport` directive (#1105)', + 'type': 'commit', + 'type_name': 'Commit', + 'updated_at': '', + 'url': 'https://github.com/useblocks/sphinx-needs/commit/6abd389369c5bbd5216f5ecdc3da1323ebe8620d', + 'url_postfix': '', + 'user': 'chrisjsewell', + }), + }), + 'needs_amount': 4, + }), + }), + }) +# --- diff --git a/tests/benchmarks/test_basic.py b/tests/benchmarks/test_basic.py index a45133136..0be43aac1 100644 --- a/tests/benchmarks/test_basic.py +++ b/tests/benchmarks/test_basic.py @@ -1,27 +1,12 @@ -import re from pathlib import Path import pytest -import responses -from tests.test_basic_doc import random_data_callback - -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True ) def test_basic_time(test_app, benchmark): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app benchmark.pedantic(app.builder.build_all, rounds=1, iterations=1) diff --git a/tests/benchmarks/test_official.py b/tests/benchmarks/test_official.py index 663a3ea86..2d07f40b3 100644 --- a/tests/benchmarks/test_official.py +++ b/tests/benchmarks/test_official.py @@ -1,11 +1,43 @@ +import json import re +import uuid from pathlib import Path +from random import randrange import memray import pytest import responses -from tests.test_basic_doc import random_data_callback +from tests.data.service_github import ( + GITHUB_ISSUE_SEARCH_ANSWER, + GITHUB_SEARCH_COMMIT_ANSWER, + GITHUB_SPECIFIC_COMMIT_ANSWER, + GITHUB_SPECIFIC_ISSUE_ANSWER, +) + + +def random_data_callback(request): + """ + Response data callback, which injects random ids, so that the generated needs get always a unique id and no + exceptions get thrown. + """ + if re.match(r"/search/issues", request.path_url): + data = GITHUB_ISSUE_SEARCH_ANSWER + data["items"][0]["number"] = randrange(10000) + elif re.match(r"/.+/issue/.+", request.path_url) or re.match( + r"/.+/pulls/.+", request.path_url + ): + data = GITHUB_SPECIFIC_ISSUE_ANSWER + data["number"] = randrange(10000) + elif re.match(r"/search/commits", request.path_url): + data = GITHUB_SEARCH_COMMIT_ANSWER + data["number"] = randrange(10000) + elif re.match(r"/.*/commits/*", request.path_url): + data = GITHUB_SPECIFIC_COMMIT_ANSWER + data["sha"] = uuid.uuid4().hex + else: + print(request.path_url) + return 200, [], json.dumps(data) @responses.activate diff --git a/tests/doc_test/doc_service_github/conf.py b/tests/doc_test/doc_service_github/conf.py new file mode 100644 index 000000000..8b599db71 --- /dev/null +++ b/tests/doc_test/doc_service_github/conf.py @@ -0,0 +1,8 @@ +version = "1" +extensions = ["sphinx_needs"] +needs_build_json = True +needs_services = { + "github-commits": { + "retry_delay": 0, + } +} diff --git a/tests/doc_test/doc_service_github/index.rst b/tests/doc_test/doc_service_github/index.rst new file mode 100644 index 000000000..d45ace335 --- /dev/null +++ b/tests/doc_test/doc_service_github/index.rst @@ -0,0 +1,23 @@ +Title +===== + +.. needservice:: github-issues + +.. needservice:: github-issues + :query: repo:useblocks/sphinx-needs + :max_amount: 1 + :max_content_lines: 2 + +.. needservice:: github-prs + :query: repo:useblocks/sphinx-needs + :max_amount: 1 + +.. needservice:: github-commits + :query: repo:useblocks/sphinx-needs error + :max_amount: 1 + +.. needservice:: github-commits + :specific: useblocks/sphinx-needs/050bec750ff2c5acf881415fa2b5efb5fcce8414 + +.. needservice:: github-commits + :specific: useblocks/sphinx-needs/rate_limit diff --git a/tests/test_basic_doc.py b/tests/test_basic_doc.py index 806d29147..2369560d1 100644 --- a/tests/test_basic_doc.py +++ b/tests/test_basic_doc.py @@ -1,70 +1,22 @@ import json import os.path -import re import sys import tempfile -import uuid from pathlib import Path -from random import randrange import pytest -import responses from sphinx import version_info from sphinx.application import Sphinx from sphinx.testing.util import SphinxTestApp from syrupy.filters import props from sphinx_needs.api.need import NeedsNoIdException -from tests.data.service_github import ( - GITHUB_ISSUE_SEARCH_ANSWER, - GITHUB_SEARCH_COMMIT_ANSWER, - GITHUB_SPECIFIC_COMMIT_ANSWER, - GITHUB_SPECIFIC_ISSUE_ANSWER, -) - -def random_data_callback(request): - """ - Response data callback, which injects random ids, so that the generated needs get always a unique id and no - exceptions get thrown. - """ - if re.match(r"/search/issues", request.path_url): - data = GITHUB_ISSUE_SEARCH_ANSWER - data["items"][0]["number"] = randrange(10000) - elif re.match(r"/.+/issue/.+", request.path_url) or re.match( - r"/.+/pulls/.+", request.path_url - ): - data = GITHUB_SPECIFIC_ISSUE_ANSWER - data["number"] = randrange(10000) - elif re.match(r"/search/commits", request.path_url): - data = GITHUB_SEARCH_COMMIT_ANSWER - data["number"] = randrange(10000) - elif re.match(r"/.*/commits/*", request.path_url): - data = GITHUB_SPECIFIC_COMMIT_ANSWER - data["sha"] = uuid.uuid4().hex - else: - print(request.path_url) - return 200, [], json.dumps(data) - -# OFFICIAL DOCUMENTATION BUILDS - - -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_basic"}], indirect=True ) def test_build_html(test_app): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() @@ -76,23 +28,12 @@ def test_build_html(test_app): assert build_dir / "DataTables-1.10.16" / "js" / "jquery.dataTables.min.js" in files -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/generic_doc"}], indirect=True, ) def test_build_html_parallel(test_app: Sphinx, snapshot_doctree): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() @@ -137,103 +78,48 @@ def test_html_head_files(test_app): assert "\\" not in head_file -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "singlehtml", "srcdir": "doc_test/doc_basic"}], indirect=True, ) def test_build_singlehtml(test_app): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "latex", "srcdir": "doc_test/doc_basic"}], indirect=True, ) def test_build_latex(test_app): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "epub", "srcdir": "doc_test/doc_basic"}], indirect=True ) def test_build_epub(test_app): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "json", "srcdir": "doc_test/doc_basic"}], indirect=True ) def test_build_json(test_app): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "needs", "srcdir": "doc_test/doc_basic"}], indirect=True, ) def test_build_needs(test_app, snapshot): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - app = test_app app.builder.build_all() json_text = Path(app.outdir, "needs.json").read_text() @@ -243,7 +129,6 @@ def test_build_needs(test_app, snapshot): # Test with needs_id_required=True and missing ids in docs. -@responses.activate @pytest.mark.parametrize( "test_app", [ @@ -257,38 +142,15 @@ def test_build_needs(test_app, snapshot): ) def test_id_required_build_html(test_app): with pytest.raises(NeedsNoIdException): - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, - re.compile(r"https://avatars.githubusercontent.com/.*"), - body="", - ) - app = test_app app.builder.build_all() -@responses.activate def test_sphinx_api_build(): """ Tests a build via the Sphinx Build API. It looks like that there are scenarios where this specific build makes trouble but no others. """ - responses.add_callback( - responses.GET, - re.compile(r"https://api.github.com/.*"), - callback=random_data_callback, - content_type="application/json", - ) - responses.add( - responses.GET, re.compile(r"https://avatars.githubusercontent.com/.*"), body="" - ) - temp_dir = tempfile.mkdtemp() src_dir = os.path.join(os.path.dirname(__file__), "doc_test", "doc_basic") diff --git a/tests/test_import.py b/tests/test_import.py index 0fb73e9ad..e40c931d2 100644 --- a/tests/test_import.py +++ b/tests/test_import.py @@ -1,8 +1,9 @@ import json +import subprocess from pathlib import Path import pytest -import requests_mock +import responses from syrupy.filters import props @@ -193,7 +194,7 @@ def test_needimport_needs_json_download(test_app, snapshot): }, } - with requests_mock.Mocker() as m: + with responses.RequestsMock() as m: m.get("http://my_company.com/docs/v1/remote-needs.json", json=remote_json) app.build() @@ -212,48 +213,13 @@ def test_needimport_needs_json_download(test_app, snapshot): indirect=True, ) def test_needimport_needs_json_download_negative(test_app): - import subprocess - app = test_app - - remote_json = { - "created": "2022-05-11T13:54:22.331741", - "current_version": "1.0", - "project": "needs test docs", - "versions": { - "1.0": { - "created": "2021-05-11T13:54:22.331724", - "filters": {}, - "filters_amount": 0, - "needs": { - "TEST_101": { - "id": "TEST_101", - "description": "TEST_101 DESCRIPTION", - "docname": "index", - "external_css": "external_link", - "external_url": "http://my_company.com/docs/v1/index.html#TEST_01", - "title": "TEST_101 TITLE", - "type": "impl", - "tags": ["ext_test"], - }, - }, - }, - }, - } - - with requests_mock.Mocker() as m: - # test with invalid url - m.get( - "http://my_wrong_name_company.com/docs/v1/remote-needs.json", - json=remote_json, - ) - - src_dir = Path(app.srcdir) - out_dir = Path(app.outdir) - output = subprocess.run( - ["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True - ) - assert ( - "NeedimportException: Getting http://my_wrong_name_company.com/docs/v1/remote-needs.json didn't work." - in output.stderr.decode("utf-8") - ) + src_dir = Path(app.srcdir) + out_dir = Path(app.outdir) + output = subprocess.run( + ["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True + ) + assert ( + "NeedimportException: Getting http://my_wrong_name_company.com/docs/v1/remote-needs.json didn't work." + in output.stderr.decode("utf-8") + ) diff --git a/tests/test_measure_time.py b/tests/test_measure_time.py index 687a45ce4..ea9a27ff9 100644 --- a/tests/test_measure_time.py +++ b/tests/test_measure_time.py @@ -1,8 +1,6 @@ import pytest -import responses -@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_measure_time"}], diff --git a/tests/test_needpie.py b/tests/test_needpie.py index 4bcaa20c4..4facc8922 100644 --- a/tests/test_needpie.py +++ b/tests/test_needpie.py @@ -4,7 +4,8 @@ from pathlib import Path import pytest -import sphinx +from sphinx import version_info +from sphinx.testing.util import SphinxTestApp @pytest.mark.parametrize( @@ -29,22 +30,32 @@ def test_sphinx_api_needpie(): src_dir = os.path.join(os.path.dirname(__file__), "doc_test/doc_needpie") shutil.copytree(src_dir, temp_dir, dirs_exist_ok=True) + if version_info >= (7, 2): + src_dir = Path(src_dir) + build_dir = Path(build_dir) + else: + from sphinx.testing.path import path + + src_dir = path(src_dir) + build_dir = path(build_dir) + with open(os.path.join(temp_dir, "warnings.txt"), "w") as warnings: - sphinx_app = sphinx.application.Sphinx( - srcdir=temp_dir, - confdir=temp_dir, - outdir=build_dir, - doctreedir=temp_dir, + sphinx_app = SphinxTestApp( + srcdir=src_dir, + builddir=build_dir, buildername="html", parallel=4, warning=warnings, ) - sphinx_app.build() - assert sphinx_app.statuscode == 0 - - # touch file to force sphinx to purge stuff - with open(temp_dir / "index.rst", "a") as f: - f.write("\n\nNew content to force rebuild") - - sphinx_app.build() - assert sphinx_app.statuscode == 0 + try: + sphinx_app.build() + assert sphinx_app.statuscode == 0 + + # touch file to force sphinx to purge stuff + with open(temp_dir / "index.rst", "a") as f: + f.write("\n\nNew content to force rebuild") + + sphinx_app.build() + assert sphinx_app.statuscode == 0 + finally: + sphinx_app.cleanup() diff --git a/tests/test_needs_external_needs_build.py b/tests/test_needs_external_needs_build.py index 0ba7c4d34..0a3a6e76e 100644 --- a/tests/test_needs_external_needs_build.py +++ b/tests/test_needs_external_needs_build.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -import requests_mock +import responses import sphinx from docutils import __version__ as doc_ver @@ -350,7 +350,7 @@ def test_external_needs_json_url(test_app): }, } - with requests_mock.Mocker() as m: + with responses.RequestsMock() as m: m.get("http://my_company.com/docs/v1/remote-needs.json", json=remote_json) app.build() diff --git a/tests/test_open_needs_service.py b/tests/test_open_needs_service.py index e88a2e1da..0f0592e27 100644 --- a/tests/test_open_needs_service.py +++ b/tests/test_open_needs_service.py @@ -1,108 +1,94 @@ from pathlib import Path import pytest -import requests +import responses from sphinx_needs.services.manager import ServiceManager - -class MockGetResponse: - def __init__(self): - self.status_code = 200 - self.url = "http://127.0.0.1:9595/" - self.text = "Mocked Get Response" - - @staticmethod - def json(): - return [ - { - "key": "NEP_001", - "type": "Requirement", - "title": "Build rocket", - "description": "We finally need to build our Neptune3000 rocket.", - "format": "txt", - "project_id": 1, - "options": { - "status": "done", - "priority": "high", - "costs": 3500000, - "approved": 1, - }, - "references": {}, - }, - { - "key": "NEP_002", - "type": "Requirement", - "title": "Test rocket power", - "description": "Lets test the rocket on a test bench", - "format": "txt", - "project_id": 1, - "options": { - "status": "open", - "priority": "high", - "costs": 500000, - "approved": 0, - }, - "references": {}, - }, - { - "key": "NEP_003", - "type": "Requirement", - "title": "Rocket painting", - "description": "Red and blue. No other colors please.", - "format": "txt", - "project_id": 1, - "options": { - "status": "open", - "priority": "low", - "costs": 20000, - "approved": 1, - }, - "references": {"links": ["NEP_001", "NEP_002"]}, - }, - { - "key": "NEP_004", - "type": "Requirement", - "title": "Pumps from company AwesomePumps", - "description": "We simply reuse the pump system ABC-Pumps from AwesomePumps Inc.", - "format": "txt", - "project_id": 1, - "options": {"status": "open", "links": "req_1"}, - "references": {"links": ["NEP_003"]}, - }, - ] - - -class MockPostResponse: - def __init__(self): - self.status_code = 200 - self.url = "http://127.0.0.1:9595/" - self.text = "Mocked Post Response" - - @staticmethod - def json(): - return { - "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9", - "token_type": "bearer", - } - - +MOCK_NEEDS = [ + { + "key": "NEP_001", + "type": "Requirement", + "title": "Build rocket", + "description": "We finally need to build our Neptune3000 rocket.", + "format": "txt", + "project_id": 1, + "options": { + "status": "done", + "priority": "high", + "costs": 3500000, + "approved": 1, + }, + "references": {}, + }, + { + "key": "NEP_002", + "type": "Requirement", + "title": "Test rocket power", + "description": "Lets test the rocket on a test bench", + "format": "txt", + "project_id": 1, + "options": { + "status": "open", + "priority": "high", + "costs": 500000, + "approved": 0, + }, + "references": {}, + }, + { + "key": "NEP_003", + "type": "Requirement", + "title": "Rocket painting", + "description": "Red and blue. No other colors please.", + "format": "txt", + "project_id": 1, + "options": { + "status": "open", + "priority": "low", + "costs": 20000, + "approved": 1, + }, + "references": {"links": ["NEP_001", "NEP_002"]}, + }, + { + "key": "NEP_004", + "type": "Requirement", + "title": "Pumps from company AwesomePumps", + "description": "We simply reuse the pump system ABC-Pumps from AwesomePumps Inc.", + "format": "txt", + "project_id": 1, + "options": {"status": "open", "links": "req_1"}, + "references": {"links": ["NEP_003"]}, + }, +] + + +@responses.activate @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_open_needs_service"}], indirect=True, ) -def test_ons_service(test_app, monkeypatch): - def mock_get(*args, **kwargs): - return MockGetResponse() - - def mock_post(*args, **kwargs): - return MockPostResponse() - - # apply the monkeypatch for requests.get to mock_get - monkeypatch.setattr(requests, "get", mock_get) - # apply the monkeypatch for requests.post to mock_post - monkeypatch.setattr(requests, "post", mock_post) +def test_ons_service(test_app): + responses.post( + "http://127.0.0.1:9595/auth/jwt/login", + status=200, + json={ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9", + "token_type": "bearer", + }, + ) + responses.get( + "http://127.0.0.1:9595/api/needs/?skip=0&limit=2", + status=200, + json=MOCK_NEEDS[:2], + ) + responses.get( + "http://127.0.0.1:9595/api/needs/?skip=0&limit=4", + status=200, + json=MOCK_NEEDS[:], + ) test_app.build() assert isinstance(test_app.needs_services, ServiceManager) diff --git a/tests/test_service_github.py b/tests/test_service_github.py new file mode 100644 index 000000000..8c79ffcce --- /dev/null +++ b/tests/test_service_github.py @@ -0,0 +1,546 @@ +import json +from pathlib import Path + +import pytest +import responses +from sphinx.util.console import strip_colors +from syrupy.filters import props + + +@responses.activate +@pytest.mark.parametrize( + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_service_github", + "no_plantuml": True, + } + ], + indirect=True, +) +def test_build(test_app, snapshot): + responses.get( + "https://api.github.com/search/issues", + match=[ + responses.matchers.query_param_matcher( + {"q": "repo:useblocks/sphinx-needs is:issue", "per_page": "1"} + ) + ], + status=200, + json=ISSUE_RESPONSE, + ) + responses.get( + "https://api.github.com/search/issues", + match=[ + responses.matchers.query_param_matcher( + {"q": "repo:useblocks/sphinx-needs is:pr", "per_page": "1"} + ) + ], + status=200, + json=PR_RESPONSE, + ) + responses.get( + "https://api.github.com/search/commits", + match=[ + responses.matchers.query_param_matcher( + {"q": "repo:useblocks/sphinx-needs error ", "per_page": "1"} + ) + ], + status=200, + json=COMMIT_RESPONSE, + ) + responses.get( + "https://api.github.com/repos/useblocks/sphinx-needs/commits/050bec750ff2c5acf881415fa2b5efb5fcce8414", + status=200, + json=COMMIT_SPECIFIC_RESPONSE, + ) + responses.get( + "https://api.github.com/repos/useblocks/sphinx-needs/commits/rate_limit", + status=300, + json={"message": "API rate limit exceeded"}, + ) + responses.get( + "https://api.github.com/rate_limit", + status=200, + json={ + "resources": { + "core": {"limit": 5000, "remaining": 4999, "reset": 1613414020} + } + }, + ) + responses.get( + "https://avatars.githubusercontent.com/u/2997570", + match=[responses.matchers.query_param_matcher({"v": "4"})], + status=200, + body=b"", + ) + responses.get( + "https://avatars.githubusercontent.com/in/29110", + match=[responses.matchers.query_param_matcher({"v": "4"})], + status=200, + body=b"", + ) + + app = test_app + app.build() + warnings = strip_colors(app._warning.getvalue().replace(str(app.srcdir), "srcdir")) + print(warnings) + assert warnings.splitlines() == [ + 'srcdir/index.rst:4: WARNING: "query" or "specific" missing as option for github service. [needs.github]', + "srcdir/index.rst:22: WARNING: GitHub: API rate limit exceeded (twice). Stop here. [needs.github]", + ] + + needs_data = json.loads((Path(app.outdir) / "needs.json").read_text("utf8")) + assert needs_data == snapshot(exclude=props("created", "avatar")) + + +ISSUE_RESPONSE = { + "total_count": 518, + "incomplete_results": False, + "items": [ + { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110", + "repository_url": "https://api.github.com/repos/useblocks/sphinx-needs", + "labels_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110/labels{/name}", + "comments_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110/comments", + "events_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110/events", + "html_url": "https://github.com/useblocks/sphinx-needs/issues/1110", + "id": 2136385896, + "node_id": "I_kwDOBHvbXc5_Vqlo", + "number": 1110, + "title": "needreport usage should count needs in post-process", + "user": { + "login": "chrisjsewell", + "id": 2997570, + "node_id": "MDQ6VXNlcjI5OTc1NzA=", + "avatar_url": "https://avatars.githubusercontent.com/u/2997570?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrisjsewell", + "html_url": "https://github.com/chrisjsewell", + "followers_url": "https://api.github.com/users/chrisjsewell/followers", + "following_url": "https://api.github.com/users/chrisjsewell/following{/other_user}", + "gists_url": "https://api.github.com/users/chrisjsewell/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrisjsewell/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrisjsewell/subscriptions", + "organizations_url": "https://api.github.com/users/chrisjsewell/orgs", + "repos_url": "https://api.github.com/users/chrisjsewell/repos", + "events_url": "https://api.github.com/users/chrisjsewell/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrisjsewell/received_events", + "type": "User", + "site_admin": False, + }, + "labels": [ + { + "id": 491973814, + "node_id": "MDU6TGFiZWw0OTE5NzM4MTQ=", + "url": "https://api.github.com/repos/useblocks/sphinx-needs/labels/bug", + "name": "bug", + "color": "ee0701", + "default": True, + "description": None, + } + ], + "state": "open", + "locked": False, + "assignee": { + "login": "chrisjsewell", + "id": 2997570, + "node_id": "MDQ6VXNlcjI5OTc1NzA=", + "avatar_url": "https://avatars.githubusercontent.com/u/2997570?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrisjsewell", + "html_url": "https://github.com/chrisjsewell", + "followers_url": "https://api.github.com/users/chrisjsewell/followers", + "following_url": "https://api.github.com/users/chrisjsewell/following{/other_user}", + "gists_url": "https://api.github.com/users/chrisjsewell/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrisjsewell/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrisjsewell/subscriptions", + "organizations_url": "https://api.github.com/users/chrisjsewell/orgs", + "repos_url": "https://api.github.com/users/chrisjsewell/repos", + "events_url": "https://api.github.com/users/chrisjsewell/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrisjsewell/received_events", + "type": "User", + "site_admin": False, + }, + "assignees": [ + { + "login": "chrisjsewell", + "id": 2997570, + "node_id": "MDQ6VXNlcjI5OTc1NzA=", + "avatar_url": "https://avatars.githubusercontent.com/u/2997570?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrisjsewell", + "html_url": "https://github.com/chrisjsewell", + "followers_url": "https://api.github.com/users/chrisjsewell/followers", + "following_url": "https://api.github.com/users/chrisjsewell/following{/other_user}", + "gists_url": "https://api.github.com/users/chrisjsewell/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrisjsewell/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrisjsewell/subscriptions", + "organizations_url": "https://api.github.com/users/chrisjsewell/orgs", + "repos_url": "https://api.github.com/users/chrisjsewell/repos", + "events_url": "https://api.github.com/users/chrisjsewell/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrisjsewell/received_events", + "type": "User", + "site_admin": False, + } + ], + "milestone": None, + "comments": 0, + "created_at": "2024-02-15T12:19:11Z", + "updated_at": "2024-02-15T12:19:30Z", + "closed_at": None, + "author_association": "MEMBER", + "active_lock_reason": None, + "body": "I've also just realised there is a bug in this directive:\r\n\r\nThe `usage` flag creates a count of user defined need types.\r\nHowever, it does this during the processing of the directive, rather than in a post-processing step.\r\nTherefore, the count will usually be incorrect.\r\n\r\n_Originally posted by @chrisjsewell in https://github.com/useblocks/sphinx-needs/issues/1105#issuecomment-1937172837_\r\n ", + "reactions": { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0, + }, + "timeline_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1110/timeline", + "performed_via_github_app": None, + "state_reason": None, + "score": 1.0, + } + ], +} + +PR_RESPONSE = { + "total_count": 546, + "incomplete_results": False, + "items": [ + { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112", + "repository_url": "https://api.github.com/repos/useblocks/sphinx-needs", + "labels_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112/labels{/name}", + "comments_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112/comments", + "events_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112/events", + "html_url": "https://github.com/useblocks/sphinx-needs/pull/1112", + "id": 2137419108, + "node_id": "PR_kwDOBHvbXc5nBjed", + "number": 1112, + "title": "👌 Capture `only` expressions for each need", + "user": { + "login": "chrisjsewell", + "id": 2997570, + "node_id": "MDQ6VXNlcjI5OTc1NzA=", + "avatar_url": "https://avatars.githubusercontent.com/u/2997570?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrisjsewell", + "html_url": "https://github.com/chrisjsewell", + "followers_url": "https://api.github.com/users/chrisjsewell/followers", + "following_url": "https://api.github.com/users/chrisjsewell/following{/other_user}", + "gists_url": "https://api.github.com/users/chrisjsewell/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrisjsewell/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrisjsewell/subscriptions", + "organizations_url": "https://api.github.com/users/chrisjsewell/orgs", + "repos_url": "https://api.github.com/users/chrisjsewell/repos", + "events_url": "https://api.github.com/users/chrisjsewell/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrisjsewell/received_events", + "type": "User", + "site_admin": False, + }, + "labels": [], + "state": "open", + "locked": False, + "assignee": None, + "assignees": [], + "milestone": None, + "comments": 2, + "created_at": "2024-02-15T20:45:12Z", + "updated_at": "2024-02-15T20:55:43Z", + "closed_at": None, + "author_association": "MEMBER", + "active_lock_reason": None, + "draft": True, + "pull_request": { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/pulls/1112", + "html_url": "https://github.com/useblocks/sphinx-needs/pull/1112", + "diff_url": "https://github.com/useblocks/sphinx-needs/pull/1112.diff", + "patch_url": "https://github.com/useblocks/sphinx-needs/pull/1112.patch", + "merged_at": None, + }, + "body": '@David-Le-Nir and @danwos, as I explained in https://github.com/useblocks/sphinx-needs/issues/1103#issuecomment-1936305902, I think this is a better solution for handling need defined within `only` directives.\r\n\r\nIn this first "read" phase, we simply just note all the parent `only` expressions of the need, storing them on an (optional) `only_expressions` field of the need data item.\r\n\r\nIf desired, in a subsequent "build" post-processing phase, called from the cached data (once per build), you could then evaluate the `only_expressions` and remove needs as a necessary (or do whatever).\r\nThis logic would go here: https://github.com/useblocks/sphinx-needs/blob/84a5f72f2e72ab1471ab2d1bb5c570d6115ef199/sphinx_needs/directives/need.py#L385\r\n\r\nthis is more in-line with the logic of the `only` directive, where everything is cached and parts of the doctree are only removed during the build phase.\r\n\r\nwould supercede #1106 \r\n\r\ncloses #1103', + "reactions": { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0, + }, + "timeline_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/1112/timeline", + "performed_via_github_app": None, + "state_reason": None, + "score": 1.0, + } + ], +} + +COMMIT_RESPONSE = { + "total_count": 23, + "incomplete_results": False, + "items": [ + { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/6abd389369c5bbd5216f5ecdc3da1323ebe8620d", + "sha": "6abd389369c5bbd5216f5ecdc3da1323ebe8620d", + "node_id": "MDY6Q29tbWl0NzUyMjU5NDk6NmFiZDM4OTM2OWM1YmJkNTIxNmY1ZWNkYzNkYTEzMjNlYmU4NjIwZA==", + "html_url": "https://github.com/useblocks/sphinx-needs/commit/6abd389369c5bbd5216f5ecdc3da1323ebe8620d", + "comments_url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/6abd389369c5bbd5216f5ecdc3da1323ebe8620d/comments", + "commit": { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/git/commits/6abd389369c5bbd5216f5ecdc3da1323ebe8620d", + "author": { + "date": "2024-02-12T08:36:07.000Z", + "name": "Chris Sewell", + "email": "chrisj_sewell@hotmail.com", + }, + "committer": { + "date": "2024-02-12T09:36:07.000+01:00", + "name": "GitHub", + "email": "noreply@github.com", + }, + "message": '🧪 Add test for `needreport` directive (#1105)\n\nCurrently there is no test for this directive, this PR adds one.\r\n\r\nThis PR also fixes the directive:\r\n\r\n- Make the options flags\r\n- Change errors in the directive to emit warnings, rather than excepting\r\nthe whole build\r\n- Allow for `template` to be specified as a directive option\r\n- Allow the the `dropdown` directive used in the default template, which\r\nrequires an external sphinx extension, to be overriden using\r\n`needs_render_context = {"report_directive": "admonition"}` (I left the\r\ndefault as `dropdown`, so as not to introduce a breaking change)', + "tree": { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/git/trees/f4d12ed387505ee76d69c9f7ef43d3a1008d3edd", + "sha": "f4d12ed387505ee76d69c9f7ef43d3a1008d3edd", + }, + "comment_count": 0, + }, + "author": { + "login": "chrisjsewell", + "id": 2997570, + "node_id": "MDQ6VXNlcjI5OTc1NzA=", + "avatar_url": "https://avatars.githubusercontent.com/u/2997570?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/chrisjsewell", + "html_url": "https://github.com/chrisjsewell", + "followers_url": "https://api.github.com/users/chrisjsewell/followers", + "following_url": "https://api.github.com/users/chrisjsewell/following{/other_user}", + "gists_url": "https://api.github.com/users/chrisjsewell/gists{/gist_id}", + "starred_url": "https://api.github.com/users/chrisjsewell/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/chrisjsewell/subscriptions", + "organizations_url": "https://api.github.com/users/chrisjsewell/orgs", + "repos_url": "https://api.github.com/users/chrisjsewell/repos", + "events_url": "https://api.github.com/users/chrisjsewell/events{/privacy}", + "received_events_url": "https://api.github.com/users/chrisjsewell/received_events", + "type": "User", + "site_admin": False, + }, + "committer": { + "login": "web-flow", + "id": 19864447, + "node_id": "MDQ6VXNlcjE5ODY0NDQ3", + "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/web-flow", + "html_url": "https://github.com/web-flow", + "followers_url": "https://api.github.com/users/web-flow/followers", + "following_url": "https://api.github.com/users/web-flow/following{/other_user}", + "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", + "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", + "organizations_url": "https://api.github.com/users/web-flow/orgs", + "repos_url": "https://api.github.com/users/web-flow/repos", + "events_url": "https://api.github.com/users/web-flow/events{/privacy}", + "received_events_url": "https://api.github.com/users/web-flow/received_events", + "type": "User", + "site_admin": False, + }, + "parents": [ + { + "url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/73b961e29583bc0ac8892ef9c4e2bfb75f7f46bb", + "html_url": "https://github.com/useblocks/sphinx-needs/commit/73b961e29583bc0ac8892ef9c4e2bfb75f7f46bb", + "sha": "73b961e29583bc0ac8892ef9c4e2bfb75f7f46bb", + } + ], + "repository": { + "id": 75225949, + "node_id": "MDEwOlJlcG9zaXRvcnk3NTIyNTk0OQ==", + "name": "sphinx-needs", + "full_name": "useblocks/sphinx-needs", + "private": False, + "owner": { + "login": "useblocks", + "id": 998587, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5ODU4Nw==", + "avatar_url": "https://avatars.githubusercontent.com/u/998587?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/useblocks", + "html_url": "https://github.com/useblocks", + "followers_url": "https://api.github.com/users/useblocks/followers", + "following_url": "https://api.github.com/users/useblocks/following{/other_user}", + "gists_url": "https://api.github.com/users/useblocks/gists{/gist_id}", + "starred_url": "https://api.github.com/users/useblocks/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/useblocks/subscriptions", + "organizations_url": "https://api.github.com/users/useblocks/orgs", + "repos_url": "https://api.github.com/users/useblocks/repos", + "events_url": "https://api.github.com/users/useblocks/events{/privacy}", + "received_events_url": "https://api.github.com/users/useblocks/received_events", + "type": "Organization", + "site_admin": False, + }, + "html_url": "https://github.com/useblocks/sphinx-needs", + "description": "Adds needs/requirements to sphinx", + "fork": False, + "url": "https://api.github.com/repos/useblocks/sphinx-needs", + "forks_url": "https://api.github.com/repos/useblocks/sphinx-needs/forks", + "keys_url": "https://api.github.com/repos/useblocks/sphinx-needs/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/useblocks/sphinx-needs/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/useblocks/sphinx-needs/teams", + "hooks_url": "https://api.github.com/repos/useblocks/sphinx-needs/hooks", + "issue_events_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/events{/number}", + "events_url": "https://api.github.com/repos/useblocks/sphinx-needs/events", + "assignees_url": "https://api.github.com/repos/useblocks/sphinx-needs/assignees{/user}", + "branches_url": "https://api.github.com/repos/useblocks/sphinx-needs/branches{/branch}", + "tags_url": "https://api.github.com/repos/useblocks/sphinx-needs/tags", + "blobs_url": "https://api.github.com/repos/useblocks/sphinx-needs/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/useblocks/sphinx-needs/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/useblocks/sphinx-needs/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/useblocks/sphinx-needs/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/useblocks/sphinx-needs/statuses/{sha}", + "languages_url": "https://api.github.com/repos/useblocks/sphinx-needs/languages", + "stargazers_url": "https://api.github.com/repos/useblocks/sphinx-needs/stargazers", + "contributors_url": "https://api.github.com/repos/useblocks/sphinx-needs/contributors", + "subscribers_url": "https://api.github.com/repos/useblocks/sphinx-needs/subscribers", + "subscription_url": "https://api.github.com/repos/useblocks/sphinx-needs/subscription", + "commits_url": "https://api.github.com/repos/useblocks/sphinx-needs/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/useblocks/sphinx-needs/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/useblocks/sphinx-needs/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/useblocks/sphinx-needs/contents/{+path}", + "compare_url": "https://api.github.com/repos/useblocks/sphinx-needs/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/useblocks/sphinx-needs/merges", + "archive_url": "https://api.github.com/repos/useblocks/sphinx-needs/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/useblocks/sphinx-needs/downloads", + "issues_url": "https://api.github.com/repos/useblocks/sphinx-needs/issues{/number}", + "pulls_url": "https://api.github.com/repos/useblocks/sphinx-needs/pulls{/number}", + "milestones_url": "https://api.github.com/repos/useblocks/sphinx-needs/milestones{/number}", + "notifications_url": "https://api.github.com/repos/useblocks/sphinx-needs/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/useblocks/sphinx-needs/labels{/name}", + "releases_url": "https://api.github.com/repos/useblocks/sphinx-needs/releases{/id}", + "deployments_url": "https://api.github.com/repos/useblocks/sphinx-needs/deployments", + }, + "score": 1.0, + } + ], +} + + +COMMIT_SPECIFIC_RESPONSE = { + "sha": "050bec750ff2c5acf881415fa2b5efb5fcce8414", + "node_id": "C_kwDOBHvbXdoAKDA1MGJlYzc1MGZmMmM1YWNmODgxNDE1ZmEyYjVlZmI1ZmNjZTg0MTQ", + "commit": { + "author": { + "name": "dependabot[bot]", + "email": "49699333+dependabot[bot]@users.noreply.github.com", + "date": "2024-02-15T14:04:06Z", + }, + "committer": { + "name": "GitHub", + "email": "noreply@github.com", + "date": "2024-02-15T14:04:06Z", + }, + "message": "Bump actions/cache from 3 to 4 (#1092)\n\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>", + "tree": { + "sha": "6e20d545a52e6a170af2e2db345401a423213490", + "url": "https://api.github.com/repos/useblocks/sphinx-needs/git/trees/6e20d545a52e6a170af2e2db345401a423213490", + }, + "url": "https://api.github.com/repos/useblocks/sphinx-needs/git/commits/050bec750ff2c5acf881415fa2b5efb5fcce8414", + "comment_count": 0, + "verification": { + "verified": True, + "reason": "valid", + "signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJlzhnWCRC1aQ7uu5UhlAAAMvAQAJpZeW/31DvZUWOnGealNbCG\n7ciH6HtEKivPr69a44ce03zkU4tsgIAhDJu5JxpiT5FUJ2oiTfO3PNnkqiJZmpCk\nawTb1bJpXbXOFQ6vpYz78N0lZXgoasigP6hfikRTlW5ZNWzg07u+PjJEGf9ogdAA\nzrxkrnx1g4O/Aj0iAh9mnX3AhByR1iefCatMSq+3oZfqM2YtkWv+LHqYoOO8ZvoA\nzHxJky0B5EA3hWu9v+6yVt6W0E3Ozq+FzmhAGEwRcTz5PVjQd/luQvis1o1k18ai\nPdqBxoIl3+tWMTceU/0KLgU8KwGyfJFDj0dmnMuEyCFlQvMro4JNPnbOea6dK3ei\nRXPB3wkhUbDm/NRKI71zG0fbHkBpvme01t1MsDA5E0qmIhtwdDfQ50ME24oa7/vv\nr7Deg6+rSpS+VgshuhoOXT/ILKwK7oU1igBfzw+QscyOfgfQRelTiuayEkJzAwzf\n1jJOhRNbJNmXAnbgvbEDIW+/YoioJiDZotTCM1Jz785RXyqwuFO9+8b8jsS2GYYP\noyLVl10DhMGeAKfEFLyUZS8VIVO4u+w4hzTWIGT2CdmO9u7gjdnAcOE0v33MCr2+\npp9j9jo6ivvaZtNWObnmG9tcB6iH4MfRNPmY18ppNHG9ZXFOsOJr8bDcz/qbMioQ\nGQnUiu5KWiD87NrdhuCo\n=DIHs\n-----END PGP SIGNATURE-----\n", + "payload": "tree 6e20d545a52e6a170af2e2db345401a423213490\nparent a85d49be05949424e38c08233871f32064075828\nauthor dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1708005846 +0100\ncommitter GitHub 1708005846 +0100\n\nBump actions/cache from 3 to 4 (#1092)\n\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\r\n", + }, + }, + "url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/050bec750ff2c5acf881415fa2b5efb5fcce8414", + "html_url": "https://github.com/useblocks/sphinx-needs/commit/050bec750ff2c5acf881415fa2b5efb5fcce8414", + "comments_url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/050bec750ff2c5acf881415fa2b5efb5fcce8414/comments", + "author": { + "login": "dependabot[bot]", + "id": 49699333, + "node_id": "MDM6Qm90NDk2OTkzMzM=", + "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/dependabot%5Bbot%5D", + "html_url": "https://github.com/apps/dependabot", + "followers_url": "https://api.github.com/users/dependabot%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/dependabot%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/dependabot%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/dependabot%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/dependabot%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/dependabot%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/dependabot%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/dependabot%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/dependabot%5Bbot%5D/received_events", + "type": "Bot", + "site_admin": False, + }, + "committer": { + "login": "web-flow", + "id": 19864447, + "node_id": "MDQ6VXNlcjE5ODY0NDQ3", + "avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/web-flow", + "html_url": "https://github.com/web-flow", + "followers_url": "https://api.github.com/users/web-flow/followers", + "following_url": "https://api.github.com/users/web-flow/following{/other_user}", + "gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}", + "starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/web-flow/subscriptions", + "organizations_url": "https://api.github.com/users/web-flow/orgs", + "repos_url": "https://api.github.com/users/web-flow/repos", + "events_url": "https://api.github.com/users/web-flow/events{/privacy}", + "received_events_url": "https://api.github.com/users/web-flow/received_events", + "type": "User", + "site_admin": False, + }, + "parents": [ + { + "sha": "a85d49be05949424e38c08233871f32064075828", + "url": "https://api.github.com/repos/useblocks/sphinx-needs/commits/a85d49be05949424e38c08233871f32064075828", + "html_url": "https://github.com/useblocks/sphinx-needs/commit/a85d49be05949424e38c08233871f32064075828", + } + ], + "stats": {"total": 4, "additions": 2, "deletions": 2}, + "files": [ + { + "sha": "4efe5c00a0c11788bfba0fe3081d7f456559178d", + "filename": ".github/workflows/benchmark.yaml", + "status": "modified", + "additions": 1, + "deletions": 1, + "changes": 2, + "blob_url": "https://github.com/useblocks/sphinx-needs/blob/050bec750ff2c5acf881415fa2b5efb5fcce8414/.github%2Fworkflows%2Fbenchmark.yaml", + "raw_url": "https://github.com/useblocks/sphinx-needs/raw/050bec750ff2c5acf881415fa2b5efb5fcce8414/.github%2Fworkflows%2Fbenchmark.yaml", + "contents_url": "https://api.github.com/repos/useblocks/sphinx-needs/contents/.github%2Fworkflows%2Fbenchmark.yaml?ref=050bec750ff2c5acf881415fa2b5efb5fcce8414", + "patch": "@@ -22,7 +22,7 @@ jobs:\n run: pytest --benchmark-json output.json -k _time tests/benchmarks\n \n - name: Download previous benchmark data\n- uses: actions/cache@v3\n+ uses: actions/cache@v4\n with:\n path: ./cache\n key: ${{ runner.os }}-benchmark", + }, + { + "sha": "bf7070bc9ca51449263ef73d2d3da384438df37a", + "filename": ".github/workflows/js_test.yml", + "status": "modified", + "additions": 1, + "deletions": 1, + "changes": 2, + "blob_url": "https://github.com/useblocks/sphinx-needs/blob/050bec750ff2c5acf881415fa2b5efb5fcce8414/.github%2Fworkflows%2Fjs_test.yml", + "raw_url": "https://github.com/useblocks/sphinx-needs/raw/050bec750ff2c5acf881415fa2b5efb5fcce8414/.github%2Fworkflows%2Fjs_test.yml", + "contents_url": "https://api.github.com/repos/useblocks/sphinx-needs/contents/.github%2Fworkflows%2Fjs_test.yml?ref=050bec750ff2c5acf881415fa2b5efb5fcce8414", + "patch": "@@ -23,7 +23,7 @@ jobs:\n run: |\n echo \"dir=$(pip cache dir)\" >> $GITHUB_OUTPUT\n - name: Pip cache\n- uses: actions/cache@v3\n+ uses: actions/cache@v4\n with:\n path: ${{ steps.pip-cache.outputs.dir }}\n key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}", + }, + ], +} diff --git a/tests/test_styles/test_style_css_js_registration.py b/tests/test_styles/test_style_css_js_registration.py index 31866c334..86016021d 100644 --- a/tests/test_styles/test_style_css_js_registration.py +++ b/tests/test_styles/test_style_css_js_registration.py @@ -1,7 +1,9 @@ import os import tempfile +from pathlib import Path -import sphinx +from sphinx import version_info +from sphinx.testing.util import SphinxTestApp def test_file_registration(): @@ -11,61 +13,72 @@ def test_file_registration(): temp_dir = tempfile.mkdtemp() src_dir = os.path.join(os.path.dirname(__file__), "../doc_test/doc_style_modern") + if version_info >= (7, 2): + src_dir = Path(src_dir) + temp_dir = Path(temp_dir) + else: + from sphinx.testing.path import path + + src_dir = path(src_dir) + temp_dir = path(temp_dir) + with open(os.path.join(temp_dir, "warnings.txt"), "w") as warnings: - sphinx_app = sphinx.application.Sphinx( + sphinx_app = SphinxTestApp( srcdir=src_dir, - confdir=src_dir, - outdir=temp_dir, - doctreedir=temp_dir, + builddir=temp_dir, buildername="html", parallel=4, warning=warnings, ) - # 1. Build - sphinx_app.build() + try: + # 1. Build + sphinx_app.build() + + # Only check Sphinx-Needs files + css_files = [ + x + for x in sphinx_app.builder.css_files + if x.startswith("_static/sphinx-needs") + ] + script_files = [ + x + for x in sphinx_app.builder.script_files + if x.startswith("_static/sphinx-needs") + ] + css_run_1 = len(css_files) + script_run_1 = len(script_files) - # Only check Sphinx-Needs files - css_files = [ - x - for x in sphinx_app.builder.css_files - if x.startswith("_static/sphinx-needs") - ] - script_files = [ - x - for x in sphinx_app.builder.script_files - if x.startswith("_static/sphinx-needs") - ] - css_run_1 = len(css_files) - script_run_1 = len(script_files) + # Check for duplicates + assert css_run_1 == len(set(css_files)) + assert script_run_1 == len(set(script_files)) - # Check for duplicates - assert css_run_1 == len(set(css_files)) - assert script_run_1 == len(set(script_files)) + assert sphinx_app.statuscode == 0 - assert sphinx_app.statuscode == 0 + # 2. Build + sphinx_app.build() - # 2. Build - sphinx_app.build() + # Only check Sphinx-Needs files + css_files = [ + x + for x in sphinx_app.builder.css_files + if x.startswith("_static/sphinx-needs") + ] + script_files = [ + x + for x in sphinx_app.builder.script_files + if x.startswith("_static/sphinx-needs") + ] + css_run_2 = len(css_files) + script_run_2 = len(script_files) - # Only check Sphinx-Needs files - css_files = [ - x - for x in sphinx_app.builder.css_files - if x.startswith("_static/sphinx-needs") - ] - script_files = [ - x - for x in sphinx_app.builder.script_files - if x.startswith("_static/sphinx-needs") - ] - css_run_2 = len(css_files) - script_run_2 = len(script_files) + # Check for duplicates + assert css_run_2 == len(set(css_files)) + assert script_run_2 == len(set(script_files)) - # Check for duplicates - assert css_run_2 == len(set(css_files)) - assert script_run_2 == len(set(script_files)) + assert sphinx_app.statuscode == 0 + assert css_run_1 == css_run_2 + assert script_run_1 == script_run_2 - assert sphinx_app.statuscode == 0 - assert css_run_1 == css_run_2 - assert script_run_1 == script_run_2 + finally: + sphinx_app.cleanup() From 4294f92f5db51abcbdea814e6631a01b1019656e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 13:48:27 +0100 Subject: [PATCH 13/24] Bump pre-commit/action from 3.0.0 to 3.0.1 (#1114) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index abccea3cf..c9f8ce82b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,7 +14,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.8' - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@v3.0.1 tests-core: name: "Core py${{ matrix.python-version }} sphinx~=${{ matrix.sphinx-version }} ${{ matrix.os }}" From c2ad574d8a060af144d51ca2301fcfd2766cf48e Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 19 Feb 2024 09:36:07 +0000 Subject: [PATCH 14/24] =?UTF-8?q?=F0=9F=94=A7=20Type=20`ExternalSource`=20?= =?UTF-8?q?config=20dict=20(#1115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sphinx_needs/config.py | 38 +++++++++++++++++++++++++++++++--- sphinx_needs/external_needs.py | 1 + 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index 785436f7e..6a8808c87 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -69,6 +69,38 @@ class ConstraintFailedType(TypedDict): """If True, append styles to existing styles, else replace existing styles.""" +class ExternalSource(TypedDict, total=False): + """Defines an external source to import needs from. + + External source will be a JSON with the key path:: + + versions -> -> needs -> + + """ + + # either + json_path: str + """A local path to a JSON file""" + # or + json_url: str + """A remote URL to a JSON object""" + + version: str + """Override `current_version` from loaded JSON (optional)""" + + base_url: str + """Used to create the `external_url` field for each need item (required)""" + + target_url: str + """Used to create the `external_url` field for each need item (optional)""" + + id_prefix: str + """Prefix all IDs from the external source with this string (optional, uppercased)""" + + css_class: str + """Added as the `external_css` field for each need item (optional)""" + + @dataclass class NeedsSphinxConfig: """A wrapper around the Sphinx configuration, @@ -300,10 +332,10 @@ def __setattr__(self, name: str, value: Any) -> None: debug_no_external_calls: bool = field( default=False, metadata={"rebuild": "html", "types": (bool,)} ) - external_needs: list[dict[str, Any]] = field( - default_factory=list, metadata={"rebuild": "html", "types": ()} + external_needs: list[ExternalSource] = field( + default_factory=list, metadata={"rebuild": "html", "types": (list,)} ) - """Reference external needs, outside of the documentation.""" + """List of external sources to load needs from.""" builder_filter: str = field( default="is_external==False", metadata={"rebuild": "html", "types": (str,)} ) diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index 331038996..47d34a5d2 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -31,6 +31,7 @@ def get_target_template(target_url: str) -> Template: def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: + """Load needs from configured external sources.""" needs_config = NeedsSphinxConfig(app.config) for source in needs_config.external_needs: if source["base_url"].endswith("/"): From aef3a0f0cad25704fc6afbeca49d975374240fb3 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 19 Feb 2024 10:18:42 +0000 Subject: [PATCH 15/24] =?UTF-8?q?=F0=9F=94=A7=20Enforce=20type=20checking?= =?UTF-8?q?=20in=20`needuml.py`=20(#1116)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 1 - sphinx_needs/diagrams_common.py | 4 +- sphinx_needs/directives/needuml.py | 98 ++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 26a3957bd..2edb51eb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,7 +139,6 @@ ignore_missing_imports = true [[tool.mypy.overrides]] module = [ 'sphinx_needs.api.need', - 'sphinx_needs.directives.needuml', ] ignore_errors = true diff --git a/sphinx_needs/diagrams_common.py b/sphinx_needs/diagrams_common.py index 0eced3edb..760e680e9 100644 --- a/sphinx_needs/diagrams_common.py +++ b/sphinx_needs/diagrams_common.py @@ -17,7 +17,7 @@ from sphinx.util.docutils import SphinxDirective from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import NeedsFilteredBaseType, NeedsPartsInfoType +from sphinx_needs.data import NeedsFilteredBaseType, NeedsInfoType, NeedsPartsInfoType from sphinx_needs.errors import NoUri from sphinx_needs.logging import get_logger from sphinx_needs.utils import get_scale, split_link_types @@ -169,7 +169,7 @@ def get_debug_container(puml_node: nodes.Element) -> nodes.container: def calculate_link( - app: Sphinx, need_info: NeedsPartsInfoType, _fromdocname: str + app: Sphinx, need_info: NeedsInfoType | NeedsPartsInfoType, _fromdocname: None | str ) -> str: """ Link calculation diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index 3fd8f3b41..326bd4430 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -2,7 +2,7 @@ import html import os -from typing import Sequence +from typing import TYPE_CHECKING, Any, Dict, List, Sequence, TypedDict from docutils import nodes from docutils.parsers.rst import directives @@ -11,13 +11,25 @@ from sphinx.util.docutils import SphinxDirective 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.diagrams_common import calculate_link from sphinx_needs.directives.needflow import make_entity_name from sphinx_needs.filter_common import filter_needs from sphinx_needs.utils import add_doc +if TYPE_CHECKING: + from sphinxcontrib.plantuml import plantuml + + +class ProcessedDataType(TypedDict): + art: str + key: None | str + arguments: dict[str, Any] + + +ProcessedNeedsType = Dict[str, List[ProcessedDataType]] + class Needuml(nodes.General, nodes.Element): pass @@ -130,8 +142,13 @@ def run(self) -> Sequence[nodes.Node]: def transform_uml_to_plantuml_node( - app, uml_content: str, parent_need_id: str, key: str, kwargs: dict, config: str -): + app: Sphinx, + uml_content: str, + parent_need_id: None | str, + key: None | str, + kwargs: dict[str, Any], + config: str, +) -> plantuml: try: if "sphinxcontrib.plantuml" not in app.config.extensions: raise ImportError @@ -158,7 +175,7 @@ def transform_uml_to_plantuml_node( puml_node["uml"] += "\n\n" # jinja2uml to translate jinja statements to uml text - (uml_content_return, processed_need_ids_return) = jinja2uml( + (uml_content_return, _) = jinja2uml( app=app, fromdocname=None, uml_content=uml_content, @@ -167,16 +184,15 @@ def transform_uml_to_plantuml_node( processed_need_ids={}, kwargs=kwargs, ) - # silently discard processed_need_ids_return puml_node["uml"] += f"\n{uml_content_return}" puml_node["uml"] += "\n@enduml\n" return puml_node -def get_debug_node_from_puml_node(puml_node): +def get_debug_node_from_puml_node(puml_node: plantuml) -> nodes.container: if isinstance(puml_node, nodes.figure): - data = puml_node.children[0]["uml"] + data = puml_node.children[0]["uml"] # type: ignore[index] data = puml_node.get("uml", "") data = "\n".join([html.escape(line) for line in data.split("\n")]) debug_para = nodes.raw("", f"
{data}
", format="html") @@ -186,20 +202,20 @@ def get_debug_node_from_puml_node(puml_node): def jinja2uml( - app, - fromdocname, + app: Sphinx, + fromdocname: None | str, uml_content: str, - parent_need_id: str, - key: str, - processed_need_ids: {}, - kwargs: dict, -) -> (str, {}): + parent_need_id: None | str, + key: None | str, + processed_need_ids: ProcessedNeedsType, + kwargs: dict[str, Any], +) -> tuple[str, ProcessedNeedsType]: # Let's render jinja templates with uml content template to 'plantuml syntax' uml # 1. Remove @startuml and @enduml uml_content = uml_content.replace("@startuml", "").replace("@enduml", "") # 2. Prepare jinja template - mem_template = Environment(loader=BaseLoader).from_string(uml_content) + mem_template = Environment(loader=BaseLoader()).from_string(uml_content) # 3. Get a new instance of Jinja Helper Functions jinja_utils = JinjaFunctions(app, fromdocname, parent_need_id, processed_need_ids) @@ -211,7 +227,7 @@ def jinja2uml( ) # 5. Get data for the jinja processing - data = {} + data: dict[str, Any] = {} # 5.1 Set default config to data data.update(**NeedsSphinxConfig(app.config).render_context) # 5.2 Set uml() kwargs to data and maybe overwrite default settings @@ -246,8 +262,12 @@ class JinjaFunctions: """ def __init__( - self, app: Sphinx, fromdocname, parent_need_id: str, processed_need_ids: dict - ): + self, + app: Sphinx, + fromdocname: None | str, + parent_need_id: None | str, + processed_need_ids: ProcessedNeedsType, + ) -> None: self.needs = SphinxNeedsData(app.env).get_or_create_needs() self.app = app self.fromdocname = fromdocname @@ -258,8 +278,10 @@ def __init__( ) self.processed_need_ids = processed_need_ids - def need_to_processed_data(self, art: str, key: str, kwargs: dict) -> {}: - d = { + def need_to_processed_data( + self, art: str, key: None | str, kwargs: dict[str, Any] + ) -> ProcessedDataType: + d: ProcessedDataType = { "art": art, "key": key, "arguments": kwargs, @@ -267,7 +289,7 @@ def need_to_processed_data(self, art: str, key: str, kwargs: dict) -> {}: return d def append_need_to_processed_needs( - self, need_id: str, art: str, key: str, kwargs: dict + self, need_id: str, art: str, key: None | str, kwargs: dict[str, Any] ) -> None: data = self.need_to_processed_data(art=art, key=key, kwargs=kwargs) if need_id not in self.processed_need_ids: @@ -275,7 +297,9 @@ def append_need_to_processed_needs( if data not in self.processed_need_ids[need_id]: self.processed_need_ids[need_id].append(data) - def append_needs_to_processed_needs(self, processed_needs_data: dict) -> None: + def append_needs_to_processed_needs( + self, processed_needs_data: ProcessedNeedsType + ) -> None: for k, v in processed_needs_data.items(): if k not in self.processed_need_ids: self.processed_need_ids[k] = [] @@ -284,17 +308,17 @@ def append_needs_to_processed_needs(self, processed_needs_data: dict) -> None: self.processed_need_ids[k].append(d) def data_in_processed_data( - self, need_id: str, art: str, key: str, kwargs: dict + self, need_id: str, art: str, key: str, kwargs: dict[str, Any] ) -> bool: data = self.need_to_processed_data(art=art, key=key, kwargs=kwargs) return (need_id in self.processed_need_ids) and ( data in self.processed_need_ids[need_id] ) - def get_processed_need_ids(self) -> {}: + def get_processed_need_ids(self) -> ProcessedNeedsType: return self.processed_need_ids - def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs) -> str: + def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs: Any) -> str: if need_id not in self.needs: raise NeedumlException( f"Jinja function uml() is called with undefined need_id: '{need_id}'." @@ -337,7 +361,7 @@ def uml_from_need(self, need_id: str, key: str = "diagram", **kwargs) -> str: return uml - def flow(self, need_id) -> str: + def flow(self, need_id: str) -> str: if need_id not in self.needs: raise NeedumlException( f"Jinja function flow is called with undefined need_id: '{need_id}'." @@ -368,7 +392,9 @@ def flow(self, need_id) -> str: return need_uml - def ref(self, need_id: str, option: str = None, text: str = None) -> str: + def ref( + self, need_id: str, option: None | str = None, text: None | str = None + ) -> str: if need_id not in self.needs: raise NeedumlException( f"Jinja function ref is called with undefined need_id: '{need_id}'." @@ -388,7 +414,7 @@ def ref(self, need_id: str, option: str = None, text: str = None) -> str: return need_uml - def filter(self, filter_string): + def filter(self, filter_string: str) -> list[NeedsInfoType]: """ Return a list of found needs that pass the given filter string. """ @@ -398,7 +424,7 @@ def filter(self, filter_string): list(self.needs.values()), needs_config, filter_string=filter_string ) - def imports(self, *args): + def imports(self, *args: str) -> str: if not self.parent_need_id: raise NeedumlException( "Jinja function 'import()' is not supported in needuml directive." @@ -408,8 +434,14 @@ def imports(self, *args): uml_ids = [] for option_name in args: # check if link option_name exists in current need object - if option_name in need_info and need_info[option_name]: - for id in need_info[option_name]: + if option_value := need_info.get(option_name): + try: + iterable_value = list(option_value) # type: ignore + except TypeError: + raise NeedumlException( + f"Option value for {option_name!r} is not iterable." + ) + for id in iterable_value: uml_ids.append(id) umls = "" if uml_ids: @@ -420,7 +452,7 @@ def imports(self, *args): umls += local_uml_from_need return umls - def need(self): + def need(self) -> NeedsInfoType: if not self.parent_need_id: raise NeedumlException( "Jinja function 'need()' is not supported in needuml directive." From 99cb3e451b39045b02bad2dd28ce308d83304d71 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Tue, 20 Feb 2024 16:15:07 +0000 Subject: [PATCH 16/24] =?UTF-8?q?=F0=9F=94=A7=20Enforce=20type=20checking?= =?UTF-8?q?=20in=20`api/need.py`=20(#1117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/conf.py | 2 + pyproject.toml | 7 +- sphinx_needs/api/need.py | 140 ++++++++++++++++++-------------- sphinx_needs/data.py | 35 +++++--- sphinx_needs/directives/need.py | 2 +- 5 files changed, 107 insertions(+), 79 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f9355ad77..448b0b914 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -45,6 +45,8 @@ nitpicky = True nitpick_ignore = [ + ("py:class", "docutils.nodes.Node"), + ("py:class", "docutils.parsers.rst.states.RSTState"), ("py:class", "T"), ("py:class", "sphinx_needs.debug.T"), ("py:class", "sphinx_needs.data.NeedsInfoType"), diff --git a/pyproject.toml b/pyproject.toml index 2edb51eb1..e089e1717 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -136,16 +136,11 @@ module = [ ] ignore_missing_imports = true -[[tool.mypy.overrides]] -module = [ - 'sphinx_needs.api.need', -] -ignore_errors = true - [[tool.mypy.overrides]] module = [ "sphinx_needs.directives.needextend", "sphinx_needs.functions.functions", + "sphinx_needs.api.need", ] # TODO dynamically overriding TypeDict keys is a bit tricky disable_error_code = "literal-required" diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 32dec8513..139432ca5 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -37,34 +37,34 @@ def add_need( app: Sphinx, - state, - docname: str, - lineno: int, - need_type, + state: None | RSTState, + docname: None | str, + lineno: None | int, + need_type: str, title: str, id: str | None = None, content: str = "", status: str | None = None, - tags=None, - constraints=None, - constraints_passed=None, - links_string: str | None = None, + tags: None | str | list[str] = None, + constraints: None | str | list[str] = None, + constraints_passed: None | bool = None, + links_string: None | str | list[str] = None, delete: bool = False, jinja_content: bool = False, hide: bool = False, hide_tags: bool = False, hide_status: bool = False, - collapse=None, - style=None, - layout=None, - template=None, + collapse: None | bool = None, + style: None | str = None, + layout: None | str = None, + template: None | str = None, pre_template: str | None = None, post_template: str | None = None, is_external: bool = False, external_url: str | None = None, external_css: str = "external_link", - **kwargs, -): + **kwargs: Any, +) -> list[nodes.Node]: """ Creates a new need and returns its node. @@ -239,6 +239,8 @@ def run(): # This may have cut also dynamic function strings, as they can contain , as well. # So let put them together again # ToDo: There may be a smart regex for the splitting. This would avoid this mess of code... + else: + tags = [] tags = _fix_list_dyn_func(tags) if constraints is None: @@ -273,6 +275,8 @@ def run(): # This may have cut also dynamic function strings, as they can contain , as well. # So let put them together again # ToDo: There may be a smart regex for the splitting. This would avoid this mess of code... + else: + constraints = [] constraints = _fix_list_dyn_func(constraints) ############################################################################################# @@ -311,7 +315,7 @@ def run(): doctype = ".rst" # Add the need and all needed information - needs_info: NeedsInfoType = { + needs_info: NeedsInfoType = { # type: ignore[typeddict-item] "docname": docname, "doctype": doctype, "lineno": lineno, @@ -473,22 +477,22 @@ def run(): for child in node_need_content.children: if isinstance(child, Needuml): needuml_id = child.rawsource - try: - needuml = SphinxNeedsData(env).get_or_create_umls().get(needuml_id) - key_name = needuml["key"] - if key_name: - # check if key_name already exists in needs_info["arch"] - if key_name in node_need_needumls_key_names: - raise NeedumlException( - f"Inside need: {need_id}, found duplicate Needuml option key name: {key_name}" - ) + if needuml := SphinxNeedsData(env).get_or_create_umls().get(needuml_id): + try: + key_name = needuml["key"] + if key_name: + # check if key_name already exists in needs_info["arch"] + if key_name in node_need_needumls_key_names: + raise NeedumlException( + f"Inside need: {need_id}, found duplicate Needuml option key name: {key_name}" + ) + else: + needs_info["arch"][key_name] = needuml["content"] + node_need_needumls_key_names.append(key_name) else: - needs_info["arch"][key_name] = needuml["content"] - node_need_needumls_key_names.append(key_name) - else: - node_need_needumls_without_key.append(needuml) - except KeyError: - pass + node_need_needumls_without_key.append(needuml) + except KeyError: + pass # only store the first needuml-node which has no key option under diagram if node_need_needumls_without_key: @@ -504,7 +508,7 @@ def run(): # Create a copy of the content needs_info["content_node"] = node_need.deepcopy() - return_nodes = [node_need] + return_nodes: list[nodes.Node] = [node_need] if not is_external: # Calculate target id, to be able to set a link back target_node = nodes.target("", "", ids=[need_id], refid=need_id, anonymous="") @@ -539,7 +543,7 @@ def del_need(app: Sphinx, need_id: str) -> None: def add_external_need( app: Sphinx, - need_type, + need_type: str, title: str | None = None, id: str | None = None, external_url: str | None = None, @@ -550,7 +554,7 @@ def add_external_need( constraints: str | None = None, links_string: str | None = None, **kwargs: Any, -): +) -> list[nodes.Node]: """ Adds an external need from an external source. This need does not have any representation in the current documentation project. @@ -570,30 +574,40 @@ def add_external_need( :param constraints: constraints as single, comma separated string. :param links_string: Links as single string. :param external_css: CSS class name as string, which is set for the tag. - :param kwargs: - :return: Empty list """ - - kwargs["state"] = None - kwargs["docname"] = None - kwargs["lineno"] = None - kwargs["need_type"] = need_type - kwargs["id"] = id - kwargs["content"] = content - kwargs["title"] = title - kwargs["status"] = status - kwargs["tags"] = tags - kwargs["constraints"] = constraints - kwargs["links_string"] = links_string - kwargs["is_external"] = True - kwargs["external_url"] = external_url - kwargs["external_css"] = external_css - - return add_need(app=app, **kwargs) - - -def _prepare_template(app: Sphinx, needs_info, template_key: str) -> str: + for fixed_key in ("state", "docname", "lineno", "is_external"): + if fixed_key in kwargs: + kwargs.pop(fixed_key) + # TODO Although it seems prudent to not silently ignore user input here, + # raising an error here currently breaks some existing tests + # raise ValueError( + # f"{fixed_key} is not allowed in kwargs for add_external_need" + # ) + + return add_need( + app=app, + state=None, + docname=None, + lineno=None, + need_type=need_type, + id=id, + content=content, + # TODO a title being None is not "type compatible" with other parts of the code base, + # however, at present changing it to an empty string breaks some existing tests. + title=title, # type: ignore + status=status, + tags=tags, + constraints=constraints, + links_string=links_string, + is_external=True, + external_url=external_url, + external_css=external_css, + **kwargs, + ) + + +def _prepare_template(app: Sphinx, needs_info: NeedsInfoType, template_key: str) -> str: needs_config = NeedsSphinxConfig(app.config) template_folder = needs_config.template_folder if not os.path.isabs(template_folder): @@ -618,10 +632,12 @@ def _prepare_template(app: Sphinx, needs_info, template_key: str) -> str: def _render_template( - content: str, docname: str, lineno: int, state: RSTState + content: str, docname: str | None, lineno: int | None, state: RSTState ) -> nodes.Element: rst = StringList() for line in content.split("\n"): + # TODO how to handle if the source mapping here, if the content is from an external need? + # (i.e. does not have a docname and lineno) rst.append(line, docname, lineno) node_need_content = nodes.Element() node_need_content.document = state.document @@ -644,7 +660,7 @@ def _render_plantuml_template( return node_need_content -def _read_in_links(links_string: str | list[str]) -> list[str]: +def _read_in_links(links_string: None | str | list[str]) -> list[str]: # Get links links = [] if links_string: @@ -759,7 +775,11 @@ def _fix_list_dyn_func(list: list[str]) -> list[str]: return new_list -def _merge_extra_options(needs_info, needs_kwargs, needs_extra_options): +def _merge_extra_options( + needs_info: NeedsInfoType, + needs_kwargs: dict[str, Any], + needs_extra_options: list[str], +) -> set[str]: """Add any extra options introduced via options_ext to needs_info""" extra_keys = set(needs_kwargs.keys()).difference(set(needs_info.keys())) @@ -774,7 +794,9 @@ def _merge_extra_options(needs_info, needs_kwargs, needs_extra_options): return extra_keys -def _merge_global_options(app: Sphinx, needs_info, global_options) -> None: +def _merge_global_options( + app: Sphinx, needs_info: NeedsInfoType, global_options: dict[str, Any] +) -> None: """Add all global defined options to needs_info""" if global_options is None: return diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index a36e362e3..9c98bd88c 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -26,17 +26,6 @@ class NeedsFilterType(TypedDict): """If set, the filter is exported with this ID in the needs.json file.""" -class NeedsBaseDataType(TypedDict): - """A base type for all data.""" - - docname: str - """Name of the document where the need is defined.""" - lineno: int - """Line number where the need is defined.""" - target_id: str - """ID of the data.""" - - class NeedsPartType(TypedDict): """Data for a single need part.""" @@ -56,12 +45,21 @@ class NeedsPartType(TypedDict): """List of need IDs, which are referencing this part.""" -class NeedsInfoType(NeedsBaseDataType): +class NeedsInfoType(TypedDict): """Data for a single need.""" + target_id: str + """ID of the data.""" id: str """ID of the data (same as target_id)""" + # TODO docname and lineno can be None, if the need is external, + # but currently this raises mypy errors for other parts of the code base + docname: str + """Name of the document where the need is defined.""" + lineno: int + """Line number where the need is defined.""" + # meta information full_title: str """Title of the need, of unlimited length.""" @@ -71,7 +69,7 @@ class NeedsInfoType(NeedsBaseDataType): tags: list[str] # rendering information - collapse: bool + collapse: None | bool """hide the meta-data information of the need.""" hide: bool """If true, the need is not rendered.""" @@ -214,6 +212,17 @@ class NeedsPartsInfoType(NeedsInfoType): id_complete: str +class NeedsBaseDataType(TypedDict): + """A base type for data items collected from directives.""" + + docname: str + """Name of the document where the need is defined.""" + lineno: int + """Line number where the need is defined.""" + target_id: str + """ID of the data.""" + + class NeedsBarType(NeedsBaseDataType): """Data for a single (matplotlib) bar diagram.""" diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 7863b161f..9c2cd4ab2 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -163,7 +163,7 @@ def run(self) -> Sequence[nodes.Node]: **need_extra_options, ) add_doc(env, self.docname) - return need_nodes # type: ignore[no-any-return] + return need_nodes def read_in_links(self, name: str) -> list[str]: # Get links From eaa5f679f6150cfb0ca1763ca73d42b960f5c96a Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 21 Feb 2024 08:22:49 +0000 Subject: [PATCH 17/24] =?UTF-8?q?=F0=9F=94=A7=20Move=20dead=20link=20need?= =?UTF-8?q?=20fields=20to=20internals=20(#1119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, `has_dead_links` and `has_forbidden_dead_links` are set as default "extra options". This means they are set to default `""` and, in principle, the user can set them to something else. This commit changes them to be "internal" fields, since this is an internal consistency check: - when initialised they should always be set to `False` (ignoring any previous value, if they are from an external source) - In the `check_links` post-processing of the needs, they are then set to `True`, if required --- sphinx_needs/api/need.py | 2 + sphinx_needs/data.py | 11 +- sphinx_needs/layout.py | 2 +- sphinx_needs/needs.py | 2 - sphinx_needs/utils.py | 6 +- tests/__snapshots__/test_basic_doc.ambr | 9 +- tests/__snapshots__/test_export_id.ambr | 33 ++- tests/__snapshots__/test_external.ambr | 21 +- tests/__snapshots__/test_import.ambr | 265 +++++++++--------- .../__snapshots__/test_need_constraints.ambr | 33 ++- tests/__snapshots__/test_needextend.ambr | 41 ++- tests/__snapshots__/test_needs_builder.ambr | 24 +- .../__snapshots__/test_needs_id_builder.ambr | 13 +- tests/__snapshots__/test_needuml.ambr | 37 ++- tests/__snapshots__/test_service_github.ambr | 16 +- 15 files changed, 257 insertions(+), 258 deletions(-) diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 139432ca5..c3d46aa90 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -354,6 +354,8 @@ def run(): "external_css": external_css or "external_link", "is_modified": False, # needed by needextend "modifications": 0, # needed by needextend + "has_dead_links": False, + "has_forbidden_dead_links": False, # these are set later in the analyse_need_locations transform "sections": [], "section_name": "", diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 9c98bd88c..c29d21ca7 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -136,6 +136,14 @@ class NeedsInfoType(TypedDict): # these all have value type `list[str]` # back links are all set in process_need_nodes (-> create_back_links) transform + # these default to False and are updated in check_links post-process + has_dead_links: bool + """True if any links reference need ids that are not found in the need list.""" + has_forbidden_dead_links: bool + """True if any links reference need ids that are not found in the need list, + and the link type does not allow dead links. + """ + # constraints information constraints: list[str] """List of constraint names, which are defined for this need.""" @@ -173,9 +181,6 @@ class NeedsInfoType(TypedDict): duration: str completion: str # constraints: str # this is already set in create_need - # these are updated in process_need_nodes (-> check_links) transform - has_dead_links: str | bool - has_forbidden_dead_links: str | bool # options from `BaseService.options` get added to every need, # via `ServiceManager.register`, which adds them to `extra_options`` # GithubService diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index e7cc60e35..a4d14b58b 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -728,7 +728,7 @@ def meta_all( :param show_empty: If true, also need data with no value will be printed. Mostly useful for debugging. :return: docutils nodes """ - default_excludes = INTERNALS.copy() + default_excludes = list(INTERNALS) if exclude is None or not isinstance(exclude, list): if defaults: diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 4d99df291..3cfdc9f46 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -517,8 +517,6 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: "hidden", "duration", "completion", - "has_dead_links", - "has_forbidden_dead_links", "constraints", ]: # Check if not already set by user diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 4476c9bb9..502c9d734 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -41,7 +41,7 @@ class NeedFunctionsType(TypedDict): NEEDS_FUNCTIONS: dict[str, NeedFunctionsType] = {} # List of internal need option names. They should not be used by or presented to user. -INTERNALS = [ +INTERNALS = ( "docname", "doctype", "lineno", @@ -82,7 +82,9 @@ class NeedFunctionsType(TypedDict): "constraints_results", "arch", "target_id", -] + "has_dead_links", + "has_forbidden_dead_links", +) MONTH_NAMES = [ "January", diff --git a/tests/__snapshots__/test_basic_doc.ambr b/tests/__snapshots__/test_basic_doc.ambr index cecb75dc6..545c59cb8 100644 --- a/tests/__snapshots__/test_basic_doc.ambr +++ b/tests/__snapshots__/test_basic_doc.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_build_needs[test_app0] dict({ 'current_version': '', @@ -30,8 +29,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'ST_001', 'id_prefix': '', @@ -98,8 +97,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'No ID', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'US_38823', 'id_prefix': '', diff --git a/tests/__snapshots__/test_export_id.ambr b/tests/__snapshots__/test_export_id.ambr index 21855b287..0c62e16ae 100644 --- a/tests/__snapshots__/test_export_id.ambr +++ b/tests/__snapshots__/test_export_id.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_export_id[test_app0] dict({ 'current_version': '1.0', @@ -136,8 +135,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_001', 'id_prefix': '', @@ -212,8 +211,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My requirement 2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_002', 'id_prefix': '', @@ -285,8 +284,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My requirement 3', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_003', 'id_prefix': '', @@ -358,8 +357,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My requirement 4', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_004', 'id_prefix': '', @@ -436,8 +435,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Req 5', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_005', 'id_prefix': '', @@ -540,8 +539,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test of requirements', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_001', 'id_prefix': '', @@ -615,8 +614,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test of requirements2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_002', 'id_prefix': '', @@ -690,8 +689,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test of requirements 5', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_003', 'id_prefix': '', diff --git a/tests/__snapshots__/test_external.ambr b/tests/__snapshots__/test_external.ambr index 1f65036ef..558593df2 100644 --- a/tests/__snapshots__/test_external.ambr +++ b/tests/__snapshots__/test_external.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_external_json[test_app0] dict({ 'current_version': '1.0', @@ -30,8 +29,8 @@ 'external_css': 'external_link', 'external_url': 'http://my_company.com/docs/v1/index.html#TEST_01', 'full_title': 'TEST_01 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'EXT_TEST_01', 'id_prefix': '', @@ -98,8 +97,8 @@ 'external_css': 'external_link', 'external_url': 'http://my_company.com/docs/v1/index.html#TEST_02', 'full_title': 'TEST_02 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'EXT_TEST_02', 'id_prefix': '', @@ -169,8 +168,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test requirement 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', @@ -237,8 +236,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test specification 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SPEC_1', 'id_prefix': '', @@ -307,8 +306,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Sub-Req', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SUB_002', 'id_prefix': '', diff --git a/tests/__snapshots__/test_import.ambr b/tests/__snapshots__/test_import.ambr index 16ec802b5..b1de86adc 100644 --- a/tests/__snapshots__/test_import.ambr +++ b/tests/__snapshots__/test_import.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_import_builder[test_app0] dict({ 'current_version': '1.0', @@ -30,8 +29,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Implementation for specification', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'IMPL_01', 'id_prefix': '', @@ -99,8 +98,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification of a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'OWN_ID_123', 'id_prefix': '', @@ -171,8 +170,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_001', 'id_prefix': '', @@ -240,8 +239,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test requirement 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', @@ -309,8 +308,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Sliced Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'ROLES_REQ_1', 'id_prefix': '', @@ -377,8 +376,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Butter on Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'ROLES_REQ_2', 'id_prefix': '', @@ -446,8 +445,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'R_22EB2', 'id_prefix': '', @@ -516,8 +515,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'R_2A9D0', 'id_prefix': '', @@ -590,8 +589,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'R_F4722', 'id_prefix': '', @@ -661,8 +660,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test specification 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SPEC_1', 'id_prefix': '', @@ -730,8 +729,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'S_01A67', 'id_prefix': '', @@ -806,8 +805,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Spec for a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'S_503A1', 'id_prefix': '', @@ -877,8 +876,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'S_D70B0', 'id_prefix': '', @@ -947,8 +946,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST IMPORT DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_01', 'id_prefix': '', @@ -1015,8 +1014,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'T_5CCAA', 'id_prefix': '', @@ -1088,8 +1087,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test for XY', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'T_C3893', 'id_prefix': '', @@ -1162,8 +1161,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Implementation for specification', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_IMPL_01', 'id_prefix': '', @@ -1235,8 +1234,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification of a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_OWN_ID_123', 'id_prefix': '', @@ -1311,8 +1310,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_REQ_001', 'id_prefix': '', @@ -1384,8 +1383,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Sliced Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_ROLES_REQ_1', 'id_prefix': '', @@ -1456,8 +1455,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Butter on Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_ROLES_REQ_2', 'id_prefix': '', @@ -1529,8 +1528,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_R_22EB2', 'id_prefix': '', @@ -1603,8 +1602,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_R_2A9D0', 'id_prefix': '', @@ -1681,8 +1680,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_R_F4722', 'id_prefix': '', @@ -1756,8 +1755,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_S_01A67', 'id_prefix': '', @@ -1836,8 +1835,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Spec for a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_S_503A1', 'id_prefix': '', @@ -1911,8 +1910,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_S_D70B0', 'id_prefix': '', @@ -1985,8 +1984,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST IMPORT DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_TEST_01', 'id_prefix': '', @@ -2057,8 +2056,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_T_5CCAA', 'id_prefix': '', @@ -2134,8 +2133,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test for XY', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'collapsed_T_C3893', 'id_prefix': '', @@ -2212,8 +2211,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Implementation for specification', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_IMPL_01', 'id_prefix': '', @@ -2284,8 +2283,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification of a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_OWN_ID_123', 'id_prefix': '', @@ -2359,8 +2358,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_REQ_001', 'id_prefix': '', @@ -2431,8 +2430,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Sliced Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_ROLES_REQ_1', 'id_prefix': '', @@ -2502,8 +2501,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Butter on Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_ROLES_REQ_2', 'id_prefix': '', @@ -2574,8 +2573,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_R_22EB2', 'id_prefix': '', @@ -2647,8 +2646,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_R_2A9D0', 'id_prefix': '', @@ -2724,8 +2723,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_R_F4722', 'id_prefix': '', @@ -2798,8 +2797,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_S_01A67', 'id_prefix': '', @@ -2877,8 +2876,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Spec for a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_S_503A1', 'id_prefix': '', @@ -2951,8 +2950,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_S_D70B0', 'id_prefix': '', @@ -3024,8 +3023,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST IMPORT DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_TEST_01', 'id_prefix': '', @@ -3094,8 +3093,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_T_5CCAA', 'id_prefix': '', @@ -3170,8 +3169,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test for XY', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'hidden_T_C3893', 'id_prefix': '', @@ -3246,8 +3245,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'AAA', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'small2_TEST_03', 'id_prefix': '', @@ -3314,8 +3313,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST_02 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'small_TEST_02', 'id_prefix': '', @@ -3384,8 +3383,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST_02 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'small_abs_path_TEST_02', 'id_prefix': '', @@ -3454,8 +3453,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST_01 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'small_depr_rel_path_TEST_01', 'id_prefix': '', @@ -3522,8 +3521,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST_01 DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'small_rel_path_TEST_01', 'id_prefix': '', @@ -3590,8 +3589,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Implementation for specification', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_IMPL_01', 'id_prefix': '', @@ -3662,8 +3661,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification of a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_OWN_ID_123', 'id_prefix': '', @@ -3737,8 +3736,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_REQ_001', 'id_prefix': '', @@ -3809,8 +3808,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Sliced Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_ROLES_REQ_1', 'id_prefix': '', @@ -3880,8 +3879,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Butter on Bread', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_ROLES_REQ_2', 'id_prefix': '', @@ -3952,8 +3951,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_R_22EB2', 'id_prefix': '', @@ -4025,8 +4024,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_R_2A9D0', 'id_prefix': '', @@ -4102,8 +4101,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'My first requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_R_F4722', 'id_prefix': '', @@ -4176,8 +4175,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification B', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_S_01A67', 'id_prefix': '', @@ -4255,8 +4254,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Spec for a requirement', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_S_503A1', 'id_prefix': '', @@ -4329,8 +4328,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Specification A', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_S_D70B0', 'id_prefix': '', @@ -4402,8 +4401,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST IMPORT DESCRIPTION', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_TEST_01', 'id_prefix': '', @@ -4473,8 +4472,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_T_5CCAA', 'id_prefix': '', @@ -4549,8 +4548,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test for XY', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'test_T_C3893', 'id_prefix': '', @@ -4635,8 +4634,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'TEST_101 TITLE', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'IMP_TEST_101', @@ -4714,8 +4713,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'A Spec', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'SPEC_1', @@ -4792,8 +4791,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'A story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'STORY_1', diff --git a/tests/__snapshots__/test_need_constraints.ambr b/tests/__snapshots__/test_need_constraints.ambr index 291821d6e..76a68ebe6 100644 --- a/tests/__snapshots__/test_need_constraints.ambr +++ b/tests/__snapshots__/test_need_constraints.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_need_constraints[test_app0] dict({ 'current_version': '', @@ -30,8 +29,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'test1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SECURITY_REQ', 'id_prefix': '', @@ -102,8 +101,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'test2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SP_109F4', 'id_prefix': '', @@ -177,8 +176,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'test3', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SP_3EBFA', 'id_prefix': '', @@ -250,8 +249,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'FAIL_01', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SP_CA3FB', 'id_prefix': '', @@ -318,8 +317,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Command line interface', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SP_TOO_001', 'id_prefix': '', @@ -393,8 +392,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'CLI', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'SP_TOO_002', 'id_prefix': '', @@ -468,8 +467,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'CLI', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_STYLE', 'id_prefix': '', @@ -540,8 +539,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'CLI2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TEST_STYLE2', 'id_prefix': '', diff --git a/tests/__snapshots__/test_needextend.ambr b/tests/__snapshots__/test_needextend.ambr index 5568efe93..6ef1d8f6f 100644 --- a/tests/__snapshots__/test_needextend.ambr +++ b/tests/__snapshots__/test_needextend.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_doc_needextend_dynamic[test_app0] dict({ 'current_version': '', @@ -30,8 +29,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', @@ -100,8 +99,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement A 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_A_1', 'id_prefix': '', @@ -168,8 +167,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement B 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_B_1', 'id_prefix': '', @@ -236,8 +235,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Requirement C 1', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'REQ_C_1', 'id_prefix': '', @@ -323,8 +322,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend Example 3', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_003', 'id_prefix': '', @@ -395,8 +394,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend Example 4', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_004', 'id_prefix': '', @@ -466,8 +465,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend Example 5', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_005', 'id_prefix': '', @@ -534,8 +533,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend Example 6', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_006', 'id_prefix': '', @@ -604,8 +603,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend Example 7', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_007', 'id_prefix': '', @@ -675,8 +674,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needextend different pages test', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'extend_test_page', 'id_prefix': '', diff --git a/tests/__snapshots__/test_needs_builder.ambr b/tests/__snapshots__/test_needs_builder.ambr index 948139ce4..e8ea71c72 100644 --- a/tests/__snapshots__/test_needs_builder.ambr +++ b/tests/__snapshots__/test_needs_builder.ambr @@ -29,8 +29,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_001', 'id_prefix': '', @@ -97,8 +97,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Negative test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', @@ -165,8 +165,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'A story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'US_63252', 'id_prefix': '', @@ -419,8 +419,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_001', 'id_prefix': '', @@ -487,8 +487,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Negative test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', @@ -555,8 +555,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'A story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'US_63252', 'id_prefix': '', diff --git a/tests/__snapshots__/test_needs_id_builder.ambr b/tests/__snapshots__/test_needs_id_builder.ambr index 9e45b666d..8eaa30b1f 100644 --- a/tests/__snapshots__/test_needs_id_builder.ambr +++ b/tests/__snapshots__/test_needs_id_builder.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_doc_needs_id_builder[test_app0] dict({ 'TC_001.json': dict({ @@ -31,8 +30,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_001', 'id_prefix': '', @@ -113,8 +112,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Negative test example', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', @@ -195,8 +194,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'A story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'US_63252', 'id_prefix': '', diff --git a/tests/__snapshots__/test_needuml.ambr b/tests/__snapshots__/test_needuml.ambr index a17713037..d18ba08a9 100644 --- a/tests/__snapshots__/test_needuml.ambr +++ b/tests/__snapshots__/test_needuml.ambr @@ -1,4 +1,3 @@ -# serializer version: 1 # name: test_doc_build_html[test_app0] dict({ 'COMP_001': dict({ @@ -33,8 +32,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test component', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'COMP_001', @@ -113,8 +112,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test interface', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'INT_001', @@ -226,8 +225,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test interface2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'INT_002', @@ -338,8 +337,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test interface3', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'INT_003', @@ -465,8 +464,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test interface4', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'INT_004', @@ -551,8 +550,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test Product', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'PROD_001', @@ -631,8 +630,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test story', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'ST_001', @@ -711,8 +710,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test story2', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'ST_002', @@ -800,8 +799,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Test System', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'hide': False, 'id': 'SYS_001', diff --git a/tests/__snapshots__/test_service_github.ambr b/tests/__snapshots__/test_service_github.ambr index afb6ca7a0..1dbcffc06 100644 --- a/tests/__snapshots__/test_service_github.ambr +++ b/tests/__snapshots__/test_service_github.ambr @@ -32,8 +32,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'Bump actions/cache from 3 to 4 (#1092)', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'GITHUB_050bec', 'id_prefix': '', @@ -107,8 +107,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': 'needreport usage should count needs in post-process', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'GITHUB_1110', 'id_prefix': '', @@ -205,8 +205,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': '👌 Capture `only` expressions for each need', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'GITHUB_1112', 'id_prefix': '', @@ -287,8 +287,8 @@ 'external_css': 'external_link', 'external_url': None, 'full_title': '🧪 Add test for `needreport` directive (#1105)', - 'has_dead_links': '', - 'has_forbidden_dead_links': '', + 'has_dead_links': False, + 'has_forbidden_dead_links': False, 'hidden': '', 'id': 'GITHUB_6abd38', 'id_prefix': '', From d162693602091f613b049b431f2cafcf2d451d71 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 21 Feb 2024 08:23:08 +0000 Subject: [PATCH 18/24] =?UTF-8?q?=F0=9F=94=A7=20Add=20better=20typing=20fo?= =?UTF-8?q?r=20`global=5Foptions`=20config=20variable=20(#1120)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sphinx_needs/api/need.py | 6 ++++-- sphinx_needs/config.py | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index c3d46aa90..5a4eb9d7c 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -23,7 +23,7 @@ NeedsTagNotAllowed, NeedsTemplateException, ) -from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.config import GlobalOptionsType, NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.directives.needuml import Needuml, NeedumlException from sphinx_needs.filter_common import filter_single_need @@ -797,7 +797,7 @@ def _merge_extra_options( def _merge_global_options( - app: Sphinx, needs_info: NeedsInfoType, global_options: dict[str, Any] + app: Sphinx, needs_info: NeedsInfoType, global_options: GlobalOptionsType ) -> None: """Add all global defined options to needs_info""" if global_options is None: @@ -817,7 +817,9 @@ def _merge_global_options( continue for single_value in values: + # TODO should first match break loop? if len(single_value) < 2 or len(single_value) > 3: + # TODO this should be validated earlier at the "config" level raise NeedsInvalidException( f"global option tuple has wrong amount of parameters: {key}" ) diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index 6a8808c87..f7bd685ff 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import MISSING, dataclass, field, fields -from typing import TYPE_CHECKING, Any, Callable, Literal, TypedDict +from typing import TYPE_CHECKING, Any, Callable, Dict, Literal, TypedDict from sphinx.application import Sphinx from sphinx.config import Config as _SphinxConfig @@ -101,6 +101,20 @@ class ExternalSource(TypedDict, total=False): """Added as the `external_css` field for each need item (optional)""" +GlobalOptionsType = Dict[str, Any] +"""Default values given to specified fields of needs + +Values can be: + +- a tuple: ``(value, filter_string)``, where the default is only applied if the filter_string is fulfilled +- a tuple: ``(value, filter_string, alternative default)``, + where the default is applied if the filter_string is fulfilled, + otherwise the alternative default is used +- a list of the tuples above +- otherwise, always set as the given value +""" + + @dataclass class NeedsSphinxConfig: """A wrapper around the Sphinx configuration, @@ -236,9 +250,10 @@ def __setattr__(self, name: str, value: Any) -> None: functions: list[Callable[..., Any]] = field( default_factory=list, metadata={"rebuild": "html", "types": (list,)} ) - global_options: dict[str, Any] = field( + global_options: GlobalOptionsType = field( default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} ) + """Default values given to specified fields of needs""" duration_option: str = field( default="duration", metadata={"rebuild": "html", "types": (str,)} ) From e142d24bc73be95ea1e4270fd2d7404372e1badf Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Wed, 21 Feb 2024 10:04:19 +0000 Subject: [PATCH 19/24] =?UTF-8?q?=F0=9F=94=A7=20Update=20pre-commit=20(#11?= =?UTF-8?q?21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- sphinx_needs/api/need.py | 2 +- sphinx_needs/config.py | 2 +- sphinx_needs/directives/needflow.py | 8 ++++---- sphinx_needs/utils.py | 8 ++------ 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a755133cc..97a1c2765 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.2.2 hooks: - id: ruff args: [--fix] diff --git a/pyproject.toml b/pyproject.toml index e089e1717..5d326fca0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ markers = [ "jstest: marks tests as JavaScript test (deselect with '-m \"not jstest\"')", ] -[tool.ruff] +[tool.ruff.lint] extend-select = [ "B", # flake8-bugbear "C4", # flake8-comprehensions diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index 5a4eb9d7c..f376257eb 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -805,7 +805,7 @@ def _merge_global_options( config = NeedsSphinxConfig(app.config) for key, value in global_options.items(): # If key already exists in needs_info, this global_option got overwritten manually in current need - if key in needs_info and needs_info[key]: + if needs_info.get(key): continue if isinstance(value, tuple): diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index f7bd685ff..cbfb9efa8 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -47,7 +47,7 @@ def extra_options(self) -> dict[str, Callable[[str], Any]]: @property def warnings( - self + self, ) -> dict[str, str | Callable[[NeedsInfoType, SphinxLoggerAdapter], bool]]: """Warning handlers that are added by the user, then called at the end of the build. diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 5f4df8d92..95856bcc2 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -398,7 +398,7 @@ def process_needflow( else: comment = "" - if "style_part" in link_type and link_type["style_part"]: + if link_type.get("style_part"): link_style = "[{style}]".format( style=link_type["style_part"] ) @@ -414,7 +414,7 @@ def process_needflow( else: comment = "" - if "style" in link_type and link_type["style"]: + if link_type.get("style"): link_style = "[{style}]".format( style=link_type["style"] ) @@ -429,12 +429,12 @@ def process_needflow( ]: continue - if "style_start" in link_type and link_type["style_start"]: + if link_type.get("style_start"): style_start = link_type["style_start"] else: style_start = "-" - if "style_end" in link_type and link_type["style_end"]: + if link_type.get("style_end"): style_end = link_type["style_end"] else: style_end = "->" diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index 502c9d734..d297eed86 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -576,11 +576,7 @@ def variant_handling( r"(:[\w':.\-\" ]+)$", variant_definition ) filter_string = re.sub(r"^\[|[:\]]$", "", filter_string) - filter_string = ( - needs_variants[filter_string] - if filter_string in needs_variants - else filter_string - ) + filter_string = needs_variants.get(filter_string, filter_string) try: # https://docs.python.org/3/library/functions.html?highlight=compile#compile filter_compiled = compile(filter_string, "", "eval") @@ -671,7 +667,7 @@ def clean_log(data: str) -> str: def node_match( - node_types: type[nodes.Element] | list[type[nodes.Element]] + node_types: type[nodes.Element] | list[type[nodes.Element]], ) -> Callable[[nodes.Node], bool]: """ Returns a condition function for doctuils.nodes.findall() From 98777ea96c19be5c09b1d8a38b76c042bd1d1323 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 22 Feb 2024 12:51:16 +0000 Subject: [PATCH 20/24] =?UTF-8?q?=F0=9F=94=A7=20Remove=20`hidden`=20field?= =?UTF-8?q?=20from=20`extra=5Foptions`=20(#1124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This field is never used, and is likely unintentional duplicate of `hide` (denoting a need that is hidden in the rendered documentation) --- docs/configuration.rst | 11 ---- docs/examples/needs.json | 2 - sphinx_needs/data.py | 1 - sphinx_needs/needs.py | 1 - tests/__snapshots__/test_basic_doc.ambr | 2 - tests/__snapshots__/test_export_id.ambr | 8 --- tests/__snapshots__/test_external.ambr | 5 -- tests/__snapshots__/test_import.ambr | 66 ------------------- .../__snapshots__/test_need_constraints.ambr | 8 --- tests/__snapshots__/test_needextend.ambr | 10 --- tests/__snapshots__/test_needs_builder.ambr | 12 ---- .../__snapshots__/test_needs_id_builder.ambr | 3 - tests/__snapshots__/test_needuml.ambr | 9 --- tests/__snapshots__/test_service_github.ambr | 4 -- .../needs_test_small.json | 3 - .../doc_needs_builder/custom_needs_test.json | 3 - .../needs.json | 3 - .../custom_needs_test.json | 3 - .../needs_test_small.json | 3 - .../needs_test_small.json | 3 - .../doc_test/doc_needsfile/needs_errors.json | 4 -- .../external_doc/needs_test_small.json | 3 - .../role_need_doc/needs_test_small.json | 3 - tests/test_needs_builder.py | 7 +- 24 files changed, 5 insertions(+), 172 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index 627060ff9..f3e225d22 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -155,17 +155,6 @@ And use it like: :tags: important;complex; :impacts: really everything -Default value: - -.. code-block:: python - - {'hidden': directives.unchanged} - -The ``hidden`` option is a globally available option always hidden and used to easily execute :ref:`dynamic_functions`. - -Extra options automatically appear in needs, if a value is set. -By using :ref:`needs_hide_options` the output of such options can be hidden. - .. note:: To filter on these options in `needlist`, `needtable`, etc. you must use the :ref:`filter` option. diff --git a/docs/examples/needs.json b/docs/examples/needs.json index 7e4310701..e9099425b 100644 --- a/docs/examples/needs.json +++ b/docs/examples/needs.json @@ -33,7 +33,6 @@ "github": "", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "test_req", "id_complete": "test_req", @@ -113,7 +112,6 @@ "github": "", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "xyz_123", "id_complete": "xyz_123", diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index c29d21ca7..e8042d909 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -177,7 +177,6 @@ class NeedsInfoType(TypedDict): # default extra options # TODO these all default to "" which I don't think is good - hidden: str duration: str completion: str # constraints: str # this is already set in create_need diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 3cfdc9f46..756acaea8 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -514,7 +514,6 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: # Own extra options for option in [ - "hidden", "duration", "completion", "constraints", diff --git a/tests/__snapshots__/test_basic_doc.ambr b/tests/__snapshots__/test_basic_doc.ambr index 545c59cb8..c387bedd5 100644 --- a/tests/__snapshots__/test_basic_doc.ambr +++ b/tests/__snapshots__/test_basic_doc.ambr @@ -31,7 +31,6 @@ 'full_title': 'Test story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'ST_001', 'id_prefix': '', 'is_external': False, @@ -99,7 +98,6 @@ 'full_title': 'No ID', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'US_38823', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_export_id.ambr b/tests/__snapshots__/test_export_id.ambr index 0c62e16ae..8c8be7c68 100644 --- a/tests/__snapshots__/test_export_id.ambr +++ b/tests/__snapshots__/test_export_id.ambr @@ -137,7 +137,6 @@ 'full_title': 'My requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_001', 'id_prefix': '', 'is_external': False, @@ -213,7 +212,6 @@ 'full_title': 'My requirement 2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_002', 'id_prefix': '', 'is_external': False, @@ -286,7 +284,6 @@ 'full_title': 'My requirement 3', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_003', 'id_prefix': '', 'is_external': False, @@ -359,7 +356,6 @@ 'full_title': 'My requirement 4', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_004', 'id_prefix': '', 'is_external': False, @@ -437,7 +433,6 @@ 'full_title': 'Req 5', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_005', 'id_prefix': '', 'is_external': False, @@ -541,7 +536,6 @@ 'full_title': 'Test of requirements', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_001', 'id_prefix': '', 'is_external': False, @@ -616,7 +610,6 @@ 'full_title': 'Test of requirements2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_002', 'id_prefix': '', 'is_external': False, @@ -691,7 +684,6 @@ 'full_title': 'Test of requirements 5', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_003', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_external.ambr b/tests/__snapshots__/test_external.ambr index 558593df2..a71aaa3f1 100644 --- a/tests/__snapshots__/test_external.ambr +++ b/tests/__snapshots__/test_external.ambr @@ -31,7 +31,6 @@ 'full_title': 'TEST_01 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'EXT_TEST_01', 'id_prefix': '', 'is_external': True, @@ -99,7 +98,6 @@ 'full_title': 'TEST_02 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'EXT_TEST_02', 'id_prefix': '', 'is_external': True, @@ -170,7 +168,6 @@ 'full_title': 'Test requirement 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', 'is_external': False, @@ -238,7 +235,6 @@ 'full_title': 'Test specification 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SPEC_1', 'id_prefix': '', 'is_external': False, @@ -308,7 +304,6 @@ 'full_title': 'Sub-Req', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SUB_002', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_import.ambr b/tests/__snapshots__/test_import.ambr index b1de86adc..a75c2c772 100644 --- a/tests/__snapshots__/test_import.ambr +++ b/tests/__snapshots__/test_import.ambr @@ -31,7 +31,6 @@ 'full_title': 'Implementation for specification', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'IMPL_01', 'id_prefix': '', 'is_external': False, @@ -100,7 +99,6 @@ 'full_title': 'Specification of a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'OWN_ID_123', 'id_prefix': '', 'is_external': False, @@ -172,7 +170,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_001', 'id_prefix': '', 'is_external': False, @@ -241,7 +238,6 @@ 'full_title': 'Test requirement 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', 'is_external': False, @@ -310,7 +306,6 @@ 'full_title': 'Sliced Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'ROLES_REQ_1', 'id_prefix': '', 'is_external': False, @@ -378,7 +373,6 @@ 'full_title': 'Butter on Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'ROLES_REQ_2', 'id_prefix': '', 'is_external': False, @@ -447,7 +441,6 @@ 'full_title': 'Requirement B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'R_22EB2', 'id_prefix': '', 'is_external': False, @@ -517,7 +510,6 @@ 'full_title': 'Requirement A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'R_2A9D0', 'id_prefix': '', 'is_external': False, @@ -591,7 +583,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'R_F4722', 'id_prefix': '', 'is_external': False, @@ -662,7 +653,6 @@ 'full_title': 'Test specification 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SPEC_1', 'id_prefix': '', 'is_external': False, @@ -731,7 +721,6 @@ 'full_title': 'Specification B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'S_01A67', 'id_prefix': '', 'is_external': False, @@ -807,7 +796,6 @@ 'full_title': 'Spec for a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'S_503A1', 'id_prefix': '', 'is_external': False, @@ -878,7 +866,6 @@ 'full_title': 'Specification A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'S_D70B0', 'id_prefix': '', 'is_external': False, @@ -948,7 +935,6 @@ 'full_title': 'TEST IMPORT DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_01', 'id_prefix': '', 'is_external': False, @@ -1016,7 +1002,6 @@ 'full_title': 'Test 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'T_5CCAA', 'id_prefix': '', 'is_external': False, @@ -1089,7 +1074,6 @@ 'full_title': 'Test for XY', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'T_C3893', 'id_prefix': '', 'is_external': False, @@ -1163,7 +1147,6 @@ 'full_title': 'Implementation for specification', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_IMPL_01', 'id_prefix': '', 'is_external': False, @@ -1236,7 +1219,6 @@ 'full_title': 'Specification of a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_OWN_ID_123', 'id_prefix': '', 'is_external': False, @@ -1312,7 +1294,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_REQ_001', 'id_prefix': '', 'is_external': False, @@ -1385,7 +1366,6 @@ 'full_title': 'Sliced Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_ROLES_REQ_1', 'id_prefix': '', 'is_external': False, @@ -1457,7 +1437,6 @@ 'full_title': 'Butter on Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_ROLES_REQ_2', 'id_prefix': '', 'is_external': False, @@ -1530,7 +1509,6 @@ 'full_title': 'Requirement B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_R_22EB2', 'id_prefix': '', 'is_external': False, @@ -1604,7 +1582,6 @@ 'full_title': 'Requirement A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_R_2A9D0', 'id_prefix': '', 'is_external': False, @@ -1682,7 +1659,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_R_F4722', 'id_prefix': '', 'is_external': False, @@ -1757,7 +1733,6 @@ 'full_title': 'Specification B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_S_01A67', 'id_prefix': '', 'is_external': False, @@ -1837,7 +1812,6 @@ 'full_title': 'Spec for a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_S_503A1', 'id_prefix': '', 'is_external': False, @@ -1912,7 +1886,6 @@ 'full_title': 'Specification A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_S_D70B0', 'id_prefix': '', 'is_external': False, @@ -1986,7 +1959,6 @@ 'full_title': 'TEST IMPORT DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_TEST_01', 'id_prefix': '', 'is_external': False, @@ -2058,7 +2030,6 @@ 'full_title': 'Test 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_T_5CCAA', 'id_prefix': '', 'is_external': False, @@ -2135,7 +2106,6 @@ 'full_title': 'Test for XY', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'collapsed_T_C3893', 'id_prefix': '', 'is_external': False, @@ -2213,7 +2183,6 @@ 'full_title': 'Implementation for specification', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_IMPL_01', 'id_prefix': '', 'is_external': False, @@ -2285,7 +2254,6 @@ 'full_title': 'Specification of a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_OWN_ID_123', 'id_prefix': '', 'is_external': False, @@ -2360,7 +2328,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_REQ_001', 'id_prefix': '', 'is_external': False, @@ -2432,7 +2399,6 @@ 'full_title': 'Sliced Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_ROLES_REQ_1', 'id_prefix': '', 'is_external': False, @@ -2503,7 +2469,6 @@ 'full_title': 'Butter on Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_ROLES_REQ_2', 'id_prefix': '', 'is_external': False, @@ -2575,7 +2540,6 @@ 'full_title': 'Requirement B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_R_22EB2', 'id_prefix': '', 'is_external': False, @@ -2648,7 +2612,6 @@ 'full_title': 'Requirement A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_R_2A9D0', 'id_prefix': '', 'is_external': False, @@ -2725,7 +2688,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_R_F4722', 'id_prefix': '', 'is_external': False, @@ -2799,7 +2761,6 @@ 'full_title': 'Specification B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_S_01A67', 'id_prefix': '', 'is_external': False, @@ -2878,7 +2839,6 @@ 'full_title': 'Spec for a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_S_503A1', 'id_prefix': '', 'is_external': False, @@ -2952,7 +2912,6 @@ 'full_title': 'Specification A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_S_D70B0', 'id_prefix': '', 'is_external': False, @@ -3025,7 +2984,6 @@ 'full_title': 'TEST IMPORT DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_TEST_01', 'id_prefix': '', 'is_external': False, @@ -3095,7 +3053,6 @@ 'full_title': 'Test 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_T_5CCAA', 'id_prefix': '', 'is_external': False, @@ -3171,7 +3128,6 @@ 'full_title': 'Test for XY', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'hidden_T_C3893', 'id_prefix': '', 'is_external': False, @@ -3247,7 +3203,6 @@ 'full_title': 'AAA', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'small2_TEST_03', 'id_prefix': '', 'is_external': False, @@ -3315,7 +3270,6 @@ 'full_title': 'TEST_02 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'small_TEST_02', 'id_prefix': '', 'is_external': False, @@ -3385,7 +3339,6 @@ 'full_title': 'TEST_02 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'small_abs_path_TEST_02', 'id_prefix': '', 'is_external': False, @@ -3455,7 +3408,6 @@ 'full_title': 'TEST_01 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'small_depr_rel_path_TEST_01', 'id_prefix': '', 'is_external': False, @@ -3523,7 +3475,6 @@ 'full_title': 'TEST_01 DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'small_rel_path_TEST_01', 'id_prefix': '', 'is_external': False, @@ -3591,7 +3542,6 @@ 'full_title': 'Implementation for specification', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_IMPL_01', 'id_prefix': '', 'is_external': False, @@ -3663,7 +3613,6 @@ 'full_title': 'Specification of a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_OWN_ID_123', 'id_prefix': '', 'is_external': False, @@ -3738,7 +3687,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_REQ_001', 'id_prefix': '', 'is_external': False, @@ -3810,7 +3758,6 @@ 'full_title': 'Sliced Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_ROLES_REQ_1', 'id_prefix': '', 'is_external': False, @@ -3881,7 +3828,6 @@ 'full_title': 'Butter on Bread', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_ROLES_REQ_2', 'id_prefix': '', 'is_external': False, @@ -3953,7 +3899,6 @@ 'full_title': 'Requirement B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_R_22EB2', 'id_prefix': '', 'is_external': False, @@ -4026,7 +3971,6 @@ 'full_title': 'Requirement A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_R_2A9D0', 'id_prefix': '', 'is_external': False, @@ -4103,7 +4047,6 @@ 'full_title': 'My first requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_R_F4722', 'id_prefix': '', 'is_external': False, @@ -4177,7 +4120,6 @@ 'full_title': 'Specification B', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_S_01A67', 'id_prefix': '', 'is_external': False, @@ -4256,7 +4198,6 @@ 'full_title': 'Spec for a requirement', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_S_503A1', 'id_prefix': '', 'is_external': False, @@ -4330,7 +4271,6 @@ 'full_title': 'Specification A', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_S_D70B0', 'id_prefix': '', 'is_external': False, @@ -4403,7 +4343,6 @@ 'full_title': 'TEST IMPORT DESCRIPTION', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_TEST_01', 'id_prefix': '', 'is_external': False, @@ -4474,7 +4413,6 @@ 'full_title': 'Test 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_T_5CCAA', 'id_prefix': '', 'is_external': False, @@ -4550,7 +4488,6 @@ 'full_title': 'Test for XY', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'test_T_C3893', 'id_prefix': '', 'is_external': False, @@ -4636,7 +4573,6 @@ 'full_title': 'TEST_101 TITLE', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'IMP_TEST_101', 'id_prefix': '', @@ -4715,7 +4651,6 @@ 'full_title': 'A Spec', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'SPEC_1', 'id_prefix': '', @@ -4793,7 +4728,6 @@ 'full_title': 'A story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'STORY_1', 'id_prefix': '', diff --git a/tests/__snapshots__/test_need_constraints.ambr b/tests/__snapshots__/test_need_constraints.ambr index 76a68ebe6..431d4353c 100644 --- a/tests/__snapshots__/test_need_constraints.ambr +++ b/tests/__snapshots__/test_need_constraints.ambr @@ -31,7 +31,6 @@ 'full_title': 'test1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SECURITY_REQ', 'id_prefix': '', 'is_external': False, @@ -103,7 +102,6 @@ 'full_title': 'test2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SP_109F4', 'id_prefix': '', 'is_external': False, @@ -178,7 +176,6 @@ 'full_title': 'test3', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SP_3EBFA', 'id_prefix': '', 'is_external': False, @@ -251,7 +248,6 @@ 'full_title': 'FAIL_01', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SP_CA3FB', 'id_prefix': '', 'is_external': False, @@ -319,7 +315,6 @@ 'full_title': 'Command line interface', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SP_TOO_001', 'id_prefix': '', 'is_external': False, @@ -394,7 +389,6 @@ 'full_title': 'CLI', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'SP_TOO_002', 'id_prefix': '', 'is_external': False, @@ -469,7 +463,6 @@ 'full_title': 'CLI', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_STYLE', 'id_prefix': '', 'is_external': False, @@ -541,7 +534,6 @@ 'full_title': 'CLI2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TEST_STYLE2', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_needextend.ambr b/tests/__snapshots__/test_needextend.ambr index 6ef1d8f6f..fd3df3987 100644 --- a/tests/__snapshots__/test_needextend.ambr +++ b/tests/__snapshots__/test_needextend.ambr @@ -31,7 +31,6 @@ 'full_title': 'Requirement 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_1', 'id_prefix': '', 'is_external': False, @@ -101,7 +100,6 @@ 'full_title': 'Requirement A 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_A_1', 'id_prefix': '', 'is_external': False, @@ -169,7 +167,6 @@ 'full_title': 'Requirement B 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_B_1', 'id_prefix': '', 'is_external': False, @@ -237,7 +234,6 @@ 'full_title': 'Requirement C 1', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'REQ_C_1', 'id_prefix': '', 'is_external': False, @@ -324,7 +320,6 @@ 'full_title': 'needextend Example 3', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_003', 'id_prefix': '', 'is_external': False, @@ -396,7 +391,6 @@ 'full_title': 'needextend Example 4', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_004', 'id_prefix': '', 'is_external': False, @@ -467,7 +461,6 @@ 'full_title': 'needextend Example 5', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_005', 'id_prefix': '', 'is_external': False, @@ -535,7 +528,6 @@ 'full_title': 'needextend Example 6', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_006', 'id_prefix': '', 'is_external': False, @@ -605,7 +597,6 @@ 'full_title': 'needextend Example 7', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_007', 'id_prefix': '', 'is_external': False, @@ -676,7 +667,6 @@ 'full_title': 'needextend different pages test', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'extend_test_page', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_needs_builder.ambr b/tests/__snapshots__/test_needs_builder.ambr index e8ea71c72..29690a5ba 100644 --- a/tests/__snapshots__/test_needs_builder.ambr +++ b/tests/__snapshots__/test_needs_builder.ambr @@ -31,7 +31,6 @@ 'full_title': 'Test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_001', 'id_prefix': '', 'is_external': False, @@ -99,7 +98,6 @@ 'full_title': 'Negative test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', 'is_external': False, @@ -167,7 +165,6 @@ 'full_title': 'A story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'US_63252', 'id_prefix': '', 'is_external': False, @@ -232,7 +229,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01', 'full_title': 'TEST_01 DESCRIPTION', - 'hidden': '', 'id': 'TEST_01', 'id_complete': 'TEST_01', 'id_parent': 'TEST_01', @@ -285,7 +281,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02', 'full_title': 'TEST_02 DESCRIPTION', - 'hidden': '', 'id': 'TEST_02', 'id_complete': 'TEST_02', 'id_parent': 'TEST_02', @@ -341,7 +336,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03', 'full_title': 'AAA', - 'hidden': '', 'id': 'TEST_03', 'id_complete': 'TEST_03', 'id_parent': 'TEST_03', @@ -421,7 +415,6 @@ 'full_title': 'Test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_001', 'id_prefix': '', 'is_external': False, @@ -489,7 +482,6 @@ 'full_title': 'Negative test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', 'is_external': False, @@ -557,7 +549,6 @@ 'full_title': 'A story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'US_63252', 'id_prefix': '', 'is_external': False, @@ -623,7 +614,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01', 'full_title': 'TEST_01 DESCRIPTION', - 'hidden': '', 'id': 'TEST_01', 'id_complete': 'TEST_01', 'id_parent': 'TEST_01', @@ -676,7 +666,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02', 'full_title': 'TEST_02 DESCRIPTION', - 'hidden': '', 'id': 'TEST_02', 'id_complete': 'TEST_02', 'id_parent': 'TEST_02', @@ -732,7 +721,6 @@ 'external_css': 'external_link', 'external_url': 'file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03', 'full_title': 'AAA', - 'hidden': '', 'id': 'TEST_03', 'id_complete': 'TEST_03', 'id_parent': 'TEST_03', diff --git a/tests/__snapshots__/test_needs_id_builder.ambr b/tests/__snapshots__/test_needs_id_builder.ambr index 8eaa30b1f..0a9e9b568 100644 --- a/tests/__snapshots__/test_needs_id_builder.ambr +++ b/tests/__snapshots__/test_needs_id_builder.ambr @@ -32,7 +32,6 @@ 'full_title': 'Test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_001', 'id_prefix': '', 'is_external': False, @@ -114,7 +113,6 @@ 'full_title': 'Negative test example', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'TC_NEG_001', 'id_prefix': '', 'is_external': False, @@ -196,7 +194,6 @@ 'full_title': 'A story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'US_63252', 'id_prefix': '', 'is_external': False, diff --git a/tests/__snapshots__/test_needuml.ambr b/tests/__snapshots__/test_needuml.ambr index d18ba08a9..0c322e9fe 100644 --- a/tests/__snapshots__/test_needuml.ambr +++ b/tests/__snapshots__/test_needuml.ambr @@ -34,7 +34,6 @@ 'full_title': 'Test component', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'COMP_001', 'id_complete': 'COMP_001', @@ -114,7 +113,6 @@ 'full_title': 'Test interface', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'INT_001', 'id_complete': 'INT_001', @@ -227,7 +225,6 @@ 'full_title': 'Test interface2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'INT_002', 'id_complete': 'INT_002', @@ -339,7 +336,6 @@ 'full_title': 'Test interface3', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'INT_003', 'id_complete': 'INT_003', @@ -466,7 +462,6 @@ 'full_title': 'Test interface4', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'INT_004', 'id_complete': 'INT_004', @@ -552,7 +547,6 @@ 'full_title': 'Test Product', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'PROD_001', 'id_complete': 'PROD_001', @@ -632,7 +626,6 @@ 'full_title': 'Test story', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'ST_001', 'id_complete': 'ST_001', @@ -712,7 +705,6 @@ 'full_title': 'Test story2', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'ST_002', 'id_complete': 'ST_002', @@ -801,7 +793,6 @@ 'full_title': 'Test System', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'hide': False, 'id': 'SYS_001', 'id_complete': 'SYS_001', diff --git a/tests/__snapshots__/test_service_github.ambr b/tests/__snapshots__/test_service_github.ambr index 1dbcffc06..5ae15c63d 100644 --- a/tests/__snapshots__/test_service_github.ambr +++ b/tests/__snapshots__/test_service_github.ambr @@ -34,7 +34,6 @@ 'full_title': 'Bump actions/cache from 3 to 4 (#1092)', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'GITHUB_050bec', 'id_prefix': '', 'is_external': False, @@ -109,7 +108,6 @@ 'full_title': 'needreport usage should count needs in post-process', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'GITHUB_1110', 'id_prefix': '', 'is_external': False, @@ -207,7 +205,6 @@ 'full_title': '👌 Capture `only` expressions for each need', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'GITHUB_1112', 'id_prefix': '', 'is_external': False, @@ -289,7 +286,6 @@ 'full_title': '🧪 Add test for `needreport` directive (#1105)', 'has_dead_links': False, 'has_forbidden_dead_links': False, - 'hidden': '', 'id': 'GITHUB_6abd38', 'id_prefix': '', 'is_external': False, diff --git a/tests/doc_test/doc_needimport_noindex/needs_test_small.json b/tests/doc_test/doc_needimport_noindex/needs_test_small.json index 8cc54de16..ab739d2aa 100644 --- a/tests/doc_test/doc_needimport_noindex/needs_test_small.json +++ b/tests/doc_test/doc_needimport_noindex/needs_test_small.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -113,7 +111,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needs_builder/custom_needs_test.json b/tests/doc_test/doc_needs_builder/custom_needs_test.json index 3be0ffb98..dd9c6b49f 100644 --- a/tests/doc_test/doc_needs_builder/custom_needs_test.json +++ b/tests/doc_test/doc_needs_builder/custom_needs_test.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -117,7 +115,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needs_builder_negative_tests/needs.json b/tests/doc_test/doc_needs_builder_negative_tests/needs.json index 3be0ffb98..dd9c6b49f 100644 --- a/tests/doc_test/doc_needs_builder_negative_tests/needs.json +++ b/tests/doc_test/doc_needs_builder_negative_tests/needs.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -117,7 +115,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needs_builder_parallel/custom_needs_test.json b/tests/doc_test/doc_needs_builder_parallel/custom_needs_test.json index 3be0ffb98..dd9c6b49f 100644 --- a/tests/doc_test/doc_needs_builder_parallel/custom_needs_test.json +++ b/tests/doc_test/doc_needs_builder_parallel/custom_needs_test.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -117,7 +115,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needs_external_needs/needs_test_small.json b/tests/doc_test/doc_needs_external_needs/needs_test_small.json index 8cc54de16..ab739d2aa 100644 --- a/tests/doc_test/doc_needs_external_needs/needs_test_small.json +++ b/tests/doc_test/doc_needs_external_needs/needs_test_small.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -113,7 +111,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needs_external_needs_with_target_url/needs_test_small.json b/tests/doc_test/doc_needs_external_needs_with_target_url/needs_test_small.json index 8cc54de16..ab739d2aa 100644 --- a/tests/doc_test/doc_needs_external_needs_with_target_url/needs_test_small.json +++ b/tests/doc_test/doc_needs_external_needs_with_target_url/needs_test_small.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -113,7 +111,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/doc_needsfile/needs_errors.json b/tests/doc_test/doc_needsfile/needs_errors.json index 2af544e08..061b7136e 100644 --- a/tests/doc_test/doc_needsfile/needs_errors.json +++ b/tests/doc_test/doc_needsfile/needs_errors.json @@ -31,7 +31,6 @@ "full_title": "Analysis issue", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "ACT_ANALYSIS", "id_complete": "ACT_ANALYSIS", @@ -106,7 +105,6 @@ "full_title": "Barks for support", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "ACT_BARKS", "id_complete": "ACT_BARKS", @@ -183,7 +181,6 @@ "full_title": "Find & Report bug", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "ACT_BUG", "id_complete": "ACT_BUG", @@ -258,7 +255,6 @@ "full_title": "Analyse bug", "has_dead_links": "", "has_forbidden_dead_links": "", - "hidden": "", "hours": "", "id": "ACT_BUG_ANALYSE", "id_complete": "ACT_BUG_ANALYSE", diff --git a/tests/doc_test/external_doc/needs_test_small.json b/tests/doc_test/external_doc/needs_test_small.json index 2af28027b..5ac982674 100644 --- a/tests/doc_test/external_doc/needs_test_small.json +++ b/tests/doc_test/external_doc/needs_test_small.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -117,7 +115,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/doc_test/role_need_doc/needs_test_small.json b/tests/doc_test/role_need_doc/needs_test_small.json index 1226f8ad7..a0192cec3 100644 --- a/tests/doc_test/role_need_doc/needs_test_small.json +++ b/tests/doc_test/role_need_doc/needs_test_small.json @@ -19,7 +19,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_01", "full_title": "TEST_01 DESCRIPTION", - "hidden": "", "id": "TEST_01", "id_complete": "TEST_01", "id_parent": "TEST_01", @@ -65,7 +64,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_02", "full_title": "TEST_02 DESCRIPTION", - "hidden": "", "id": "TEST_02", "id_complete": "TEST_02", "id_parent": "TEST_02", @@ -117,7 +115,6 @@ "external_css": "external_link", "external_url": "file:///home/daniel/workspace/sphinx/sphinxcontrib-needs/tests/doc_test/external_doc/__error__#TEST_03", "full_title": "AAA", - "hidden": "", "id": "TEST_03", "id_complete": "TEST_03", "id_parent": "TEST_03", diff --git a/tests/test_needs_builder.py b/tests/test_needs_builder.py index 61fa2c1d5..a53b568c8 100644 --- a/tests/test_needs_builder.py +++ b/tests/test_needs_builder.py @@ -77,9 +77,12 @@ def test_needs_html_and_json(test_app): srcdir = app.srcdir build_dir = os.path.join(app.outdir, "../needs") - subprocess.run( - ["sphinx-build", "-b", "needs", srcdir, build_dir], capture_output=True + print(build_dir) + output = subprocess.run( + ["python", "-m", "sphinx.cmd.build", "-b", "needs", srcdir, build_dir], + capture_output=True, ) + print(output) needs_json_path_2 = os.path.join(build_dir, "needs.json") assert os.path.exists(needs_json_path_2) From 9c8c721194ae32b018b34db787124dd9ac3f7b20 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 22 Feb 2024 12:58:37 +0000 Subject: [PATCH 21/24] =?UTF-8?q?=F0=9F=94=A7=20=20Remove=20`constraints`?= =?UTF-8?q?=20from=20`extra=5Foptions`=20(#1123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `constraints` is a core sphinx-need option, and so it is not necessary to add it to the `extra_options` --- sphinx_needs/data.py | 1 - sphinx_needs/directives/need.py | 2 ++ sphinx_needs/directives/needimport.py | 1 + sphinx_needs/external_needs.py | 2 +- sphinx_needs/needs.py | 1 - 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index e8042d909..f18cf2944 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -179,7 +179,6 @@ class NeedsInfoType(TypedDict): # TODO these all default to "" which I don't think is good duration: str completion: str - # constraints: str # this is already set in create_need # options from `BaseService.options` get added to every need, # via `ServiceManager.register`, which adds them to `extra_options`` # GithubService diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 9c2cd4ab2..d9ba3b24e 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -130,6 +130,7 @@ def run(self) -> Sequence[nodes.Node]: post_template = self.options.get("post_template") duration = self.options.get("duration") completion = self.options.get("completion") + constraints = self.options.get("constraints", []) need_extra_options = {"duration": duration, "completion": completion} for extra_link in self.needs_config.extra_links: @@ -160,6 +161,7 @@ def run(self) -> Sequence[nodes.Node]: layout=layout, delete=delete_opt, jinja_content=jinja_content, + constraints=constraints, **need_extra_options, ) add_doc(env, self.docname) diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index cf9d26cc3..487915c28 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -212,6 +212,7 @@ def run(self) -> Sequence[nodes.Node]: "style", "layout", "need_type", + "constraints", *[x["option"] for x in extra_links], *NEEDS_CONFIG.extra_options, ) diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index 47d34a5d2..706a8f96d 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -144,7 +144,7 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, _docname: str) -> No need_params["links"] = need.get("links", []) need_params["tags"] = ",".join(need.get("tags", [])) need_params["status"] = need.get("status") - need_params["constraints"] = ",".join(need.get("constraints", [])) + need_params["constraints"] = need.get("constraints", []) del need_params["description"] diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index 756acaea8..c165cd4ad 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -516,7 +516,6 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: for option in [ "duration", "completion", - "constraints", ]: # Check if not already set by user if option not in NEEDS_CONFIG.extra_options: From 3719cbd83c1cc44c2d8978f9f4c1a4e9d18d8964 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 23 Feb 2024 08:15:43 +0000 Subject: [PATCH 22/24] =?UTF-8?q?=F0=9F=94=A7=20Remove=20use=20of=20deprec?= =?UTF-8?q?ated=20`needs=5Fextra=5Foptions`=20as=20`dict`=20(#1126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/configuration.rst | 7 +------ docs/layout_styles.rst | 15 ++++++++------- tests/doc_test/add_sections/conf.py | 12 +++++------- tests/doc_test/doc_df_calc_sum/conf.py | 8 +------- tests/doc_test/doc_df_check_linked_values/conf.py | 4 +--- tests/doc_test/doc_df_user_functions/conf.py | 4 +--- tests/doc_test/doc_dynamic_functions/conf.py | 4 +--- tests/doc_test/doc_layout/conf.py | 7 +------ tests/doc_test/doc_needs_filter_data/conf.py | 6 +----- tests/doc_test/extra_options/conf.py | 8 +------- 10 files changed, 21 insertions(+), 54 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index f3e225d22..1914ce56c 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -165,12 +165,7 @@ And use it like: .. code-block:: python - from docutils.parsers.rst import directives - - needs_extra_options = { - "my_extra_option": directives.unchanged, - "another_option": directives.unchanged, - } + needs_extra_options = ["my_extra_option", "another_option"] **index.rst** diff --git a/docs/layout_styles.rst b/docs/layout_styles.rst index 0d0b613c1..37e70803f 100644 --- a/docs/layout_styles.rst +++ b/docs/layout_styles.rst @@ -307,16 +307,15 @@ The line for the ``side`` area could look like:: The used layout takes the value from ``author`` and puts some image-path related information around it. -Here is the complete used code:: +Here is the complete used code + +.. code-block:: python # conf.py # ------- - from docutils.parsers.rst import directives # Make the author option valid - needs_extra_options = { - "author": directives.unchanged, - } + needs_extra_options = ["author"] # Define own layout needs_layouts = { @@ -331,8 +330,10 @@ Here is the complete used code:: } } - # rst-code of the above need - # -------------------------- +.. code-block:: restructuredtext + + rst-code of the above need + -------------------------- .. spec:: My test spec :author: daniel diff --git a/tests/doc_test/add_sections/conf.py b/tests/doc_test/add_sections/conf.py index c684207da..ea5667392 100644 --- a/tests/doc_test/add_sections/conf.py +++ b/tests/doc_test/add_sections/conf.py @@ -1,12 +1,10 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] -needs_extra_options = { - "introduced": directives.unchanged, - "updated": directives.unchanged, - "impacts": directives.unchanged, -} +needs_extra_options = [ + "introduced", + "updated", + "impacts", +] needs_template_collapse = """ .. _{{id}}: diff --git a/tests/doc_test/doc_df_calc_sum/conf.py b/tests/doc_test/doc_df_calc_sum/conf.py index ee4795ac0..e98febfc9 100644 --- a/tests/doc_test/doc_df_calc_sum/conf.py +++ b/tests/doc_test/doc_df_calc_sum/conf.py @@ -1,5 +1,3 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] needs_types = [ @@ -33,8 +31,4 @@ }, ] -needs_extra_options = { - "test_func": directives.unchanged, - "hours": directives.unchanged, - "amount": directives.unchanged, -} +needs_extra_options = ["test_func", "hours", "amount"] diff --git a/tests/doc_test/doc_df_check_linked_values/conf.py b/tests/doc_test/doc_df_check_linked_values/conf.py index 2a93cda81..0b41a6348 100644 --- a/tests/doc_test/doc_df_check_linked_values/conf.py +++ b/tests/doc_test/doc_df_check_linked_values/conf.py @@ -1,5 +1,3 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] needs_types = [ @@ -33,4 +31,4 @@ }, ] -needs_extra_options = {"test_func": directives.unchanged, "hours": directives.unchanged} +needs_extra_options = ["test_func", "hours"] diff --git a/tests/doc_test/doc_df_user_functions/conf.py b/tests/doc_test/doc_df_user_functions/conf.py index 14945940d..20f8cea84 100644 --- a/tests/doc_test/doc_df_user_functions/conf.py +++ b/tests/doc_test/doc_df_user_functions/conf.py @@ -1,5 +1,3 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] needs_types = [ @@ -33,7 +31,7 @@ }, ] -needs_extra_options = {"test_func": directives.unchanged, "hours": directives.unchanged} +needs_extra_options = ["test_func", "hours"] def my_own_function(app, need, needs): diff --git a/tests/doc_test/doc_dynamic_functions/conf.py b/tests/doc_test/doc_dynamic_functions/conf.py index e474af668..111e1eab6 100644 --- a/tests/doc_test/doc_dynamic_functions/conf.py +++ b/tests/doc_test/doc_dynamic_functions/conf.py @@ -1,5 +1,3 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] needs_types = [ @@ -33,4 +31,4 @@ }, ] -needs_extra_options = {"test_func": directives.unchanged} +needs_extra_options = ["test_func"] diff --git a/tests/doc_test/doc_layout/conf.py b/tests/doc_test/doc_layout/conf.py index 899d6306c..c118c5817 100644 --- a/tests/doc_test/doc_layout/conf.py +++ b/tests/doc_test/doc_layout/conf.py @@ -1,5 +1,3 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] needs_types = [ @@ -33,10 +31,7 @@ }, ] -needs_extra_options = { - "author": directives.unchanged, -} - +needs_extra_options = ["author"] needs_layouts = { "example": { diff --git a/tests/doc_test/doc_needs_filter_data/conf.py b/tests/doc_test/doc_needs_filter_data/conf.py index 8c62e9c97..0addc00ad 100644 --- a/tests/doc_test/doc_needs_filter_data/conf.py +++ b/tests/doc_test/doc_needs_filter_data/conf.py @@ -1,8 +1,6 @@ import os import sys -from docutils.parsers.rst import directives - sys.path.insert(0, os.path.abspath("./")) extensions = ["sphinx_needs", "sphinxcontrib.plantuml"] @@ -49,9 +47,7 @@ def custom_func(): needs_filter_data = {"current_variant": "project_x", "sphinx_tag": custom_func()} -needs_extra_options = { - "variant": directives.unchanged, -} +needs_extra_options = ["variant"] needs_warnings = { "variant_not_equal_current_variant": "variant != current_variant", diff --git a/tests/doc_test/extra_options/conf.py b/tests/doc_test/extra_options/conf.py index 7518f91ca..323f4113d 100644 --- a/tests/doc_test/extra_options/conf.py +++ b/tests/doc_test/extra_options/conf.py @@ -1,12 +1,6 @@ -from docutils.parsers.rst import directives - extensions = ["sphinx_needs"] -needs_extra_options = { - "introduced": directives.unchanged, - "updated": directives.unchanged, - "impacts": directives.unchanged, -} +needs_extra_options = ["introduced", "updated", "impacts"] def setup(app): From eb81f2ebf1f35171e2268e93ed60a078e2063da1 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 23 Feb 2024 08:22:33 +0000 Subject: [PATCH 23/24] =?UTF-8?q?=F0=9F=91=8C=20Remove=20hard-coding=20of?= =?UTF-8?q?=20`completion`=20and=20`duration`=20need=20fields=20(#1127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These fields are used by `needgantt` but their name can be configured by `needs_duration_option` and `needs_completion_option`. So in this commit, we remove the hard-coding and dynamically use these configuration options to set what option/key names are added. --- sphinx_needs/config.py | 2 ++ sphinx_needs/data.py | 26 +++++++++++++------------- sphinx_needs/defaults.py | 2 -- sphinx_needs/directives/need.py | 4 +--- sphinx_needs/needs.py | 23 +++++++++-------------- 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index cbfb9efa8..df26d2fe8 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -257,9 +257,11 @@ def __setattr__(self, name: str, value: Any) -> None: duration_option: str = field( default="duration", metadata={"rebuild": "html", "types": (str,)} ) + """Added to options on need directives, and key on need data items, for use by ``needgantt``""" completion_option: str = field( default="completion", metadata={"rebuild": "html", "types": (str,)} ) + """Added to options on need directives, and key on need data items, for use by ``needgantt``""" needextend_strict: bool = field( default=True, metadata={"rebuild": "html", "types": (bool,)} ) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index f18cf2944..bf5d407e2 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -175,35 +175,35 @@ class NeedsInfoType(TypedDict): parent_need: str """Simply the first parent id""" - # default extra options - # TODO these all default to "" which I don't think is good - duration: str - completion: str - # options from `BaseService.options` get added to every need, - # via `ServiceManager.register`, which adds them to `extra_options`` - # GithubService + # Fields added dynamically by services: + # options from ``BaseService.options`` get added to ``NEEDS_CONFIG.extra_options``, + # via `ServiceManager.register`, + # which in turn means they are added to every need via ``add_need`` + # ``GithubService.options`` avatar: str closed_at: str created_at: str max_amount: str service: str specific: str - # _type: str # type is already set in create_need + ## type: str # although this is already an internal field updated_at: str user: str - # OpenNeedsService + # ``OpenNeedsService.options`` params: str prefix: str url_postfix: str - # shared GithubService and OpenNeedsService + # shared ``GithubService.options`` and ``OpenNeedsService.options`` max_content_lines: str id_prefix: str query: str url: str - # Note there are also: - # - dynamic default options that can be set by needs_extra_options config - # - dynamic global options that can be set by needs_global_options config + # Note there are also these dynamic keys: + # - items in ``needs_extra_options`` + ``needs_duration_option`` + ``needs_completion_option``, + # which get added to ``NEEDS_CONFIG.extra_options``, + # and in turn means they are added to every need via ``add_need`` (as strings) + # - keys in ``needs_global_options`` config are added to every need via ``add_need`` class NeedsPartsInfoType(NeedsInfoType): diff --git a/sphinx_needs/defaults.py b/sphinx_needs/defaults.py index 5445e2572..5df8eddb4 100644 --- a/sphinx_needs/defaults.py +++ b/sphinx_needs/defaults.py @@ -219,8 +219,6 @@ "template": directives.unchanged_required, "pre_template": directives.unchanged_required, "post_template": directives.unchanged_required, - "duration": directives.unchanged_required, - "completion": directives.unchanged_required, "constraints": directives.unchanged_required, "constraints_passed": directives.unchanged_required, "constraints_results": directives.unchanged_required, diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index d9ba3b24e..eb400ec37 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -128,11 +128,9 @@ def run(self) -> Sequence[nodes.Node]: template = self.options.get("template") pre_template = self.options.get("pre_template") post_template = self.options.get("post_template") - duration = self.options.get("duration") - completion = self.options.get("completion") constraints = self.options.get("constraints", []) - need_extra_options = {"duration": duration, "completion": completion} + need_extra_options = {} for extra_link in self.needs_config.extra_links: need_extra_options[extra_link["option"]] = self.options.get( extra_link["option"], "" diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index c165cd4ad..9b802e5e4 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -364,9 +364,8 @@ def load_config(app: Sphinx, *_args: Any) -> None: "Sphinx-Needs 0.7.2 is list. Please see docs for details." ) - extra_options = NEEDS_CONFIG.extra_options for option in needs_config.extra_options: - if option in extra_options: + if option in NEEDS_CONFIG.extra_options: LOGGER.warning( f'extra_option "{option}" already registered. [needs.config]', type="needs", @@ -374,6 +373,11 @@ def load_config(app: Sphinx, *_args: Any) -> None: ) NEEDS_CONFIG.extra_options[option] = directives.unchanged + # ensure options for ``needgantt`` functionality are added to the extra options + for option in (needs_config.duration_option, needs_config.completion_option): + if option not in NEEDS_CONFIG.extra_options: + NEEDS_CONFIG.extra_options[option] = directives.unchanged_required + # Get extra links and create a dictionary of needed options. extra_links_raw = needs_config.extra_links extra_links = {} @@ -384,8 +388,8 @@ def load_config(app: Sphinx, *_args: Any) -> None: title_from_content = needs_config.title_from_content # Update NeedDirective to use customized options - NeedDirective.option_spec.update(extra_options) - NeedserviceDirective.option_spec.update(extra_options) + NeedDirective.option_spec.update(NEEDS_CONFIG.extra_options) + NeedserviceDirective.option_spec.update(NEEDS_CONFIG.extra_options) # Update NeedDirective to use customized links NeedDirective.option_spec.update(extra_links) @@ -426,7 +430,7 @@ def load_config(app: Sphinx, *_args: Any) -> None: "-links_back": directives.flag, } ) - for key, value in extra_options.items(): + for key, value in NEEDS_CONFIG.extra_options.items(): NeedextendDirective.option_spec.update( { key: value, @@ -512,15 +516,6 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: for needs_func in needs_config.functions: register_func(needs_func) - # Own extra options - for option in [ - "duration", - "completion", - ]: - # Check if not already set by user - if option not in NEEDS_CONFIG.extra_options: - NEEDS_CONFIG.extra_options[option] = directives.unchanged - # The default link name. Must exist in all configurations. Therefore we set it here # for the user. common_links = [] From cf5598f7f026b01b706fba08d6b1d47c58c14ab9 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 23 Feb 2024 08:31:27 +0000 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=91=8C=20Improve=20warnings=20for?= =?UTF-8?q?=20invalid=20filters=20(add=20source=20location=20and=20subtype?= =?UTF-8?q?)=20(#1128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sphinx_needs/api/need.py | 16 +++++++--- sphinx_needs/builder.py | 12 +++++-- sphinx_needs/directives/needbar.py | 9 ++++-- sphinx_needs/directives/needextend.py | 15 ++++----- sphinx_needs/directives/needgantt.py | 14 ++++++-- sphinx_needs/directives/needpie.py | 9 ++++-- sphinx_needs/filter_common.py | 32 +++++++++++++------ sphinx_needs/functions/common.py | 6 +++- sphinx_needs/roles/need_count.py | 23 +++++++++++-- sphinx_needs/warnings.py | 5 ++- tests/doc_test/filter_doc/filter_all.rst | 15 +++++---- tests/doc_test/filter_doc/filter_no_needs.rst | 5 +-- tests/doc_test/filter_doc/filter_search.rst | 14 ++++---- tests/doc_test/filter_doc/filter_tags_or.rst | 8 +++-- tests/doc_test/filter_doc/index.rst | 25 +++++++++++---- tests/test_filter.py | 13 ++++++++ 16 files changed, 161 insertions(+), 60 deletions(-) diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index f376257eb..ec5c39f7a 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -153,8 +153,10 @@ def run(): if need_type not in configured_need_types: logger.warning( f"Couldn't create need {id}. Reason: The need-type (i.e. `{need_type}`) is not set " - "in the project's 'need_types' configuration in conf.py. [needs]", + "in the project's 'need_types' configuration in conf.py. [needs.add]", type="needs", + subtype="add", + location=(docname, lineno) if docname else None, ) for ntype in types: @@ -219,9 +221,11 @@ def run(): for i in range(len(tags)): if len(tags[i]) == 0 or tags[i].isspace(): logger.warning( - f"Scruffy tag definition found in need {need_id}. " - "Defined tag contains spaces only. [needs]", + f"Scruffy tag definition found in need {need_id!r}. " + "Defined tag contains spaces only. [needs.add]", type="needs", + subtype="add", + location=(docname, lineno) if docname else None, ) else: new_tags.append(tags[i]) @@ -256,9 +260,11 @@ def run(): for i in range(len(constraints)): if len(constraints[i]) == 0 or constraints[i].isspace(): logger.warning( - f"Scruffy tag definition found in need {need_id}. " - "Defined constraint contains spaces only. [needs]", + f"Scruffy constraint definition found in need {need_id!r}. " + "Defined constraint contains spaces only. [needs.add]", type="needs", + subtype="add", + location=(docname, lineno) if docname else None, ) else: new_constraints.append(constraints[i]) diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index b4ebea207..21114c091 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -85,7 +85,10 @@ def finish(self) -> None: filter_string = needs_config.builder_filter filtered_needs: list[NeedsInfoType] = filter_needs( - data.get_or_create_needs().values(), needs_config, filter_string + data.get_or_create_needs().values(), + needs_config, + filter_string, + append_warning="(from need_builder_filter)", ) for need in filtered_needs: @@ -182,7 +185,12 @@ def finish(self) -> None: filter_string = needs_config.builder_filter from sphinx_needs.filter_common import filter_needs - filtered_needs = filter_needs(needs, needs_config, filter_string) + filtered_needs = filter_needs( + needs, + needs_config, + filter_string, + append_warning="(from need_builder_filter)", + ) needs_build_json_per_id_path = needs_config.build_json_per_id_path needs_dir = os.path.join(self.outdir, needs_build_json_per_id_path) if not os.path.exists(needs_dir): diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index 598b425a8..f41c286f2 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -157,7 +157,10 @@ def run(self) -> Sequence[nodes.Node]: add_doc(env, env.docname) - return [targetnode, Needbar("")] + bar_node = Needbar("") + self.set_source_info(bar_node) + + return [targetnode, bar_node] # Algorithm: @@ -301,7 +304,9 @@ def process_needbar( if element.isdigit(): line_number.append(float(element)) else: - result = len(filter_needs(need_list, needs_config, element)) + result = len( + filter_needs(need_list, needs_config, element, location=node) + ) line_number.append(float(result)) local_data_number.append(line_number) diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 054d97f53..b123da3c6 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -98,15 +98,12 @@ def extend_needs_data( logger.info(error) continue else: - # a filter string - try: - found_needs = filter_needs( - all_needs.values(), needs_config, need_filter - ) - except NeedsInvalidFilter as e: - raise NeedsInvalidFilter( - f"Filter not valid for needextend on page {current_needextend['docname']}:\n{e}" - ) + found_needs = filter_needs( + all_needs.values(), + needs_config, + need_filter, + location=(current_needextend["docname"], current_needextend["lineno"]), + ) for found_need in found_needs: # Work in the stored needs, not on the search result diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index 5f8e8f3f1..aed1a52aa 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -124,7 +124,10 @@ def run(self) -> Sequence[nodes.Node]: add_doc(env, env.docname) - return [targetnode] + [Needgantt("")] + gantt_node = Needgantt("") + self.set_source_info(gantt_node) + + return [targetnode, gantt_node] def get_link_type_option(self, name: str, default: str = "") -> list[str]: link_types = [ @@ -244,10 +247,17 @@ def process_needgantt( complete_option = current_needgantt["completion_option"] complete = need[complete_option] # type: ignore[literal-required] if not (duration and duration.isdigit()): + need_location = ( + f" (located: {need['docname']}:{need['lineno']})" + if need["docname"] + else "" + ) logger.warning( "Duration not set or invalid for needgantt chart. " - "Need: {}. Duration: {} [needs]".format(need["id"], duration), + f"Need: {need['id']!r}{need_location}. Duration: {duration!r} [needs.gantt]", type="needs", + subtype="gantt", + location=node, ) duration = 1 gantt_element = "[{}] as [{}] lasts {} days\n".format( diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index 700c25247..a01eb032e 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -102,7 +102,10 @@ def run(self) -> Sequence[nodes.Node]: } add_doc(env, env.docname) - return [targetnode, Needpie("")] + pie_node = Needpie("") + self.set_source_info(pie_node) + + return [targetnode, pie_node] @measure_time("needpie") @@ -162,7 +165,9 @@ def process_needpie( if line.isdigit(): sizes.append(abs(float(line))) else: - result = len(filter_needs(need_list, needs_config, line)) + result = len( + filter_needs(need_list, needs_config, line, location=node) + ) sizes.append(result) elif current_needpie["filter_func"] and not content: try: diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 0ba740372..be4c02c99 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -9,6 +9,7 @@ from types import CodeType from typing import Any, Iterable, TypedDict, TypeVar +from docutils import nodes from docutils.parsers.rst import directives from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective @@ -180,7 +181,10 @@ def process_filters( found_needs_by_options.append(need_info) # Get need by filter string found_needs_by_string = filter_needs( - all_needs_incl_parts, needs_config, filter_data["filter"] + all_needs_incl_parts, + needs_config, + filter_data["filter"], + location=(filter_data["docname"], filter_data["lineno"]), ) # Make an intersection of both lists found_needs = intersection_of_need_results( @@ -190,7 +194,10 @@ def process_filters( # There is no other config as the one for filter string. # So we only need this result. found_needs = filter_needs( - all_needs_incl_parts, needs_config, filter_data["filter"] + all_needs_incl_parts, + needs_config, + filter_data["filter"], + location=(filter_data["docname"], filter_data["lineno"]), ) else: # Provides only a copy of needs to avoid data manipulations. @@ -296,6 +303,9 @@ def filter_needs( config: NeedsSphinxConfig, filter_string: None | str = "", current_need: NeedsInfoType | None = None, + *, + location: tuple[str, int | None] | nodes.Node | None = None, + append_warning: str = "", ) -> list[V]: """ Filters given needs based on a given filter string. @@ -305,6 +315,8 @@ def filter_needs( :param config: NeedsSphinxConfig object :param filter_string: strings, which gets evaluated against each need :param current_need: current need, which uses the filter. + :param location: source location for error reporting (docname, line number) + :param append_warning: additional text to append to any failed filter warning :return: list of found needs """ @@ -328,13 +340,15 @@ def filter_needs( ): found_needs.append(filter_need) except Exception as e: - if not error_reported: # Let's report a filter-problem only onces - location = ( - (current_need["docname"], current_need["lineno"]) - if current_need - else None + if not error_reported: # Let's report a filter-problem only once + if append_warning: + append_warning = f" {append_warning}" + log.warning( + f"{e}{append_warning} [needs.filter]", + type="needs", + subtype="filter", + location=location, ) - log.warning(str(e) + " [needs]", type="needs", location=location) error_reported = True return found_needs @@ -381,5 +395,5 @@ def filter_single_need( else: result = bool(eval(filter_string, filter_context)) except Exception as e: - raise NeedsInvalidFilter(f"Filter {filter_string} not valid. Error: {e}.") + raise NeedsInvalidFilter(f"Filter {filter_string!r} not valid. Error: {e}.") return result diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index 467743294..4ab57647f 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -166,7 +166,11 @@ def copy( if filter: result = filter_needs( - needs.values(), NeedsSphinxConfig(app.config), filter, need + needs.values(), + NeedsSphinxConfig(app.config), + filter, + need, + location=(need["docname"], need["lineno"]), ) if result: need = result[0] diff --git a/sphinx_needs/roles/need_count.py b/sphinx_needs/roles/need_count.py index 15275a090..0d6577181 100644 --- a/sphinx_needs/roles/need_count.py +++ b/sphinx_needs/roles/need_count.py @@ -37,11 +37,28 @@ def process_need_count( filters = filter.split(" ? ") if len(filters) == 1: need_list = prepare_need_list(all_needs) # adds parts to need_list - amount = str(len(filter_needs(need_list, needs_config, filters[0]))) + amount = str( + len( + filter_needs( + need_list, + needs_config, + filters[0], + location=node_need_count, + ) + ) + ) elif len(filters) == 2: need_list = prepare_need_list(all_needs) # adds parts to need_list - amount_1 = len(filter_needs(need_list, needs_config, filters[0])) - amount_2 = len(filter_needs(need_list, needs_config, filters[1])) + amount_1 = len( + filter_needs( + need_list, needs_config, filters[0], location=node_need_count + ) + ) + amount_2 = len( + filter_needs( + need_list, needs_config, filters[1], location=node_need_count + ) + ) amount = f"{amount_1 / amount_2 * 100:2.1f}" elif len(filters) > 2: raise NeedsInvalidFilter( diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 1f9a47c2c..658c67dfc 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -62,7 +62,10 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: if isinstance(warning_filter, str): # filter string used result = filter_needs( - checked_needs.values(), needs_config, warning_filter + checked_needs.values(), + needs_config, + warning_filter, + append_warning=f"(from warning filter {warning_name!r})", ) elif callable(warning_filter): # custom defined filter code used from conf.py diff --git a/tests/doc_test/filter_doc/filter_all.rst b/tests/doc_test/filter_doc/filter_all.rst index 4f9ae9d04..1cd55f491 100644 --- a/tests/doc_test/filter_doc/filter_all.rst +++ b/tests/doc_test/filter_doc/filter_all.rst @@ -2,30 +2,33 @@ filter_all ========== .. req:: req_a_not - :tags: 1; + :tags: 1 :status: open :hide: + :duration: 1 .. req:: req_b_found - :tags: 2; + :tags: 2 :status: closed .. req:: req_c_not - :tags: 1;2; + :tags: 1;2 :status: open :hide: + :duration: 1 .. req:: req_d_found - :tags: 1;2; + :tags: 1;2 :status: closed + :duration: 1 .. story:: story_1_not - :tags: 3; + :tags: 3 :status: none :hide: .. story:: story_2_found - :tags: 2; + :tags: 2 :status: none .. test:: my_test diff --git a/tests/doc_test/filter_doc/filter_no_needs.rst b/tests/doc_test/filter_doc/filter_no_needs.rst index 8cb606e82..40c7c006d 100644 --- a/tests/doc_test/filter_doc/filter_no_needs.rst +++ b/tests/doc_test/filter_doc/filter_no_needs.rst @@ -3,13 +3,14 @@ filter_no_needs .. req:: filter_warning_req_a :id: FILTER_001 - :tags: 1; + :tags: 1 :status: open :hide: + :duration: 1 .. req:: filter_warning_req_b :id: FILTER_002 - :tags: 2; + :tags: 2 :status: closed :hide: diff --git a/tests/doc_test/filter_doc/filter_search.rst b/tests/doc_test/filter_doc/filter_search.rst index dfb0c57d9..ce6613f85 100644 --- a/tests/doc_test/filter_doc/filter_search.rst +++ b/tests/doc_test/filter_doc/filter_search.rst @@ -3,40 +3,40 @@ filter_search .. req:: search_a :id: AAAA - :tags: filter_search; + :tags: filter_search :status: open .. req:: search_b :id: BBB - :tags: filter_search; + :tags: filter_search :status: closed :hide: .. req:: search_c :id: CCC - :tags: filter_search; + :tags: filter_search :status: open :hide: .. req:: search_d :id: DDD - :tags: filter_search; + :tags: filter_search :status: closed :hide: .. story:: search_2_1 :id: ABC - :tags: filter_search; + :tags: filter_search :status: none .. story:: search_2_2 :id: CBA - :tags: filter_search; + :tags: filter_search :status: none .. test:: test_email :id: TEST_123 - :tags: filter_search; + :tags: filter_search :status: none me@myemail.com diff --git a/tests/doc_test/filter_doc/filter_tags_or.rst b/tests/doc_test/filter_doc/filter_tags_or.rst index f7cc23b0f..85a21c455 100644 --- a/tests/doc_test/filter_doc/filter_tags_or.rst +++ b/tests/doc_test/filter_doc/filter_tags_or.rst @@ -5,13 +5,15 @@ Testing filter with and or -------------------------- .. req:: req_a - :tags: 1; + :tags: 1 + :duration: 1 .. req:: req_b - :tags: 2; + :tags: 2 .. req:: req_c - :tags: 1;2; + :tags: 1;2 + :duration: 1 .. needfilter:: :filter: "1" in tags or "2" in tags diff --git a/tests/doc_test/filter_doc/index.rst b/tests/doc_test/filter_doc/index.rst index ce43a9ff3..d5ab0643b 100644 --- a/tests/doc_test/filter_doc/index.rst +++ b/tests/doc_test/filter_doc/index.rst @@ -13,14 +13,14 @@ Testing simple filter --------------------- .. story:: story_a_1 - :tags: a; + :tags: a .. story:: story_b_1 - :tags: b; + :tags: b :hide: .. story:: story_a_b_1 - :tags: a;b; + :tags: a;b .. needfilter:: :filter: "a" in tags @@ -30,15 +30,28 @@ Testing filter with and or -------------------------- .. req:: req_a_1 - :tags: 1; + :tags: 1 :hide: + :duration: 1 .. req:: req_b_1 - :tags: 2; + :tags: 2 :hide: .. req:: req_c_1 - :tags: 1;2; + :tags: 1;2 + :duration: 1 .. needfilter:: :filter: "1" in tags and "2" in tags + +Testing bad filters +------------------- + +.. needfilter:: + :filter: xxx + +.. needlist:: + :filter: yyy + +:need_count:`zzz` diff --git a/tests/test_filter.py b/tests/test_filter.py index f8c44b00d..a1834d601 100644 --- a/tests/test_filter.py +++ b/tests/test_filter.py @@ -1,6 +1,7 @@ from pathlib import Path import pytest +from sphinx.util.console import strip_colors @pytest.mark.parametrize( @@ -11,6 +12,18 @@ def test_filter_build_html(test_app): app = test_app app.build() + + warnings = strip_colors( + app._warning.getvalue().replace(str(app.srcdir), "srcdir") + ).splitlines() + for w in warnings: + print(w) + assert warnings == [ + "srcdir/index.rst:51: WARNING: Filter 'xxx' not valid. Error: name 'xxx' is not defined. [needs.filter]", + "srcdir/index.rst:54: WARNING: Filter 'yyy' not valid. Error: name 'yyy' is not defined. [needs.filter]", + "srcdir/index.rst:57: WARNING: Filter 'zzz' not valid. Error: name 'zzz' is not defined. [needs.filter]", + ] + html = Path(app.outdir, "index.html").read_text() assert "story_a_1" in html assert "story_b_1" not in html