dayhoff-tools 1.4.7__tar.gz → 1.4.8__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.
Files changed (32) hide show
  1. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/PKG-INFO +1 -1
  2. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/engine_commands.py +68 -43
  3. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/pyproject.toml +1 -1
  4. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/README.md +0 -0
  5. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/__init__.py +0 -0
  6. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/chemistry/standardizer.py +0 -0
  7. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/chemistry/utils.py +0 -0
  8. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/__init__.py +0 -0
  9. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/cloud_commands.py +0 -0
  10. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/main.py +0 -0
  11. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/swarm_commands.py +0 -0
  12. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/cli/utility_commands.py +0 -0
  13. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/base.py +0 -0
  14. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/deploy_aws.py +0 -0
  15. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/deploy_gcp.py +0 -0
  16. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/deploy_utils.py +0 -0
  17. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/job_runner.py +0 -0
  18. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/processors.py +0 -0
  19. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/deployment/swarm.py +0 -0
  20. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/embedders.py +0 -0
  21. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/fasta.py +0 -0
  22. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/file_ops.py +0 -0
  23. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/h5.py +0 -0
  24. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/gcp.py +0 -0
  25. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/gtdb.py +0 -0
  26. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/kegg.py +0 -0
  27. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/mmseqs.py +0 -0
  28. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/structure.py +0 -0
  29. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/intake/uniprot.py +0 -0
  30. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/logs.py +0 -0
  31. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/sqlite.py +0 -0
  32. {dayhoff_tools-1.4.7 → dayhoff_tools-1.4.8}/dayhoff_tools/warehouse.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.4.7
3
+ Version: 1.4.8
4
4
  Summary: Common tools for all the repos at Dayhoff Labs
5
5
  Author: Daniel Martin-Alarcon
6
6
  Author-email: dma@dayhofflabs.com
