From adc56ec1a18c129533e27ceca72f95d1e6d4836b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Mon, 18 Dec 2023 23:41:42 +0100 Subject: [PATCH 1/4] Sketch warns as errors array --- conan/api/output.py | 20 +++++++++++-------- conan/internal/conan_app.py | 16 ++++++++++++--- conans/model/conf.py | 4 ++-- .../integration/command_v2/test_output.py | 20 ++++++++++++------- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/conan/api/output.py b/conan/api/output.py index ad8e541c379..37f9e9c84a0 100644 --- a/conan/api/output.py +++ b/conan/api/output.py @@ -1,3 +1,4 @@ +import fnmatch import sys from colorama import Fore, Style @@ -51,7 +52,7 @@ class ConanOutput: # Singleton _conan_output_level = LEVEL_STATUS _silent_warn_tags = [] - _warnings_as_errors = False + _warnings_as_errors = [] def __init__(self, scope=""): self.stream = sys.stderr @@ -62,7 +63,7 @@ def __init__(self, scope=""): @classmethod def define_silence_warnings(cls, warnings): - cls._silent_warn_tags = warnings or [] + cls._silent_warn_tags = warnings @classmethod def set_warnings_as_errors(cls, value): @@ -208,17 +209,20 @@ def success(self, msg): self._write_message(msg, fg=Color.BRIGHT_GREEN) return self + @staticmethod + def _warn_tag_matches(warn_tag, patterns): + lookup_tag = warn_tag or "unknown" + return any(fnmatch.fnmatch(lookup_tag, pattern) for pattern in patterns) + def warning(self, msg, warn_tag=None): - def _skip_warning(): - return (warn_tag is not None and warn_tag in self._silent_warn_tags - or warn_tag is None and "unknown" in self._silent_warn_tags) - if self._conan_output_level <= LEVEL_WARNING or (self._warnings_as_errors and self._conan_output_level <= LEVEL_ERROR): - if _skip_warning(): + _treat_as_error = self._warn_tag_matches(warn_tag, self._warnings_as_errors) + if self._conan_output_level <= LEVEL_WARNING or (_treat_as_error and self._conan_output_level <= LEVEL_ERROR): + if self._warn_tag_matches(warn_tag, self._silent_warn_tags): return self warn_tag_msg = "" if warn_tag is None else f"{warn_tag}: " output = f"{warn_tag_msg}{msg}" - if self._warnings_as_errors: + if _treat_as_error: self.error(output) else: self._write_message(f"WARN: {output}", Color.YELLOW) diff --git a/conan/internal/conan_app.py b/conan/internal/conan_app.py index 38f8cab84d3..1b508b13470 100644 --- a/conan/internal/conan_app.py +++ b/conan/internal/conan_app.py @@ -64,6 +64,16 @@ def __init__(self, cache_folder, global_conf): @staticmethod def _configure(global_conf): - ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", - default=False, check_type=bool)) - ConanOutput.define_silence_warnings(global_conf.get("core:skip_warnings", check_type=list)) + legacy_warnings_as_errors_bool = global_conf.get("core:warnings_as_errors", None, check_type=bool) + if legacy_warnings_as_errors_bool is not None: + # Print this before setting the warnings as errors, + # else only this will be printed and Conan will exit + ConanOutput().warning("Boolean 'core:warnings_as_errors' key is deprecated, " + "use a list of patterns instead, e.g. ['*'] to treat all warnings as errors", + warn_tag="deprecated") + ConanOutput.set_warnings_as_errors(["*"] if legacy_warnings_as_errors_bool else []) + else: + ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", + default=[], check_type=list)) + ConanOutput.define_silence_warnings(global_conf.get("core:skip_warnings", + default=[], check_type=list)) diff --git a/conans/model/conf.py b/conans/model/conf.py index 7a295072fef..64851f19db4 100644 --- a/conans/model/conf.py +++ b/conans/model/conf.py @@ -12,8 +12,8 @@ BUILT_IN_CONFS = { "core:required_conan_version": "Raise if current version does not match the defined range.", "core:non_interactive": "Disable interactive user input, raises error if input necessary", - "core:warnings_as_errors": "Treat warnings as errors", - "core:skip_warnings": "Do not show warnings in this list", + "core:warnings_as_errors": "Treat warnings matching any of the patterns in this list as errors", + "core:skip_warnings": "Do not show warnings matching any of the patterns in this list", "core:default_profile": "Defines the default host profile ('default' by default)", "core:default_build_profile": "Defines the default build profile ('default' by default)", "core:allow_uppercase_pkg_names": "Temporarily (will be removed in 2.X) allow uppercase names", diff --git a/conans/test/integration/command_v2/test_output.py b/conans/test/integration/command_v2/test_output.py index 75e3afc1e56..d313962f159 100644 --- a/conans/test/integration/command_v2/test_output.py +++ b/conans/test/integration/command_v2/test_output.py @@ -186,7 +186,7 @@ def test_output_level_envvar(): class TestWarningHandling: warning_lines = ("self.output.warning('Tagged warning', warn_tag='tag')", - "self.output.warning('Untagged warning')") + "self.output.warning('Untagged warning')") error_lines = ("self.output.error('Tagged error', error_type='exception')", "self.output.error('Untagged error')") @@ -199,6 +199,7 @@ def test_warning_as_error(self): assert "WARN: Untagged warning" in t.out assert "WARN: tag: Tagged warning" in t.out + # Deprecated boolean, still works t.save_home({"global.conf": "core:warnings_as_errors=True"}) t.run("create . -vwarning", assert_error=True) assert "ConanException: tag: Tagged warning" in t.out @@ -243,12 +244,17 @@ def test_exception_errors(self): t = TestClient(light=True) t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*self.error_lines)}) - t.save_home({"global.conf": "core:warnings_as_errors=False"}) - t.run("create .") - assert "ERROR: Tagged error" in t.out - assert "ERROR: Untagged error" in t.out - - t.save_home({"global.conf": "core:warnings_as_errors=True"}) + # t.save_home({"global.conf": "core:warnings_as_errors=False"}) + # t.run("create .") + # assert "ERROR: Tagged error" in t.out + # assert "ERROR: Untagged error" in t.out + # + # t.save_home({"global.conf": "core:warnings_as_errors=True"}) + # t.run("create .", assert_error=True) + # assert "ERROR: Tagged error" in t.out + # assert "ConanException: Untagged error" in t.out + + t.save_home({"global.conf": "core:warnings_as_errors=['*']"}) t.run("create .", assert_error=True) assert "ERROR: Tagged error" in t.out assert "ConanException: Untagged error" in t.out From 778b926f3478dd81d531632e0bc7a7a5d20f5a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 19 Dec 2023 02:13:02 +0100 Subject: [PATCH 2/4] Fix logic --- conan/internal/conan_app.py | 13 ++-------- .../integration/command_v2/test_output.py | 24 +++++++------------ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/conan/internal/conan_app.py b/conan/internal/conan_app.py index 1b508b13470..a0c3f384462 100644 --- a/conan/internal/conan_app.py +++ b/conan/internal/conan_app.py @@ -64,16 +64,7 @@ def __init__(self, cache_folder, global_conf): @staticmethod def _configure(global_conf): - legacy_warnings_as_errors_bool = global_conf.get("core:warnings_as_errors", None, check_type=bool) - if legacy_warnings_as_errors_bool is not None: - # Print this before setting the warnings as errors, - # else only this will be printed and Conan will exit - ConanOutput().warning("Boolean 'core:warnings_as_errors' key is deprecated, " - "use a list of patterns instead, e.g. ['*'] to treat all warnings as errors", - warn_tag="deprecated") - ConanOutput.set_warnings_as_errors(["*"] if legacy_warnings_as_errors_bool else []) - else: - ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", - default=[], check_type=list)) + ConanOutput.set_warnings_as_errors(global_conf.get("core:warnings_as_errors", + default=[], check_type=list)) ConanOutput.define_silence_warnings(global_conf.get("core:skip_warnings", default=[], check_type=list)) diff --git a/conans/test/integration/command_v2/test_output.py b/conans/test/integration/command_v2/test_output.py index d313962f159..20d60fe77f5 100644 --- a/conans/test/integration/command_v2/test_output.py +++ b/conans/test/integration/command_v2/test_output.py @@ -190,28 +190,27 @@ class TestWarningHandling: error_lines = ("self.output.error('Tagged error', error_type='exception')", "self.output.error('Untagged error')") - def test_warning_as_error(self): + def test_warning_as_error_deprecated_syntax(self): t = TestClient(light=True) t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*self.warning_lines)}) - t.save_home({"global.conf": "core:warnings_as_errors=False"}) + t.save_home({"global.conf": "core:warnings_as_errors=[]"}) t.run("create . -vwarning") assert "WARN: Untagged warning" in t.out assert "WARN: tag: Tagged warning" in t.out - # Deprecated boolean, still works - t.save_home({"global.conf": "core:warnings_as_errors=True"}) + t.save_home({"global.conf": "core:warnings_as_errors=['*']"}) t.run("create . -vwarning", assert_error=True) assert "ConanException: tag: Tagged warning" in t.out # We bailed early, didn't get a chance to print this one assert "Untagged warning" not in t.out - t.save_home({"global.conf": """core:warnings_as_errors=True\ncore:skip_warnings=["tag"]"""}) + t.save_home({"global.conf": """core:warnings_as_errors=['*']\ncore:skip_warnings=["tag"]"""}) t.run("create . -verror", assert_error=True) assert "ConanException: Untagged warning" in t.out assert "Tagged warning" not in t.out - t.save_home({"global.conf": "core:warnings_as_errors=False"}) + t.save_home({"global.conf": "core:warnings_as_errors=[]"}) t.run("create . -verror") assert "ERROR: Untagged warning" not in t.out assert "ERROR: tag: Tagged warning" not in t.out @@ -244,15 +243,10 @@ def test_exception_errors(self): t = TestClient(light=True) t.save({"conanfile.py": GenConanfile("foo", "1.0").with_package(*self.error_lines)}) - # t.save_home({"global.conf": "core:warnings_as_errors=False"}) - # t.run("create .") - # assert "ERROR: Tagged error" in t.out - # assert "ERROR: Untagged error" in t.out - # - # t.save_home({"global.conf": "core:warnings_as_errors=True"}) - # t.run("create .", assert_error=True) - # assert "ERROR: Tagged error" in t.out - # assert "ConanException: Untagged error" in t.out + t.save_home({"global.conf": "core:warnings_as_errors=[]"}) + t.run("create .") + assert "ERROR: Tagged error" in t.out + assert "ERROR: Untagged error" in t.out t.save_home({"global.conf": "core:warnings_as_errors=['*']"}) t.run("create .", assert_error=True) From 4c30e2b34624ff14cb93506729dcd5c090657a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 19 Dec 2023 12:33:01 +0100 Subject: [PATCH 3/4] Raise even when not printing anything --- conan/api/output.py | 7 +++---- conans/test/integration/command_v2/test_output.py | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/conan/api/output.py b/conan/api/output.py index 37f9e9c84a0..d42f0347d50 100644 --- a/conan/api/output.py +++ b/conan/api/output.py @@ -229,11 +229,10 @@ def warning(self, msg, warn_tag=None): return self def error(self, msg, error_type=None): + if self._warnings_as_errors and error_type != "exception": + raise ConanException(msg) if self._conan_output_level <= LEVEL_ERROR: - if self._warnings_as_errors and error_type != "exception": - raise ConanException(msg) - else: - self._write_message("ERROR: {}".format(msg), Color.RED) + self._write_message("ERROR: {}".format(msg), Color.RED) return self def flush(self): diff --git a/conans/test/integration/command_v2/test_output.py b/conans/test/integration/command_v2/test_output.py index 20d60fe77f5..3c40fefef0f 100644 --- a/conans/test/integration/command_v2/test_output.py +++ b/conans/test/integration/command_v2/test_output.py @@ -252,3 +252,7 @@ def test_exception_errors(self): t.run("create .", assert_error=True) assert "ERROR: Tagged error" in t.out assert "ConanException: Untagged error" in t.out + + t.run("create . -vquiet", assert_error=True) + assert "ERROR: Tagged error" not in t.out + assert "ConanException: Untagged error" not in t.out From 0139a6e5f32a8c3c3fb4c3f81b04d862b3260d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 19 Dec 2023 15:31:59 +0100 Subject: [PATCH 4/4] Add list of warn tags to conf descriptions --- conans/model/conf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conans/model/conf.py b/conans/model/conf.py index 64851f19db4..2f433de2d0a 100644 --- a/conans/model/conf.py +++ b/conans/model/conf.py @@ -12,8 +12,10 @@ BUILT_IN_CONFS = { "core:required_conan_version": "Raise if current version does not match the defined range.", "core:non_interactive": "Disable interactive user input, raises error if input necessary", - "core:warnings_as_errors": "Treat warnings matching any of the patterns in this list as errors", - "core:skip_warnings": "Do not show warnings matching any of the patterns in this list", + "core:warnings_as_errors": "Treat warnings matching any of the patterns in this list as errors and then raise an exception. " + "Current warning tags are 'network', 'deprecated'", + "core:skip_warnings": "Do not show warnings matching any of the patterns in this list. " + "Current warning tags are 'network', 'deprecated'", "core:default_profile": "Defines the default host profile ('default' by default)", "core:default_build_profile": "Defines the default build profile ('default' by default)", "core:allow_uppercase_pkg_names": "Temporarily (will be removed in 2.X) allow uppercase names",