Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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.12] - 2026-01-02

### Added

### Changed
- If the command is executed in verbose mode (`-v`), the standard Python traceback is used to display errors.

### Fixed

## [0.1.11] - 2025-09-10

### Added
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "CliFire"
version = "0.1.11"
version = "0.1.12"
description = "Minimal CLI framework to build Python commands quickly and elegantly."
authors = [
{ name = "Roberto Lizana", email = "rober.lizana@gmail.com" }
Expand Down
1 change: 1 addition & 0 deletions src/clifire/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ def fire(self, command_line: str = None):
out.setup(
not self.get_option('no_ansi'),
show_icons=self.show_messages_with_icons,
verbose=self.get_option('verbose'),
)
res = cmd.launch(cmd.command_line)
if type(res) is int and res != 0:
Expand Down
2 changes: 1 addition & 1 deletion src/clifire/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def load_file(filename):


def main(command_line: str = None):
app = application.App(name='CliFire', version='0.1.11')
app = application.App(name='CliFire', version='0.1.12')
current_dir = os.getcwd()
out.debug(f'Search commands in {current_dir} folder and parents')
loaded = False
Expand Down
26 changes: 18 additions & 8 deletions src/clifire/out.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@
from rich.prompt import Prompt
from rich.table import Table

traceback.install(
show_locals=True,
theme='monokai',
suppress=[],
max_frames=2,
)
_traceback_handler = None

CONSOLE = Console()
CONSOLE_WIDTH = CONSOLE.width
Expand All @@ -35,8 +30,10 @@
ICON_WARN = '▲'


def setup(ansi: bool = True, show_icons: bool = None) -> None:
global ICON_SHOW, CONSOLE, CONSOLE_WIDTH
def setup(
ansi: bool = True, show_icons: bool = None, verbose: bool = False
) -> None:
global ICON_SHOW, CONSOLE, CONSOLE_WIDTH, _traceback_handler
if show_icons is not None:
ICON_SHOW = show_icons
if ansi:
Expand All @@ -51,6 +48,19 @@ def setup(ansi: bool = True, show_icons: bool = None) -> None:
CONSOLE.soft_wrap = False
CONSOLE.width = 10000
debug('Console output setup with no ANSI')
if verbose:
if sys.excepthook != sys.__excepthook__:
sys.excepthook = sys.__excepthook__
debug('Using standard Python traceback (verbose mode)')
else:
if _traceback_handler is None or sys.excepthook == sys.__excepthook__:
_traceback_handler = traceback.install(
show_locals=True,
theme='monokai',
suppress=[],
max_frames=2,
)
debug('Using Rich pretty traceback')


class LiveText:
Expand Down
37 changes: 37 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,40 @@ def test_path():
)

assert app.path() == os.getcwd()


def test_traceback_verbose_mode(capsys, monkeypatch):
class CommandError(command.Command):
_name = 'error'

def fire(self):
return 1 / 0 # ZeroDivisionError

app = application.App()
app.add_command(CommandError)
monkeypatch.setattr(sys, 'argv', ['test', 'error', '-v'])
original_excepthook = sys.__excepthook__
with pytest.raises(ZeroDivisionError):
app.fire('error -v')
assert sys.excepthook == original_excepthook
printed = output(capsys)
assert 'Using standard Python traceback (verbose mode)' in printed


def test_traceback_pretty_mode(capsys):
class CommandError(command.Command):
_name = 'error'

def fire(self):
return 1 / 0 # ZeroDivisionError

original_excepthook = sys.excepthook
app = application.App()
app.add_command(CommandError)
with pytest.raises(ZeroDivisionError):
app.fire('error')
assert sys.excepthook != sys.__excepthook__
assert (
sys.excepthook != original_excepthook
or original_excepthook != sys.__excepthook__
)