dayhoff-tools 1.7.1__py3-none-any.whl → 1.7.3__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.
dayhoff_tools/cli/main.py CHANGED
@@ -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
- app = typer.Typer()
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,27 @@ 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(invoke_without_command=True)
68
+ def _version_option(
69
+ ctx: typer.Context,
70
+ version_flag: bool = typer.Option(
71
+ False,
72
+ "--version",
73
+ "-v",
74
+ help="Print version and exit.",
75
+ is_eager=True,
76
+ )
77
+ ):
78
+ """Global options for the dh CLI (e.g., version)."""
79
+ if version_flag:
80
+ typer.echo(_get_dht_version())
81
+ raise typer.Exit()
82
+ # If no subcommand provided, show help instead of 'Missing command'
83
+ if ctx.invoked_subcommand is None:
84
+ typer.echo(ctx.get_help())
85
+ raise typer.Exit()
86
+
87
+
53
88
  @app.command("wheel")
54
89
  def build_and_upload_wheel_command(
55
90
  bump: str = typer.Option(
@@ -112,6 +112,7 @@ def build_and_upload_wheel(bump_part: str = "patch"):
112
112
  print("Using UV_PUBLISH_TOKEN for authentication.")
113
113
 
114
114
  pyproject_path = "pyproject.toml"
115
+ mac_manifest_path = "pyproject.mac.toml"
115
116
  current_version = None # Initialize in case the first try block fails
116
117
 
117
118
  try:
@@ -173,6 +174,25 @@ def build_and_upload_wheel(bump_part: str = "patch"):
173
174
  with open(pyproject_path, "w") as f:
174
175
  f.write(new_content)
175
176
  print(f"Updated {pyproject_path} with version {new_version}")
177
+
178
+ # Mirror version in pyproject.mac.toml if present (best-effort)
179
+ mac_updated = False
180
+ try:
181
+ mac_path = Path(mac_manifest_path)
182
+ if mac_path.exists():
183
+ mac_content = mac_path.read_text()
184
+ mac_pattern = re.compile(
185
+ f'^version\s*=\s*"{re.escape(current_version)}"', re.MULTILINE
186
+ )
187
+ mac_new_content, mac_replacements = mac_pattern.subn(
188
+ f'version = "{new_version}"', mac_content
189
+ )
190
+ if mac_replacements > 0:
191
+ mac_path.write_text(mac_new_content)
192
+ mac_updated = True
193
+ print(f"Updated {mac_manifest_path} with version {new_version}")
194
+ except Exception as e:
195
+ print(f"Warning: Could not update {mac_manifest_path}: {e}")
176
196
  # --- End Version Bumping Logic ---
177
197
 
178
198
  # Build wheel and sdist
@@ -191,6 +211,27 @@ def build_and_upload_wheel(bump_part: str = "patch"):
191
211
 
192
212
  print(f"Successfully built and uploaded version {new_version} to PyPI")
193
213
 
214
+ # Re-install DHT in current venv when building from DHT itself
215
+ try:
216
+ proj_name = None
217
+ try:
218
+ proj_toml = toml.load(pyproject_path)
219
+ proj_name = (
220
+ proj_toml.get("project", {}).get("name")
221
+ if isinstance(proj_toml, dict)
222
+ else None
223
+ )
224
+ except Exception:
225
+ pass
226
+ if proj_name == "dayhoff-tools":
227
+ print("Re-installing dayhoff-tools into the active environment…")
228
+ reinstall_cmd = ["uv", "pip", "install", "-e", "."]
229
+ print(f"Running command: {BLUE}{' '.join(reinstall_cmd)}{RESET}")
230
+ subprocess.run(reinstall_cmd, check=True)
231
+ print("dayhoff-tools reinstalled in the current environment.")
232
+ except subprocess.CalledProcessError as e:
233
+ print(f"Warning: Failed to reinstall dayhoff-tools locally: {e}")
234
+
194
235
  except FileNotFoundError:
195
236
  print(f"Error: {pyproject_path} not found.")
196
237
  # No version change happened, so no rollback needed
@@ -219,6 +260,24 @@ def build_and_upload_wheel(bump_part: str = "patch"):
219
260
  print(
220
261
  f"Warning: Could not find version {new_version} to revert in {pyproject_path}."
221
262
  )
263
+
264
+ # Also revert mac manifest if we updated it
265
+ try:
266
+ mac_path = Path(mac_manifest_path)
267
+ if mac_path.exists():
268
+ mac_content_revert = mac_path.read_text()
269
+ mac_reverted, mac_num = pattern_revert.subn(
270
+ f'version = "{current_version}"', mac_content_revert
271
+ )
272
+ if mac_num > 0:
273
+ mac_path.write_text(mac_reverted)
274
+ print(
275
+ f"Successfully reverted version in {mac_manifest_path}."
276
+ )
277
+ except Exception as e2:
278
+ print(
279
+ f"Warning: Failed to revert version change in {mac_manifest_path}: {e2}"
280
+ )
222
281
  except Exception as revert_e:
223
282
  print(
224
283
  f"Warning: Failed to revert version change in {pyproject_path}: {revert_e}"
@@ -248,6 +307,23 @@ def build_and_upload_wheel(bump_part: str = "patch"):
248
307
  print(
249
308
  f"Warning: Could not find version {new_version} to revert in {pyproject_path}."
250
309
  )
310
+ # Also revert mac manifest if necessary
311
+ try:
312
+ mac_path = Path(mac_manifest_path)
313
+ if mac_path.exists():
314
+ mac_content_revert = mac_path.read_text()
315
+ mac_reverted, mac_num = pattern_revert.subn(
316
+ f'version = "{current_version}"', mac_content_revert
317
+ )
318
+ if mac_num > 0:
319
+ mac_path.write_text(mac_reverted)
320
+ print(
321
+ f"Successfully reverted version in {mac_manifest_path}."
322
+ )
323
+ except Exception as e2:
324
+ print(
325
+ f"Warning: Failed to revert version change in {mac_manifest_path}: {e2}"
326
+ )
251
327
  except Exception as revert_e:
252
328
  print(
253
329
  f"Warning: Failed to revert version change in {pyproject_path}: {revert_e}"
@@ -265,42 +341,72 @@ def install_dependencies(
265
341
  help="Install the local project package itself (with 'full' extras) into the environment.",
266
342
  ),
267
343
  ):
268
- """Install dependencies based on pyproject.toml.
269
-
270
- Ensures uv.lock matches pyproject.toml and syncs the environment.
271
- When -p is used, installs the local project with its [full] optional dependencies.
344
+ """Install dependencies respecting Mac devcontainer flow and the active venv.
345
+
346
+ Behavior:
347
+ - If running on Mac devcontainer (STUDIO_PLATFORM=mac) and `pyproject.mac.toml` exists:
348
+ * Ensure `.mac_uv_project/pyproject.toml` is a copy of `pyproject.mac.toml`
349
+ * Run `uv lock` and `uv sync` in `.mac_uv_project` and always target the active venv with `--active`
350
+ * If `install_project` is true, install the project from repo root into the active env (editable, [full])
351
+ - Otherwise (default path):
352
+ * Operate in repo root, ensure lock, and `uv sync --active` so we don't create `.venv` accidentally
353
+ * If `install_project` is true, sync without `--no-install-project` (installs project)
272
354
  """
273
355
  # ANSI color codes
274
356
  BLUE = "\033[94m"
275
357
  RESET = "\033[0m"
276
358
 
277
359
  try:
278
- # Step 1: Ensure lock file matches pyproject.toml
279
- print("Ensuring lock file matches pyproject.toml...")
280
- lock_cmd = ["uv", "lock"]
281
- print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
282
- subprocess.run(lock_cmd, check=True, capture_output=True)
283
-
284
- if install_project:
285
- # Step 2a: Install the project with 'full' extras
286
- print("Installing the local project with 'full' extras...")
287
- # The .[full] syntax tells pip to install the current project ('.')
288
- # with its 'full' optional dependencies.
289
- pip_install_cmd = ["uv", "pip", "install", "-e", ".[full]"]
290
- print(f"Running command: {BLUE}{' '.join(pip_install_cmd)}{RESET}")
291
- subprocess.run(pip_install_cmd, check=True)
292
-
293
- print("Project installed with 'full' extras successfully.")
360
+ is_mac = os.environ.get("STUDIO_PLATFORM") == "mac"
361
+ mac_manifest = Path("pyproject.mac.toml")
362
+ if is_mac and mac_manifest.exists():
363
+ # Mac devcontainer flow
364
+ mac_uv_dir = Path(".mac_uv_project")
365
+ mac_uv_dir.mkdir(parents=True, exist_ok=True)
366
+ mac_pyproject = mac_uv_dir / "pyproject.toml"
367
+ mac_pyproject.write_text(mac_manifest.read_text())
368
+
369
+ # Ensure lock matches manifest (in mac temp dir)
370
+ print("Ensuring lock file matches pyproject.mac.toml (Mac devcon)…")
371
+ lock_cmd = ["uv", "lock"]
372
+ print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
373
+ subprocess.run(lock_cmd, check=True, capture_output=True, cwd=str(mac_uv_dir))
374
+
375
+ # Sync into the active environment
376
+ if install_project:
377
+ print("Syncing dependencies into ACTIVE env and installing project [full]…")
378
+ sync_cmd = ["uv", "sync", "--all-groups", "--active"]
379
+ print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
380
+ subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
381
+ # Install project from repo root
382
+ pip_install_cmd = ["uv", "pip", "install", "-e", ".[full]"]
383
+ print(f"Running command: {BLUE}{' '.join(pip_install_cmd)}{RESET}")
384
+ subprocess.run(pip_install_cmd, check=True)
385
+ print("Project installed with 'full' extras successfully.")
386
+ else:
387
+ print("Syncing dependencies into ACTIVE env (project not installed)…")
388
+ sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
389
+ print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
390
+ subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
391
+ print("Dependencies synced successfully (project not installed).")
294
392
  else:
295
- # Original behavior: Sync environment without installing the project
296
- print(
297
- "Syncing environment with lock file (project itself will not be installed)..."
298
- )
299
- # --all-groups ensures all non-project dependencies (like dev) are installed
300
- sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project"]
301
- print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
302
- subprocess.run(sync_cmd, check=True)
303
- print("Dependencies synced successfully (project not installed).")
393
+ # Default behavior in repo root, but ensure we target the active env
394
+ print("Ensuring lock file matches pyproject.toml…")
395
+ lock_cmd = ["uv", "lock"]
396
+ print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
397
+ subprocess.run(lock_cmd, check=True, capture_output=True)
398
+
399
+ if install_project:
400
+ print("Syncing dependencies into ACTIVE env (installing project)…")
401
+ sync_cmd = ["uv", "sync", "--all-groups", "--active"]
402
+ print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
403
+ subprocess.run(sync_cmd, check=True)
404
+ else:
405
+ print("Syncing dependencies into ACTIVE env (project not installed)…")
406
+ sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
407
+ print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
408
+ subprocess.run(sync_cmd, check=True)
409
+ print("Dependencies synced successfully (project not installed).")
304
410
 
305
411
  except subprocess.CalledProcessError as e:
306
412
  stderr_output = e.stderr.decode() if e.stderr else "No stderr output."
@@ -327,19 +433,27 @@ def update_dependencies(
327
433
  help="Update all dependencies instead of just dayhoff-tools.",
328
434
  ),
329
435
  ):
330
- """Update dependencies to newer versions.
331
-
332
- Default Action (no flags): Updates only 'dayhoff-tools' package to latest,
333
- updates pyproject.toml, and syncs.
334
-
335
- Flags:
336
- --all/-a: Updates all dependencies to latest compatible versions (`uv lock --upgrade`)
337
- and syncs the environment. Overrides the default.
436
+ """Update dependencies to newer versions (Mac-aware, active venv friendly).
437
+
438
+ - Default Action (no flags): Updates only 'dayhoff-tools' package to latest,
439
+ updates the manifest's version constraint, and syncs.
440
+ - Flags:
441
+ --all/-a: Updates all dependencies (uv lock --upgrade) and syncs.
442
+
443
+ Mac devcontainer behavior:
444
+ - If STUDIO_PLATFORM=mac and `pyproject.mac.toml` exists, operate in `.mac_uv_project/`:
445
+ copy `pyproject.mac.toml` to `.mac_uv_project/pyproject.toml`, run uv there, and always
446
+ use `--active` for sync so installs target the active venv.
447
+ - Update the constraint inside `pyproject.mac.toml`. If not found, try `pyproject.toml`.
448
+ Otherwise (non-Mac), operate in repo root and also target the active venv during sync.
338
449
  """
339
450
  # ANSI color codes
340
451
  BLUE = "\033[94m"
341
452
  RESET = "\033[0m"
342
453
 
454
+ is_mac = os.environ.get("STUDIO_PLATFORM") == "mac"
455
+ mac_manifest = Path("pyproject.mac.toml")
456
+ mac_uv_dir = Path(".mac_uv_project")
343
457
  lock_file_path = Path("uv.lock")
344
458
  pyproject_path = Path("pyproject.toml")
345
459
 
@@ -363,10 +477,19 @@ def update_dependencies(
363
477
  )
364
478
 
365
479
  try:
480
+ # Choose working directory for uv operations
481
+ uv_cwd = None
482
+ manifest_path_for_constraint = pyproject_path
483
+ if is_mac and mac_manifest.exists():
484
+ mac_uv_dir.mkdir(parents=True, exist_ok=True)
485
+ (mac_uv_dir / "pyproject.toml").write_text(mac_manifest.read_text())
486
+ uv_cwd = str(mac_uv_dir)
487
+ lock_file_path = mac_uv_dir / "uv.lock"
488
+ manifest_path_for_constraint = mac_manifest
366
489
  # Step 1: Run the update lock command
367
490
  print(action_description)
368
491
  print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
369
- subprocess.run(lock_cmd, check=True, capture_output=True)
492
+ subprocess.run(lock_cmd, check=True, capture_output=True, cwd=uv_cwd)
370
493
 
371
494
  # Step 2: Update pyproject.toml only if doing the dayhoff update (default)
372
495
  if run_pyproject_update:
@@ -395,9 +518,9 @@ def update_dependencies(
395
518
  return
396
519
 
397
520
  print(f"Found dayhoff-tools version {locked_version} in lock file.")
398
- print(f"Updating {pyproject_path} version constraint...")
521
+ print(f"Updating {manifest_path_for_constraint} version constraint...")
399
522
  try:
400
- content = pyproject_path.read_text()
523
+ content = manifest_path_for_constraint.read_text()
401
524
 
402
525
  package_name = "dayhoff-tools"
403
526
  package_name_esc = re.escape(package_name)
@@ -420,27 +543,41 @@ def update_dependencies(
420
543
 
421
544
  new_content, num_replacements = pattern.subn(_repl, content)
422
545
  if num_replacements > 0:
423
- pyproject_path.write_text(new_content)
546
+ manifest_path_for_constraint.write_text(new_content)
424
547
  print(
425
- f"Updated dayhoff-tools constraint in {pyproject_path} to '{new_constraint_text}'"
548
+ f"Updated dayhoff-tools constraint in {manifest_path_for_constraint} to '{new_constraint_text}'"
426
549
  )
427
550
  else:
428
- print(
429
- f"Warning: Could not find dayhoff-tools dependency line in {pyproject_path} to update constraint."
430
- )
551
+ # Fallback: try the root pyproject if we were targeting mac manifest
552
+ if manifest_path_for_constraint != pyproject_path and pyproject_path.exists():
553
+ content2 = pyproject_path.read_text()
554
+ new_content2, n2 = pattern.subn(_repl, content2)
555
+ if n2 > 0:
556
+ pyproject_path.write_text(new_content2)
557
+ print(
558
+ f"Updated dayhoff-tools constraint in {pyproject_path} to '{new_constraint_text}'"
559
+ )
560
+ else:
561
+ print(
562
+ f"Warning: Could not find dayhoff-tools dependency line in {manifest_path_for_constraint} or {pyproject_path} to update constraint."
563
+ )
564
+ else:
565
+ print(
566
+ f"Warning: Could not find dayhoff-tools dependency line in {manifest_path_for_constraint} to update constraint."
567
+ )
431
568
  except FileNotFoundError:
432
- print(f"Error: {pyproject_path} not found.")
569
+ print(f"Error: {manifest_path_for_constraint} not found.")
433
570
  return
434
571
  except Exception as e:
435
- print(f"Error updating {pyproject_path}: {e}")
572
+ print(f"Error updating {manifest_path_for_constraint}: {e}")
436
573
  print("Proceeding with sync despite pyproject.toml update error.")
437
574
 
438
575
  # Step 3: Sync environment
439
576
  print("Syncing environment with updated lock file...")
440
577
  # Always use --no-install-project for updates
441
- sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project"]
578
+ sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
442
579
  print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
443
- subprocess.run(sync_cmd, check=True)
580
+ subprocess.run(sync_cmd, check=True, cwd=uv_cwd)
444
581
 
445
582
  # Final status message
446
583
  if update_all:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -4,9 +4,9 @@ dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P
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
6
  dayhoff_tools/cli/engine_commands.py,sha256=_NQI9x4VKtAC9vTDdA4NGrILf2kb005t3Kw0aZ5oroE,96485
7
- dayhoff_tools/cli/main.py,sha256=tRN7WCBHg6uyNp6rA54pKTCoVmBntta2i0Yas3bUpZ4,4853
7
+ dayhoff_tools/cli/main.py,sha256=LLMybU9KbtV_F4rwvoYAQZKTTF1nswlSZIfDMKdkh00,5925
8
8
  dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
9
- dayhoff_tools/cli/utility_commands.py,sha256=FRZTPrjsG_qmIIqoNxd1Q1vVkS_5w8aY33IrVYVNCLg,18131
9
+ dayhoff_tools/cli/utility_commands.py,sha256=1GeflsuINXvTS1Ib7_UqGLHSixqn0tQXYx-NEarwveo,25859
10
10
  dayhoff_tools/deployment/base.py,sha256=mYp560l6hSDFtyY2H42VoM8k9VUzfwuiyh9Knqpgc28,17441
11
11
  dayhoff_tools/deployment/deploy_aws.py,sha256=GvZpE2YIFA5Dl9rkAljFjtUypmPDNbWgw8NicHYTP24,18265
12
12
  dayhoff_tools/deployment/deploy_gcp.py,sha256=xgaOVsUDmP6wSEMYNkm1yRNcVskfdz80qJtCulkBIAM,8860
@@ -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.7.1.dist-info/METADATA,sha256=SfgSoCKU9G3NVk4aeQprKBvjn8-bHUXPy5jT2EIS1sA,2914
31
- dayhoff_tools-1.7.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
- dayhoff_tools-1.7.1.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
- dayhoff_tools-1.7.1.dist-info/RECORD,,
30
+ dayhoff_tools-1.7.3.dist-info/METADATA,sha256=WAnl4yvnUfIK6m7enSKQ154Pcy0IplNFNrXnVhsmy1c,2914
31
+ dayhoff_tools-1.7.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
32
+ dayhoff_tools-1.7.3.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
33
+ dayhoff_tools-1.7.3.dist-info/RECORD,,