cli-error 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/README.md +0 -4
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/docker-compose.yml +0 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/README.md +5 -5
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t15-reconsider-derive-debug-stderr-console.md +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t16-optionally-show-locals-in-debug.md +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t17-test-debug-traceback-includes-the.md +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t19-consolidate-shared-test-helpers-across.md +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/PKG-INFO +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/README.md +24 -6
- {cli_error-0.1.0 → cli_error-0.1.1}/pyproject.toml +1 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/_console.py +11 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/_reporter.py +18 -6
- cli_error-0.1.1/tests/helpers.py +33 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/tests/test_console.py +13 -39
- {cli_error-0.1.0 → cli_error-0.1.1}/tests/test_errors.py +2 -3
- {cli_error-0.1.0 → cli_error-0.1.1}/tests/test_render.py +4 -19
- {cli_error-0.1.0 → cli_error-0.1.1}/tests/test_reporter.py +175 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/uv.lock +35 -35
- cli_error-0.1.0/.claude/scheduled_tasks.lock +0 -1
- cli_error-0.1.0/.tasker/.closed +0 -16
- cli_error-0.1.0/.tasker/.recent +0 -1
- {cli_error-0.1.0 → cli_error-0.1.1}/.flake8 +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.github/workflows/ci.yml +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.github/workflows/release.yml +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.gitignore +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.mcp.json +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/.env.example +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/base/Dockerfile +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/base/tmux.conf +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/base/zshrc +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/run.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/scripts/_lib.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/scripts/attach.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/scripts/build.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/scripts/claude.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/scripts/shell.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/variants/python/Dockerfile +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/variants/python/compose.override.yml +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/worktree_run.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.sandbox/worktree_shell.sh +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/.gitignore +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/archive/.gitkeep +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t01-rename-package-structure-clierror-cliexit.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t02-fluent-builder-for-clierror-context.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t03-render-formatted-error.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t04-error-handler-errorreporter-context-manager.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t05-cause-chain-rendering.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t06-debug-traceback.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t07-console-creation-with-style-overrides.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t08-library-documentation-readme.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t09-deployment-setup-ci-release-workflows.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t10-unify-markup-construction-template-args.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t11-refine-detail-single-last-wins.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t12-revisit-props-markup-first-prop.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t13-store-detail-as-ready-to.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t14-handle-empty-message-cause-in.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t18-add-debug-gated-diagnostic-helpers.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/s01t20-add-a-safe-post-construction.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/CLAUDE.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/CONTEXT.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/docs/adr/0001-error-formatting-and-theming.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/docs/adr/0002-cli-reporter-output-facade.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/docs/agents/dev-loop.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/docs/agents/task-tracker.md +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/__init__.py +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/_errors.py +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/_render.py +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/src/cli_error/py.typed +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/tests/__init__.py +0 -0
- {cli_error-0.1.0 → cli_error-0.1.1}/tox.ini +0 -0
|
@@ -47,10 +47,6 @@ build when the images already exist.
|
|
|
47
47
|
Claude credentials live in `~/.claude` / `~/.claude.json` on the host (bind-mounted
|
|
48
48
|
into the container), so authentication persists across container rebuilds.
|
|
49
49
|
|
|
50
|
-
The host's `~/repo-skills` is bind-mounted **read-only** at `~/repo-skills` inside
|
|
51
|
-
the container as a reference source, so the sandbox can consult it without
|
|
52
|
-
modifying it or leaking container-generated files back onto the host.
|
|
53
|
-
|
|
54
50
|
## Daily usage
|
|
55
51
|
|
|
56
52
|
```bash
|
{cli_error-0.1.0 → cli_error-0.1.1}/.tasker/s01-extract-repo-skills-error-reporting/README.md
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: s01
|
|
3
3
|
slug: extract-repo-skills-error-reporting
|
|
4
|
-
status:
|
|
4
|
+
status: done
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Extract repo-skills error reporting into cli-error library
|
|
@@ -53,9 +53,9 @@ status: pending
|
|
|
53
53
|
- [x] [s01t12](s01t12-revisit-props-markup-first-prop.md): Revisit props: markup-first `prop` with template+args, roles as thin helpers
|
|
54
54
|
- [x] [s01t13](s01t13-store-detail-as-ready-to.md): Store detail as ready-to-print [misc] markup (restore render-seam invariant)
|
|
55
55
|
- [x] [s01t14](s01t14-handle-empty-message-cause-in.md): Handle empty-message cause in cause-chain rendering
|
|
56
|
-
- [
|
|
57
|
-
- [
|
|
58
|
-
- [
|
|
56
|
+
- [x] [s01t15](s01t15-reconsider-derive-debug-stderr-console.md): Debug stderr console inherits user intent (no_color + theme), not per-stream geometry
|
|
57
|
+
- [x] [s01t16](s01t16-optionally-show-locals-in-debug.md): Add opt-in show_locals to the debug traceback
|
|
58
|
+
- [x] [s01t17](s01t17-test-debug-traceback-includes-the.md): Test: debug traceback includes the exception cause chain
|
|
59
59
|
- [x] [s01t18](s01t18-add-debug-gated-diagnostic-helpers.md): Rename ErrorReporter to CliReporter and grow it into the CLI output façade
|
|
60
|
-
- [
|
|
60
|
+
- [x] [s01t19](s01t19-consolidate-shared-test-helpers-across.md): Consolidate shared test helpers into tests/_helpers.py
|
|
61
61
|
- [x] [s01t20](s01t20-add-a-safe-post-construction.md): Add a safe post-construction debug toggle to CliReporter
|
|
@@ -198,10 +198,12 @@ if __name__ == "__main__":
|
|
|
198
198
|
main()
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
`CliReporter(console, *, debug=False, console_err=None)` — when
|
|
202
|
-
omitted a stderr console is built for you
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
`CliReporter(console, *, debug=False, show_locals=False, console_err=None)` — when
|
|
202
|
+
`console_err` is omitted a stderr console is built for you, inheriting the passed
|
|
203
|
+
console's `no_color` and theme so debug output matches your main console. Wire
|
|
204
|
+
`--debug` at construction as above; the flag is also a plain public attribute you
|
|
205
|
+
can toggle later (`reporter.debug = True`). `show_locals` sets the default for
|
|
206
|
+
`debug_traceback` (see below).
|
|
205
207
|
|
|
206
208
|
### Cause chains
|
|
207
209
|
|
|
@@ -233,7 +235,7 @@ Because they self-gate, you can sprinkle them freely without guarding each call.
|
|
|
233
235
|
| Method | Emits (only when `debug`) |
|
|
234
236
|
| ------------------------------ | -------------------------------------------------- |
|
|
235
237
|
| `debug_print(template, **args)`| a trusted template with escaped args |
|
|
236
|
-
| `debug_traceback()`
|
|
238
|
+
| `debug_traceback(show_locals=None)` | the currently-handled exception traceback |
|
|
237
239
|
| `debug_cmd(cmd, cwd=None)` | a subprocess command line and optional working dir |
|
|
238
240
|
| `debug_output(stdout, stderr)` | captured subprocess output (empty streams skipped) |
|
|
239
241
|
|
|
@@ -250,7 +252,10 @@ reporter.debug_output(result.stdout, result.stderr)
|
|
|
250
252
|
|
|
251
253
|
`debug_traceback()` is what `handler()` calls for you on an uncaught exception,
|
|
252
254
|
so a `--debug` run automatically shows the full traceback on stderr alongside
|
|
253
|
-
the rendered error.
|
|
255
|
+
the rendered error. Pass `show_locals=True` (per call, or as the
|
|
256
|
+
`CliReporter(show_locals=True)` default) to include each frame's local variables
|
|
257
|
+
— useful when debugging, but keep it developer-only since locals can expose
|
|
258
|
+
secrets like tokens or passwords.
|
|
254
259
|
|
|
255
260
|
## Customizing the theme
|
|
256
261
|
|
|
@@ -285,3 +290,16 @@ The repository is checked with `tox` (typecheck, test, lint):
|
|
|
285
290
|
```bash
|
|
286
291
|
uv run tox
|
|
287
292
|
```
|
|
293
|
+
|
|
294
|
+
## Release Notes
|
|
295
|
+
|
|
296
|
+
### v0.1.1
|
|
297
|
+
- `debug_traceback` can now render per-frame locals — opt in with `CliReporter(show_locals=True)` or a per-call `debug_traceback(show_locals=True)` override (locals may expose secrets, so keep it developer-only).
|
|
298
|
+
- The auto-built stderr console now inherits the injected console's `no_color` and theme intent, so debug output matches your main console's styling.
|
|
299
|
+
|
|
300
|
+
### v0.1.0
|
|
301
|
+
- `CliError` — structured exceptions with Rich-markup messages and a fluent builder for hints, detail blocks, and labelled context properties.
|
|
302
|
+
- `CliReporter` — output/exit façade: a context manager that maps exceptions to exit codes, renders the `caused by:` cause chain, and gates debug diagnostics behind a `debug` flag.
|
|
303
|
+
- `CliExit` — a clean-exit signal that prints its message and exits with status 0.
|
|
304
|
+
- Theming via `make_console`, `make_theme`, and `DEFAULT_STYLES` — Rich consoles with default style roles, per-role overrides, `no_color` degradation, and stderr routing.
|
|
305
|
+
- Automatic escaping of untrusted template/property values, plus standalone `render_error`, `render_template`, and `print_error` helpers.
|
|
@@ -38,3 +38,14 @@ def make_console(
|
|
|
38
38
|
stderr=stderr,
|
|
39
39
|
no_color=no_color,
|
|
40
40
|
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def derive_stderr_console(console: Console) -> Console:
|
|
44
|
+
"""Build a stderr console that inherits ``console``'s user intent."""
|
|
45
|
+
return make_console(
|
|
46
|
+
stderr=True,
|
|
47
|
+
no_color=console.no_color,
|
|
48
|
+
styles={
|
|
49
|
+
role: console.get_style(role, default=Style()) for role in DEFAULT_STYLES
|
|
50
|
+
},
|
|
51
|
+
)
|
|
@@ -7,7 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
|
|
10
|
-
from ._console import
|
|
10
|
+
from ._console import derive_stderr_console
|
|
11
11
|
from ._errors import CliExit, print_error
|
|
12
12
|
from ._render import render_template
|
|
13
13
|
|
|
@@ -15,18 +15,18 @@ from ._render import render_template
|
|
|
15
15
|
class CliReporter:
|
|
16
16
|
"""Single integration point for a CLI's terminal output."""
|
|
17
17
|
|
|
18
|
-
debug: bool = False
|
|
19
|
-
|
|
20
18
|
def __init__(
|
|
21
19
|
self,
|
|
22
20
|
console: Console,
|
|
23
21
|
*,
|
|
24
22
|
debug: bool = False,
|
|
23
|
+
show_locals: bool = False,
|
|
25
24
|
console_err: Console | None = None,
|
|
26
25
|
) -> None:
|
|
27
26
|
self.debug = debug
|
|
27
|
+
self.show_locals = show_locals
|
|
28
28
|
self.console = console
|
|
29
|
-
self.console_err = console_err or
|
|
29
|
+
self.console_err = console_err or derive_stderr_console(console)
|
|
30
30
|
|
|
31
31
|
def print(self, template: str, /, *, end: str = "\n", **args: Any) -> None:
|
|
32
32
|
"""Render a trusted template with escaped args to the stdout console."""
|
|
@@ -41,11 +41,19 @@ class CliReporter:
|
|
|
41
41
|
if self.debug:
|
|
42
42
|
_emit(self.console_err, template, end, **args)
|
|
43
43
|
|
|
44
|
-
def debug_traceback(self) -> None:
|
|
44
|
+
def debug_traceback(self, *, show_locals: bool | None = None) -> None:
|
|
45
45
|
"""Emit the currently-handled traceback to the stderr console.
|
|
46
46
|
|
|
47
47
|
No-op unless ``debug`` is set. Safely no-ops outside an ``except``
|
|
48
48
|
block: ``print_exception`` reads ``sys.exc_info()``.
|
|
49
|
+
|
|
50
|
+
``show_locals`` overrides the instance default per call; when left as
|
|
51
|
+
``None`` the traceback defers to the ``show_locals`` flag the reporter
|
|
52
|
+
was constructed with.
|
|
53
|
+
|
|
54
|
+
Caution: ``show_locals`` renders every frame's local variables verbatim
|
|
55
|
+
to stderr, which may expose secrets (tokens, passwords). Keep it gated
|
|
56
|
+
behind an explicit developer-only flag.
|
|
49
57
|
"""
|
|
50
58
|
if not self.debug:
|
|
51
59
|
return
|
|
@@ -53,7 +61,11 @@ class CliReporter:
|
|
|
53
61
|
if sys.exc_info()[0] is None:
|
|
54
62
|
return
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
effective = show_locals
|
|
65
|
+
if effective is None:
|
|
66
|
+
effective = self.show_locals
|
|
67
|
+
|
|
68
|
+
self.console_err.print_exception(show_locals=effective)
|
|
57
69
|
|
|
58
70
|
def debug_cmd(self, cmd: list[str], cwd: Path | None = None) -> None:
|
|
59
71
|
"""Emit a subprocess command (and optional cwd) as a debug diagnostic."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.theme import Theme
|
|
5
|
+
|
|
6
|
+
from cli_error import DEFAULT_STYLES
|
|
7
|
+
|
|
8
|
+
_ROLES = tuple(DEFAULT_STYLES)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def forced_terminal(theme: Theme, *, no_color: bool = False) -> Console:
|
|
12
|
+
return Console(
|
|
13
|
+
theme=theme,
|
|
14
|
+
force_terminal=True,
|
|
15
|
+
color_system="standard",
|
|
16
|
+
no_color=no_color,
|
|
17
|
+
highlight=False,
|
|
18
|
+
width=80,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def theme_from(console: Console, roles: tuple[str, ...] = _ROLES) -> Theme:
|
|
23
|
+
return Theme({role: console.get_style(role) for role in roles})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def capture_emit(console: Console, emit: Callable[[Console], None]) -> str:
|
|
27
|
+
with console.capture() as cap:
|
|
28
|
+
emit(console)
|
|
29
|
+
return cap.get()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def capture(console: Console, markup: str) -> str:
|
|
33
|
+
return capture_emit(console, lambda c: c.print(markup))
|
|
@@ -1,33 +1,7 @@
|
|
|
1
|
-
from rich.console import Console
|
|
2
1
|
from rich.style import Style
|
|
3
|
-
from rich.theme import Theme
|
|
4
2
|
|
|
5
3
|
from cli_error import DEFAULT_STYLES, make_console, make_theme
|
|
6
|
-
|
|
7
|
-
_ROLES = ("id", "data", "path", "cmd", "misc", "warn", "err")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _forced(theme: Theme, *, no_color: bool = False) -> Console:
|
|
11
|
-
"""A forced-ANSI console carrying ``theme``; ``no_color`` strips escapes."""
|
|
12
|
-
return Console(
|
|
13
|
-
theme=theme,
|
|
14
|
-
force_terminal=True,
|
|
15
|
-
color_system="standard",
|
|
16
|
-
no_color=no_color,
|
|
17
|
-
highlight=False,
|
|
18
|
-
width=80,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _terminal(theme_source: Console) -> Console:
|
|
23
|
-
"""A forced-ANSI console carrying ``theme_source``'s resolved roles."""
|
|
24
|
-
return _forced(Theme({role: theme_source.get_style(role) for role in _ROLES}))
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _capture(console: Console, markup: str) -> str:
|
|
28
|
-
with console.capture() as capture:
|
|
29
|
-
console.print(markup)
|
|
30
|
-
return capture.get()
|
|
4
|
+
from tests.helpers import _ROLES, capture, forced_terminal, theme_from
|
|
31
5
|
|
|
32
6
|
|
|
33
7
|
def test_default_console_maps_roles_to_canonical_styles() -> None:
|
|
@@ -37,8 +11,8 @@ def test_default_console_maps_roles_to_canonical_styles() -> None:
|
|
|
37
11
|
|
|
38
12
|
|
|
39
13
|
def test_default_roles_render_as_distinct_ansi_runs() -> None:
|
|
40
|
-
console =
|
|
41
|
-
out =
|
|
14
|
+
console = forced_terminal(theme_from(make_console()))
|
|
15
|
+
out = capture(console, "[id]a[/id][path]b[/path][err]c[/err]")
|
|
42
16
|
|
|
43
17
|
id_run = console.get_style("id").render("a")
|
|
44
18
|
path_run = console.get_style("path").render("b")
|
|
@@ -79,18 +53,18 @@ def test_unknown_role_key_is_added_and_never_raises() -> None:
|
|
|
79
53
|
console = make_console(styles={"custom": "magenta"})
|
|
80
54
|
assert console.get_style("custom") == Style.parse("magenta")
|
|
81
55
|
# An undefined role degrades to plain text on a colour terminal, no error.
|
|
82
|
-
terminal =
|
|
83
|
-
assert
|
|
56
|
+
terminal = forced_terminal(theme_from(make_console()))
|
|
57
|
+
assert capture(terminal, "[undefined]x[/undefined]") == "x\n"
|
|
84
58
|
|
|
85
59
|
|
|
86
60
|
def test_no_color_suppresses_ansi_on_a_forced_terminal() -> None:
|
|
87
61
|
# Positive control: an identical forced terminal DOES emit ANSI, proving the
|
|
88
62
|
# no_color flag below is load-bearing rather than vacuously satisfied.
|
|
89
|
-
coloured =
|
|
90
|
-
plain =
|
|
63
|
+
coloured = forced_terminal(make_theme(), no_color=False)
|
|
64
|
+
plain = forced_terminal(make_theme(), no_color=True)
|
|
91
65
|
|
|
92
|
-
coloured_out =
|
|
93
|
-
plain_out =
|
|
66
|
+
coloured_out = capture(coloured, "[id]x[/id]")
|
|
67
|
+
plain_out = capture(plain, "[id]x[/id]")
|
|
94
68
|
|
|
95
69
|
assert "\x1b" in coloured_out
|
|
96
70
|
assert "\x1b" not in plain_out
|
|
@@ -99,7 +73,7 @@ def test_no_color_suppresses_ansi_on_a_forced_terminal() -> None:
|
|
|
99
73
|
|
|
100
74
|
def test_no_color_console_exposes_flag_and_renders_plain_text() -> None:
|
|
101
75
|
console = make_console(no_color=True)
|
|
102
|
-
out =
|
|
76
|
+
out = capture(console, "[id]x[/id]")
|
|
103
77
|
|
|
104
78
|
assert out == "x\n"
|
|
105
79
|
assert "\x1b" not in out
|
|
@@ -125,8 +99,8 @@ def test_default_styles_is_public_and_has_the_seven_roles() -> None:
|
|
|
125
99
|
|
|
126
100
|
|
|
127
101
|
def test_make_theme_is_attachable_to_a_plain_console() -> None:
|
|
128
|
-
console =
|
|
129
|
-
out =
|
|
102
|
+
console = forced_terminal(make_theme())
|
|
103
|
+
out = capture(console, "[id]x[/id]")
|
|
130
104
|
assert console.get_style("id").render("x") in out
|
|
131
105
|
|
|
132
106
|
|
|
@@ -141,4 +115,4 @@ def test_override_value_may_be_a_prebuilt_style_object() -> None:
|
|
|
141
115
|
console = make_console(styles={"id": override})
|
|
142
116
|
|
|
143
117
|
assert console.get_style("id") == override
|
|
144
|
-
assert
|
|
118
|
+
assert capture(console, "[id]x[/id]") == "x\n"
|
|
@@ -2,6 +2,7 @@ import pytest
|
|
|
2
2
|
from rich.console import Console
|
|
3
3
|
|
|
4
4
|
from cli_error import CliError, CliExit, escape, make_console
|
|
5
|
+
from tests.helpers import capture
|
|
5
6
|
|
|
6
7
|
PROP_METHODS = ["prop_id", "prop_path", "prop_data", "prop_cmd", "prop_misc", "prop"]
|
|
7
8
|
PROP_HELPERS = {
|
|
@@ -90,9 +91,7 @@ def test_make_console_returns_console() -> None:
|
|
|
90
91
|
console = make_console()
|
|
91
92
|
assert isinstance(console, Console)
|
|
92
93
|
assert console.stderr is False
|
|
93
|
-
|
|
94
|
-
console.print("[err]x[/err]")
|
|
95
|
-
assert "x" in capture.get()
|
|
94
|
+
assert "x" in capture(console, "[err]x[/err]")
|
|
96
95
|
|
|
97
96
|
|
|
98
97
|
def test_make_console_stderr_variant_sets_flag_and_is_themed() -> None:
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
from rich.console import Console
|
|
2
|
-
from rich.theme import Theme
|
|
3
|
-
|
|
4
1
|
from cli_error import CliError, make_console, print_error, render_error
|
|
2
|
+
from tests.helpers import capture_emit, forced_terminal, theme_from
|
|
5
3
|
|
|
6
4
|
|
|
7
5
|
def _render(ex: Exception) -> str:
|
|
8
6
|
console = make_console()
|
|
9
|
-
|
|
10
|
-
print_error(ex, console)
|
|
11
|
-
return capture.get()
|
|
7
|
+
return capture_emit(console, lambda c: print_error(ex, c))
|
|
12
8
|
|
|
13
9
|
|
|
14
10
|
def test_full_layout_renders_in_documented_order() -> None:
|
|
@@ -73,20 +69,9 @@ def test_no_props_detail_or_hint_emits_only_message() -> None:
|
|
|
73
69
|
|
|
74
70
|
def test_role_markup_is_applied_to_values_on_a_terminal() -> None:
|
|
75
71
|
palette = make_console()
|
|
76
|
-
|
|
77
|
-
{role: palette.get_style(role) for role in ("id", "path", "misc", "err")}
|
|
78
|
-
)
|
|
79
|
-
console = Console(
|
|
80
|
-
theme=theme,
|
|
81
|
-
force_terminal=True,
|
|
82
|
-
color_system="standard",
|
|
83
|
-
highlight=False,
|
|
84
|
-
width=80,
|
|
85
|
-
)
|
|
72
|
+
console = forced_terminal(theme_from(palette, roles=("id", "path", "misc", "err")))
|
|
86
73
|
error = CliError("m").prop_id("who", "abc").prop_path("where", "/p").detail("dee")
|
|
87
|
-
|
|
88
|
-
print_error(error, console)
|
|
89
|
-
out = capture.get()
|
|
74
|
+
out = capture_emit(console, lambda c: print_error(error, c))
|
|
90
75
|
|
|
91
76
|
id_run = console.get_style("id").render("abc")
|
|
92
77
|
path_run = console.get_style("path").render("/p")
|
|
@@ -2,6 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
from rich.console import Console
|
|
5
|
+
from rich.style import Style
|
|
5
6
|
|
|
6
7
|
import cli_error
|
|
7
8
|
from cli_error import CliError, CliExit, CliReporter, make_console, make_theme
|
|
@@ -17,6 +18,64 @@ def make_color_console() -> Console:
|
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
def derive_console_err(console: Console) -> Console:
|
|
22
|
+
"""Auto-derived stderr console for a debug reporter wired to ``console``."""
|
|
23
|
+
return CliReporter(console, debug=True).console_err
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _Inner(Exception):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _Outer(Exception):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _inner_raise() -> None:
|
|
35
|
+
raise ValueError("boom")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _raise_boom() -> None:
|
|
39
|
+
_inner_raise()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def record_show_locals(
|
|
43
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
44
|
+
*,
|
|
45
|
+
instance_flag: bool,
|
|
46
|
+
via_handler: bool,
|
|
47
|
+
call_arg: bool | None = None,
|
|
48
|
+
) -> list[bool | None]:
|
|
49
|
+
"""Capture the ``show_locals`` value each ``print_exception`` receives.
|
|
50
|
+
|
|
51
|
+
Rich only emits a per-frame locals section when it can read the frame's
|
|
52
|
+
source file, so asserting on rendered output length is fragile across
|
|
53
|
+
environments (the locals are silently dropped where the source is
|
|
54
|
+
unavailable). These cases exercise the flag-resolution logic instead, so we
|
|
55
|
+
spy on the value handed to rich rather than on what rich renders from it.
|
|
56
|
+
"""
|
|
57
|
+
console_err = make_color_console()
|
|
58
|
+
reporter = CliReporter(
|
|
59
|
+
make_console(), debug=True, show_locals=instance_flag, console_err=console_err
|
|
60
|
+
)
|
|
61
|
+
recorded: list[bool | None] = []
|
|
62
|
+
|
|
63
|
+
def spy(*, show_locals: bool | None = None, **_: object) -> None:
|
|
64
|
+
recorded.append(show_locals)
|
|
65
|
+
|
|
66
|
+
monkeypatch.setattr(console_err, "print_exception", spy)
|
|
67
|
+
if via_handler:
|
|
68
|
+
with pytest.raises(SystemExit):
|
|
69
|
+
with reporter.handler():
|
|
70
|
+
_raise_boom()
|
|
71
|
+
else:
|
|
72
|
+
try:
|
|
73
|
+
_raise_boom()
|
|
74
|
+
except ValueError:
|
|
75
|
+
reporter.debug_traceback(show_locals=call_arg)
|
|
76
|
+
return recorded
|
|
77
|
+
|
|
78
|
+
|
|
20
79
|
@pytest.fixture
|
|
21
80
|
def console() -> Console:
|
|
22
81
|
return make_console()
|
|
@@ -353,3 +412,119 @@ def test_debug_traceback_noops_outside_except_block(
|
|
|
353
412
|
) -> None:
|
|
354
413
|
debug_reporter.debug_traceback()
|
|
355
414
|
assert capsys.readouterr().err == ""
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@pytest.mark.parametrize(
|
|
418
|
+
("instance_flag", "expected"),
|
|
419
|
+
[
|
|
420
|
+
(False, False),
|
|
421
|
+
(True, True),
|
|
422
|
+
],
|
|
423
|
+
)
|
|
424
|
+
def test_show_locals_via_handler(
|
|
425
|
+
monkeypatch: pytest.MonkeyPatch, instance_flag: bool, expected: bool
|
|
426
|
+
) -> None:
|
|
427
|
+
recorded = record_show_locals(
|
|
428
|
+
monkeypatch, instance_flag=instance_flag, via_handler=True
|
|
429
|
+
)
|
|
430
|
+
assert recorded == [expected]
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
@pytest.mark.parametrize(
|
|
434
|
+
("instance_flag", "call_arg", "expected"),
|
|
435
|
+
[
|
|
436
|
+
(False, True, True),
|
|
437
|
+
(True, False, False),
|
|
438
|
+
(True, None, True),
|
|
439
|
+
(False, None, False),
|
|
440
|
+
],
|
|
441
|
+
)
|
|
442
|
+
def test_debug_traceback_show_locals_resolution(
|
|
443
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
444
|
+
instance_flag: bool,
|
|
445
|
+
call_arg: bool | None,
|
|
446
|
+
expected: bool,
|
|
447
|
+
) -> None:
|
|
448
|
+
recorded = record_show_locals(
|
|
449
|
+
monkeypatch, instance_flag=instance_flag, via_handler=False, call_arg=call_arg
|
|
450
|
+
)
|
|
451
|
+
assert recorded == [expected]
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def test_debug_traceback_show_locals_short_circuits_when_not_debug() -> None:
|
|
455
|
+
console_err = make_color_console()
|
|
456
|
+
reporter = CliReporter(make_console(), console_err=console_err)
|
|
457
|
+
with console_err.capture() as capture:
|
|
458
|
+
try:
|
|
459
|
+
_raise_boom()
|
|
460
|
+
except ValueError:
|
|
461
|
+
reporter.debug_traceback(show_locals=True)
|
|
462
|
+
assert capture.get() == ""
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
@pytest.mark.parametrize("no_color", [True, False])
|
|
466
|
+
def test_derived_console_err_inherits_no_color_intent(no_color: bool) -> None:
|
|
467
|
+
# With no ``console_err`` injected, the derived stderr console must carry the
|
|
468
|
+
# injected stdout console's ``no_color`` intent. The parametrized pair proves
|
|
469
|
+
# the flag is load-bearing rather than hard-coded.
|
|
470
|
+
console_err = derive_console_err(make_console(no_color=no_color))
|
|
471
|
+
assert console_err.no_color is no_color
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_derived_console_err_inherits_overridden_theme_role() -> None:
|
|
475
|
+
# An overridden role on the injected console must re-resolve on the derived
|
|
476
|
+
# stderr console (theme intent, not just no_color, is inherited).
|
|
477
|
+
console = make_console(styles={"misc": "yellow"})
|
|
478
|
+
console_err = derive_console_err(console)
|
|
479
|
+
assert console_err.get_style("misc") == console.get_style("misc")
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def test_derived_console_err_does_not_inherit_geometry() -> None:
|
|
483
|
+
# Width is left to Rich's per-stream detection, not copied from the injected
|
|
484
|
+
# console — an artificially narrow injected console must not constrain it.
|
|
485
|
+
console = make_console()
|
|
486
|
+
console.width = 20
|
|
487
|
+
console_err = derive_console_err(console)
|
|
488
|
+
assert console_err.width == make_console(stderr=True).width
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def test_derived_console_err_does_not_inherit_color_system() -> None:
|
|
492
|
+
# Color system is left to Rich's per-stream detection, not copied from the
|
|
493
|
+
# injected console — a forced-color injected console must not impose its
|
|
494
|
+
# ``standard`` color system on the derived stderr stream.
|
|
495
|
+
console = make_color_console()
|
|
496
|
+
console_err = derive_console_err(console)
|
|
497
|
+
assert console_err.color_system == make_console(stderr=True).color_system
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def test_derived_console_err_tolerates_themeless_console() -> None:
|
|
501
|
+
# The public constructor accepts any Rich console; a themeless one (no role
|
|
502
|
+
# styles defined) must degrade to unstyled roles rather than crash on
|
|
503
|
+
# ``get_style`` during derivation.
|
|
504
|
+
console_err = derive_console_err(Console())
|
|
505
|
+
assert console_err.get_style("misc") == Style()
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def test_debug_traceback_emits_full_cause_chain(
|
|
509
|
+
debug_reporter: CliReporter, capsys: pytest.CaptureFixture[str]
|
|
510
|
+
) -> None:
|
|
511
|
+
# A chained exception raised through ``handler()`` under debug must emit the
|
|
512
|
+
# WHOLE cause chain to stderr, not just the outermost frame. Assert on the
|
|
513
|
+
# two type names (stable) rather than Rich's traceback layout (brittle).
|
|
514
|
+
with pytest.raises(SystemExit):
|
|
515
|
+
with debug_reporter.handler():
|
|
516
|
+
try:
|
|
517
|
+
raise _Inner("inner boom")
|
|
518
|
+
except _Inner as inner:
|
|
519
|
+
raise _Outer("outer boom") from inner
|
|
520
|
+
|
|
521
|
+
err = capsys.readouterr().err
|
|
522
|
+
assert "_Outer" in err
|
|
523
|
+
assert "_Inner" in err
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def test_injected_console_err_is_used_as_is() -> None:
|
|
527
|
+
# The explicit ``console_err`` escape hatch bypasses derivation entirely.
|
|
528
|
+
console_err = make_color_console()
|
|
529
|
+
reporter = CliReporter(make_console(no_color=True), console_err=console_err)
|
|
530
|
+
assert reporter.console_err is console_err
|
|
@@ -90,7 +90,7 @@ wheels = [
|
|
|
90
90
|
|
|
91
91
|
[[package]]
|
|
92
92
|
name = "cli-error"
|
|
93
|
-
version = "0.1.
|
|
93
|
+
version = "0.1.1"
|
|
94
94
|
source = { editable = "." }
|
|
95
95
|
dependencies = [
|
|
96
96
|
{ name = "rich" },
|
|
@@ -153,11 +153,11 @@ wheels = [
|
|
|
153
153
|
|
|
154
154
|
[[package]]
|
|
155
155
|
name = "filelock"
|
|
156
|
-
version = "3.29.
|
|
156
|
+
version = "3.29.6"
|
|
157
157
|
source = { registry = "https://pypi.org/simple" }
|
|
158
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
158
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e0/c8/35bdf04fb30755e2ed758f877edf3eb4a243c2463d3a258cc28b18b7a6e2/filelock-3.29.6.tar.gz", hash = "sha256:895c532ef3f4ef04972b9446a8c4e2931a5c399ff3c4be4c9369f2639b80f793", size = 70301, upload-time = "2026-07-06T23:08:08.577Z" }
|
|
159
159
|
wheels = [
|
|
160
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/bb/49/7467c2946ccd9617f7da38187071bdc45bb9a95df51f4d63d6622432ce4e/filelock-3.29.6-py3-none-any.whl", hash = "sha256:14d5f5597d2e0c4dbd774cfb6d8132da1db44da83732aab679d54f7dcf97ab65", size = 45478, upload-time = "2026-07-06T23:08:07.197Z" },
|
|
161
161
|
]
|
|
162
162
|
|
|
163
163
|
[[package]]
|
|
@@ -430,15 +430,15 @@ wheels = [
|
|
|
430
430
|
|
|
431
431
|
[[package]]
|
|
432
432
|
name = "python-discovery"
|
|
433
|
-
version = "1.4.
|
|
433
|
+
version = "1.4.3"
|
|
434
434
|
source = { registry = "https://pypi.org/simple" }
|
|
435
435
|
dependencies = [
|
|
436
436
|
{ name = "filelock" },
|
|
437
437
|
{ name = "platformdirs" },
|
|
438
438
|
]
|
|
439
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
439
|
+
sdist = { url = "https://files.pythonhosted.org/packages/66/26/8b004cc36f430345136f6f00fa1aa9ed596c8ed1e8504625fa79522ff39c/python_discovery-1.4.3.tar.gz", hash = "sha256:ad57d7045a862460d4a235986c33f13ed707d3aeb9153fa47eb7dfd0d4673289", size = 70438, upload-time = "2026-07-03T13:21:51.621Z" }
|
|
440
440
|
wheels = [
|
|
441
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
441
|
+
{ url = "https://files.pythonhosted.org/packages/28/78/9b77ecb4644d1bbea94d29abf78f21c47eca6eb79e9745b702ec0bed2e19/python_discovery-1.4.3-py3-none-any.whl", hash = "sha256:b6e1e4a7d9e3f6948c39746ffe8218225162d738ba39d05ab1d2f6c1cac4878c", size = 33885, upload-time = "2026-07-03T13:21:50.174Z" },
|
|
442
442
|
]
|
|
443
443
|
|
|
444
444
|
[[package]]
|
|
@@ -494,7 +494,7 @@ wheels = [
|
|
|
494
494
|
|
|
495
495
|
[[package]]
|
|
496
496
|
name = "tox"
|
|
497
|
-
version = "4.56.
|
|
497
|
+
version = "4.56.2"
|
|
498
498
|
source = { registry = "https://pypi.org/simple" }
|
|
499
499
|
dependencies = [
|
|
500
500
|
{ name = "cachetools" },
|
|
@@ -508,9 +508,9 @@ dependencies = [
|
|
|
508
508
|
{ name = "tomli-w" },
|
|
509
509
|
{ name = "virtualenv" },
|
|
510
510
|
]
|
|
511
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
511
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a4/be/0a4370df64b4610c653482dd55e0a6818fa31c551fce7659c253e3668638/tox-4.56.2.tar.gz", hash = "sha256:346442f3982a5c1b792abcbc476a90f821031154251ea3506401c4c51c62cc2a", size = 286251, upload-time = "2026-07-07T02:11:16.729Z" }
|
|
512
512
|
wheels = [
|
|
513
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
513
|
+
{ url = "https://files.pythonhosted.org/packages/03/66/34fdfb42ff381da15404bf8aa92b1b0e6ddad7263e4ec23bee3daf359d50/tox-4.56.2-py3-none-any.whl", hash = "sha256:245b60f2610e126070b289a862d6288401a387147e86a30c9316cb0ab276137b", size = 217329, upload-time = "2026-07-07T02:11:15.04Z" },
|
|
514
514
|
]
|
|
515
515
|
|
|
516
516
|
[[package]]
|
|
@@ -549,33 +549,33 @@ wheels = [
|
|
|
549
549
|
|
|
550
550
|
[[package]]
|
|
551
551
|
name = "uv"
|
|
552
|
-
version = "0.11.
|
|
553
|
-
source = { registry = "https://pypi.org/simple" }
|
|
554
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
555
|
-
wheels = [
|
|
556
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
557
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
558
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
559
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
560
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
561
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
562
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
563
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
564
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
565
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
566
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
567
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
568
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
569
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
570
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
571
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
572
|
-
{ url = "https://files.pythonhosted.org/packages/d0/
|
|
573
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
552
|
+
version = "0.11.27"
|
|
553
|
+
source = { registry = "https://pypi.org/simple" }
|
|
554
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/b8bb8bf2044561e3349ebe75be0d8abdeb19577886753b49200ec6caea0d/uv-0.11.27.tar.gz", hash = "sha256:3469204521869f0e6bdea17b02c1d86db2d0150820895653a6152cab206fb00b", size = 5978029, upload-time = "2026-07-06T21:00:29.995Z" }
|
|
555
|
+
wheels = [
|
|
556
|
+
{ url = "https://files.pythonhosted.org/packages/b2/fe/8a080fb603f276ceb2d960454b2b5998cc6e5c0c6d5dbbbeb48b141de4f2/uv-0.11.27-py3-none-linux_armv6l.whl", hash = "sha256:6cedad2185b16bd5da77096d4813159c9c3c572897537256eecdb4092941adc0", size = 25490659, upload-time = "2026-07-06T20:59:29.003Z" },
|
|
557
|
+
{ url = "https://files.pythonhosted.org/packages/d1/15/886a2ef7b00c1a4fcf706e7a6b946b3f7485a55f42d0aa9b5572caed58b6/uv-0.11.27-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:42b273459864c67f3736ae2a63929df463975d885bbc1afd8f53f7e0fb1f2269", size = 24498434, upload-time = "2026-07-06T20:59:33.042Z" },
|
|
558
|
+
{ url = "https://files.pythonhosted.org/packages/2d/6e/45335717c4372bf955c87bc05c6462dd4c0c6087ed12c3991b14cbba77cd/uv-0.11.27-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b316464a1028a43478b74424b4b78925c463758baec93cb754fb2392ba175138", size = 23248727, upload-time = "2026-07-06T20:59:36.201Z" },
|
|
559
|
+
{ url = "https://files.pythonhosted.org/packages/0a/fa/41dde1e350c5a277b797371faf96fb013c2c13ed18df6efc51ed8ec9bb5b/uv-0.11.27-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:3ca478883d78270ceed57f229d1b480dbcadf5d79b2ce9624574807d6ec48554", size = 25208126, upload-time = "2026-07-06T20:59:39.984Z" },
|
|
560
|
+
{ url = "https://files.pythonhosted.org/packages/00/e3/bc6d4af24ab533db4c741cebade3a58b91fc0563059d39e838161dd210c8/uv-0.11.27-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:232dfb7549c8a4217ab1970dea0de731926b8ca21769ed89951ad8f8c3bce8b6", size = 25030974, upload-time = "2026-07-06T20:59:43.378Z" },
|
|
561
|
+
{ url = "https://files.pythonhosted.org/packages/c3/0c/82e736930d3f78ca6c3286ef31627d7155e20191af88afc40ec29486f400/uv-0.11.27-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c0cf7c7960587a231252cbe4d7a032b5a02c29cfab3fcc4e75a915b1c5cacee", size = 25015115, upload-time = "2026-07-06T20:59:46.694Z" },
|
|
562
|
+
{ url = "https://files.pythonhosted.org/packages/a0/5b/512ebf3b24b708ea2adda4ec4d4bd23b2404d7a7ea3bd24d2b92210ab680/uv-0.11.27-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47b5863ef715ffe4c29e792c9884a4e8b8016b0628d3613ac84c696acc128a78", size = 26487914, upload-time = "2026-07-06T20:59:50.414Z" },
|
|
563
|
+
{ url = "https://files.pythonhosted.org/packages/98/d5/3871185331266156fbd09ab0ad7ddc69ca759b3a28ece5488bdb64f152d9/uv-0.11.27-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5764155d2a578a41d2827f464d4ce5b7788632e732cb304e2025fb3d043b46f", size = 27393306, upload-time = "2026-07-06T20:59:53.852Z" },
|
|
564
|
+
{ url = "https://files.pythonhosted.org/packages/24/0f/9fe3cc9b8d498f2e426b3cda90d337b076d64f19c2d55cb0b7d091c4bfe9/uv-0.11.27-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d806e1067c8eb9f8ba5dbdca46484512d224070e8473442319d4c1fbc3246f4d", size = 26563495, upload-time = "2026-07-06T20:59:56.945Z" },
|
|
565
|
+
{ url = "https://files.pythonhosted.org/packages/e7/be/18393c15ac0c982fefa34944135ead3adc565da5023d973eff5081a95a62/uv-0.11.27-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cb783b9db3868e51e8da1956aaa7e800262ad1af03dfc8b024f3b04ecb447c2", size = 26652374, upload-time = "2026-07-06T21:00:00.256Z" },
|
|
566
|
+
{ url = "https://files.pythonhosted.org/packages/b8/93/1bb8bee2b0bcc6e9655518ecaa9a792b1952dd99ae7366fe4f6c82754361/uv-0.11.27-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:8e2b86d3dee515eeba6ea001d8b3eac20b204db07d39b04187c755a490a4fba6", size = 25341573, upload-time = "2026-07-06T21:00:03.789Z" },
|
|
567
|
+
{ url = "https://files.pythonhosted.org/packages/e6/e0/fae0d1c972b25ac192ac807c1b1baf0f12070c40c8af2eb8e22310ea8846/uv-0.11.27-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:d325b8bd2665ce3ac58d33f977683cfb2cb8eb951effab06fad8518426bc0a90", size = 26051363, upload-time = "2026-07-06T21:00:07.281Z" },
|
|
568
|
+
{ url = "https://files.pythonhosted.org/packages/e5/a2/4c0c103ea431f5d70cd33f10c754b5b1de9687fea982ca1a20a4eee0a4a7/uv-0.11.27-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:3d5c9036b558a323f254e3d16038183c22c55e0d06b921f1ed9706953311e52f", size = 26148896, upload-time = "2026-07-06T21:00:10.56Z" },
|
|
569
|
+
{ url = "https://files.pythonhosted.org/packages/bb/b7/a514307a90ca9f1cb59efd0898958dea6c1092ad5e4e6b6f2eb4d55109db/uv-0.11.27-py3-none-musllinux_1_1_i686.whl", hash = "sha256:4cc04e625e7bfac6975fd384d6598169cea0030995ff3b557950ec3077974a4d", size = 25701138, upload-time = "2026-07-06T21:00:13.772Z" },
|
|
570
|
+
{ url = "https://files.pythonhosted.org/packages/bf/46/c6cfa675d727a199cfce32f7a30350ce6e47f699c3cd7f0433a619c671d4/uv-0.11.27-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:109d8e145b390d0e14f2aefd9708a7522a4236d6bee20361a7558e77a14c0531", size = 26913232, upload-time = "2026-07-06T21:00:17.233Z" },
|
|
571
|
+
{ url = "https://files.pythonhosted.org/packages/a4/04/0070ecdea37baa9d38187abedc78eed9a52a2e6cb1d4f175c580640f3456/uv-0.11.27-py3-none-win32.whl", hash = "sha256:b330d2ab6af0f79396fb350a9fed8ed1aa92516310f7533de9a8dfebc4e147cd", size = 24374672, upload-time = "2026-07-06T21:00:20.399Z" },
|
|
572
|
+
{ url = "https://files.pythonhosted.org/packages/d0/b9/f3df522d2e609fe511e5bee24b34c91ce281ce472594121fa1d5fa159d9d/uv-0.11.27-py3-none-win_amd64.whl", hash = "sha256:e20f25921ed4c46d32ac18a9b2e610c0384439fd367f647f2b68e0e1d90e9885", size = 27275452, upload-time = "2026-07-06T21:00:23.903Z" },
|
|
573
|
+
{ url = "https://files.pythonhosted.org/packages/8d/2e/4ea9c3bbf95a21c627a3dfa359f5a3266e648cd103c5ade374a6e4265c5f/uv-0.11.27-py3-none-win_arm64.whl", hash = "sha256:a41da27667e95c0df939e99e77a4d9169fb895df0f6788b2ab65ed5efdc1395f", size = 25561477, upload-time = "2026-07-06T21:00:27.333Z" },
|
|
574
574
|
]
|
|
575
575
|
|
|
576
576
|
[[package]]
|
|
577
577
|
name = "virtualenv"
|
|
578
|
-
version = "21.
|
|
578
|
+
version = "21.6.0"
|
|
579
579
|
source = { registry = "https://pypi.org/simple" }
|
|
580
580
|
dependencies = [
|
|
581
581
|
{ name = "distlib" },
|
|
@@ -583,7 +583,7 @@ dependencies = [
|
|
|
583
583
|
{ name = "platformdirs" },
|
|
584
584
|
{ name = "python-discovery" },
|
|
585
585
|
]
|
|
586
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
586
|
+
sdist = { url = "https://files.pythonhosted.org/packages/74/65/ec1d92091671e6407d3e7c1f5801413bb7b2b57630a50cae7750456ba0ed/virtualenv-21.6.0.tar.gz", hash = "sha256:e18a4d750f2b64dea73e72ffde3922f3c52365fabdc8157ebd3da20d031c4734", size = 5526111, upload-time = "2026-07-06T22:49:56.972Z" }
|
|
587
587
|
wheels = [
|
|
588
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
588
|
+
{ url = "https://files.pythonhosted.org/packages/b8/e7/2fbd0cc1653c53eed8f10670538bb547de2b3e37aacad283faa82a71094b/virtualenv-21.6.0-py3-none-any.whl", hash = "sha256:bce9d097950fef9d81129b333babfb7767072850c2f1acce0ec536708401bfd1", size = 5506216, upload-time = "2026-07-06T22:49:54.941Z" },
|
|
589
589
|
]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"sessionId":"d6c578d2-0e67-41a3-9ab3-6eb663d5b9d5","pid":25,"procStart":"26355062","acquiredAt":1783286663093}
|
cli_error-0.1.0/.tasker/.closed
DELETED
cli_error-0.1.0/.tasker/.recent
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
s01t20
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|