From 32acfa6b9fcdf5e903e6b488c5936983fdf6e696 Mon Sep 17 00:00:00 2001 From: AramJonghu Date: Mon, 25 May 2026 19:03:00 +0200 Subject: [PATCH] pyright/ruff error fixes. Autoinstall check of autocomplete --- cli/src/zshell/__init__.py | 34 +++++++++++++++++++ cli/src/zshell/subcommands/record.py | 44 +++++++++++-------------- cli/src/zshell/subcommands/scheme.py | 20 +++-------- cli/src/zshell/subcommands/wallpaper.py | 4 +-- 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/cli/src/zshell/__init__.py b/cli/src/zshell/__init__.py index 882f858..9622faf 100644 --- a/cli/src/zshell/__init__.py +++ b/cli/src/zshell/__init__.py @@ -1,5 +1,10 @@ from __future__ import annotations +import sys +from pathlib import Path + +import click import typer +from typer._completion_shared import install, _get_shell_name from zshell.subcommands import shell, scheme, screenshot, wallpaper, record app = typer.Typer(name="zshell-cli") @@ -11,5 +16,34 @@ app.add_typer(wallpaper.app, name="wallpaper") app.add_typer(record.app, name="record") +def _completion_installed() -> bool: + shell = _get_shell_name() + match shell: + case "zsh": + return (Path.home() / ".zfunc" / "_zshell-cli").exists() + case "bash": + return (Path.home() / ".bash_completions" / "zshell-cli.sh").exists() + case "fish": + return (Path.home() / ".config" / "fish" / "completions" / "zshell-cli.fish").exists() + return False + + +def _auto_install_completion() -> None: + if not sys.stdout.isatty(): + return + if _completion_installed(): + return + shell = _get_shell_name() + if shell is None: + return + try: + _, path = install(prog_name="zshell-cli") + click.secho(f"zshell-cli: Shell completion installed ({shell}: {path})", fg="green") + click.echo("zshell-cli: Restart your shell or source the file to enable tab-completion.") + except Exception: + pass + + def main() -> None: + _auto_install_completion() app() diff --git a/cli/src/zshell/subcommands/record.py b/cli/src/zshell/subcommands/record.py index 986687c..00a9c07 100644 --- a/cli/src/zshell/subcommands/record.py +++ b/cli/src/zshell/subcommands/record.py @@ -18,8 +18,7 @@ TEMP_RECORDING = STATE_DIR / "recording.mp4" REPLAY_RECORDING = STATE_DIR / "replay.mp4" NOTIF_ID_FILE = STATE_DIR / "notifid.txt" -RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR", - str(Path(HOME) / "Videos/Recordings")) +RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR", str(Path(HOME) / "Videos/Recordings")) def _read_extra_args() -> list[str]: @@ -36,7 +35,7 @@ def _is_recording() -> bool: return subprocess.run(["pidof", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0 -def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5000) -> Optional[int]: +def _notify(summary: str, body: str = "", actions: list | None = None, timeout: int = 5000) -> Optional[int]: args = ["notify-send", summary, body, "-t", str(timeout), "-p"] if actions: for action in actions: @@ -49,14 +48,12 @@ def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5 def _close_notification(notif_id: int): - subprocess.run(["notify-send", "--close", str(notif_id)], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run(["notify-send", "--close", str(notif_id)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) def _get_monitors() -> list[dict]: try: - res = subprocess.run(["hyprctl", "monitors", "-j"], - capture_output=True, text=True) + res = subprocess.run(["hyprctl", "monitors", "-j"], capture_output=True, text=True) return json.loads(res.stdout) except Exception: return [] @@ -92,6 +89,7 @@ def _slurp_region() -> Optional[str]: def _parse_geometry(geometry: str) -> Optional[tuple[int, int, int, int]]: import re + match = re.match(r"(\d+)x(\d+)\+(\d+)\+(\d+)", geometry) if match: return int(match.group(3)), int(match.group(4)), int(match.group(1)), int(match.group(2)) @@ -139,8 +137,7 @@ def start_recording(region: Optional[str], sound: bool): cmd.extend(extra_args) cmd.extend(["-o", str(TEMP_RECORDING)]) - subprocess.Popen(cmd, start_new_session=True, - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.Popen(cmd, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) notif_id = _notify("Recording started", f"Saving to {TEMP_RECORDING}") if notif_id is not None: @@ -148,14 +145,12 @@ def start_recording(region: Optional[str], sound: bool): time.sleep(1) if not _is_recording(): - _notify("Recording failed", - "Check gpu-screen-recorder output.", timeout=5000) + _notify("Recording failed", "Check gpu-screen-recorder output.", timeout=5000) raise typer.Exit(code=1) def stop_recording(clipboard: bool): - subprocess.run(["pkill", "-f", RECORDER], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run(["pkill", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) for _ in range(50): if not _is_recording(): @@ -178,30 +173,31 @@ def stop_recording(clipboard: bool): NOTIF_ID_FILE.unlink() if clipboard: - subprocess.run(["wl-copy", "--type", "text/uri-list", f"file://{final_path}"], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + ["wl-copy", "--type", "text/uri-list", f"file://{final_path}"], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) _notify("Recording stopped", f"Saved to {final_path}", timeout=5000) def toggle_pause(): - subprocess.run(["pkill", "-USR2", "-f", RECORDER], - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run(["pkill", "-USR2", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) typer.echo("Toggled pause.") @app.command() def record( region: Optional[str] = typer.Option( - None, "--region", "-r", + None, + "--region", + "-r", help="Record a region. Use 'slurp' (or omit value) to select interactively, or give 'WxH+X+Y'.", ), - sound: bool = typer.Option( - False, "--sound", "-s", help="Record audio from default output."), - pause: bool = typer.Option( - False, "--pause", "-p", help="Toggle pause/resume."), - clipboard: bool = typer.Option( - False, "--clipboard", "-c", help="Copy the final recording path to clipboard."), + sound: bool = typer.Option(False, "--sound", "-s", help="Record audio from default output."), + pause: bool = typer.Option(False, "--pause", "-p", help="Toggle pause/resume."), + clipboard: bool = typer.Option(False, "--clipboard", "-c", help="Copy the final recording path to clipboard."), ): """Start or stop a screen recording with gpu-screen-recorder.""" if pause: diff --git a/cli/src/zshell/subcommands/scheme.py b/cli/src/zshell/subcommands/scheme.py index 4b18eef..067489d 100644 --- a/cli/src/zshell/subcommands/scheme.py +++ b/cli/src/zshell/subcommands/scheme.py @@ -105,9 +105,7 @@ def list_presets( @app.command() def generate( - image_path: Optional[Path] = typer.Option( - None, help="Path to source image. Required for image mode." - ), + image_path: Optional[Path] = typer.Option(None, help="Path to source image. Required for image mode."), scheme: Optional[str] = typer.Option( None, help="Color scheme algorithm to use for image mode. Ignored in preset mode.", @@ -266,15 +264,11 @@ def generate( def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct: diff = difference_degrees(from_hct.hue, to_hct.hue) rotation = min(diff * 0.8, 100) - output_hue = sanitize_degrees_double( - from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue) - ) + output_hue = sanitize_degrees_double(from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue)) tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost))) return Hct.from_hct(output_hue, from_hct.chroma, tone) - def terminal_palette( - colors: dict[str, str], mode: str, variant: str - ) -> dict[str, str]: + def terminal_palette(colors: dict[str, str], mode: str, variant: str) -> dict[str, str]: light = mode.lower() == "light" key_hex = ( @@ -576,17 +570,13 @@ def generate( schemes = list_schemes() if accent and p_scheme in schemes: meta = schemes[p_scheme] - var_accents = next( - (v.accents for v in meta.variants if v.id == p_variant), () - ) + var_accents = next((v.accents for v in meta.variants if v.id == p_variant), ()) if accent not in var_accents: available = ", ".join(var_accents) if var_accents else "none" raise typer.BadParameter( f"Accent '{accent}' not available for '{p_scheme}:{p_variant}'. Available accents: {available}" ) - palette_obj = get_palette( - p_scheme, p_variant, mode or config_mode, accent=accent - ) + palette_obj = get_palette(p_scheme, p_variant, mode or config_mode, accent=accent) colors = palette_obj.colors effective_mode = palette_obj.mode name = palette_obj.scheme diff --git a/cli/src/zshell/subcommands/wallpaper.py b/cli/src/zshell/subcommands/wallpaper.py index b803b54..58f6c85 100644 --- a/cli/src/zshell/subcommands/wallpaper.py +++ b/cli/src/zshell/subcommands/wallpaper.py @@ -34,9 +34,9 @@ def lockscreen( return if size[0] < 3840 or size[1] < 2160: - img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST) + img = img.resize((size[0] // 2, size[1] // 2), Image.Resampling.NEAREST) else: - img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST) + img = img.resize((size[0] // 4, size[1] // 4), Image.Resampling.NEAREST) img = img.filter(ImageFilter.GaussianBlur(blur_amount))