dayhoff-tools 1.6.13__py3-none-any.whl → 1.6.15__py3-none-any.whl

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.
@@ -1610,6 +1610,36 @@ def studio_status(
1610
1610
  console.print(panel)
1611
1611
 
1612
1612
 
1613
+ def _is_studio_attached(target_studio_id: str, target_vm_id: str) -> bool:
1614
+ """Return True when the given studio already shows as attached to the VM.
1615
+
1616
+ Using this extra check lets us stop the outer retry loop as soon as the
1617
+ asynchronous attach operation actually finishes, even in the unlikely
1618
+ event that the operation-tracking DynamoDB record is not yet updated.
1619
+ """
1620
+ # First try the per-studio endpoint – fastest.
1621
+ resp = make_api_request("GET", f"/studios/{target_studio_id}")
1622
+ if resp.status_code == 200:
1623
+ data = resp.json()
1624
+ if (
1625
+ data.get("status") == "in-use"
1626
+ and data.get("attached_vm_id") == target_vm_id
1627
+ ):
1628
+ return True
1629
+ # Fallback: list + filter (covers edge-cases where the direct endpoint
1630
+ # is slower to update IAM/APIGW mapping than the list endpoint).
1631
+ list_resp = make_api_request("GET", "/studios")
1632
+ if list_resp.status_code == 200:
1633
+ for stu in list_resp.json().get("studios", []):
1634
+ if (
1635
+ stu.get("studio_id") == target_studio_id
1636
+ and stu.get("status") == "in-use"
1637
+ and stu.get("attached_vm_id") == target_vm_id
1638
+ ):
1639
+ return True
1640
+ return False
1641
+
1642
+
1613
1643
  @studio_app.command("attach")
1614
1644
  def attach_studio(
1615
1645
  engine_name_or_id: str = typer.Argument(help="Engine name or instance ID"),
@@ -1710,36 +1740,6 @@ def attach_studio(
1710
1740
 
1711
1741
  console.print(f"Attaching studio to engine [cyan]{engine['name']}[/cyan]...")
1712
1742
 
1713
- # Helper --------------------------------------------------------------
1714
- def _is_studio_attached(target_studio_id: str, target_vm_id: str) -> bool:
1715
- """Return True when the given studio already shows as attached to the VM.
1716
-
1717
- Using this extra check lets us stop the outer retry loop as soon as the
1718
- asynchronous attach operation actually finishes, even in the unlikely
1719
- event that the operation-tracking DynamoDB record is not yet updated.
1720
- """
1721
- # First try the per-studio endpoint – fastest.
1722
- resp = make_api_request("GET", f"/studios/{target_studio_id}")
1723
- if resp.status_code == 200:
1724
- data = resp.json()
1725
- if (
1726
- data.get("status") == "in-use"
1727
- and data.get("attached_vm_id") == target_vm_id
1728
- ):
1729
- return True
1730
- # Fallback: list + filter (covers edge-cases where the direct endpoint
1731
- # is slower to update IAM/APIGW mapping than the list endpoint).
1732
- list_resp = make_api_request("GET", "/studios")
1733
- if list_resp.status_code == 200:
1734
- for stu in list_resp.json().get("studios", []):
1735
- if (
1736
- stu.get("studio_id") == target_studio_id
1737
- and stu.get("status") == "in-use"
1738
- and stu.get("attached_vm_id") == target_vm_id
1739
- ):
1740
- return True
1741
- return False
1742
-
1743
1743
  # Determine retry strategy based on whether we just started the engine
1744
1744
  if engine_started_now:
1745
1745
  max_attempts = 40 # About 7 minutes total with exponential backoff
@@ -1764,22 +1764,16 @@ def attach_studio(
1764
1764
  last_error = None
1765
1765
 
1766
1766
  for attempt in range(max_attempts):
1767
- # If the attach already completed in the previous iteration (but we
1768
- # didn't notice because the operation table wasn't updated yet),
1769
- # bail out early.
1767
+ # Check if the attach already completed
1770
1768
  if _is_studio_attached(studio["studio_id"], engine["instance_id"]):
1771
- console.print("[dim]DEBUG: Studio already attached (detected by _is_studio_attached)[/dim]")
1772
1769
  success = True
1773
1770
  break
1774
1771
 
1775
- console.print(f"[dim]DEBUG: Attempt {attempt + 1}/{max_attempts}[/dim]")
1776
1772
  success, error_msg = _attempt_studio_attach(
1777
1773
  studio, engine, target_user, public_key
1778
1774
  )
1779
- console.print(f"[dim]DEBUG: Attempt result: success={success}, error_msg={error_msg}[/dim]")
1780
1775
 
1781
1776
  if success:
1782
- console.print("[dim]DEBUG: Success! Breaking out of retry loop[/dim]")
1783
1777
  break # success!
1784
1778
 
1785
1779
  if error_msg:
@@ -1851,33 +1845,19 @@ def _attempt_studio_attach(studio, engine, target_user, public_key):
1851
1845
  },
1852
1846
  )
1853
1847
 
1854
- # DEBUG: Log what we're getting back
1855
- console.print(f"[dim]DEBUG: Attach response: {response.status_code}[/dim]")
1856
- if response.status_code not in (200, 202):
1857
- try:
1858
- console.print(f"[dim]DEBUG: Error: {response.json().get('error', 'No error field')}[/dim]")
1859
- except:
1860
- console.print(f"[dim]DEBUG: Could not parse error response[/dim]")
1861
-
1862
1848
  # Fast-path success
1863
1849
  if response.status_code == 200:
1864
- console.print("[dim]DEBUG: Got 200 - immediate success[/dim]")
1865
1850
  return True, None
1866
1851
 
1867
1852
  # Asynchronous path – API returned 202 Accepted and operation tracking ID
1868
1853
  if response.status_code == 202:
1869
- op_id = response.json().get("operation_id")
1870
- console.print(f"[dim]DEBUG: Got 202 - operation {op_id} started[/dim]")
1871
-
1872
- # Instead of polling the operation status (which seems broken),
1873
- # just wait a bit and check if the studio is actually attached
1874
- # This is what dh studio status does and it works reliably
1854
+ # The operation status polling is broken in the Lambda, so we just
1855
+ # wait and check if the studio is actually attached
1875
1856
  time.sleep(5) # Give the async operation a moment to start
1876
1857
 
1877
- # Now check periodically if the studio is attached
1858
+ # Check periodically if the studio is attached
1878
1859
  for check in range(20): # Check for up to 60 seconds
1879
1860
  if _is_studio_attached(studio["studio_id"], engine["instance_id"]):
1880
- console.print("[dim]DEBUG: Studio attachment detected via status check[/dim]")
1881
1861
  return True, None
1882
1862
  time.sleep(3)
1883
1863
 
@@ -1924,45 +1904,8 @@ def _attempt_studio_attach(studio, engine, target_user, public_key):
1924
1904
  return False, None
1925
1905
 
1926
1906
 
1927
- def _poll_operation(operation_id: str, timeout: int = 600) -> Tuple[bool, str]:
1928
- """Poll `/operations/<id>` until it reports COMPLETED or FAILED.
1929
-
1930
- Returns (True, "") on success or (False, error_message) on failure or
1931
- timeout.
1932
-
1933
- IMPORTANT: This helper deliberately *avoids* creating its own Rich Live/
1934
- spinner so that it can be called from inside another Progress/Live context
1935
- (the outer "Attaching studio…" progress bar). Nested Live renderables lead
1936
- to constant hide/show repaint cycles, which is what caused the flickering
1937
- the user observed.
1938
- """
1939
- start_ts = time.time()
1940
- while True:
1941
- # ---- timeout guard -------------------------------------------------
1942
- if time.time() - start_ts > timeout:
1943
- return False, "Timed out waiting for attach operation to complete"
1944
-
1945
- # ---- fetch status ---------------------------------------------------
1946
- op_resp = make_api_request("GET", f"/operations/{operation_id}")
1947
- if op_resp.status_code != 200:
1948
- console.print(f"[dim]DEBUG: Operation poll failed: HTTP {op_resp.status_code}[/dim]")
1949
- return False, (
1950
- f"Failed to fetch operation status (HTTP {op_resp.status_code})"
1951
- )
1952
- data = op_resp.json()
1953
- op_status = data.get("status")
1954
- console.print(f"[dim]DEBUG: Operation status: {op_status}[/dim]")
1955
-
1956
- # ---- interpret state -----------------------------------------------
1957
- if op_status == "COMPLETED":
1958
- console.print("[dim]DEBUG: Operation completed successfully[/dim]")
1959
- return True, ""
1960
- if op_status == "FAILED":
1961
- console.print(f"[dim]DEBUG: Operation failed: {data.get('error', 'Operation failed')}[/dim]")
1962
- return False, data.get("error", "Operation failed")
1963
-
1964
- # Not finished yet – wait a little and poll again (no live spinner)
1965
- time.sleep(3)
1907
+ # Note: _poll_operation was removed because the Lambda's operation tracking is broken.
1908
+ # We now use _is_studio_attached() to check if the studio is actually attached instead.
1966
1909
 
1967
1910
 
1968
1911
  @studio_app.command("detach")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.6.13
3
+ Version: 1.6.15
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
@@ -3,7 +3,7 @@ dayhoff_tools/chemistry/standardizer.py,sha256=uMn7VwHnx02nc404eO6fRuS4rsl4dvSPf
3
3
  dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P-_M,2257
4
4
  dayhoff_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  dayhoff_tools/cli/cloud_commands.py,sha256=33qcWLmq-FwEXMdL3F0OHm-5Stlh2r65CldyEZgQ1no,40904
6
- dayhoff_tools/cli/engine_commands.py,sha256=Loc-ppwuuuAZbj5j2s0aNT5_R1u7oiOx7bDjjWuw3y8,98069
6
+ dayhoff_tools/cli/engine_commands.py,sha256=4svrENpNa5OYqooeWuiXvoXe9h4jfuU9iUBoFOVIEMk,94855
7
7
  dayhoff_tools/cli/main.py,sha256=tRN7WCBHg6uyNp6rA54pKTCoVmBntta2i0Yas3bUpZ4,4853
8
8
  dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
9
9
  dayhoff_tools/cli/utility_commands.py,sha256=FRZTPrjsG_qmIIqoNxd1Q1vVkS_5w8aY33IrVYVNCLg,18131
@@ -27,7 +27,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
27
27
  dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
28
28
  dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
29
29
  dayhoff_tools/warehouse.py,sha256=heaYc64qplgN3_1WVPFmqj53goStioWwY5NqlWc4c0s,24453
30
- dayhoff_tools-1.6.13.dist-info/METADATA,sha256=Vj0JcsRhUcJE8witxYBmGTiCWLdunubI_01WecoV3Xw,2915
31
- dayhoff_tools-1.6.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
- dayhoff_tools-1.6.13.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
- dayhoff_tools-1.6.13.dist-info/RECORD,,
30
+ dayhoff_tools-1.6.15.dist-info/METADATA,sha256=4jiZolH68BiY1BfLPJFloriU69fF0unsDcLoUKPHlYc,2915
31
+ dayhoff_tools-1.6.15.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
+ dayhoff_tools-1.6.15.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
+ dayhoff_tools-1.6.15.dist-info/RECORD,,