Add YAPF style + ignore, and format tracked Python files

This commit is contained in:
2025-12-29 18:42:02 -08:00
parent c019c00aed
commit 507946a3e4
108 changed files with 11664 additions and 6494 deletions

View File

@@ -59,7 +59,8 @@ def find_requirements(root: Path) -> Optional[Path]:
for p in root.iterdir():
if not p.is_dir():
continue
for child in (p,):
for child in (p,
):
candidate = child / "requirements.txt"
if candidate.exists():
return candidate
@@ -68,10 +69,22 @@ def find_requirements(root: Path) -> Optional[Path]:
return None
def install_requirements(venv_py: Path, req_path: Path, reinstall: bool = False) -> bool:
def install_requirements(
venv_py: Path,
req_path: Path,
reinstall: bool = False
) -> bool:
try:
print(f"Installing {req_path} into venv ({venv_py})...")
subprocess.run([str(venv_py), "-m", "pip", "install", "--upgrade", "pip"], check=True)
subprocess.run(
[str(venv_py),
"-m",
"pip",
"install",
"--upgrade",
"pip"],
check=True
)
install_cmd = [str(venv_py), "-m", "pip", "install", "-r", str(req_path)]
if reinstall:
install_cmd = [
@@ -138,7 +151,11 @@ def verify_imports(venv_py: Path, packages: List[str]) -> bool:
for pkg in packages:
try:
out = subprocess.run(
[str(venv_py), "-m", "pip", "show", pkg],
[str(venv_py),
"-m",
"pip",
"show",
pkg],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
@@ -158,7 +175,9 @@ def verify_imports(venv_py: Path, packages: List[str]) -> bool:
import_name = import_map.get(pkg, pkg)
try:
subprocess.run(
[str(venv_py), "-c", f"import {import_name}"],
[str(venv_py),
"-c",
f"import {import_name}"],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
@@ -167,7 +186,10 @@ def verify_imports(venv_py: Path, packages: List[str]) -> bool:
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
missing.append(pkg)
if missing:
print("The following packages were not importable in the venv:", ", ".join(missing))
print(
"The following packages were not importable in the venv:",
", ".join(missing)
)
return False
return True
@@ -199,7 +221,9 @@ def install_service_windows(
try:
schtasks = shutil.which("schtasks")
if not schtasks:
print("schtasks not available on this system; cannot install Windows scheduled task.")
print(
"schtasks not available on this system; cannot install Windows scheduled task."
)
return False
bat = repo_root / "run-client.bat"
@@ -238,7 +262,9 @@ def uninstall_service_windows(service_name: str) -> bool:
try:
schtasks = shutil.which("schtasks")
if not schtasks:
print("schtasks not available on this system; cannot remove scheduled task.")
print(
"schtasks not available on this system; cannot remove scheduled task."
)
return False
cmd = [schtasks, "/Delete", "/TN", service_name, "/F"]
subprocess.run(cmd, check=True)
@@ -253,13 +279,25 @@ def uninstall_service_windows(service_name: str) -> bool:
def install_service_systemd(
service_name: str, repo_root: Path, venv_py: Path, headless: bool = True, detached: bool = True
service_name: str,
repo_root: Path,
venv_py: Path,
headless: bool = True,
detached: bool = True
) -> bool:
try:
systemctl = shutil.which("systemctl")
if not systemctl:
print("systemctl not available; falling back to crontab @reboot (if present).")
return install_service_cron(service_name, repo_root, venv_py, headless, detached)
print(
"systemctl not available; falling back to crontab @reboot (if present)."
)
return install_service_cron(
service_name,
repo_root,
venv_py,
headless,
detached
)
unit_dir = Path.home() / ".config" / "systemd" / "user"
unit_dir.mkdir(parents=True, exist_ok=True)
@@ -270,7 +308,12 @@ def install_service_systemd(
unit_file.write_text(content, encoding="utf-8")
subprocess.run([systemctl, "--user", "daemon-reload"], check=True)
subprocess.run(
[systemctl, "--user", "enable", "--now", f"{service_name}.service"], check=True
[systemctl,
"--user",
"enable",
"--now",
f"{service_name}.service"],
check=True
)
print(f"systemd user service '{service_name}' installed and started.")
return True
@@ -289,9 +332,15 @@ def uninstall_service_systemd(service_name: str) -> bool:
print("systemctl not available; cannot uninstall systemd service.")
return False
subprocess.run(
[systemctl, "--user", "disable", "--now", f"{service_name}.service"], check=False
[systemctl,
"--user",
"disable",
"--now",
f"{service_name}.service"],
check=False
)
unit_file = Path.home() / ".config" / "systemd" / "user" / f"{service_name}.service"
unit_file = Path.home(
) / ".config" / "systemd" / "user" / f"{service_name}.service"
if unit_file.exists():
unit_file.unlink()
subprocess.run([systemctl, "--user", "daemon-reload"], check=True)
@@ -303,7 +352,11 @@ def uninstall_service_systemd(service_name: str) -> bool:
def install_service_cron(
service_name: str, repo_root: Path, venv_py: Path, headless: bool = True, detached: bool = True
service_name: str,
repo_root: Path,
venv_py: Path,
headless: bool = True,
detached: bool = True
) -> bool:
try:
crontab = shutil.which("crontab")
@@ -312,7 +365,11 @@ def install_service_cron(
return False
entry = f"@reboot {venv_py} {str(repo_root / 'run_client.py')} --detached {'--headless' if headless else '--gui'} # {service_name}\n"
proc = subprocess.run(
[crontab, "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
[crontab,
"-l"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
existing = proc.stdout if proc.returncode == 0 else ""
if entry.strip() in existing:
@@ -337,7 +394,11 @@ def uninstall_service_cron(service_name: str, repo_root: Path, venv_py: Path) ->
print("crontab not available; cannot remove reboot cron job.")
return False
proc = subprocess.run(
[crontab, "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
[crontab,
"-l"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if proc.returncode != 0:
print("No crontab found for user; nothing to remove.")
@@ -356,21 +417,37 @@ def uninstall_service_cron(service_name: str, repo_root: Path, venv_py: Path) ->
def install_service_auto(
service_name: str, repo_root: Path, venv_py: Path, headless: bool = True, detached: bool = True
service_name: str,
repo_root: Path,
venv_py: Path,
headless: bool = True,
detached: bool = True
) -> bool:
try:
if os.name == "nt":
return install_service_windows(
service_name, repo_root, venv_py, headless=headless, detached=detached
service_name,
repo_root,
venv_py,
headless=headless,
detached=detached
)
else:
if shutil.which("systemctl"):
return install_service_systemd(
service_name, repo_root, venv_py, headless=headless, detached=detached
service_name,
repo_root,
venv_py,
headless=headless,
detached=detached
)
else:
return install_service_cron(
service_name, repo_root, venv_py, headless=headless, detached=detached
service_name,
repo_root,
venv_py,
headless=headless,
detached=detached
)
except Exception as exc:
print("install_service_auto error:", exc)
@@ -391,7 +468,11 @@ def uninstall_service_auto(service_name: str, repo_root: Path, venv_py: Path) ->
return False
def print_activation_instructions(repo_root: Path, venv_dir: Path, venv_py: Path) -> None:
def print_activation_instructions(
repo_root: Path,
venv_dir: Path,
venv_py: Path
) -> None:
print("\nActivation and run examples:")
# PowerShell
print(f" PowerShell:\n . {shlex.quote(str(venv_dir))}\\Scripts\\Activate.ps1")
@@ -417,7 +498,9 @@ def detach_kwargs_for_platform():
return kwargs
def find_venv_python(repo_root: Path, venv_arg: Optional[str], venv_name: str) -> Optional[Path]:
def find_venv_python(repo_root: Path,
venv_arg: Optional[str],
venv_name: str) -> Optional[Path]:
# venv_arg may be a python executable or a directory
if venv_arg:
p = Path(venv_arg)
@@ -454,7 +537,9 @@ def _python_can_import(python_exe: Path, modules: List[str]) -> bool:
# Build a short import test string. Use semicolons to ensure any import error results in non-zero exit.
imports = ";".join([f"import {m}" for m in modules])
out = subprocess.run(
[str(python_exe), "-c", imports],
[str(python_exe),
"-c",
imports],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=10,
@@ -466,11 +551,17 @@ def _python_can_import(python_exe: Path, modules: List[str]) -> bool:
def main(argv: Optional[List[str]] = None) -> int:
p = argparse.ArgumentParser(
description="Run hydrus_client.py using the repo-local venv Python (top-level helper)"
description=
"Run hydrus_client.py using the repo-local venv Python (top-level helper)"
)
p.add_argument("--venv", help="Path to venv dir or python executable (overrides default .venv)")
p.add_argument(
"--venv-name", default=".venv", help="Name of the venv folder to look for (default: .venv)"
"--venv",
help="Path to venv dir or python executable (overrides default .venv)"
)
p.add_argument(
"--venv-name",
default=".venv",
help="Name of the venv folder to look for (default: .venv)"
)
p.add_argument(
"--client",
@@ -490,22 +581,26 @@ def main(argv: Optional[List[str]] = None) -> int:
p.add_argument(
"--reinstall",
action="store_true",
help="Force re-install dependencies from requirements.txt into the venv (uses --force-reinstall)",
help=
"Force re-install dependencies from requirements.txt into the venv (uses --force-reinstall)",
)
p.add_argument(
"--verify",
action="store_true",
help="Verify that packages from requirements.txt are importable in the venv (after install)",
help=
"Verify that packages from requirements.txt are importable in the venv (after install)",
)
p.add_argument(
"--no-verify",
action="store_true",
help="Skip verification and do not prompt to install missing dependencies; proceed to run with the chosen Python",
help=
"Skip verification and do not prompt to install missing dependencies; proceed to run with the chosen Python",
)
p.add_argument(
"--headless",
action="store_true",
help="Attempt to launch the client without showing the Qt GUI (best-effort). Default for subsequent runs; first run will show GUI unless --headless is supplied",
help=
"Attempt to launch the client without showing the Qt GUI (best-effort). Default for subsequent runs; first run will show GUI unless --headless is supplied",
)
p.add_argument(
"--gui",
@@ -513,12 +608,15 @@ def main(argv: Optional[List[str]] = None) -> int:
help="Start the client with the GUI visible (overrides headless/default) ",
)
p.add_argument(
"--detached", action="store_true", help="Start the client and do not wait (detached)"
"--detached",
action="store_true",
help="Start the client and do not wait (detached)"
)
p.add_argument(
"--install-service",
action="store_true",
help="Install a user-level start-on-boot service/scheduled task for the hydrus client",
help=
"Install a user-level start-on-boot service/scheduled task for the hydrus client",
)
p.add_argument(
"--uninstall-service",
@@ -531,7 +629,9 @@ def main(argv: Optional[List[str]] = None) -> int:
help="Name of the service / scheduled task to install (default: hydrus-client)",
)
p.add_argument(
"--cwd", default=None, help="Working directory to start the client in (default: repo root)"
"--cwd",
default=None,
help="Working directory to start the client in (default: repo root)"
)
p.add_argument("--quiet", action="store_true", help="Reduce output")
p.add_argument(
@@ -557,9 +657,13 @@ def main(argv: Optional[List[str]] = None) -> int:
def _is_running_in_virtualenv() -> bool:
try:
return hasattr(sys, "real_prefix") or getattr(sys, "base_prefix", None) != getattr(
sys, "prefix", None
)
return hasattr(sys,
"real_prefix") or getattr(sys,
"base_prefix",
None
) != getattr(sys,
"prefix",
None)
except Exception:
return False
@@ -619,7 +723,9 @@ def main(argv: Optional[List[str]] = None) -> int:
"Create one with: python -m venv .venv (inside your hydrus repo) and then re-run this helper, or use the installer to create it for you."
)
print_activation_instructions(
repo_root, repo_root / args.venv_name, repo_root / args.venv_name
repo_root,
repo_root / args.venv_name,
repo_root / args.venv_name
)
return 2
@@ -645,7 +751,9 @@ def main(argv: Optional[List[str]] = None) -> int:
if pkgs:
okv = verify_imports(venv_py, pkgs)
if not okv:
print("Verification failed; see instructions above to re-run installation.")
print(
"Verification failed; see instructions above to re-run installation."
)
# If not installing but user asked to verify, do verification only
if args.verify and not (args.install_deps or args.reinstall):
@@ -698,7 +806,11 @@ def main(argv: Optional[List[str]] = None) -> int:
if args.install_service:
ok = install_service_auto(
args.service_name, repo_root, venv_py, headless=use_headless, detached=True
args.service_name,
repo_root,
venv_py,
headless=use_headless,
detached=True
)
return 0 if ok else 6
if args.uninstall_service:
@@ -723,7 +835,11 @@ def main(argv: Optional[List[str]] = None) -> int:
env = os.environ.copy()
if headless:
if os.name == "posix" and shutil.which("xvfb-run"):
xvfb_cmd = ["xvfb-run", "--auto-servernum", "--server-args=-screen 0 1024x768x24"]
xvfb_cmd = [
"xvfb-run",
"--auto-servernum",
"--server-args=-screen 0 1024x768x24"
]
cmd = xvfb_cmd + cmd
if not args.quiet:
print("Headless: using xvfb-run to provide a virtual X server")
@@ -744,7 +860,10 @@ def main(argv: Optional[List[str]] = None) -> int:
if args.detached:
try:
kwargs = detach_kwargs_for_platform()
kwargs.update({"cwd": str(cwd), "env": env})
kwargs.update({
"cwd": str(cwd),
"env": env
})
subprocess.Popen(cmd, **kwargs)
print("Hydrus client launched (detached).")
return 0