dayhoff-tools 1.7.0__tar.gz → 1.7.2__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.
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/PKG-INFO +1 -1
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/engine_commands.py +49 -3
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/main.py +31 -1
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/utility_commands.py +110 -49
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/pyproject.toml +1 -1
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/README.md +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/__init__.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/chemistry/standardizer.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/chemistry/utils.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/__init__.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/cloud_commands.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/cli/swarm_commands.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/base.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/deploy_aws.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/deploy_gcp.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/deploy_utils.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/job_runner.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/processors.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/deployment/swarm.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/embedders.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/fasta.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/file_ops.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/h5.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/gcp.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/gtdb.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/kegg.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/mmseqs.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/structure.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/intake/uniprot.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/logs.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/sqlite.py +0 -0
- {dayhoff_tools-1.7.0 → dayhoff_tools-1.7.2}/dayhoff_tools/warehouse.py +0 -0
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Engine and Studio management commands for DHT CLI."""
|
2
2
|
|
3
3
|
import json
|
4
|
+
import os
|
4
5
|
import re
|
5
6
|
import shutil
|
6
7
|
import subprocess
|
@@ -365,13 +366,58 @@ def resolve_engine(name_or_id: str, engines: List[Dict]) -> Dict:
|
|
365
366
|
|
366
367
|
|
367
368
|
def get_ssh_public_key() -> str:
|
368
|
-
"""Get the user's SSH public key.
|
369
|
+
"""Get the user's SSH public key.
|
370
|
+
|
371
|
+
Discovery order (container-friendly):
|
372
|
+
1) DHT_SSH_PUBLIC_KEY env var (direct key content)
|
373
|
+
2) DHT_SSH_PUBLIC_KEY_PATH env var (path to a .pub file)
|
374
|
+
3) ssh-agent via `ssh-add -L` (requires SSH_AUTH_SOCK)
|
375
|
+
4) Conventional files: ~/.ssh/id_ed25519.pub, ~/.ssh/id_rsa.pub
|
376
|
+
|
377
|
+
Raises:
|
378
|
+
FileNotFoundError: If no public key can be discovered.
|
379
|
+
"""
|
380
|
+
# 1) Direct env var content
|
381
|
+
env_key = os.environ.get("DHT_SSH_PUBLIC_KEY")
|
382
|
+
if env_key and env_key.strip():
|
383
|
+
return env_key.strip()
|
384
|
+
|
385
|
+
# 2) Env var path
|
386
|
+
env_path = os.environ.get("DHT_SSH_PUBLIC_KEY_PATH")
|
387
|
+
if env_path:
|
388
|
+
p = Path(env_path).expanduser()
|
389
|
+
if p.is_file():
|
390
|
+
try:
|
391
|
+
return p.read_text().strip()
|
392
|
+
except Exception:
|
393
|
+
pass
|
394
|
+
|
395
|
+
# 3) Agent lookup (ssh-add -L)
|
396
|
+
try:
|
397
|
+
if shutil.which("ssh-add") is not None:
|
398
|
+
proc = subprocess.run(["ssh-add", "-L"], capture_output=True, text=True)
|
399
|
+
if proc.returncode == 0 and proc.stdout:
|
400
|
+
keys = [line.strip() for line in proc.stdout.splitlines() if line.strip()]
|
401
|
+
# Prefer ed25519, then rsa
|
402
|
+
for pref in ("ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256"):
|
403
|
+
for k in keys:
|
404
|
+
if k.startswith(pref + " "):
|
405
|
+
return k
|
406
|
+
# Fallback to first key if types not matched
|
407
|
+
if keys:
|
408
|
+
return keys[0]
|
409
|
+
except Exception:
|
410
|
+
pass
|
411
|
+
|
412
|
+
# 4) Conventional files
|
369
413
|
home = Path.home()
|
370
414
|
key_paths = [home / ".ssh" / "id_ed25519.pub", home / ".ssh" / "id_rsa.pub"]
|
371
|
-
|
372
415
|
for key_path in key_paths:
|
373
416
|
if key_path.is_file():
|
374
|
-
|
417
|
+
try:
|
418
|
+
return key_path.read_text().strip()
|
419
|
+
except Exception:
|
420
|
+
continue
|
375
421
|
|
376
422
|
raise FileNotFoundError(
|
377
423
|
"No SSH public key found. Please create one with 'ssh-keygen' first."
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Entry file for the CLI, which aggregates and aliases all commands."""
|
2
2
|
|
3
3
|
import sys
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
4
5
|
|
5
6
|
import typer
|
6
7
|
|
@@ -21,7 +22,20 @@ from dayhoff_tools.warehouse import (
|
|
21
22
|
import_from_warehouse_typer,
|
22
23
|
)
|
23
24
|
|
24
|
-
|
25
|
+
def _get_dht_version() -> str:
|
26
|
+
try:
|
27
|
+
return version("dayhoff-tools")
|
28
|
+
except PackageNotFoundError:
|
29
|
+
# Fallback to package __version__ if running from source
|
30
|
+
try:
|
31
|
+
from dayhoff_tools import __version__ # type: ignore
|
32
|
+
|
33
|
+
return __version__
|
34
|
+
except Exception:
|
35
|
+
return "unknown"
|
36
|
+
|
37
|
+
|
38
|
+
app = typer.Typer(help=f"Dayhoff Tools (dh) v{_get_dht_version()}\n\nUse 'dh --version' to print version and exit.")
|
25
39
|
|
26
40
|
# Utility commands
|
27
41
|
app.command("clean")(delete_local_branch)
|
@@ -50,6 +64,22 @@ app.add_typer(engine_app, name="engine", help="Manage compute engines for develo
|
|
50
64
|
app.add_typer(studio_app, name="studio", help="Manage persistent development studios.")
|
51
65
|
|
52
66
|
|
67
|
+
@app.callback()
|
68
|
+
def _version_option(
|
69
|
+
version_flag: bool = typer.Option(
|
70
|
+
False,
|
71
|
+
"--version",
|
72
|
+
"-v",
|
73
|
+
help="Print version and exit.",
|
74
|
+
is_eager=True,
|
75
|
+
)
|
76
|
+
):
|
77
|
+
"""Global options for the dh CLI (e.g., version)."""
|
78
|
+
if version_flag:
|
79
|
+
typer.echo(_get_dht_version())
|
80
|
+
raise typer.Exit()
|
81
|
+
|
82
|
+
|
53
83
|
@app.command("wheel")
|
54
84
|
def build_and_upload_wheel_command(
|
55
85
|
bump: str = typer.Option(
|
@@ -265,42 +265,72 @@ def install_dependencies(
|
|
265
265
|
help="Install the local project package itself (with 'full' extras) into the environment.",
|
266
266
|
),
|
267
267
|
):
|
268
|
-
"""Install dependencies
|
269
|
-
|
270
|
-
|
271
|
-
|
268
|
+
"""Install dependencies respecting Mac devcontainer flow and the active venv.
|
269
|
+
|
270
|
+
Behavior:
|
271
|
+
- If running on Mac devcontainer (STUDIO_PLATFORM=mac) and `pyproject.mac.toml` exists:
|
272
|
+
* Ensure `.mac_uv_project/pyproject.toml` is a copy of `pyproject.mac.toml`
|
273
|
+
* Run `uv lock` and `uv sync` in `.mac_uv_project` and always target the active venv with `--active`
|
274
|
+
* If `install_project` is true, install the project from repo root into the active env (editable, [full])
|
275
|
+
- Otherwise (default path):
|
276
|
+
* Operate in repo root, ensure lock, and `uv sync --active` so we don't create `.venv` accidentally
|
277
|
+
* If `install_project` is true, sync without `--no-install-project` (installs project)
|
272
278
|
"""
|
273
279
|
# ANSI color codes
|
274
280
|
BLUE = "\033[94m"
|
275
281
|
RESET = "\033[0m"
|
276
282
|
|
277
283
|
try:
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
#
|
288
|
-
|
289
|
-
|
290
|
-
print(f"Running command: {BLUE}{' '.join(
|
291
|
-
subprocess.run(
|
292
|
-
|
293
|
-
|
284
|
+
is_mac = os.environ.get("STUDIO_PLATFORM") == "mac"
|
285
|
+
mac_manifest = Path("pyproject.mac.toml")
|
286
|
+
if is_mac and mac_manifest.exists():
|
287
|
+
# Mac devcontainer flow
|
288
|
+
mac_uv_dir = Path(".mac_uv_project")
|
289
|
+
mac_uv_dir.mkdir(parents=True, exist_ok=True)
|
290
|
+
mac_pyproject = mac_uv_dir / "pyproject.toml"
|
291
|
+
mac_pyproject.write_text(mac_manifest.read_text())
|
292
|
+
|
293
|
+
# Ensure lock matches manifest (in mac temp dir)
|
294
|
+
print("Ensuring lock file matches pyproject.mac.toml (Mac devcon)…")
|
295
|
+
lock_cmd = ["uv", "lock"]
|
296
|
+
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
297
|
+
subprocess.run(lock_cmd, check=True, capture_output=True, cwd=str(mac_uv_dir))
|
298
|
+
|
299
|
+
# Sync into the active environment
|
300
|
+
if install_project:
|
301
|
+
print("Syncing dependencies into ACTIVE env and installing project [full]…")
|
302
|
+
sync_cmd = ["uv", "sync", "--all-groups", "--active"]
|
303
|
+
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
304
|
+
subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
|
305
|
+
# Install project from repo root
|
306
|
+
pip_install_cmd = ["uv", "pip", "install", "-e", ".[full]"]
|
307
|
+
print(f"Running command: {BLUE}{' '.join(pip_install_cmd)}{RESET}")
|
308
|
+
subprocess.run(pip_install_cmd, check=True)
|
309
|
+
print("Project installed with 'full' extras successfully.")
|
310
|
+
else:
|
311
|
+
print("Syncing dependencies into ACTIVE env (project not installed)…")
|
312
|
+
sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
|
313
|
+
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
314
|
+
subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
|
315
|
+
print("Dependencies synced successfully (project not installed).")
|
294
316
|
else:
|
295
|
-
#
|
296
|
-
print(
|
297
|
-
|
298
|
-
)
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
317
|
+
# Default behavior in repo root, but ensure we target the active env
|
318
|
+
print("Ensuring lock file matches pyproject.toml…")
|
319
|
+
lock_cmd = ["uv", "lock"]
|
320
|
+
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
321
|
+
subprocess.run(lock_cmd, check=True, capture_output=True)
|
322
|
+
|
323
|
+
if install_project:
|
324
|
+
print("Syncing dependencies into ACTIVE env (installing project)…")
|
325
|
+
sync_cmd = ["uv", "sync", "--all-groups", "--active"]
|
326
|
+
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
327
|
+
subprocess.run(sync_cmd, check=True)
|
328
|
+
else:
|
329
|
+
print("Syncing dependencies into ACTIVE env (project not installed)…")
|
330
|
+
sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
|
331
|
+
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
332
|
+
subprocess.run(sync_cmd, check=True)
|
333
|
+
print("Dependencies synced successfully (project not installed).")
|
304
334
|
|
305
335
|
except subprocess.CalledProcessError as e:
|
306
336
|
stderr_output = e.stderr.decode() if e.stderr else "No stderr output."
|
@@ -327,19 +357,27 @@ def update_dependencies(
|
|
327
357
|
help="Update all dependencies instead of just dayhoff-tools.",
|
328
358
|
),
|
329
359
|
):
|
330
|
-
"""Update dependencies to newer versions.
|
331
|
-
|
332
|
-
Default Action (no flags): Updates only 'dayhoff-tools' package to latest,
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
360
|
+
"""Update dependencies to newer versions (Mac-aware, active venv friendly).
|
361
|
+
|
362
|
+
- Default Action (no flags): Updates only 'dayhoff-tools' package to latest,
|
363
|
+
updates the manifest's version constraint, and syncs.
|
364
|
+
- Flags:
|
365
|
+
--all/-a: Updates all dependencies (uv lock --upgrade) and syncs.
|
366
|
+
|
367
|
+
Mac devcontainer behavior:
|
368
|
+
- If STUDIO_PLATFORM=mac and `pyproject.mac.toml` exists, operate in `.mac_uv_project/`:
|
369
|
+
copy `pyproject.mac.toml` to `.mac_uv_project/pyproject.toml`, run uv there, and always
|
370
|
+
use `--active` for sync so installs target the active venv.
|
371
|
+
- Update the constraint inside `pyproject.mac.toml`. If not found, try `pyproject.toml`.
|
372
|
+
Otherwise (non-Mac), operate in repo root and also target the active venv during sync.
|
338
373
|
"""
|
339
374
|
# ANSI color codes
|
340
375
|
BLUE = "\033[94m"
|
341
376
|
RESET = "\033[0m"
|
342
377
|
|
378
|
+
is_mac = os.environ.get("STUDIO_PLATFORM") == "mac"
|
379
|
+
mac_manifest = Path("pyproject.mac.toml")
|
380
|
+
mac_uv_dir = Path(".mac_uv_project")
|
343
381
|
lock_file_path = Path("uv.lock")
|
344
382
|
pyproject_path = Path("pyproject.toml")
|
345
383
|
|
@@ -363,10 +401,19 @@ def update_dependencies(
|
|
363
401
|
)
|
364
402
|
|
365
403
|
try:
|
404
|
+
# Choose working directory for uv operations
|
405
|
+
uv_cwd = None
|
406
|
+
manifest_path_for_constraint = pyproject_path
|
407
|
+
if is_mac and mac_manifest.exists():
|
408
|
+
mac_uv_dir.mkdir(parents=True, exist_ok=True)
|
409
|
+
(mac_uv_dir / "pyproject.toml").write_text(mac_manifest.read_text())
|
410
|
+
uv_cwd = str(mac_uv_dir)
|
411
|
+
lock_file_path = mac_uv_dir / "uv.lock"
|
412
|
+
manifest_path_for_constraint = mac_manifest
|
366
413
|
# Step 1: Run the update lock command
|
367
414
|
print(action_description)
|
368
415
|
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
369
|
-
subprocess.run(lock_cmd, check=True, capture_output=True)
|
416
|
+
subprocess.run(lock_cmd, check=True, capture_output=True, cwd=uv_cwd)
|
370
417
|
|
371
418
|
# Step 2: Update pyproject.toml only if doing the dayhoff update (default)
|
372
419
|
if run_pyproject_update:
|
@@ -395,9 +442,9 @@ def update_dependencies(
|
|
395
442
|
return
|
396
443
|
|
397
444
|
print(f"Found dayhoff-tools version {locked_version} in lock file.")
|
398
|
-
print(f"Updating {
|
445
|
+
print(f"Updating {manifest_path_for_constraint} version constraint...")
|
399
446
|
try:
|
400
|
-
content =
|
447
|
+
content = manifest_path_for_constraint.read_text()
|
401
448
|
|
402
449
|
package_name = "dayhoff-tools"
|
403
450
|
package_name_esc = re.escape(package_name)
|
@@ -420,27 +467,41 @@ def update_dependencies(
|
|
420
467
|
|
421
468
|
new_content, num_replacements = pattern.subn(_repl, content)
|
422
469
|
if num_replacements > 0:
|
423
|
-
|
470
|
+
manifest_path_for_constraint.write_text(new_content)
|
424
471
|
print(
|
425
|
-
f"Updated dayhoff-tools constraint in {
|
472
|
+
f"Updated dayhoff-tools constraint in {manifest_path_for_constraint} to '{new_constraint_text}'"
|
426
473
|
)
|
427
474
|
else:
|
428
|
-
|
429
|
-
|
430
|
-
|
475
|
+
# Fallback: try the root pyproject if we were targeting mac manifest
|
476
|
+
if manifest_path_for_constraint != pyproject_path and pyproject_path.exists():
|
477
|
+
content2 = pyproject_path.read_text()
|
478
|
+
new_content2, n2 = pattern.subn(_repl, content2)
|
479
|
+
if n2 > 0:
|
480
|
+
pyproject_path.write_text(new_content2)
|
481
|
+
print(
|
482
|
+
f"Updated dayhoff-tools constraint in {pyproject_path} to '{new_constraint_text}'"
|
483
|
+
)
|
484
|
+
else:
|
485
|
+
print(
|
486
|
+
f"Warning: Could not find dayhoff-tools dependency line in {manifest_path_for_constraint} or {pyproject_path} to update constraint."
|
487
|
+
)
|
488
|
+
else:
|
489
|
+
print(
|
490
|
+
f"Warning: Could not find dayhoff-tools dependency line in {manifest_path_for_constraint} to update constraint."
|
491
|
+
)
|
431
492
|
except FileNotFoundError:
|
432
|
-
print(f"Error: {
|
493
|
+
print(f"Error: {manifest_path_for_constraint} not found.")
|
433
494
|
return
|
434
495
|
except Exception as e:
|
435
|
-
print(f"Error updating {
|
496
|
+
print(f"Error updating {manifest_path_for_constraint}: {e}")
|
436
497
|
print("Proceeding with sync despite pyproject.toml update error.")
|
437
498
|
|
438
499
|
# Step 3: Sync environment
|
439
500
|
print("Syncing environment with updated lock file...")
|
440
501
|
# Always use --no-install-project for updates
|
441
|
-
sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project"]
|
502
|
+
sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
|
442
503
|
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
443
|
-
subprocess.run(sync_cmd, check=True)
|
504
|
+
subprocess.run(sync_cmd, check=True, cwd=uv_cwd)
|
444
505
|
|
445
506
|
# Final status message
|
446
507
|
if update_all:
|
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
|
|
5
5
|
|
6
6
|
[project]
|
7
7
|
name = "dayhoff-tools"
|
8
|
-
version = "1.7.
|
8
|
+
version = "1.7.2"
|
9
9
|
description = "Common tools for all the repos at Dayhoff Labs"
|
10
10
|
authors = [
|
11
11
|
{name = "Daniel Martin-Alarcon", email = "dma@dayhofflabs.com"}
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|