Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
@@ -50,7 +50,11 @@ except Exception:
|
||||
def detach_kwargs_for_platform():
|
||||
kwargs = {}
|
||||
if os.name == "nt":
|
||||
CREATE_NEW_PROCESS_GROUP = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
||||
CREATE_NEW_PROCESS_GROUP = getattr(
|
||||
subprocess,
|
||||
"CREATE_NEW_PROCESS_GROUP",
|
||||
0
|
||||
)
|
||||
DETACHED_PROCESS = getattr(subprocess, "DETACHED_PROCESS", 0)
|
||||
flags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
|
||||
if flags:
|
||||
@@ -70,7 +74,11 @@ def find_git_executable() -> Optional[str]:
|
||||
# Quick sanity check
|
||||
try:
|
||||
subprocess.run(
|
||||
[git, "--version"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
[git,
|
||||
"--version"],
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
return git
|
||||
except Exception:
|
||||
@@ -88,7 +96,11 @@ def is_git_repo(path: Path) -> bool:
|
||||
return False
|
||||
try:
|
||||
subprocess.run(
|
||||
[git, "-C", str(path), "rev-parse", "--is-inside-work-tree"],
|
||||
[git,
|
||||
"-C",
|
||||
str(path),
|
||||
"rev-parse",
|
||||
"--is-inside-work-tree"],
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
@@ -99,7 +111,11 @@ def is_git_repo(path: Path) -> bool:
|
||||
|
||||
|
||||
def run_git_clone(
|
||||
git: str, repo: str, dest: Path, branch: Optional[str] = None, depth: Optional[int] = None
|
||||
git: str,
|
||||
repo: str,
|
||||
dest: Path,
|
||||
branch: Optional[str] = None,
|
||||
depth: Optional[int] = None
|
||||
) -> None:
|
||||
# Build git clone with options before the repository argument. Support shallow clones
|
||||
# via --depth when requested.
|
||||
@@ -113,7 +129,12 @@ def run_git_clone(
|
||||
if branch:
|
||||
cmd += ["--branch", branch]
|
||||
cmd += [repo, str(dest)]
|
||||
logging.info("Cloning: %s -> %s (depth=%s)", repo, dest, str(depth) if depth else "full")
|
||||
logging.info(
|
||||
"Cloning: %s -> %s (depth=%s)",
|
||||
repo,
|
||||
dest,
|
||||
str(depth) if depth else "full"
|
||||
)
|
||||
subprocess.run(cmd, check=True)
|
||||
|
||||
|
||||
@@ -123,7 +144,11 @@ def run_git_pull(git: str, dest: Path) -> None:
|
||||
|
||||
|
||||
def download_and_extract_zip(
|
||||
repo_url: str, dest: Path, branch_candidates: Tuple[str, ...] = ("main", "master")
|
||||
repo_url: str,
|
||||
dest: Path,
|
||||
branch_candidates: Tuple[str,
|
||||
...] = ("main",
|
||||
"master")
|
||||
) -> None:
|
||||
"""Download the GitHub repo zip and extract it into dest.
|
||||
|
||||
@@ -181,7 +206,10 @@ def download_and_extract_zip(
|
||||
target.unlink()
|
||||
shutil.move(str(entry), str(dest))
|
||||
logging.info(
|
||||
"Downloaded and extracted %s (branch: %s) into %s", repo_url, branch, dest
|
||||
"Downloaded and extracted %s (branch: %s) into %s",
|
||||
repo_url,
|
||||
branch,
|
||||
dest
|
||||
)
|
||||
return
|
||||
except Exception as exc:
|
||||
@@ -277,9 +305,9 @@ def maybe_reexec_under_project_venv(root: Path, disable: bool = False) -> None:
|
||||
script_path = Path(sys.argv[0]).resolve()
|
||||
except Exception:
|
||||
script_path = None
|
||||
args = [str(py), str(script_path) if script_path is not None else sys.argv[0]] + sys.argv[
|
||||
1:
|
||||
]
|
||||
args = [str(py),
|
||||
str(script_path) if script_path is not None else sys.argv[0]
|
||||
] + sys.argv[1:]
|
||||
logging.debug("Exec args: %s", args)
|
||||
os.execvpe(str(py), args, env)
|
||||
except Exception as exc:
|
||||
@@ -325,12 +353,21 @@ def fix_permissions_windows(path: Path, user: Optional[str] = None) -> bool:
|
||||
except Exception:
|
||||
user = getpass.getuser()
|
||||
|
||||
logging.info("Attempting Windows ownership/ACL fix for %s (owner=%s)", path, user)
|
||||
logging.info(
|
||||
"Attempting Windows ownership/ACL fix for %s (owner=%s)",
|
||||
path,
|
||||
user
|
||||
)
|
||||
|
||||
# Try to take ownership (best-effort)
|
||||
try:
|
||||
subprocess.run(
|
||||
["takeown", "/F", str(path), "/R", "/D", "Y"],
|
||||
["takeown",
|
||||
"/F",
|
||||
str(path),
|
||||
"/R",
|
||||
"/D",
|
||||
"Y"],
|
||||
check=False,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
@@ -341,14 +378,28 @@ def fix_permissions_windows(path: Path, user: Optional[str] = None) -> bool:
|
||||
rc_setowner = 1
|
||||
rc_grant = 1
|
||||
try:
|
||||
out = subprocess.run(["icacls", str(path), "/setowner", user, "/T", "/C"], check=False)
|
||||
out = subprocess.run(
|
||||
["icacls",
|
||||
str(path),
|
||||
"/setowner",
|
||||
user,
|
||||
"/T",
|
||||
"/C"],
|
||||
check=False
|
||||
)
|
||||
rc_setowner = int(out.returncode)
|
||||
except Exception:
|
||||
rc_setowner = 1
|
||||
|
||||
try:
|
||||
out = subprocess.run(
|
||||
["icacls", str(path), "/grant", f"{user}:(OI)(CI)F", "/T", "/C"], check=False
|
||||
["icacls",
|
||||
str(path),
|
||||
"/grant",
|
||||
f"{user}:(OI)(CI)F",
|
||||
"/T",
|
||||
"/C"],
|
||||
check=False
|
||||
)
|
||||
rc_grant = int(out.returncode)
|
||||
except Exception:
|
||||
@@ -368,7 +419,9 @@ def fix_permissions_windows(path: Path, user: Optional[str] = None) -> bool:
|
||||
|
||||
|
||||
def fix_permissions_unix(
|
||||
path: Path, user: Optional[str] = None, group: Optional[str] = None
|
||||
path: Path,
|
||||
user: Optional[str] = None,
|
||||
group: Optional[str] = None
|
||||
) -> bool:
|
||||
"""Attempt to chown/chmod recursively for a Unix-like system.
|
||||
|
||||
@@ -398,7 +451,13 @@ def fix_permissions_unix(
|
||||
)
|
||||
|
||||
try:
|
||||
subprocess.run(["chown", "-R", f"{user}:{group or pw.pw_gid}", str(path)], check=True)
|
||||
subprocess.run(
|
||||
["chown",
|
||||
"-R",
|
||||
f"{user}:{group or pw.pw_gid}",
|
||||
str(path)],
|
||||
check=True
|
||||
)
|
||||
except Exception:
|
||||
# Best-effort fallback: chown/chmod individual entries
|
||||
for root_dir, dirs, files in os.walk(path):
|
||||
@@ -426,14 +485,20 @@ def fix_permissions_unix(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
logging.info("Unix permission fix attempted (some changes may require root privilege).")
|
||||
logging.info(
|
||||
"Unix permission fix attempted (some changes may require root privilege)."
|
||||
)
|
||||
return True
|
||||
except Exception as exc:
|
||||
logging.debug("Unix fix-permissions error: %s", exc)
|
||||
return False
|
||||
|
||||
|
||||
def fix_permissions(path: Path, user: Optional[str] = None, group: Optional[str] = None) -> bool:
|
||||
def fix_permissions(
|
||||
path: Path,
|
||||
user: Optional[str] = None,
|
||||
group: Optional[str] = None
|
||||
) -> bool:
|
||||
try:
|
||||
if os.name == "nt":
|
||||
return fix_permissions_windows(path, user=user)
|
||||
@@ -554,9 +619,8 @@ def open_in_editor(path: Path) -> bool:
|
||||
pass
|
||||
|
||||
# Linux: use xdg-open only if a display is available and xdg-open exists
|
||||
if shutil.which("xdg-open") and (
|
||||
os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
|
||||
):
|
||||
if shutil.which("xdg-open") and (os.environ.get("DISPLAY")
|
||||
or os.environ.get("WAYLAND_DISPLAY")):
|
||||
try:
|
||||
subprocess.run(["xdg-open", str(path)], check=False)
|
||||
logging.info("Opened %s with default application", path)
|
||||
@@ -565,7 +629,8 @@ def open_in_editor(path: Path) -> bool:
|
||||
pass
|
||||
|
||||
logging.debug(
|
||||
"No available method to open %s automatically (headless or no opener installed)", path
|
||||
"No available method to open %s automatically (headless or no opener installed)",
|
||||
path
|
||||
)
|
||||
return False
|
||||
except Exception as exc:
|
||||
@@ -574,12 +639,15 @@ def open_in_editor(path: Path) -> bool:
|
||||
|
||||
|
||||
def main(argv: Optional[list[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Clone Hydrus into a 'hydrusnetwork' directory.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Clone Hydrus into a 'hydrusnetwork' directory."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--root",
|
||||
"-r",
|
||||
default=".",
|
||||
help="Root folder to create the hydrusnetwork directory in (default: current working directory)",
|
||||
help=
|
||||
"Root folder to create the hydrusnetwork directory in (default: current working directory)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dest-name",
|
||||
@@ -588,7 +656,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
help="Name of the destination folder (default: hydrusnetwork)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--repo", default="https://github.com/hydrusnetwork/hydrus", help="Repository URL to clone"
|
||||
"--repo",
|
||||
default="https://github.com/hydrusnetwork/hydrus",
|
||||
help="Repository URL to clone"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--update",
|
||||
@@ -602,31 +672,40 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
help="Remove existing destination directory before cloning",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--branch", "-b", default=None, help="Branch to clone (passed to git clone --branch)."
|
||||
"--branch",
|
||||
"-b",
|
||||
default=None,
|
||||
help="Branch to clone (passed to git clone --branch)."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--depth",
|
||||
type=int,
|
||||
default=1,
|
||||
help="If set, pass --depth to git clone (default: 1 for a shallow clone). Use --full to perform a full clone instead.",
|
||||
help=
|
||||
"If set, pass --depth to git clone (default: 1 for a shallow clone). Use --full to perform a full clone instead.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--full", action="store_true", help="Perform a full clone (no --depth passed to git clone)"
|
||||
"--full",
|
||||
action="store_true",
|
||||
help="Perform a full clone (no --depth passed to git clone)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--git",
|
||||
action="store_true",
|
||||
help="Use git clone instead of fetching repository ZIP (opt-in). Default: fetch ZIP (smaller).",
|
||||
help=
|
||||
"Use git clone instead of fetching repository ZIP (opt-in). Default: fetch ZIP (smaller).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-fallback",
|
||||
action="store_true",
|
||||
help="If set, do not attempt to download ZIP when git is missing (only relevant with --git)",
|
||||
help=
|
||||
"If set, do not attempt to download ZIP when git is missing (only relevant with --git)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fix-permissions",
|
||||
action="store_true",
|
||||
help="Fix ownership/permissions on the obtained repo (OS-aware). Requires elevated privileges for some actions.",
|
||||
help=
|
||||
"Fix ownership/permissions on the obtained repo (OS-aware). Requires elevated privileges for some actions.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fix-permissions-user",
|
||||
@@ -641,7 +720,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
parser.add_argument(
|
||||
"--no-venv",
|
||||
action="store_true",
|
||||
help="Do not create a venv inside the cloned repo (default: create a .venv folder)",
|
||||
help=
|
||||
"Do not create a venv inside the cloned repo (default: create a .venv folder)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--venv-name",
|
||||
@@ -649,7 +729,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
help="Name of the venv directory to create inside the repo (default: .venv)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--recreate-venv", action="store_true", help="Remove existing venv and create a fresh one"
|
||||
"--recreate-venv",
|
||||
action="store_true",
|
||||
help="Remove existing venv and create a fresh one"
|
||||
)
|
||||
# By default install dependencies into the created venv; use --no-install-deps to opt out
|
||||
group_install = parser.add_mutually_exclusive_group()
|
||||
@@ -657,7 +739,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
"--install-deps",
|
||||
dest="install_deps",
|
||||
action="store_true",
|
||||
help="Install dependencies from requirements.txt into the created venv (default).",
|
||||
help=
|
||||
"Install dependencies from requirements.txt into the created venv (default).",
|
||||
)
|
||||
group_install.add_argument(
|
||||
"--no-install-deps",
|
||||
@@ -669,17 +752,20 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
parser.add_argument(
|
||||
"--reinstall-deps",
|
||||
action="store_true",
|
||||
help="If present, force re-install dependencies into the created venv using pip --force-reinstall.",
|
||||
help=
|
||||
"If present, force re-install dependencies into the created venv using pip --force-reinstall.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-open-client",
|
||||
action="store_true",
|
||||
help="(ignored) installer no longer opens hydrus_client.py automatically; use the run_client helper to launch the client when ready.",
|
||||
help=
|
||||
"(ignored) installer no longer opens hydrus_client.py automatically; use the run_client helper to launch the client when ready.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--run-client",
|
||||
action="store_true",
|
||||
help="Run hydrus_client.py using the repo-local venv's Python (if present). This runs the client in the foreground unless --run-client-detached is specified.",
|
||||
help=
|
||||
"Run hydrus_client.py using the repo-local venv's Python (if present). This runs the client in the foreground unless --run-client-detached is specified.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--run-client-detached",
|
||||
@@ -689,7 +775,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
parser.add_argument(
|
||||
"--run-client-headless",
|
||||
action="store_true",
|
||||
help="If used with --run-client, attempt to run hydrus_client.py without showing the Qt GUI (best-effort)",
|
||||
help=
|
||||
"If used with --run-client, attempt to run hydrus_client.py without showing the Qt GUI (best-effort)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--install-service",
|
||||
@@ -759,7 +846,10 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
)
|
||||
return 0
|
||||
|
||||
logging.info("Destination %s is already a git repository.", dest)
|
||||
logging.info(
|
||||
"Destination %s is already a git repository.",
|
||||
dest
|
||||
)
|
||||
print("")
|
||||
print("Select an action:")
|
||||
print(
|
||||
@@ -779,13 +869,21 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
if choice == "1":
|
||||
# Install dependencies into the repository venv (create venv if needed)
|
||||
try:
|
||||
venv_dir = dest / str(getattr(args, "venv_name", ".venv"))
|
||||
venv_dir = dest / str(
|
||||
getattr(args,
|
||||
"venv_name",
|
||||
".venv")
|
||||
)
|
||||
if venv_dir.exists():
|
||||
logging.info("Using existing venv at %s", venv_dir)
|
||||
else:
|
||||
logging.info("Creating venv at %s", venv_dir)
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "venv", str(venv_dir)], check=True
|
||||
[sys.executable,
|
||||
"-m",
|
||||
"venv",
|
||||
str(venv_dir)],
|
||||
check=True
|
||||
)
|
||||
venv_py = get_python_in_venv(venv_dir)
|
||||
except Exception as e:
|
||||
@@ -793,24 +891,45 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
return 8
|
||||
|
||||
if not venv_py:
|
||||
logging.error("Could not locate python in venv %s", venv_dir)
|
||||
logging.error(
|
||||
"Could not locate python in venv %s",
|
||||
venv_dir
|
||||
)
|
||||
return 9
|
||||
|
||||
req = find_requirements(dest)
|
||||
if not req:
|
||||
logging.info(
|
||||
"No requirements.txt found in %s; nothing to install.", dest
|
||||
"No requirements.txt found in %s; nothing to install.",
|
||||
dest
|
||||
)
|
||||
return 0
|
||||
|
||||
logging.info("Installing dependencies from %s into venv", req)
|
||||
logging.info(
|
||||
"Installing dependencies from %s into venv",
|
||||
req
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
[str(venv_py), "-m", "pip", "install", "--upgrade", "pip"],
|
||||
[
|
||||
str(venv_py),
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
"pip"
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[str(venv_py), "-m", "pip", "install", "-r", str(req)],
|
||||
[
|
||||
str(venv_py),
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-r",
|
||||
str(req)
|
||||
],
|
||||
cwd=str(dest),
|
||||
check=True,
|
||||
)
|
||||
@@ -822,7 +941,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
# Post-install verification
|
||||
pkgs = parse_requirements_file(req)
|
||||
if pkgs:
|
||||
logging.info("Verifying installed packages inside the venv...")
|
||||
logging.info(
|
||||
"Verifying installed packages inside the venv..."
|
||||
)
|
||||
any_missing = False
|
||||
import_map = {
|
||||
"pyyaml": "yaml",
|
||||
@@ -843,7 +964,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
mod = import_map.get(pkg, pkg)
|
||||
try:
|
||||
subprocess.run(
|
||||
[str(venv_py), "-c", f"import {mod}"],
|
||||
[str(venv_py),
|
||||
"-c",
|
||||
f"import {mod}"],
|
||||
check=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
@@ -869,7 +992,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
|
||||
elif choice == "2":
|
||||
if not git:
|
||||
logging.error("Git not found; cannot --update without git")
|
||||
logging.error(
|
||||
"Git not found; cannot --update without git"
|
||||
)
|
||||
return 2
|
||||
try:
|
||||
run_git_pull(git, dest)
|
||||
@@ -882,17 +1007,29 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
elif choice == "3":
|
||||
# Install a user-level service to start the hydrus client on boot
|
||||
try:
|
||||
venv_dir = dest / str(getattr(args, "venv_name", ".venv"))
|
||||
venv_dir = dest / str(
|
||||
getattr(args,
|
||||
"venv_name",
|
||||
".venv")
|
||||
)
|
||||
if not venv_dir.exists():
|
||||
logging.info(
|
||||
"Creating venv at %s to perform service install", venv_dir
|
||||
"Creating venv at %s to perform service install",
|
||||
venv_dir
|
||||
)
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "venv", str(venv_dir)], check=True
|
||||
[sys.executable,
|
||||
"-m",
|
||||
"venv",
|
||||
str(venv_dir)],
|
||||
check=True
|
||||
)
|
||||
venv_py = get_python_in_venv(venv_dir)
|
||||
except Exception as e:
|
||||
logging.error("Failed to prepare venv for service install: %s", e)
|
||||
logging.error(
|
||||
"Failed to prepare venv for service install: %s",
|
||||
e
|
||||
)
|
||||
return 8
|
||||
|
||||
if not venv_py:
|
||||
@@ -986,7 +1123,13 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
try:
|
||||
# Default behavior when using git: shallow clone (depth=1) unless --full specified.
|
||||
depth_to_use = None if getattr(args, "full", False) else args.depth
|
||||
run_git_clone(git, args.repo, dest, branch=args.branch, depth=depth_to_use)
|
||||
run_git_clone(
|
||||
git,
|
||||
args.repo,
|
||||
dest,
|
||||
branch=args.branch,
|
||||
depth=depth_to_use
|
||||
)
|
||||
logging.info("Repository cloned into %s", dest)
|
||||
obtained = True
|
||||
obtained_by = "git"
|
||||
@@ -1023,7 +1166,13 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
if not venv_dir.exists():
|
||||
logging.info("Creating venv at %s", venv_dir)
|
||||
try:
|
||||
subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True)
|
||||
subprocess.run(
|
||||
[sys.executable,
|
||||
"-m",
|
||||
"venv",
|
||||
str(venv_dir)],
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error("Failed to create venv: %s", e)
|
||||
return 8
|
||||
@@ -1038,17 +1187,30 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
logging.info("Venv ready: %s", venv_py)
|
||||
|
||||
# Optionally install or reinstall requirements.txt
|
||||
if getattr(args, "install_deps", False) or getattr(args, "reinstall_deps", False):
|
||||
if getattr(args,
|
||||
"install_deps",
|
||||
False) or getattr(args,
|
||||
"reinstall_deps",
|
||||
False):
|
||||
req = find_requirements(dest)
|
||||
if req and req.exists():
|
||||
logging.info(
|
||||
"Installing dependencies from %s into venv (reinstall=%s)",
|
||||
req,
|
||||
bool(getattr(args, "reinstall_deps", False)),
|
||||
bool(getattr(args,
|
||||
"reinstall_deps",
|
||||
False)),
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
[str(venv_py), "-m", "pip", "install", "--upgrade", "pip"],
|
||||
[
|
||||
str(venv_py),
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
"pip"
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
if getattr(args, "reinstall_deps", False):
|
||||
@@ -1068,7 +1230,14 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
)
|
||||
else:
|
||||
subprocess.run(
|
||||
[str(venv_py), "-m", "pip", "install", "-r", str(req)],
|
||||
[
|
||||
str(venv_py),
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-r",
|
||||
str(req)
|
||||
],
|
||||
cwd=str(dest),
|
||||
check=True,
|
||||
)
|
||||
@@ -1080,7 +1249,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
# Post-install verification: ensure packages are visible inside the venv
|
||||
pkgs = parse_requirements_file(req)
|
||||
if pkgs:
|
||||
logging.info("Verifying installed packages inside the venv...")
|
||||
logging.info(
|
||||
"Verifying installed packages inside the venv..."
|
||||
)
|
||||
any_missing = False
|
||||
# Small mapping for known differences between package name and import name
|
||||
import_map = {
|
||||
@@ -1101,14 +1272,19 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
for pkg in pkgs:
|
||||
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,
|
||||
)
|
||||
if out.returncode != 0 or not out.stdout.strip():
|
||||
logging.warning(
|
||||
"Package '%s' not found in venv (pip show failed).", pkg
|
||||
"Package '%s' not found in venv (pip show failed).",
|
||||
pkg
|
||||
)
|
||||
any_missing = True
|
||||
continue
|
||||
@@ -1116,7 +1292,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
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,
|
||||
@@ -1129,7 +1309,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
)
|
||||
any_missing = True
|
||||
except Exception as exc:
|
||||
logging.debug("Verification error for package %s: %s", pkg, exc)
|
||||
logging.debug(
|
||||
"Verification error for package %s: %s",
|
||||
pkg,
|
||||
exc
|
||||
)
|
||||
any_missing = True
|
||||
|
||||
if any_missing:
|
||||
@@ -1182,7 +1366,10 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
logging.info(" source %s/bin/activate", venv_dir)
|
||||
|
||||
# Optionally open/run hydrus_client.py in the repo for convenience (open by default if present).
|
||||
client_candidates = [dest / "hydrus_client.py", dest / "client" / "hydrus_client.py"]
|
||||
client_candidates = [
|
||||
dest / "hydrus_client.py",
|
||||
dest / "client" / "hydrus_client.py"
|
||||
]
|
||||
client_found = None
|
||||
for p in client_candidates:
|
||||
if p.exists():
|
||||
@@ -1197,12 +1384,18 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
if cand.exists():
|
||||
run_client_script = cand
|
||||
break
|
||||
if getattr(args, "install_service", False) or getattr(args, "uninstall_service", False):
|
||||
if getattr(args,
|
||||
"install_service",
|
||||
False) or getattr(args,
|
||||
"uninstall_service",
|
||||
False):
|
||||
if not venv_py:
|
||||
venv_dir = dest / str(getattr(args, "venv_name", ".venv"))
|
||||
venv_py = get_python_in_venv(venv_dir)
|
||||
if not venv_py:
|
||||
logging.error("Could not locate python in repo venv; cannot manage service.")
|
||||
logging.error(
|
||||
"Could not locate python in repo venv; cannot manage service."
|
||||
)
|
||||
else:
|
||||
if getattr(args, "install_service", False):
|
||||
if run_client_script.exists():
|
||||
@@ -1224,7 +1417,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
else:
|
||||
if install_service_auto:
|
||||
ok = install_service_auto(
|
||||
args.service_name, dest, venv_py, headless=True, detached=True
|
||||
args.service_name,
|
||||
dest,
|
||||
venv_py,
|
||||
headless=True,
|
||||
detached=True
|
||||
)
|
||||
if ok:
|
||||
logging.info("Service installed (user-level).")
|
||||
@@ -1253,7 +1450,11 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
logging.error("Service uninstall failed: %s", e)
|
||||
else:
|
||||
if uninstall_service_auto:
|
||||
ok = uninstall_service_auto(args.service_name, dest, venv_py)
|
||||
ok = uninstall_service_auto(
|
||||
args.service_name,
|
||||
dest,
|
||||
venv_py
|
||||
)
|
||||
if ok:
|
||||
logging.info("Service removed.")
|
||||
else:
|
||||
@@ -1294,17 +1495,27 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
if getattr(args, "run_client_detached", False):
|
||||
cmd.append("--detached")
|
||||
|
||||
logging.info("Running hydrus client via helper: %s", cmd)
|
||||
logging.info(
|
||||
"Running hydrus client via helper: %s",
|
||||
cmd
|
||||
)
|
||||
try:
|
||||
if getattr(args, "run_client_detached", False):
|
||||
kwargs = detach_kwargs_for_platform()
|
||||
kwargs.update({"cwd": str(dest)})
|
||||
kwargs.update({
|
||||
"cwd": str(dest)
|
||||
})
|
||||
subprocess.Popen(cmd, **kwargs)
|
||||
logging.info("Hydrus client launched (detached).")
|
||||
logging.info(
|
||||
"Hydrus client launched (detached)."
|
||||
)
|
||||
else:
|
||||
subprocess.run(cmd, cwd=str(dest))
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error("run_client.py exited non-zero: %s", e)
|
||||
logging.error(
|
||||
"run_client.py exited non-zero: %s",
|
||||
e
|
||||
)
|
||||
else:
|
||||
# Fallback: call the client directly; support headless by setting
|
||||
# QT_QPA_PLATFORM or using xvfb-run on Linux.
|
||||
@@ -1327,23 +1538,34 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
)
|
||||
|
||||
logging.info(
|
||||
"Running hydrus client with %s: %s", venv_py, client_found
|
||||
"Running hydrus client with %s: %s",
|
||||
venv_py,
|
||||
client_found
|
||||
)
|
||||
if getattr(args, "run_client_detached", False):
|
||||
try:
|
||||
kwargs = detach_kwargs_for_platform()
|
||||
kwargs.update({"cwd": str(dest), "env": env})
|
||||
kwargs.update({
|
||||
"cwd": str(dest),
|
||||
"env": env
|
||||
})
|
||||
subprocess.Popen(cmd, **kwargs)
|
||||
logging.info("Hydrus client launched (detached).")
|
||||
logging.info(
|
||||
"Hydrus client launched (detached)."
|
||||
)
|
||||
except Exception as exc:
|
||||
logging.exception(
|
||||
"Failed to launch client detached: %s", exc
|
||||
"Failed to launch client detached: %s",
|
||||
exc
|
||||
)
|
||||
else:
|
||||
try:
|
||||
subprocess.run(cmd, cwd=str(dest), env=env)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error("hydrus client exited non-zero: %s", e)
|
||||
logging.error(
|
||||
"hydrus client exited non-zero: %s",
|
||||
e
|
||||
)
|
||||
except Exception as exc:
|
||||
logging.exception("Failed to run hydrus client: %s", exc)
|
||||
|
||||
@@ -1360,9 +1582,9 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
# Helpful hint: show the new run_client helper and direct run example
|
||||
try:
|
||||
helper_to_show = (
|
||||
run_client_script
|
||||
if (run_client_script and run_client_script.exists())
|
||||
else (script_dir / "run_client.py")
|
||||
run_client_script if
|
||||
(run_client_script and run_client_script.exists()) else
|
||||
(script_dir / "run_client.py")
|
||||
)
|
||||
if venv_py:
|
||||
logging.info(
|
||||
@@ -1375,13 +1597,15 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
)
|
||||
else:
|
||||
logging.info(
|
||||
"To run the Hydrus client: python %s [args]", dest / "hydrus_client.py"
|
||||
"To run the Hydrus client: python %s [args]",
|
||||
dest / "hydrus_client.py"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
logging.debug(
|
||||
"No hydrus_client.py found to open or run (looked in %s).", client_candidates
|
||||
"No hydrus_client.py found to open or run (looked in %s).",
|
||||
client_candidates
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user