Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user