diff --git a/CHANGELOG.md b/CHANGELOG.md index e1fc526..86e9bdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.10] - 2025-07-21 + +### Added +- New parameter in the `Application` class constructor called `show_messages_with_icons` that allows displaying icons in output messages. Default is `True`. +- Added the `__delitem__` method to `config` to remove an item from the configuration dictionary. + +### Changed +- In `out`, renamed the `force_icon` parameter to `show_icon` to make it more descriptive. + +### Fixed +- Broken links to documentation fixed in `README.md` + ## [0.1.9] - 2025-07-15 ### Added diff --git a/README.md b/README.md index c9240fe..e2e7c92 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ In action: ![Demo](./docs/docs/assets/records/quick_start_hello.svg) -For more details, see our [Quick Start Guide](https://rlizana.github.io/clifire/en/quick-start.md). +For more details, see our [Quick Start Guide](https://rlizana.github.io/clifire/en/quick-start). ## Documentation @@ -129,7 +129,7 @@ To contribute to CliFire: 6. **Update the CHANGELOG.md** with your changes. 7. **Commit and push** your changes, and then create a pull request. -For further contribution details, please see our [Contributing Guide](https://rlizana.github.io/clifire/en/contributing.md). +For further contribution details, please see our [Contributing Guide](https://rlizana.github.io/clifire/en/contributing). ## License diff --git a/fire/main.py b/fire/main.py index 51b1a48..55cea28 100644 --- a/fire/main.py +++ b/fire/main.py @@ -86,7 +86,7 @@ def precommit(cmd): ''' Launch pre-commit ''' - cmd.app.shell('pre-commit run --all-files', capture_output=False) + cmd.app.shell('rye run pre-commit run --all-files', capture_output=False) @command.fire diff --git a/pyproject.toml b/pyproject.toml index ff853ca..dcd4bdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "CliFire" -version = "0.1.9" +version = "0.1.10" description = "Minimal CLI framework to build Python commands quickly and elegantly." authors = [ { name = "Roberto Lizana", email = "rober.lizana@gmail.com" } diff --git a/src/clifire/application.py b/src/clifire/application.py index 2b180a6..932d41f 100644 --- a/src/clifire/application.py +++ b/src/clifire/application.py @@ -22,6 +22,7 @@ def __init__( command_help=commands.help.CommandHelp, command_version=commands.version.CommandVersion, template_folder=None, + show_messages_with_icons: bool = True, ): App.current_app = self self.name = name @@ -55,6 +56,7 @@ def __init__( self.template = None if template_folder: self.template = template.Template(template_folder) + self.show_messages_with_icons = show_messages_with_icons def _add_option_verbose(self): self.add_option( @@ -179,7 +181,10 @@ def fire(self, command_line: str = None): else: command_line = self._clean_command_line(command_line) cmd = self.get_command(command_line) - out.setup(not self.get_option('no_ansi')) + out.setup( + not self.get_option('no_ansi'), + show_icons=self.show_messages_with_icons, + ) res = cmd.launch(cmd.command_line) if type(res) is int and res != 0: sys.exit(res) diff --git a/src/clifire/config.py b/src/clifire/config.py index c2a47b8..f0f0fc4 100644 --- a/src/clifire/config.py +++ b/src/clifire/config.py @@ -76,6 +76,12 @@ def __getitem__(self, key): def __setitem__(self, key, value): self.__dict__[key] = value + def __delitem__(self, key): + if key in self.__dict__: + del self.__dict__[key] + else: + raise KeyError(f'Key "{key}" not found') + def __contains__(self, key): return key in self.__dict__ diff --git a/src/clifire/main.py b/src/clifire/main.py index 8cbd6dd..0dc5f71 100644 --- a/src/clifire/main.py +++ b/src/clifire/main.py @@ -31,7 +31,7 @@ def load_file(filename): def main(command_line: str = None): - app = application.App(name='CliFire', version='0.1.9') + app = application.App(name='CliFire', version='0.1.10') current_dir = os.getcwd() out.debug(f'Search commands in {current_dir} folder and parents') loaded = False diff --git a/src/clifire/out.py b/src/clifire/out.py index 18ec577..6fa6dbd 100644 --- a/src/clifire/out.py +++ b/src/clifire/out.py @@ -29,12 +29,16 @@ COLOR_WARN = 'yellow' COLOR_ERROR = 'red' +ICON_SHOW = False ICON_SUCCESS = '✓' ICON_ERROR = '✗' ICON_WARN = '▲' -def setup(ansi: bool = True): +def setup(ansi: bool = True, show_icons: bool = None) -> None: + global ICON_SHOW, CONSOLE, CONSOLE_WIDTH + if show_icons is not None: + ICON_SHOW = show_icons if ansi: CONSOLE.no_color = False CONSOLE.highlight = True @@ -112,23 +116,23 @@ def info(self, text: str, end=False): if end: self.stop() - def warn(self, text: str, end=False, icon: bool = False): + def warn(self, text: str, end=False, icon: bool = None): self._text = text_color( - text, color=COLOR_WARN, icon=ICON_WARN, force_icon=icon + text, color=COLOR_WARN, icon=ICON_WARN, show_icon=icon ) if end: self.stop() - def success(self, text: str, end=True, icon: bool = False): + def success(self, text: str, end=True, icon: bool = None): self._text = text_color( - text, color=COLOR_SUCCESS, icon=ICON_SUCCESS, force_icon=icon + text, color=COLOR_SUCCESS, icon=ICON_SUCCESS, show_icon=icon ) if end: self.stop() - def error(self, text: str, end=True, icon: bool = False): + def error(self, text: str, end=True, icon: bool = None): self._text = text_color( - text, color=COLOR_ERROR, icon=ICON_ERROR, force_icon=icon + text, color=COLOR_ERROR, icon=ICON_ERROR, show_icon=icon ) if end: self.stop() @@ -192,11 +196,13 @@ def text_color( text: str, color: str = COLOR_NORMAL, icon: str = None, - force_icon: bool = False, + show_icon: bool = False, ) -> str: + if show_icon is None: + show_icon = ICON_SHOW text = str(text) if icon and not text.startswith(icon): - if CONSOLE.no_color is True or force_icon: + if CONSOLE.no_color is True or show_icon: text = f'{icon} {text}' return f'[{color}]{text}[/{color}]' @@ -205,9 +211,9 @@ def _print( text: str, color: str = COLOR_NORMAL, icon: str = None, - force_icon: bool = False, + show_icon: bool = None, ) -> None: - text = text_color(text, color=color, icon=icon, force_icon=force_icon) + text = text_color(text, color=color, icon=icon, show_icon=show_icon) CONSOLE.print(text) @@ -215,20 +221,20 @@ def info(text: str) -> None: _print(text, COLOR_INFO) -def success(text: str, icon: bool = False) -> None: - _print(text, color=COLOR_SUCCESS, icon=ICON_SUCCESS, force_icon=icon) +def success(text: str, icon: bool = None) -> None: + _print(text, color=COLOR_SUCCESS, icon=ICON_SUCCESS, show_icon=icon) -def warn(text: str, icon: bool = False) -> None: - _print(text, color=COLOR_WARN, icon=ICON_WARN, force_icon=icon) +def warn(text: str, icon: bool = None) -> None: + _print(text, color=COLOR_WARN, icon=ICON_WARN, show_icon=icon) -def error(text: str, icon: bool = False) -> None: - _print(text, color=COLOR_ERROR, icon=ICON_ERROR, force_icon=icon) +def error(text: str, icon: bool = None) -> None: + _print(text, color=COLOR_ERROR, icon=ICON_ERROR, show_icon=icon) def critical(text: str, code: int = 1) -> None: - error(text) + error(text, icon=False) sys.exit(code) diff --git a/tests/test_config.py b/tests/test_config.py index b7fff85..3d38e93 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -83,3 +83,16 @@ def test_config_query(temp_dir): assert conf.query_get('parent.child_1.name', 'Deleted') == 'Deleted' with pytest.raises(KeyError): conf.query_del('parent.child_2.name') + + +def test_config_del(temp_dir): + config_file = os.path.join(temp_dir, 'config.yaml') + test_data = { + 'key_1': 1, + 'key_2': 2, + } + conf = config.Config(config_file=config_file, create=False, **test_data) + del conf['key_1'] + assert 'key_1' not in conf + with pytest.raises(KeyError): + del conf['key_1'] diff --git a/tests/test_output.py b/tests/test_output.py index a9ab96c..acb3c5b 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -23,17 +23,17 @@ def test_info(capsys): def test_success(capsys): - out.success('Success sample message') + out.success('Success sample message', icon=False) assert 'Success sample message\n' == output(capsys) def test_warn(capsys): - out.warn('Warn sample message') + out.warn('Warn sample message', icon=False) assert 'Warn sample message\n' == output(capsys) def test_error(capsys): - out.warn('Error sample message') + out.warn('Error sample message', icon=False) assert 'Error sample message\n' == output(capsys) @@ -246,7 +246,7 @@ def test_rule(capsys): def test_no_ansi(capsys): - out.setup(ansi=False) + out.setup(ansi=False, show_icons=False) assert out.CONSOLE.no_color is True out.success('Success') assert '✓ Success' in output(capsys) @@ -265,7 +265,7 @@ def test_no_ansi(capsys): live.cancel() output(capsys) - out.setup(ansi=True) + out.setup(ansi=True, show_icons=False) assert out.CONSOLE.no_color is False out.success('Success') assert '✓' not in output(capsys)