polytool 0.2.0__tar.gz → 0.2.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.
- {polytool-0.2.0 → polytool-0.2.1}/PKG-INFO +1 -1
- {polytool-0.2.0 → polytool-0.2.1}/pyproject.toml +3 -3
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/__init__.py +1 -1
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/__main__.py +2 -2
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/__init__.py +19 -3
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/img.py +2 -2
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/shot.py +1 -1
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/errors.py +27 -17
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/ffmpeg.py +3 -1
- {polytool-0.2.0 → polytool-0.2.1}/.gitignore +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/LICENSE +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/README.md +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/docs/README.md +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/clip.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/color.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/convert.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/cron.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/data.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/dl.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/enc.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/file.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/gen.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/net.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/pdf.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/qr.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/text.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/cli/vid.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/__init__.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/console.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/io.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/lazy.py +0 -0
- {polytool-0.2.0 → polytool-0.2.1}/src/polytool/core/progress.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polytool
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: One-binary CLI bundling 26 everyday utilities — image/video/PDF conversion, background removal, OCR, QR codes, hashing, downloads, and more
|
|
5
5
|
Project-URL: Homepage, https://github.com/k6w/polytool
|
|
6
6
|
Project-URL: Repository, https://github.com/k6w/polytool
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "polytool"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "One-binary CLI bundling 26 everyday utilities — image/video/PDF conversion, background removal, OCR, QR codes, hashing, downloads, and more"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -76,8 +76,8 @@ dev = [
|
|
|
76
76
|
]
|
|
77
77
|
|
|
78
78
|
[project.scripts]
|
|
79
|
-
polytool = "polytool.cli:
|
|
80
|
-
pt = "polytool.cli:
|
|
79
|
+
polytool = "polytool.cli:run"
|
|
80
|
+
pt = "polytool.cli:run"
|
|
81
81
|
|
|
82
82
|
[project.urls]
|
|
83
83
|
Homepage = "https://github.com/k6w/polytool"
|
|
@@ -7,6 +7,8 @@ Heavy imports (Pillow, ffmpeg, rembg) happen inside command bodies via
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import sys
|
|
11
|
+
|
|
10
12
|
import typer
|
|
11
13
|
|
|
12
14
|
from polytool import __version__
|
|
@@ -28,9 +30,7 @@ from polytool.cli import (
|
|
|
28
30
|
text,
|
|
29
31
|
vid,
|
|
30
32
|
)
|
|
31
|
-
from polytool.core.errors import
|
|
32
|
-
|
|
33
|
-
install_excepthook()
|
|
33
|
+
from polytool.core.errors import PolytoolError, render_panel
|
|
34
34
|
|
|
35
35
|
app = typer.Typer(
|
|
36
36
|
name="polytool",
|
|
@@ -38,9 +38,25 @@ app = typer.Typer(
|
|
|
38
38
|
no_args_is_help=True,
|
|
39
39
|
add_completion=False,
|
|
40
40
|
rich_markup_mode="rich",
|
|
41
|
+
# We handle PolytoolError ourselves via the `main()` wrapper below; disable
|
|
42
|
+
# Typer's pretty-traceback so it doesn't squash our hints.
|
|
43
|
+
pretty_exceptions_enable=False,
|
|
41
44
|
)
|
|
42
45
|
|
|
43
46
|
|
|
47
|
+
def run() -> None:
|
|
48
|
+
"""Console-script entry point — runs the Typer app and renders PolytoolError nicely.
|
|
49
|
+
|
|
50
|
+
(Defined with a unique name to avoid clashing with the ``@app.callback()``
|
|
51
|
+
function below — both would otherwise be named ``main`` in this module.)
|
|
52
|
+
"""
|
|
53
|
+
try:
|
|
54
|
+
app()
|
|
55
|
+
except PolytoolError as exc:
|
|
56
|
+
render_panel(exc.message, exc.hint)
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
|
|
44
60
|
def _version_callback(value: bool) -> None:
|
|
45
61
|
if value:
|
|
46
62
|
typer.echo(f"polytool {__version__}")
|
|
@@ -41,7 +41,7 @@ def _open_image(source: Path):
|
|
|
41
41
|
except ImportError as exc:
|
|
42
42
|
raise PolytoolError(
|
|
43
43
|
"HEIC support missing.",
|
|
44
|
-
hint="Install: [cyan]uv tool install 'polytool[img]'[/cyan]",
|
|
44
|
+
hint="Install: [cyan]uv tool install 'polytool\\[img]'[/cyan]",
|
|
45
45
|
) from exc
|
|
46
46
|
if suffix == ".avif":
|
|
47
47
|
try:
|
|
@@ -49,7 +49,7 @@ def _open_image(source: Path):
|
|
|
49
49
|
except ImportError as exc:
|
|
50
50
|
raise PolytoolError(
|
|
51
51
|
"AVIF support missing.",
|
|
52
|
-
hint="Install: [cyan]uv tool install 'polytool[img]'[/cyan]",
|
|
52
|
+
hint="Install: [cyan]uv tool install 'polytool\\[img]'[/cyan]",
|
|
53
53
|
) from exc
|
|
54
54
|
|
|
55
55
|
try:
|
|
@@ -121,7 +121,7 @@ def cmd_install() -> None:
|
|
|
121
121
|
except FileNotFoundError as exc:
|
|
122
122
|
raise PolytoolError(
|
|
123
123
|
"Playwright not found.",
|
|
124
|
-
hint="Install: [cyan]uv tool install 'polytool[shot]'[/cyan]",
|
|
124
|
+
hint="Install: [cyan]uv tool install 'polytool\\[shot]'[/cyan]",
|
|
125
125
|
) from exc
|
|
126
126
|
except subprocess.CalledProcessError as exc:
|
|
127
127
|
raise PolytoolError(f"playwright install failed (exit {exc.returncode})") from exc
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import sys
|
|
6
5
|
from typing import NoReturn
|
|
7
6
|
|
|
8
7
|
import typer
|
|
@@ -11,8 +10,20 @@ from rich.panel import Panel
|
|
|
11
10
|
from polytool.core.console import err_console
|
|
12
11
|
|
|
13
12
|
|
|
13
|
+
def render_panel(message: str, hint: str | None) -> None:
|
|
14
|
+
"""Render the standard red error panel to stderr."""
|
|
15
|
+
body = f"[red bold]{message}[/red bold]"
|
|
16
|
+
if hint:
|
|
17
|
+
body += f"\n\n[dim]Hint:[/dim] {hint}"
|
|
18
|
+
err_console.print(Panel(body, border_style="red", title="Error"))
|
|
19
|
+
|
|
20
|
+
|
|
14
21
|
class PolytoolError(Exception):
|
|
15
|
-
"""A user-facing error with an optional fix hint.
|
|
22
|
+
"""A user-facing error with an optional fix hint.
|
|
23
|
+
|
|
24
|
+
The CLI entry point (``polytool.cli.main``) catches this and renders
|
|
25
|
+
``render_panel`` instead of letting Python or Typer print a stack trace.
|
|
26
|
+
"""
|
|
16
27
|
|
|
17
28
|
def __init__(self, message: str, hint: str | None = None) -> None:
|
|
18
29
|
super().__init__(message)
|
|
@@ -24,10 +35,16 @@ class MissingExtraError(PolytoolError):
|
|
|
24
35
|
"""Raised when an optional dependency group isn't installed."""
|
|
25
36
|
|
|
26
37
|
def __init__(self, module: str, extra: str) -> None:
|
|
38
|
+
from rich.markup import escape
|
|
39
|
+
|
|
27
40
|
message = f"Missing optional dependency: '{module}' (from the '{extra}' extra)."
|
|
41
|
+
# rich.markup.escape only escapes `[` — `]` is left literal because
|
|
42
|
+
# Rich only treats `[` as the markup-tag opener.
|
|
43
|
+
spec = escape(f"polytool[{extra}]")
|
|
44
|
+
full = escape("polytool[full]")
|
|
28
45
|
hint = (
|
|
29
|
-
f"Install with: [cyan]uv tool install '
|
|
30
|
-
f"Or for everything: [cyan]uv tool install '
|
|
46
|
+
f"Install with: [cyan]uv tool install '{spec}'[/cyan]\n"
|
|
47
|
+
f"Or for everything: [cyan]uv tool install '{full}'[/cyan]"
|
|
31
48
|
)
|
|
32
49
|
super().__init__(message, hint)
|
|
33
50
|
self.module = module
|
|
@@ -36,21 +53,14 @@ class MissingExtraError(PolytoolError):
|
|
|
36
53
|
|
|
37
54
|
def fail(message: str, hint: str | None = None) -> NoReturn:
|
|
38
55
|
"""Print a red error panel and exit with code 1."""
|
|
39
|
-
|
|
40
|
-
if hint:
|
|
41
|
-
body += f"\n\n[dim]Hint:[/dim] {hint}"
|
|
42
|
-
err_console.print(Panel(body, border_style="red", title="Error"))
|
|
56
|
+
render_panel(message, hint)
|
|
43
57
|
raise typer.Exit(code=1)
|
|
44
58
|
|
|
45
59
|
|
|
46
60
|
def install_excepthook() -> None:
|
|
47
|
-
"""
|
|
48
|
-
original = sys.excepthook
|
|
49
|
-
|
|
50
|
-
def hook(exc_type, exc_value, tb):
|
|
51
|
-
if isinstance(exc_value, PolytoolError):
|
|
52
|
-
fail(exc_value.message, exc_value.hint)
|
|
53
|
-
return
|
|
54
|
-
original(exc_type, exc_value, tb)
|
|
61
|
+
"""No-op kept for backwards compatibility.
|
|
55
62
|
|
|
56
|
-
sys.excepthook
|
|
63
|
+
Earlier versions installed a ``sys.excepthook``. The CLI entry-point now
|
|
64
|
+
handles ``PolytoolError`` directly via a try/except wrapper.
|
|
65
|
+
"""
|
|
66
|
+
return
|
|
@@ -28,8 +28,10 @@ def ffmpeg_path() -> str:
|
|
|
28
28
|
|
|
29
29
|
from polytool.core.errors import PolytoolError
|
|
30
30
|
|
|
31
|
+
from rich.markup import escape
|
|
32
|
+
|
|
31
33
|
raise PolytoolError(
|
|
32
34
|
"ffmpeg not found.",
|
|
33
35
|
hint="Install ffmpeg system-wide, or install the 'vid' extra: "
|
|
34
|
-
"[cyan]uv tool install 'polytool[vid]'[/cyan]",
|
|
36
|
+
f"[cyan]uv tool install '{escape('polytool[vid]')}'[/cyan]",
|
|
35
37
|
)
|
|
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
|