@@ -1618,33 +1618,12 @@ def attach_studio(
1618
1618
  console.print("[red]❌ Failed to start engine[/red]")
1619
1619
  raise typer.Exit(1)
1620
1620
  console.print("[green]✓ Engine started[/green]")
1621
- console.print("Waiting for engine to be ready...")
1622
- # Wait until the engine is fully running & marked ready (max 5 min)
1623
- console.print("Waiting for engine to be ready (this can take a couple of minutes)…")
1624
- # Progress classes already imported at module level – do not re-import to avoid UnboundLocalError
1625
-
1626
- with Progress(SpinnerColumn(), TimeElapsedColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as prog:
1627
- prog.add_task("Awaiting engine readiness…", total=None)
1628
- max_attempts = 30 # 30 × 10 s ≈ 5 min
1629
- for _ in range(max_attempts):
1630
- time.sleep(10)
1631
-
1632
- status_resp = make_api_request("GET", f"/engines/{engine['instance_id']}")
1633
- if status_resp.status_code != 200:
1634
- continue
1635
-
1636
- detailed = status_resp.json().get("engine", {})
1637
- state_ok = detailed.get("state", "").lower() == "running"
1638
- ready_ok = detailed.get("ready") is True
1639
-
1640
- if state_ok and ready_ok:
1641
- break
1642
- else:
1643
- console.print("[yellow]Engine is still starting up. If attachment fails, wait a little longer and retry.[/yellow]")
1621
+ # No further waiting here – attachment attempts below handle retry logic while the
1622
+ # engine finishes booting.
1644
1623
  else:
1645
1624
  raise typer.Exit(1)
1646
1625
 
1647
- # Get SSH key
1626
+ # Retrieve SSH public key (required for authorised_keys provisioning)
1648
1627
  try:
1649
1628
  public_key = get_ssh_public_key()
1650
1629
  except FileNotFoundError as e:
@@ -1655,34 +1634,80 @@ def attach_studio(
1655
1634
 
1656
1635
  with Progress(
1657
1636
  SpinnerColumn(),
1637
+ TimeElapsedColumn(),
1658
1638
  TextColumn("[progress.description]{task.description}"),
1659
1639
  transient=True,
1660
- ) as progress:
1661
- task = progress.add_task("Attaching studio...", total=100)
1640
+ ) as prog:
1641
+ task = prog.add_task("Attaching studio (engine is still booting)…", total=None)
1642
+ ATTEMPT_LIMIT = 40 # ~6-7 min max (40 × 10 s)
1643
+ RETRY_DELAY = 10
1644
+ for attempt in range(ATTEMPT_LIMIT):
1645
+ success, error_msg = _attempt_studio_attach(studio, engine, target_user, public_key)
1662
1646
 
1663
- response = make_api_request(
1664
- "POST",
1665
- f"/studios/{studio['studio_id']}/attach",
1666
- json_data={
1667
- "vm_id": engine["instance_id"],
1668
- "user": target_user, # Use target_user instead of username
1669
- "public_key": public_key,
1670
- },
1671
- )
1647
+ if success:
1648
+ break # success!
1649
+
1650
+ # Update spinner description with attempt number
1651
+ prog.update(task, description=f"Attaching studio (engine is still booting)… {attempt+1}/{ATTEMPT_LIMIT}")
1652
+
1653
+ if error_msg:
1654
+ console.print(f"[red]❌ Failed to attach studio: {error_msg}[/red]")
1655
+ return
1656
+
1657
+ time.sleep(RETRY_DELAY)
1658
+
1659
+ else:
1660
+ console.print("[yellow]Engine is still starting up – please retry in a minute.[/yellow]")
1661
+ return
1662
+
1663
+ # Successful attach path
1664
+ console.print(f"[green]✓ Studio attached successfully![/green]")
1672
1665
 
1673
- progress.update(task, completed=100)
1666
+ # Update SSH config - use target_user for the connection
1667
+ update_ssh_config_entry(engine["name"], engine["instance_id"], target_user)
1668
+ console.print(f"[green]✓ SSH config updated[/green]")
1669
+ console.print(f"\nConnect with: [cyan]ssh {engine['name']}[/cyan]")
1670
+ console.print(f"Files are at: [cyan]/studios/{target_user}[/cyan]")
1671
+
1672
+
1673
+ def _attempt_studio_attach(studio, engine, target_user, public_key):
1674
+ response = make_api_request(
1675
+ "POST",
1676
+ f"/studios/{studio['studio_id']}/attach",
1677
+ json_data={
1678
+ "vm_id": engine["instance_id"],
1679
+ "user": target_user,
1680
+ "public_key": public_key,
1681
+ },
1682
+ )
1674
1683
 
1675
1684
  if response.status_code == 200:
1676
- console.print(f"[green]✓ Studio attached successfully![/green]")
1685
+ return True, None
1677
1686
 
1678
- # Update SSH config - use target_user for the connection
1679
- update_ssh_config_entry(engine["name"], engine["instance_id"], target_user)
1680
- console.print(f"[green]✓ SSH config updated[/green]")
1681
- console.print(f"\nConnect with: [cyan]ssh {engine['name']}[/cyan]")
1682
- console.print(f"Files are at: [cyan]/studios/{target_user}[/cyan]")
1687
+ # --- determine if we should retry ---
1688
+ recoverable = False
1689
+ if response.status_code in (409, 503):
1690
+ recoverable = True
1683
1691
  else:
1692
+ err_msg = response.json().get("error", "").lower()
1693
+ RECOVERABLE_PATTERNS = [
1694
+ "not ready",
1695
+ "still starting",
1696
+ "initializing",
1697
+ "failed to mount",
1698
+ "device busy",
1699
+ "not available",
1700
+ ]
1701
+ if any(p in err_msg for p in RECOVERABLE_PATTERNS):
1702
+ recoverable = True
1703
+
1704
+ if not recoverable:
1705
+ # fatal – abort immediately and show message
1684
1706
  error = response.json().get("error", "Unknown error")
1685
- console.print(f"[red]❌ Failed to attach studio: {error}[/red]")
1707
+ return False, error
1708
+
1709
+ # otherwise wait and retry
1710
+ return False, None
1686
1711
 
1687
1712
 
1688
1713
  @studio_app.command("detach")
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
5
5
 
6
6
  [project]
7
7
  name = "dayhoff-tools"
8
- version = "1.4.7"
8
+ version = "1.4.8"
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