From 0e210b86c9d804b0057228efedebbada01690203 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 26 Aug 2024 11:25:07 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Move=20data=20from=20global=20en?= =?UTF-8?q?v=20to=20individual=20nodes=20(#1237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR changes logic for nearly all directives that require post-processing, so that all information coming from the directive is stored directly on the node in the doctree, rather than centrally in the `BuildEnvironment`. It is unnecessary to store this data centrally and it increases complexity (having to keep it in-sync) and the size of the `BuildEnvironment`. The only node I have not done this for is `needuml`, since there is some additional interplay between the `arch` need field, which I do not fully understand yet. --- sphinx_needs/data.py | 110 ------------------ sphinx_needs/directives/needbar.py | 17 ++- sphinx_needs/directives/needextract.py | 13 +-- sphinx_needs/directives/needfilter.py | 13 +-- sphinx_needs/directives/needflow.py | 11 +- sphinx_needs/directives/needgantt.py | 14 +-- sphinx_needs/directives/needlist.py | 12 +- sphinx_needs/directives/needpie.py | 16 ++- sphinx_needs/directives/needsequence.py | 12 +- sphinx_needs/directives/needtable.py | 16 +-- sphinx_needs/directives/needuml.py | 5 +- ...build_html_parallel[test_app0].doctree.xml | 2 +- 12 files changed, 63 insertions(+), 178 deletions(-) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 40329532a..d2c644322 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -736,17 +736,6 @@ def get_or_create_services(self) -> ServiceManager: self.env.app.needs_services = ServiceManager(self.env.app) return self.env.app.needs_services - def get_or_create_bars(self) -> dict[str, NeedsBarType]: - """Get all bar charts, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needbar - except AttributeError: - self.env.need_all_needbar = {} - return self.env.need_all_needbar - def get_or_create_extends(self) -> dict[str, NeedsExtendType]: """Get all need modifications, mapped by ID. @@ -758,96 +747,6 @@ def get_or_create_extends(self) -> dict[str, NeedsExtendType]: self.env.need_all_needextend = {} return self.env.need_all_needextend - def get_or_create_extracts(self) -> dict[str, NeedsExtractType]: - """Get all need extractions, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needextracts - except AttributeError: - self.env.need_all_needextracts = {} - return self.env.need_all_needextracts - - def _get_or_create_filters(self) -> dict[str, _NeedsFilterType]: - """Get all need filters, mapped by ID. - - .. deprecated:: 0.2.0 - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needfilters - except AttributeError: - self.env.need_all_needfilters = {} - return self.env.need_all_needfilters - - def get_or_create_flows(self) -> dict[str, NeedsFlowType]: - """Get all need flow charts, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needflows - except AttributeError: - self.env.need_all_needflows = {} - return self.env.need_all_needflows - - def get_or_create_gantts(self) -> dict[str, NeedsGanttType]: - """Get all need gantt charts, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needgantts - except AttributeError: - self.env.need_all_needgantts = {} - return self.env.need_all_needgantts - - def get_or_create_lists(self) -> dict[str, NeedsListType]: - """Get all need gantt charts, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needlists - except AttributeError: - self.env.need_all_needlists = {} - return self.env.need_all_needlists - - def get_or_create_pies(self) -> dict[str, NeedsPieType]: - """Get all need gantt charts, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needpie - except AttributeError: - self.env.need_all_needpie = {} - return self.env.need_all_needpie - - def get_or_create_sequences(self) -> dict[str, NeedsSequenceType]: - """Get all need sequence diagrams, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needsequences - except AttributeError: - self.env.need_all_needsequences = {} - return self.env.need_all_needsequences - - def get_or_create_tables(self) -> dict[str, NeedsTableType]: - """Get all need tables, mapped by ID. - - This is lazily created and cached in the environment. - """ - try: - return self.env.need_all_needtables - except AttributeError: - self.env.need_all_needtables = {} - return self.env.need_all_needtables - def get_or_create_umls(self) -> dict[str, NeedsUmlType]: """Get all need uml diagrams, mapped by ID. @@ -924,14 +823,5 @@ def _merge(name: str, is_complex_dict: bool = False) -> None: ) _merge("needs_all_docs", is_complex_dict=True) - _merge("need_all_needbar") _merge("need_all_needextend") - _merge("need_all_needextracts") - _merge("need_all_needfilters") - _merge("need_all_needflows") - _merge("need_all_needgantts") - _merge("need_all_needlists") - _merge("need_all_needpie") - _merge("need_all_needsequences") - _merge("need_all_needtables") _merge("needs_all_needumls") diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index f41c286f2..bc255ff12 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -9,7 +9,7 @@ from sphinx.application import Sphinx from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsBarType, SphinxNeedsData from sphinx_needs.filter_common import FilterBase, filter_needs, prepare_need_list from sphinx_needs.logging import get_logger from sphinx_needs.utils import ( @@ -127,9 +127,7 @@ def run(self) -> Sequence[nodes.Node]: transpose = "transpose" in self.options horizontal = "horizontal" in self.options - # 2. Stores infos for needbar - data = SphinxNeedsData(self.env).get_or_create_bars() - data[targetid] = { + data_attributes: NeedsBarType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -155,11 +153,11 @@ def run(self) -> Sequence[nodes.Node]: "text_color": text_color, } - add_doc(env, env.docname) - - bar_node = Needbar("") + bar_node = Needbar("", **data_attributes) self.set_source_info(bar_node) + add_doc(env, env.docname) + return [targetnode, bar_node] @@ -202,8 +200,7 @@ def process_needbar( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needbar = needs_data.get_or_create_bars()[id] + current_needbar: NeedsBarType = node.attributes if matplotlib is None: message = "Matplotlib missing for needbar plot" @@ -473,7 +470,7 @@ def process_needbar( # 9. final storage # We need to calculate an unique bar-image file name - hash_value = hashlib.sha256(id.encode()).hexdigest()[:5] + hash_value = hashlib.sha256(node.attributes["ids"][0].encode()).hexdigest()[:5] image_node = save_matplotlib_figure( app, figure, f"need_bar_{hash_value}", fromdocname ) diff --git a/sphinx_needs/directives/needextract.py b/sphinx_needs/directives/needextract.py index 9ceba1a09..7cbaefae0 100644 --- a/sphinx_needs/directives/needextract.py +++ b/sphinx_needs/directives/needextract.py @@ -10,7 +10,7 @@ from sphinx_needs.api.exceptions import NeedsInvalidFilter from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsExtractType, SphinxNeedsData from sphinx_needs.directives.utils import ( no_needs_found_paragraph, used_filter_paragraph, @@ -50,9 +50,7 @@ def run(self) -> Sequence[nodes.Node]: filter_arg = self.arguments[0] if self.arguments else None - # Add the need and all needed information - data = SphinxNeedsData(env).get_or_create_extracts() - data[targetid] = { + attributes: NeedsExtractType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -62,10 +60,12 @@ def run(self) -> Sequence[nodes.Node]: "filter_arg": filter_arg, **self.collect_filter_attributes(), } + node = Needextract("", **attributes) + self.set_source_info(node) add_doc(env, env.docname, "needextract") - return [targetnode, Needextract("")] + return [targetnode, node] def process_needextract( @@ -85,8 +85,7 @@ def process_needextract( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needextract = SphinxNeedsData(env).get_or_create_extracts()[id] + current_needextract: NeedsExtractType = node.attributes all_needs = SphinxNeedsData(env).get_or_create_needs() content: list[nodes.Element] = [] diff --git a/sphinx_needs/directives/needfilter.py b/sphinx_needs/directives/needfilter.py index 158743a7c..5fb20bb83 100644 --- a/sphinx_needs/directives/needfilter.py +++ b/sphinx_needs/directives/needfilter.py @@ -12,7 +12,7 @@ from sphinx.errors import NoUri from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import SphinxNeedsData, _NeedsFilterType from sphinx_needs.diagrams_common import create_legend from sphinx_needs.directives.utils import no_needs_found_paragraph from sphinx_needs.filter_common import FilterBase, process_filters @@ -55,9 +55,7 @@ def run(self) -> Sequence[nodes.Node]: ) targetnode = nodes.target("", "", ids=[targetid]) - # Add the need and all needed information - data = SphinxNeedsData(env)._get_or_create_filters() - data[targetid] = { + attributes: _NeedsFilterType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -68,10 +66,12 @@ def run(self) -> Sequence[nodes.Node]: "layout": self.options.get("layout", "list"), **self.collect_filter_attributes(), } + node = Needfilter("", **attributes) + self.set_source_info(node) add_doc(env, env.docname) - return [targetnode, Needfilter("")] + return [targetnode, node] def process_needfilters( @@ -94,8 +94,7 @@ def process_needfilters( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needfilter = SphinxNeedsData(env)._get_or_create_filters()[id] + current_needfilter: _NeedsFilterType = node.attributes content: nodes.Element | list[nodes.Element] if current_needfilter["layout"] == "list": diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 7194a05c0..79299533d 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -92,9 +92,7 @@ def run(self) -> Sequence[nodes.Node]: if config_name and config_name in needs_config.flow_configs: configs.append(needs_config.flow_configs[config_name]) - # Add the need and all needed information - data = SphinxNeedsData(env).get_or_create_flows() - data[targetid] = { + attributes: NeedsFlowType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -116,10 +114,12 @@ def run(self) -> Sequence[nodes.Node]: "caption": self.arguments[0] if self.arguments else None, **self.collect_filter_attributes(), } + node = Needflow("", **attributes) + self.set_source_info(node) add_doc(env, env.docname) - return [targetnode, Needflow("")] + return [targetnode, node] def make_entity_name(name: str) -> str: @@ -372,8 +372,7 @@ def process_needflow( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needflow = env_data.get_or_create_flows()[id] + current_needflow: NeedsFlowType = node.attributes option_link_types = [link.upper() for link in current_needflow["link_types"]] for lt in option_link_types: diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index 0f11bb267..b682a8a75 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -13,7 +13,7 @@ ) from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsGanttType, SphinxNeedsData from sphinx_needs.diagrams_common import ( DiagramBase, add_config, @@ -104,8 +104,7 @@ def run(self) -> Sequence[nodes.Node]: "completion_option", needs_config.completion_option ) - # Add the needgantt and all needed information - SphinxNeedsData(env).get_or_create_gantts()[targetid] = { + attributes: NeedsGanttType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -122,11 +121,11 @@ def run(self) -> Sequence[nodes.Node]: **self.collect_diagram_attributes(), } - add_doc(env, env.docname) - - gantt_node = Needgantt("") + gantt_node = Needgantt("", **attributes) self.set_source_info(gantt_node) + add_doc(env, env.docname) + return [targetnode, gantt_node] def get_link_type_option(self, name: str, default: str = "") -> list[str]: @@ -170,8 +169,7 @@ def process_needgantt( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needgantt = SphinxNeedsData(env).get_or_create_gantts()[id] + current_needgantt: NeedsGanttType = node.attributes all_needs_dict = SphinxNeedsData(env).get_or_create_needs() content = [] diff --git a/sphinx_needs/directives/needlist.py b/sphinx_needs/directives/needlist.py index 461c17f5d..336728a9e 100644 --- a/sphinx_needs/directives/needlist.py +++ b/sphinx_needs/directives/needlist.py @@ -7,7 +7,7 @@ from sphinx.application import Sphinx from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsListType, SphinxNeedsData from sphinx_needs.directives.utils import ( no_needs_found_paragraph, used_filter_paragraph, @@ -46,8 +46,7 @@ def run(self) -> Sequence[nodes.Node]: ) targetnode = nodes.target("", "", ids=[targetid]) - # Add the need and all needed information - SphinxNeedsData(env).get_or_create_lists()[targetid] = { + attributes: NeedsListType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -56,10 +55,12 @@ def run(self) -> Sequence[nodes.Node]: "show_filters": "show_filters" in self.options, **self.collect_filter_attributes(), } + list_node = Needlist("", **attributes) + self.set_source_info(list_node) add_doc(env, env.docname) - return [targetnode, Needlist("")] + return [targetnode, list_node] def process_needlist( @@ -82,8 +83,7 @@ def process_needlist( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needfilter = SphinxNeedsData(env).get_or_create_lists()[id] + current_needfilter: NeedsListType = node.attributes 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 69cf1c991..44f3b803f 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -8,7 +8,7 @@ from sphinx.application import Sphinx from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsPieType, SphinxNeedsData from sphinx_needs.debug import measure_time from sphinx_needs.directives.utils import no_needs_found_paragraph from sphinx_needs.filter_common import FilterBase, filter_needs, prepare_need_list @@ -83,8 +83,7 @@ def run(self) -> Sequence[nodes.Node]: shadow = "shadow" in self.options - # Stores infos for needpie - SphinxNeedsData(env).get_or_create_pies()[targetid] = { + attributes: NeedsPieType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -100,11 +99,11 @@ def run(self) -> Sequence[nodes.Node]: "filter_func": self.collect_filter_attributes()["filter_func"], "filter_warning": self.collect_filter_attributes()["filter_warning"], } - add_doc(env, env.docname) - - pie_node = Needpie("") + pie_node = Needpie("", **attributes) self.set_source_info(pie_node) + add_doc(env, env.docname) + return [targetnode, pie_node] @@ -137,8 +136,7 @@ def process_needpie( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needpie = needs_data.get_or_create_pies()[id] + current_needpie: NeedsPieType = node.attributes if matplotlib is None: message = "Matplotlib missing for needpie plot" @@ -296,7 +294,7 @@ def process_needpie( # Final storage # We need to calculate an unique pie-image file name - hash_value = hashlib.sha256(id.encode()).hexdigest()[:5] + hash_value = hashlib.sha256(node.attributes["ids"][0].encode()).hexdigest()[:5] image_node = save_matplotlib_figure( app, fig, f"need_pie_{hash_value}", fromdocname ) diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index bba1269dc..88ccafe70 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -12,7 +12,7 @@ ) from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import NeedsInfoType, SphinxNeedsData +from sphinx_needs.data import NeedsInfoType, NeedsSequenceType, SphinxNeedsData from sphinx_needs.diagrams_common import ( DiagramBase, add_config, @@ -61,8 +61,7 @@ def run(self) -> Sequence[nodes.Node]: f"See file {env.docname}:{self.lineno}" ) - # Add the needsequence and all needed information - SphinxNeedsData(env).get_or_create_sequences()[targetid] = { + attributes: NeedsSequenceType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -70,10 +69,12 @@ def run(self) -> Sequence[nodes.Node]: **self.collect_filter_attributes(), **self.collect_diagram_attributes(), } + node = Needsequence("", **attributes) + self.set_source_info(node) add_doc(env, env.docname) - return [targetnode] + [Needsequence("")] + return [targetnode, node] def process_needsequence( @@ -99,8 +100,7 @@ def process_needsequence( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needsequence = needs_data.get_or_create_sequences()[id] + current_needsequence: NeedsSequenceType = node.attributes option_link_types = [ link.upper() for link in current_needsequence["link_types"] diff --git a/sphinx_needs/directives/needtable.py b/sphinx_needs/directives/needtable.py index 629e96708..b77fcbfa7 100644 --- a/sphinx_needs/directives/needtable.py +++ b/sphinx_needs/directives/needtable.py @@ -9,7 +9,7 @@ from sphinx_needs.api.exceptions import NeedsInvalidException from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import NeedsInfoType, SphinxNeedsData +from sphinx_needs.data import NeedsInfoType, NeedsTableType, SphinxNeedsData from sphinx_needs.debug import measure_time from sphinx_needs.directives.utils import ( get_option_list, @@ -92,8 +92,7 @@ def run(self) -> Sequence[nodes.Node]: if self.arguments: title = self.arguments[0] - # Add the need and all needed information - SphinxNeedsData(env).get_or_create_tables()[targetid] = { + attributes: NeedsTableType = { "docname": env.docname, "lineno": self.lineno, "target_id": targetid, @@ -111,10 +110,12 @@ def run(self) -> Sequence[nodes.Node]: "show_parts": self.options.get("show_parts", False) is None, **self.collect_filter_attributes(), } + node = Needtable("", **attributes) + self.set_source_info(node) add_doc(env, env.docname) - return [targetnode] + [Needtable("")] + return [targetnode, node] @measure_time("needtable") @@ -154,8 +155,7 @@ def process_needtables( remove_node_from_tree(node) continue - id = node.attributes["ids"][0] - current_needtable = needs_data.get_or_create_tables()[id] + current_needtable: NeedsTableType = node.attributes if current_needtable["style"] == "" or current_needtable[ "style" @@ -179,7 +179,9 @@ def process_needtables( if style != "TABLE": classes.extend(needs_config.table_classes) - table_node = nodes.table(classes=classes, ids=[id + "-table_node"]) + table_node = nodes.table( + classes=classes, ids=[node.attributes["ids"][0] + "-table_node"] + ) tgroup = nodes.tgroup(cols=len(current_needtable["columns"])) # Define Table column width diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index ba9c35d99..20b6797ff 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -129,7 +129,10 @@ def run(self) -> Sequence[nodes.Node]: add_doc(env, env.docname) - return [targetnode] + [Needuml(targetid)] + node = Needuml(targetid) + self.set_source_info(node) + + return [targetnode, node] class NeedarchDirective(NeedumlDirective): diff --git a/tests/__snapshots__/test_basic_doc/test_build_html_parallel[test_app0].doctree.xml b/tests/__snapshots__/test_basic_doc/test_build_html_parallel[test_app0].doctree.xml index 1ea1a6d60..369ab7c58 100644 --- a/tests/__snapshots__/test_basic_doc/test_build_html_parallel[test_app0].doctree.xml +++ b/tests/__snapshots__/test_basic_doc/test_build_html_parallel[test_app0].doctree.xml @@ -9,4 +9,4 @@ - +