diff --git a/CHANGELOG.md b/CHANGELOG.md index 72c8244..8f73e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project (partially) adheres to [Semantic Versioning](https://semver.org ## [Unreleased] ### Added - Python 3.13 & 3.14 official support +- `entries_color` config option validation ## [v4.15.0.0] - 2024-09-30 ### Added diff --git a/archey/colors.py b/archey/colors.py index 7a133ae..efe25df 100644 --- a/archey/colors.py +++ b/archey/colors.py @@ -9,7 +9,8 @@ from archey.environment import Environment # REGEXP compiled pattern matching ANSI/ECMA-48 color escape codes. -ANSI_ECMA_REGEXP = re.compile(r"\x1b\[\d+(?:(?:;\d+)+)?m") +ANSI_TEXT_CODES_REGEXP = re.compile(r"\d+(?:(?:;\d+)+)?") +ANSI_ECMA_REGEXP = re.compile(rf"\x1b\[{ANSI_TEXT_CODES_REGEXP.pattern}m") class Style: diff --git a/archey/configuration.py b/archey/configuration.py index ea89ba4..bb75bcc 100644 --- a/archey/configuration.py +++ b/archey/configuration.py @@ -6,6 +6,7 @@ from copy import deepcopy from typing import Any, Dict +from archey.colors import ANSI_TEXT_CODES_REGEXP from archey.singleton import Singleton from archey.utility import Utility @@ -51,6 +52,9 @@ def __init__(self, config_path=None): self._load_configuration(os.path.expanduser("~/.config/archey4/")) self._load_configuration(os.getcwd()) + # Perform various validations + self._validate_configuration() + def get(self, key: str, default=None) -> Any: """ A binding method to imitate the `dict.get()` behavior. @@ -90,6 +94,19 @@ def _load_configuration(self, path: str) -> None: logging.ERROR if self.get("suppress_warnings") else logging.WARN ) + def _validate_configuration(self) -> None: + # entries_color + entries_color = self._config.get("entries_color") + if entries_color: + if ( + not isinstance(entries_color, str) + or ANSI_TEXT_CODES_REGEXP.fullmatch(entries_color) is None + ): + logging.warning( + "Couldn't validate 'entries_color' configuration option value, ignoring..." + ) + self._config["entries_color"] = DEFAULT_CONFIG["entries_color"] + def __iter__(self): """When used as an iterator, directly yield `_config` elements""" return iter(self._config.items()) diff --git a/archey/test/test_archey_configuration.py b/archey/test/test_archey_configuration.py index bb73f69..c60b94f 100644 --- a/archey/test/test_archey_configuration.py +++ b/archey/test/test_archey_configuration.py @@ -132,6 +132,45 @@ def test_load_configuration(self): }, ) + @patch( + # `_load_configuration` method is mocked to "ignore" local system configurations. + "archey.configuration.Configuration._load_configuration", + Mock(), + ) + def test_validate_configuration(self): + """Test configuration option values validation""" + configuration = Configuration() + + # OK + with patch.dict( + configuration._config, # pylint: disable=protected-access + { + "entries_color": "0;1;2", + }, + ): + configuration._validate_configuration() # pylint: disable=protected-access + self.assertEqual(configuration.get("entries_color"), "0;1;2") + + # KO + with patch.dict( + configuration._config, # pylint: disable=protected-access + { + "entries_color": True, + }, + ): + configuration._validate_configuration() # pylint: disable=protected-access + self.assertEqual(configuration.get("entries_color"), "") + + # KO + with patch.dict( + configuration._config, # pylint: disable=protected-access + { + "entries_color": "true", + }, + ): + configuration._validate_configuration() # pylint: disable=protected-access + self.assertEqual(configuration.get("entries_color"), "") + def test_instantiation_config_path(self): """Test for configuration loading from specific user-defined path""" with tempfile.TemporaryDirectory() as temp_dir: