diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index d9c88ce24..645da4a3e 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -28,7 +28,7 @@ from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.directives.needuml import Needuml, NeedumlException from sphinx_needs.filter_common import filter_single_need -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.nodes import Need from sphinx_needs.roles.need_part import find_parts, update_need_with_parts from sphinx_needs.utils import jinja_parse @@ -167,11 +167,11 @@ def run(): # Log messages for need elements that could not be imported. configured_need_types = [ntype["directive"] for ntype in types] if need_type not in configured_need_types: - logger.warning( + log_warning( + logger, 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.add]", - type="needs", - subtype="add", + "in the project's 'need_types' configuration in conf.py.", + "add", location=(docname, lineno) if docname else None, ) @@ -243,11 +243,11 @@ def run(): new_tags = [] # Shall contain only valid tags for i in range(len(tags)): if len(tags[i]) == 0 or tags[i].isspace(): - logger.warning( + log_warning( + logger, f"Scruffy tag definition found in need {need_id!r}. " - "Defined tag contains spaces only. [needs.add]", - type="needs", - subtype="add", + "Defined tag contains spaces only.", + "add", location=(docname, lineno) if docname else None, ) else: @@ -282,11 +282,11 @@ def run(): new_constraints = [] # Shall contain only valid constraints for i in range(len(constraints)): if len(constraints[i]) == 0 or constraints[i].isspace(): - logger.warning( + log_warning( + logger, f"Scruffy constraint definition found in need {need_id!r}. " - "Defined constraint contains spaces only. [needs.add]", - type="needs", - subtype="add", + "Defined constraint contains spaces only.", + "add", location=(docname, lineno) if docname else None, ) else: @@ -325,10 +325,10 @@ def run(): "from the content, then ensure the first sentence of the " "requirements are different." ) - logger.warning( - message + " [needs.duplicate_id]", - type="needs", - subtype="duplicate_id", + log_warning( + logger, + message, + "duplicate_id", location=(docname, lineno) if docname else None, ) return [] @@ -613,7 +613,7 @@ def del_need(app: Sphinx, need_id: str) -> None: if need_id in needs: del needs[need_id] else: - logger.warning(f"Given need id {need_id} not exists! [needs]", type="needs") + log_warning(logger, f"Given need id {need_id} not exists!", None, None) def add_external_need( @@ -715,10 +715,12 @@ def _read_in_links(links_string: None | str | list[str]) -> list[str]: link_list = links_string for link in link_list: if link.isspace(): - logger.warning( + log_warning( + logger, f"Grubby link definition found in need {id}. " - "Defined link contains spaces only. [needs]", - type="needs", + "Defined link contains spaces only.", + None, + None, ) else: links.append(link.strip()) diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index 092fc7a38..8ecd729a5 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -11,7 +11,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.directives.need import post_process_needs_data -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.needsfile import NeedsList LOGGER = get_logger(__name__) @@ -49,10 +49,11 @@ def write( ) -> None: if not SphinxNeedsData(self.env).has_export_filters: return - LOGGER.warning( + log_warning( + LOGGER, "At least one use of `export_id` directive option, requires a slower build", - type="needs", - subtype="build", + "build", + None, ) return super().write(build_docnames, updated_docnames, method) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 05bc0bd4d..7761f9fef 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -8,6 +8,8 @@ from sphinx.util.logging import getLogger +from sphinx_needs.logging import log_warning + if TYPE_CHECKING: from docutils.nodes import Element, Text from sphinx.application import Sphinx @@ -784,10 +786,10 @@ def merge_data( f"A need with ID {other_id} already exists, " f"title: {other_need['title']!r}." ) - LOGGER.warning( - message + " [needs.duplicate_id]", - type="needs", - subtype="duplicate_id", + log_warning( + LOGGER, + message, + "duplicate_id", location=(_docname, other_need["lineno"]) if _docname else None, ) else: diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index c58d4e31a..f69444fdb 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -26,7 +26,7 @@ ) from sphinx_needs.functions.functions import check_and_get_content from sphinx_needs.layout import build_need -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.need_constraints import process_constraints from sphinx_needs.nodes import Need from sphinx_needs.utils import ( @@ -153,10 +153,11 @@ 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( + log_warning( + LOGGER, f"Grubby link definition found in need '{self.trimmed_title}'. " - "Defined link contains spaces only. [needs]", - type="needs", + "Defined link contains spaces only.", + None, location=(self.env.docname, self.lineno), ) else: @@ -210,10 +211,11 @@ def _get_full_title(self) -> str: `:title_from_content:` was set, and '' if no title is to be derived).""" if len(self.arguments) > 0: # a title was passed if "title_from_content" in self.options: - self.log.warning( + log_warning( + self.log, f'need "{self.arguments[0]}" has :title_from_content: set, ' - f"but a title was provided. (see file {self.docname}) [needs]", - type="needs", + f"but a title was provided. (see file {self.docname})", + None, location=(self.env.docname, self.lineno), ) return self.arguments[0] # type: ignore[no-any-return] @@ -471,17 +473,18 @@ def check_links(needs: dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> N # 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", + log_warning( + LOGGER, + f"{need['external_url']}: {message}", + "external_link_outgoing", + None, ) else: - LOGGER.warning( - f"{message} [needs.link_outgoing]", + log_warning( + LOGGER, + message, + "link_outgoing", location=(need["docname"], need["lineno"]), - type="needs", - subtype="link_outgoing", ) diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index bc255ff12..d3f16f4bc 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -11,7 +11,7 @@ from sphinx_needs.config import NeedsSphinxConfig 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.logging import get_logger, log_warning from sphinx_needs.utils import ( add_doc, import_matplotlib, @@ -185,12 +185,13 @@ def process_needbar( matplotlib = import_matplotlib() if matplotlib is None and found_nodes and needs_config.include_needs: - logger.warning( + log_warning( + logger, "Matplotlib is not installed and required by needbar. " - "Install with `sphinx-needs[plotting]` to use. [needs.mpl]", + "Install with `sphinx-needs[plotting]` to use.", + "mpl", + None, once=True, - type="needs", - subtype="mpl", ) # NEEDFLOW diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 8dd10debd..a6e84364c 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -11,7 +11,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsExtendType, NeedsInfoType, SphinxNeedsData from sphinx_needs.filter_common import filter_needs -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import add_doc logger = get_logger(__name__) @@ -112,14 +112,14 @@ def extend_needs_data( needs_config.id_regex, need_filter ): # an unknown ID - error = f"Provided id {need_filter!r} for needextend does not exist. [needs.extend]" + error = f"Provided id {need_filter!r} for needextend does not exist." if current_needextend["strict"]: raise NeedsInvalidFilter(error) else: - logger.warning( + log_warning( + logger, error, - type="needs", - subtype="extend", + "extend", location=( current_needextend["docname"], current_needextend["lineno"], @@ -139,10 +139,10 @@ def extend_needs_data( ), ) except NeedsInvalidFilter as e: - logger.warning( - f"Invalid filter {need_filter!r}: {e} [needs.extend]", - type="needs", - subtype="extend", + log_warning( + logger, + f"Invalid filter {need_filter!r}: {e}", + "extend", location=( current_needextend["docname"], current_needextend["lineno"], @@ -162,9 +162,10 @@ def extend_needs_data( if option_name in link_names: for item, is_function in _split_value(value): if (not is_function) and (item not in all_needs): - logger.warning( - f"Provided link id {item} for needextend does not exist. [needs]", - type="needs", + log_warning( + logger, + f"Provided link id {item} for needextend does not exist.", + None, location=( current_needextend["docname"], current_needextend["lineno"], @@ -196,9 +197,10 @@ def extend_needs_data( need[option] = [] for item, is_function in _split_value(value): if (not is_function) and (item not in all_needs): - logger.warning( - f"Provided link id {item} for needextend does not exist. [needs]", - type="needs", + log_warning( + logger, + f"Provided link id {item} for needextend does not exist.", + None, location=( current_needextend["docname"], current_needextend["lineno"], diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 79299533d..3e13e7258 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -22,7 +22,7 @@ from sphinx_needs.diagrams_common import calculate_link, create_legend from sphinx_needs.directives.utils import no_needs_found_paragraph from sphinx_needs.filter_common import FilterBase, filter_single_need, process_filters -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import ( add_doc, get_scale, @@ -377,13 +377,15 @@ def process_needflow( option_link_types = [link.upper() for link in current_needflow["link_types"]] for lt in option_link_types: if lt not in link_type_names: - logger.warning( - "Unknown link type {link_type} in needflow {flow}. Allowed values: {link_types} [needs]".format( + log_warning( + logger, + "Unknown link type {link_type} in needflow {flow}. Allowed values: {link_types}".format( link_type=lt, flow=current_needflow["target_id"], link_types=",".join(link_type_names), ), - type="needs", + None, + None, ) # compute the allowed link names diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index b682a8a75..6df1c191d 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -27,7 +27,7 @@ no_needs_found_paragraph, ) from sphinx_needs.filter_common import FilterBase, filter_single_need, process_filters -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import MONTH_NAMES, add_doc, remove_node_from_tree logger = get_logger(__name__) @@ -250,11 +250,11 @@ def process_needgantt( if need["docname"] else "" ) - logger.warning( + log_warning( + logger, "Duration not set or invalid for needgantt chart. " - f"Need: {need['id']!r}{need_location}. Duration: {duration!r} [needs.gantt]", - type="needs", - subtype="gantt", + f"Need: {need['id']!r}{need_location}. Duration: {duration!r}", + "gantt", location=node, ) duration = 1 diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index 1f5ecd745..72828bdd0 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -18,6 +18,7 @@ from sphinx_needs.debug import measure_time from sphinx_needs.defaults import string_to_boolean from sphinx_needs.filter_common import filter_single_need +from sphinx_needs.logging import log_warning from sphinx_needs.needsfile import check_needs_file from sphinx_needs.utils import add_doc, logger @@ -95,11 +96,12 @@ def run(self) -> Sequence[nodes.Node]: ) if os.path.exists(old_need_import_path): correct_need_import_path = old_need_import_path - logger.warning( + log_warning( + logger, "Deprecation warning: Relative path must be relative to the current document in future, " "not to the conf.py location. Use a starting '/', like '/needs.json', to make the path " - "relative to conf.py. [needs]", - type="needs", + "relative to conf.py.", + None, location=(self.env.docname, self.lineno), ) else: @@ -172,9 +174,10 @@ def run(self) -> Sequence[nodes.Node]: if filter_single_need(filter_context, needs_config, filter_string): needs_list_filtered[key] = need except Exception as e: - logger.warning( - f"needimport: Filter {filter_string} not valid. Error: {e}. {self.docname}{self.lineno} [needs]", - type="needs", + log_warning( + logger, + f"needimport: Filter {filter_string} not valid. Error: {e}. {self.docname}{self.lineno}", + None, location=(self.env.docname, self.lineno), ) diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index 44f3b803f..5a2bda1d4 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -12,7 +12,7 @@ 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 -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import ( add_doc, check_and_get_external_filter_func, @@ -121,12 +121,13 @@ def process_needpie( matplotlib = import_matplotlib() if matplotlib is None and found_nodes and needs_config.include_needs: - logger.warning( + log_warning( + logger, "Matplotlib is not installed and required by needpie. " - "Install with `sphinx-needs[plotting]` to use. [needs.mpl]", + "Install with `sphinx-needs[plotting]` to use.", + "mpl", + None, once=True, - type="needs", - subtype="mpl", ) # NEEDFLOW diff --git a/sphinx_needs/directives/needreport.py b/sphinx_needs/directives/needreport.py index 516a70abe..b35933277 100644 --- a/sphinx_needs/directives/needreport.py +++ b/sphinx_needs/directives/needreport.py @@ -11,6 +11,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.directives.utils import analyse_needs_metrics +from sphinx_needs.logging import log_warning from sphinx_needs.utils import add_doc LOGGER = logging.getLogger(__name__) @@ -31,11 +32,11 @@ def run(self) -> Sequence[nodes.raw]: needs_config = NeedsSphinxConfig(env.config) if not set(self.options).intersection({"types", "links", "options", "usage"}): - LOGGER.warning( - "No options specified to generate need report [needs.report]", + log_warning( + LOGGER, + "No options specified to generate need report", + "report", location=self.get_location(), - type="needs", - subtype="report", ) return [] @@ -63,11 +64,11 @@ def run(self) -> Sequence[nodes.raw]: ) if not need_report_template_path.is_file(): - LOGGER.warning( - f"Could not load needs report template file {need_report_template_path} [needs.report]", + log_warning( + LOGGER, + f"Could not load needs report template file {need_report_template_path}", + "report", location=self.get_location(), - type="needs", - subtype="report", ) return [] diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index 88ccafe70..cc604c286 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -23,7 +23,7 @@ ) from sphinx_needs.directives.utils import no_needs_found_paragraph from sphinx_needs.filter_common import FilterBase -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import add_doc, remove_node_from_tree logger = get_logger(__name__) @@ -107,14 +107,16 @@ def process_needsequence( ] for lt in option_link_types: if lt not in link_type_names: - logger.warning( + log_warning( + logger, "Unknown link type {link_type} in needsequence {flow}. Allowed values:" - " {link_types} [needs]".format( + " {link_types}".format( link_type=lt, flow=current_needsequence["target_id"], link_types=",".join(link_type_names), ), - type="needs", + None, + None, ) content = [] diff --git a/sphinx_needs/environment.py b/sphinx_needs/environment.py index 5724c385e..aae33af5d 100644 --- a/sphinx_needs/environment.py +++ b/sphinx_needs/environment.py @@ -9,6 +9,7 @@ from sphinx.util.fileutil import copy_asset, copy_asset_file from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.logging import log_warning from sphinx_needs.utils import logger _STATIC_DIR_NAME = "_static" @@ -69,10 +70,11 @@ def install_styles_static_files(app: Sphinx, env: BuildEnvironment) -> None: app, dest_dir.joinpath(Path(config.css).name).relative_to(statics_dir) ) else: - logger.warning( - f"needs_css not an existing file: {config.css} [needs.config]", - type="needs", - subtype="config", + log_warning( + logger, + f"needs_css not an existing file: {config.css}", + "config", + None, ) diff --git a/sphinx_needs/external_needs.py b/sphinx_needs/external_needs.py index 46bb49347..93bc595c2 100644 --- a/sphinx_needs/external_needs.py +++ b/sphinx_needs/external_needs.py @@ -13,7 +13,7 @@ from sphinx_needs.api import add_external_need, del_need from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import SphinxNeedsData -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import clean_log, import_prefix_link_edit log = get_logger(__name__) @@ -170,11 +170,11 @@ def load_external_needs(app: Sphinx, env: BuildEnvironment, docname: str) -> Non # delete the already existing external need from api need del_need(app, ext_need_id) else: - log.warning( + log_warning( + log, f'During external needs handling, an identical ID was detected: {ext_need_id} ' - f'from needs_external_needs url: {source["base_url"]} [needs.duplicate_id]', - type="needs", - subtype="duplicate_id", + f'from needs_external_needs url: {source["base_url"]}', + "duplicate_id", location=docname if docname else None, ) return None diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 6a370de60..53d21b987 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -22,6 +22,7 @@ SphinxNeedsData, ) from sphinx_needs.debug import measure_time, measure_time_func +from sphinx_needs.logging import log_warning from sphinx_needs.roles.need_part import iter_need_parts from sphinx_needs.utils import check_and_get_external_filter_func from sphinx_needs.utils import logger as log @@ -120,9 +121,8 @@ 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( + log, f"Sorting parameter {sort_key} not valid: Error: {e}", None, None ) # check if include external needs @@ -222,7 +222,7 @@ def process_filters( ) filter_func(**context) else: - log.warning("Something went wrong running filter [needs]", type="needs") + log_warning(log, "Something went wrong running filter", None, None) return [] # The filter results may be dirty, as it may continue manipulated needs. @@ -331,10 +331,10 @@ def filter_needs( 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", + log_warning( + log, + f"{e}{append_warning}", + "filter", location=location, ) error_reported = True diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index 6c9f73727..97f56b315 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -16,6 +16,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType from sphinx_needs.filter_common import filter_needs, filter_single_need +from sphinx_needs.logging import log_warning from sphinx_needs.utils import logger @@ -340,9 +341,11 @@ 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", + log_warning( + logger, + f"CheckLinkedValues: Filter {filter_string} not valid: Error: {e}", + None, + None, ) need_value = need[search_option] # type: ignore[literal-required] @@ -456,8 +459,8 @@ def calc_sum( except ValueError: pass except NeedsInvalidFilter as ex: - logger.warning( - f"Given filter is not valid. Error: {ex} [needs]", type="needs" + log_warning( + logger, f"Given filter is not valid. Error: {ex}", None, None ) with contextlib.suppress(ValueError): diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index f766f96d9..3f696614e 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -30,6 +30,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsCoreFields, NeedsInfoType, SphinxNeedsData from sphinx_needs.debug import measure_time +from sphinx_needs.logging import log_warning from sphinx_needs.utils import match_string_link LOGGER = getLogger(__name__) @@ -1092,10 +1093,10 @@ def collapse_button( if src.startswith("icon:"): svg = _read_icon(src[5:]) if svg is None: - LOGGER.warning( + log_warning( + LOGGER, f"Icon {src[5:]!r} not found.", - type="needs", - subtype="layout", + "layout", location=self.node, ) else: diff --git a/sphinx_needs/logging.py b/sphinx_needs/logging.py index c328fce23..22ec314c3 100644 --- a/sphinx_needs/logging.py +++ b/sphinx_needs/logging.py @@ -1,8 +1,38 @@ from __future__ import annotations +from docutils.nodes import Node +from sphinx import version_info from sphinx.util import logging from sphinx.util.logging import SphinxLoggerAdapter def get_logger(name: str) -> SphinxLoggerAdapter: return logging.getLogger(name) + + +def log_warning( + logger: SphinxLoggerAdapter, + message: str, + subtype: str | None, + /, + location: str | tuple[str | None, int | None] | Node | None, + *, + color: str | None = None, + once: bool = False, +) -> None: + # Since sphinx in v7.3, sphinx will show warning types if `show_warning_types=True` is set, + # and in v8.0 this was made the default. + if version_info < (8,): + if subtype: + message += f" [needs.{subtype}]" + else: + message += " [needs]" + + logger.warning( + message, + type="needs", + subtype=subtype, + location=location, + color=color, + once=once, + ) diff --git a/sphinx_needs/need_constraints.py b/sphinx_needs/need_constraints.py index b1925ab8a..ebcd1bb26 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -6,7 +6,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType from sphinx_needs.filter_common import filter_single_need -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning logger = get_logger(__name__) @@ -76,12 +76,12 @@ def process_constraints( # log/except if needed if "warn" in failed_options.get("on_fail", []): - logger.warning( - f"Constraint {cmd} for need {need_id} FAILED! severity: {severity} {need.get('constraints_error', '')} [needs.constraint]", - type="needs", - subtype="constraint", - color="red", + log_warning( + logger, + f"Constraint {cmd} for need {need_id} FAILED! severity: {severity} {need.get('constraints_error', '')}", + "constraint", location=(need["docname"], need["lineno"]), + color="red", ) if "break" in failed_options.get("on_fail", []): raise NeedsConstraintFailed( diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index a88142bd1..76ee6a6d9 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -93,7 +93,7 @@ ) from sphinx_needs.external_needs import load_external_needs from sphinx_needs.functions import NEEDS_COMMON_FUNCTIONS, register_func -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.roles import NeedsXRefRole from sphinx_needs.roles.need_count import NeedCount, process_need_count from sphinx_needs.roles.need_func import NeedFunc, process_need_func @@ -366,10 +366,11 @@ def load_config(app: Sphinx, *_args: Any) -> None: for option in needs_config.extra_options: if option in NEEDS_CONFIG.extra_options: - LOGGER.warning( - f'extra_option "{option}" already registered. [needs.config]', - type="needs", - subtype="config", + log_warning( + LOGGER, + f'extra_option "{option}" already registered.', + "config", + None, ) NEEDS_CONFIG.add_extra_option( option, "Added by needs_extra_options config", override=True @@ -461,24 +462,27 @@ def load_config(app: Sphinx, *_args: Any) -> None: if name not in NEEDS_CONFIG.warnings: NEEDS_CONFIG.warnings[name] = check else: - LOGGER.warning( - f"{name!r} in 'needs_warnings' is already registered. [needs.config]", - type="needs", - subtype="config", + log_warning( + LOGGER, + f"{name!r} in 'needs_warnings' is already registered.", + "config", + None, ) if needs_config.constraints_failed_color: - LOGGER.warning( - 'Config option "needs_constraints_failed_color" is deprecated. Please use "needs_constraint_failed_options" styles instead. [needs.config]', - type="needs", - subtype="config", + log_warning( + LOGGER, + 'Config option "needs_constraints_failed_color" is deprecated. Please use "needs_constraint_failed_options" styles instead.', + "config", + None, ) 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", + log_warning( + LOGGER, + 'Config option "needs_constraints_failed_color" is deprecated. Please use `suppress_warnings = ["needs.link_outgoing"]` instead.', + "config", + None, ) diff --git a/sphinx_needs/needsfile.py b/sphinx_needs/needsfile.py index 552f0fd19..b3db1fd7b 100644 --- a/sphinx_needs/needsfile.py +++ b/sphinx_needs/needsfile.py @@ -17,7 +17,7 @@ from sphinx_needs.config import NEEDS_CONFIG, NeedsSphinxConfig from sphinx_needs.data import NeedsCoreFields, NeedsFilterType, NeedsInfoType -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning log = get_logger(__name__) @@ -210,9 +210,7 @@ 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" - ) + log_warning(self.log, f"Could not load needs json file {file}", None, None) else: errors = check_needs_file(file) # We only care for schema errors here, all other possible errors @@ -226,8 +224,8 @@ 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" + log_warning( + self.log, f"Could not decode json file {file}", None, None ) else: self.needs_list = needs_list diff --git a/sphinx_needs/roles/need_incoming.py b/sphinx_needs/roles/need_incoming.py index ece951aef..66dccc75c 100644 --- a/sphinx_needs/roles/need_incoming.py +++ b/sphinx_needs/roles/need_incoming.py @@ -7,6 +7,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import SphinxNeedsData from sphinx_needs.errors import NoUri +from sphinx_needs.logging import log_warning from sphinx_needs.utils import check_and_calc_base_url_rel_path, logger @@ -91,8 +92,10 @@ def process_need_incoming( pass else: - logger.warning( - f"need {node_need_backref['reftarget']} not found [needs]", + log_warning( + logger, + f"need {node_need_backref['reftarget']} not found", + None, location=node_need_backref, ) diff --git a/sphinx_needs/roles/need_part.py b/sphinx_needs/roles/need_part.py index 8e9ebff05..6384e738b 100644 --- a/sphinx_needs/roles/need_part.py +++ b/sphinx_needs/roles/need_part.py @@ -19,7 +19,7 @@ from sphinx.util.nodes import make_refnode from sphinx_needs.data import NeedsInfoType -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning log = get_logger(__name__) @@ -76,11 +76,13 @@ def update_need_with_parts( need["parts"] = {} if inline_id in need["parts"]: - log.warning( - "part_need id {} in need {} is already taken. need_part may get overridden. [needs]".format( + log_warning( + log, + "part_need id {} in need {} is already taken. need_part may get overridden.".format( inline_id, need["id"] ), - type="needs", + None, + None, ) need["parts"][inline_id] = { diff --git a/sphinx_needs/roles/need_ref.py b/sphinx_needs/roles/need_ref.py index 923c033a0..e2f7e4b8c 100644 --- a/sphinx_needs/roles/need_ref.py +++ b/sphinx_needs/roles/need_ref.py @@ -10,7 +10,7 @@ from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.errors import NoUri -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning from sphinx_needs.utils import check_and_calc_base_url_rel_path, split_need_id log = get_logger(__name__) @@ -112,9 +112,10 @@ def process_need_ref( try: link_text = ref_name.format(**dict_need) except KeyError as e: - log.warning( - f"option placeholder {e} for need {node_need_ref['reftarget']} not found [needs]", - type="needs", + log_warning( + log, + f"option placeholder {e} for need {node_need_ref['reftarget']} not found", + None, location=node_need_ref, ) else: @@ -125,7 +126,7 @@ def process_need_ref( link_text = needs_config.role_need_template.format(**dict_need) except KeyError as e: link_text = f'"the config parameter needs_role_need_template uses not supported placeholders: {e} "' - log.warning(link_text + " [needs]", type="needs") + log_warning(log, link_text, None, None) node_need_ref[0].children[0] = nodes.Text(link_text) # type: ignore[index] @@ -152,10 +153,10 @@ def process_need_ref( new_node_ref["classes"].append(target_need["external_css"]) else: - log.warning( - f"linked need {node_need_ref['reftarget']} not found [needs.link_ref]", - type="needs", - subtype="link_ref", + log_warning( + log, + f"linked need {node_need_ref['reftarget']} not found", + "link_ref", location=node_need_ref, ) diff --git a/sphinx_needs/services/github.py b/sphinx_needs/services/github.py index 6a14dda12..8cf071c7a 100644 --- a/sphinx_needs/services/github.py +++ b/sphinx_needs/services/github.py @@ -15,6 +15,7 @@ from sphinx_needs.api import add_need_type from sphinx_needs.api.exceptions import NeedsApiConfigException from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.logging import log_warning from sphinx_needs.services.base import BaseService from sphinx_needs.services.config.github import ( CONFIG_OPTIONS, @@ -395,9 +396,9 @@ class _SendException(Exception): def create_warning(directive: SphinxDirective, message: str) -> None: - LOGGER.warning( - message + " [needs.github]", - type="needs", - subtype="github", + log_warning( + LOGGER, + message, + "github", location=directive.get_location(), ) diff --git a/sphinx_needs/utils.py b/sphinx_needs/utils.py index cdf4d2fa0..d9a1c4f92 100644 --- a/sphinx_needs/utils.py +++ b/sphinx_needs/utils.py @@ -16,7 +16,7 @@ from sphinx_needs.config import LinkOptionsType, NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.defaults import NEEDS_PROFILING -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning try: from typing import TypedDict @@ -318,9 +318,11 @@ def check_and_get_external_filter_func(filter_func_ref: str | None) -> tuple[Any try: 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", + log_warning( + logger, + f'Filter function not valid "{filter_func_ref}". Example: my_module:my_func', + None, + None, ) return filter_func, filter_args @@ -334,9 +336,11 @@ 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", + log_warning( + logger, + f"Could not import filter function: {filter_func_ref}", + None, + None, ) return filter_func, filter_args @@ -472,10 +476,12 @@ def match_string_link( ) except Exception as e: - logger.warning( + log_warning( + logger, f'Problems dealing with string to link transformation for value "{data}" of ' - f'option "{need_key}". Error: {e} [needs]', - type="needs", + f'option "{need_key}". Error: {e}', + None, + None, ) else: return ref_item @@ -545,10 +551,10 @@ def match_variants( if bool(eval(filter_string, context.copy())): return result.group(2).lstrip(":") except Exception as e: - logger.warning( - f"Error in filter {filter_string!r}: {e} [needs.variant]", - type="needs", - subtype="variant", + log_warning( + logger, + f"Error in filter {filter_string!r}: {e}", + "variant", location=location, ) @@ -630,9 +636,10 @@ def split_link_types(link_types: str, location: Any) -> list[str]: def _is_valid(link_type: str) -> bool: if len(link_type) == 0 or link_type.isspace(): - logger.warning( - "Scruffy link_type definition found. Defined link_type contains spaces only. [needs]", - type="needs", + log_warning( + logger, + "Scruffy link_type definition found. Defined link_type contains spaces only.", + None, location=location, ) return False @@ -650,16 +657,18 @@ 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(): - logger.warning( - f'scale value must be a number. "{scale}" found [needs]', - type="needs", + log_warning( + logger, + f'scale value must be a number. "{scale}" found', + None, location=location, ) return "100" if int(scale) < 1 or int(scale) > 300: - logger.warning( - f'scale value must be between 1 and 300. "{scale}" found [needs]', - type="needs", + log_warning( + logger, + f'scale value must be between 1 and 300. "{scale}" found', + None, location=location, ) return "100" diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 658c67dfc..94346f3b9 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -11,7 +11,7 @@ from sphinx_needs.config import NEEDS_CONFIG, NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.filter_common import filter_needs -from sphinx_needs.logging import get_logger +from sphinx_needs.logging import get_logger, log_warning logger = get_logger(__name__) @@ -74,10 +74,7 @@ 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", - ) + log_warning(logger, f"Unknown needs warnings filter {warning_filter}!") if len(result) == 0: logger.info(f"{warning_name}: passed") @@ -100,14 +97,16 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: warning_text = warning_filter if warnings_always_warn: - logger.warning( - "{}: failed\n\t\tfailed needs: {} ({})\n\t\tused filter: {} [needs]".format( + log_warning( + logger, + "{}: failed\n\t\tfailed needs: {} ({})\n\t\tused filter: {}".format( warning_name, len(need_ids), ", ".join(need_ids), warning_text, ), - type="needs", + None, + None, ) else: logger.info( @@ -121,7 +120,9 @@ def process_warnings(app: Sphinx, exception: Exception | None) -> None: warning_raised = True if warning_raised: - logger.warning( - "warnings were raised. See console / log output for details. [needs]", - type="needs", + log_warning( + logger, + "warnings were raised. See console / log output for details.", + None, + None, )