dayhoff-tools 1.14.14__py3-none-any.whl → 1.15.0__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 +2 -59
- dayhoff_tools/cli/utility_commands.py +32 -1037
- {dayhoff_tools-1.14.14.dist-info → dayhoff_tools-1.15.0.dist-info}/METADATA +2 -3
- {dayhoff_tools-1.14.14.dist-info → dayhoff_tools-1.15.0.dist-info}/RECORD +6 -6
- {dayhoff_tools-1.14.14.dist-info → dayhoff_tools-1.15.0.dist-info}/WHEEL +0 -0
- {dayhoff_tools-1.14.14.dist-info → dayhoff_tools-1.15.0.dist-info}/entry_points.txt +0 -0
dayhoff_tools/cli/main.py
CHANGED
|
@@ -5,19 +5,13 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
7
|
from dayhoff_tools.cli.cloud_commands import aws_app, gcp_app
|
|
8
|
+
from dayhoff_tools.cli.engine1 import engine_app as engine1_app
|
|
9
|
+
from dayhoff_tools.cli.engine1 import studio_app as studio1_app
|
|
8
10
|
from dayhoff_tools.cli.github_commands import gh_app
|
|
9
|
-
from dayhoff_tools.cli.engine1 import (
|
|
10
|
-
engine_app as engine1_app,
|
|
11
|
-
studio_app as studio1_app,
|
|
12
|
-
)
|
|
13
11
|
from dayhoff_tools.cli.utility_commands import (
|
|
14
|
-
add_dependency,
|
|
15
12
|
build_and_upload_wheel,
|
|
16
13
|
delete_local_branch,
|
|
17
|
-
remove_dependency,
|
|
18
|
-
sync_with_toml,
|
|
19
14
|
test_github_actions_locally,
|
|
20
|
-
update_dependencies,
|
|
21
15
|
)
|
|
22
16
|
from dayhoff_tools.warehouse import (
|
|
23
17
|
_warn_if_gcp_default_sa,
|
|
@@ -48,19 +42,6 @@ app = typer.Typer(
|
|
|
48
42
|
# Utility commands
|
|
49
43
|
app.command("clean")(delete_local_branch)
|
|
50
44
|
|
|
51
|
-
# Dependency Management
|
|
52
|
-
app.command(
|
|
53
|
-
"tomlsync",
|
|
54
|
-
help="Sync environment with platform-specific TOML manifest (install/update dependencies).",
|
|
55
|
-
)(sync_with_toml)
|
|
56
|
-
app.command("add", help="Add a dependency to all platform manifests.")(add_dependency)
|
|
57
|
-
app.command("remove", help="Remove a dependency from all platform manifests.")(
|
|
58
|
-
remove_dependency
|
|
59
|
-
)
|
|
60
|
-
app.command("update", help="Update dayhoff-tools (or all deps) and sync environment.")(
|
|
61
|
-
update_dependencies
|
|
62
|
-
)
|
|
63
|
-
|
|
64
45
|
# Other Utilities
|
|
65
46
|
app.command("gha")(test_github_actions_locally)
|
|
66
47
|
app.command("wadd")(add_to_warehouse_typer)
|
|
@@ -183,44 +164,6 @@ def build_and_upload_wheel_command(
|
|
|
183
164
|
build_and_upload_wheel(bump_part=bump)
|
|
184
165
|
|
|
185
166
|
|
|
186
|
-
# Use lazy loading for slow-loading swarm commands
|
|
187
|
-
@app.command("reset")
|
|
188
|
-
def reset_wrapper(
|
|
189
|
-
firestore_collection: str = typer.Option(prompt=True),
|
|
190
|
-
old_status: str = typer.Option(default="failed", prompt=True),
|
|
191
|
-
new_status: str = typer.Option(default="available", prompt=True),
|
|
192
|
-
delete_old: bool = typer.Option(default=True, prompt=True),
|
|
193
|
-
):
|
|
194
|
-
"""Find all the documents in the database with a given status, and
|
|
195
|
-
make a new document with the same name and a new status."""
|
|
196
|
-
from dayhoff_tools.cli.swarm_commands import reset_failed_cards
|
|
197
|
-
|
|
198
|
-
reset_failed_cards(firestore_collection, old_status, new_status, delete_old)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
@app.command("zombie")
|
|
202
|
-
def zombie_wrapper(
|
|
203
|
-
firestore_collection: str = typer.Option(prompt=True),
|
|
204
|
-
delete_old: bool = typer.Option(default=True, prompt=True),
|
|
205
|
-
minutes_threshold: int = typer.Option(default=60, prompt=True),
|
|
206
|
-
):
|
|
207
|
-
"""Find all the documents in the database with status "assigned", and "last_updated"
|
|
208
|
-
older than a specified threshold, and make a new "available" document for them."""
|
|
209
|
-
from dayhoff_tools.cli.swarm_commands import reset_zombie_cards
|
|
210
|
-
|
|
211
|
-
reset_zombie_cards(firestore_collection, delete_old, minutes_threshold)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
@app.command("status")
|
|
215
|
-
def status_wrapper(
|
|
216
|
-
firestore_collection: str = typer.Argument(),
|
|
217
|
-
):
|
|
218
|
-
"""Count the various statuses of items in a given collection."""
|
|
219
|
-
from dayhoff_tools.cli.swarm_commands import get_firestore_collection_status
|
|
220
|
-
|
|
221
|
-
get_firestore_collection_status(firestore_collection)
|
|
222
|
-
|
|
223
|
-
|
|
224
167
|
# Deployment commands - use lazy loading but preserve argument passing
|
|
225
168
|
@app.command("deploy")
|
|
226
169
|
def deploy_command(
|
|
@@ -110,24 +110,11 @@ def build_and_upload_wheel(bump_part: str = "patch"):
|
|
|
110
110
|
publish_cmd = ["uv", "publish", "--token", token]
|
|
111
111
|
print("Using UV_PUBLISH_TOKEN for authentication.")
|
|
112
112
|
|
|
113
|
-
#
|
|
114
|
-
pyproject_path =
|
|
115
|
-
|
|
116
|
-
"pyproject.
|
|
117
|
-
"pyproject.mac.toml",
|
|
118
|
-
"pyproject.workstation.toml",
|
|
119
|
-
]:
|
|
120
|
-
if Path(candidate).exists():
|
|
121
|
-
pyproject_path = candidate
|
|
122
|
-
break
|
|
123
|
-
|
|
124
|
-
if not pyproject_path:
|
|
125
|
-
print(
|
|
126
|
-
"Error: No platform-specific manifest found (pyproject.aws.toml, pyproject.mac.toml, or pyproject.workstation.toml)"
|
|
127
|
-
)
|
|
113
|
+
# Use standard pyproject.toml
|
|
114
|
+
pyproject_path = "pyproject.toml"
|
|
115
|
+
if not Path(pyproject_path).exists():
|
|
116
|
+
print("Error: pyproject.toml not found in current directory.")
|
|
128
117
|
return
|
|
129
|
-
|
|
130
|
-
print(f"Using manifest: {pyproject_path}")
|
|
131
118
|
current_version = None # Initialize in case the first try block fails
|
|
132
119
|
|
|
133
120
|
try:
|
|
@@ -190,90 +177,33 @@ def build_and_upload_wheel(bump_part: str = "patch"):
|
|
|
190
177
|
f.write(new_content)
|
|
191
178
|
print(f"Updated {pyproject_path} with version {new_version}")
|
|
192
179
|
|
|
193
|
-
# Mirror version in all other platform manifests (best-effort)
|
|
194
|
-
other_manifests = []
|
|
195
|
-
for candidate in [
|
|
196
|
-
"pyproject.aws.toml",
|
|
197
|
-
"pyproject.mac.toml",
|
|
198
|
-
"pyproject.workstation.toml",
|
|
199
|
-
]:
|
|
200
|
-
if Path(candidate).exists() and candidate != pyproject_path:
|
|
201
|
-
other_manifests.append(Path(candidate))
|
|
202
|
-
|
|
203
|
-
for manifest_path in other_manifests:
|
|
204
|
-
try:
|
|
205
|
-
content = manifest_path.read_text()
|
|
206
|
-
pattern = re.compile(
|
|
207
|
-
f'^version\s*=\s*"{re.escape(current_version)}"', re.MULTILINE
|
|
208
|
-
)
|
|
209
|
-
new_content, replacements = pattern.subn(
|
|
210
|
-
f'version = "{new_version}"', content
|
|
211
|
-
)
|
|
212
|
-
if replacements > 0:
|
|
213
|
-
manifest_path.write_text(new_content)
|
|
214
|
-
print(f"Updated {manifest_path} with version {new_version}")
|
|
215
|
-
except Exception as e:
|
|
216
|
-
print(f"Warning: Could not update {manifest_path}: {e}")
|
|
217
180
|
# --- End Version Bumping Logic ---
|
|
218
181
|
|
|
219
182
|
# Build wheel and sdist
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if pyproject_path != "pyproject.toml":
|
|
224
|
-
if Path("pyproject.toml").exists():
|
|
225
|
-
Path("pyproject.toml").rename("pyproject.toml.build.bak")
|
|
226
|
-
backup_created = True
|
|
227
|
-
Path(pyproject_path).read_text()
|
|
228
|
-
with open("pyproject.toml", "w") as f:
|
|
229
|
-
f.write(Path(pyproject_path).read_text())
|
|
230
|
-
temp_pyproject_created = True
|
|
183
|
+
build_cmd = ["uv", "build"]
|
|
184
|
+
print(f"Running command: {BLUE}{' '.join(build_cmd)}{RESET}")
|
|
185
|
+
subprocess.run(build_cmd, check=True)
|
|
231
186
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
print(f"Running command: {BLUE}{' '.join(build_cmd)}{RESET}")
|
|
236
|
-
subprocess.run(build_cmd, check=True)
|
|
237
|
-
|
|
238
|
-
# Upload using uv publish with explicit arguments
|
|
239
|
-
# Print masked command in blue
|
|
240
|
-
print(f"Running command: {BLUE}{' '.join(publish_cmd_safe_print)}{RESET}")
|
|
241
|
-
subprocess.run(
|
|
242
|
-
publish_cmd, # Use the actual command with token
|
|
243
|
-
check=True,
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
print(f"Successfully built and uploaded version {new_version} to PyPI")
|
|
187
|
+
# Upload to PyPI
|
|
188
|
+
print(f"Running command: {BLUE}{' '.join(publish_cmd_safe_print)}{RESET}")
|
|
189
|
+
subprocess.run(publish_cmd, check=True)
|
|
247
190
|
|
|
248
|
-
|
|
249
|
-
# (Keep temp pyproject.toml until after this step)
|
|
250
|
-
try:
|
|
251
|
-
proj_name = None
|
|
252
|
-
try:
|
|
253
|
-
proj_toml = toml.load(pyproject_path)
|
|
254
|
-
proj_name = (
|
|
255
|
-
proj_toml.get("project", {}).get("name")
|
|
256
|
-
if isinstance(proj_toml, dict)
|
|
257
|
-
else None
|
|
258
|
-
)
|
|
259
|
-
except Exception:
|
|
260
|
-
pass
|
|
261
|
-
if proj_name == "dayhoff-tools":
|
|
262
|
-
print("Re-installing dayhoff-tools into the active environment…")
|
|
263
|
-
reinstall_cmd = ["uv", "pip", "install", "-e", ".[full]"]
|
|
264
|
-
print(f"Running command: {BLUE}{' '.join(reinstall_cmd)}{RESET}")
|
|
265
|
-
subprocess.run(reinstall_cmd, check=True)
|
|
266
|
-
print("dayhoff-tools reinstalled in the current environment.")
|
|
267
|
-
except subprocess.CalledProcessError as e:
|
|
268
|
-
print(f"Warning: Failed to reinstall dayhoff-tools locally: {e}")
|
|
191
|
+
print(f"Successfully built and uploaded version {new_version} to PyPI")
|
|
269
192
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
193
|
+
# Re-install DHT in Pixi environment when building from DHT itself
|
|
194
|
+
try:
|
|
195
|
+
proj_toml = toml.load(pyproject_path)
|
|
196
|
+
proj_name = proj_toml.get("project", {}).get("name")
|
|
197
|
+
if proj_name == "dayhoff-tools":
|
|
198
|
+
print("Re-installing dayhoff-tools into the Pixi environment...")
|
|
199
|
+
reinstall_cmd = ["pixi", "install"]
|
|
200
|
+
print(f"Running command: {BLUE}{' '.join(reinstall_cmd)}{RESET}")
|
|
201
|
+
subprocess.run(reinstall_cmd, check=True)
|
|
202
|
+
print("dayhoff-tools reinstalled in the Pixi environment.")
|
|
203
|
+
except subprocess.CalledProcessError as e:
|
|
204
|
+
print(f"Warning: Failed to reinstall dayhoff-tools locally: {e}")
|
|
205
|
+
except Exception:
|
|
206
|
+
pass # Not dayhoff-tools or couldn't read toml
|
|
277
207
|
|
|
278
208
|
except FileNotFoundError:
|
|
279
209
|
print(f"Error: {pyproject_path} not found.")
|
|
@@ -304,42 +234,18 @@ def build_and_upload_wheel(bump_part: str = "patch"):
|
|
|
304
234
|
f"Warning: Could not find version {new_version} to revert in {pyproject_path}."
|
|
305
235
|
)
|
|
306
236
|
|
|
307
|
-
# Also revert other platform manifests
|
|
308
|
-
for candidate in [
|
|
309
|
-
"pyproject.aws.toml",
|
|
310
|
-
"pyproject.mac.toml",
|
|
311
|
-
"pyproject.workstation.toml",
|
|
312
|
-
]:
|
|
313
|
-
if Path(candidate).exists() and candidate != pyproject_path:
|
|
314
|
-
try:
|
|
315
|
-
content_revert = Path(candidate).read_text()
|
|
316
|
-
reverted, num = pattern_revert.subn(
|
|
317
|
-
f'version = "{current_version}"', content_revert
|
|
318
|
-
)
|
|
319
|
-
if num > 0:
|
|
320
|
-
Path(candidate).write_text(reverted)
|
|
321
|
-
print(f"Successfully reverted version in {candidate}.")
|
|
322
|
-
except Exception as e2:
|
|
323
|
-
print(
|
|
324
|
-
f"Warning: Failed to revert version change in {candidate}: {e2}"
|
|
325
|
-
)
|
|
326
237
|
except Exception as revert_e:
|
|
327
|
-
print(
|
|
328
|
-
f"Warning: Failed to revert version change in {pyproject_path}: {revert_e}"
|
|
329
|
-
)
|
|
238
|
+
print(f"Warning: Failed to revert version change: {revert_e}")
|
|
330
239
|
except Exception as e:
|
|
331
240
|
print(f"An unexpected error occurred: {e}")
|
|
332
|
-
#
|
|
241
|
+
# Attempt rollback if version was bumped
|
|
333
242
|
if current_version and "new_version" in locals() and new_version:
|
|
334
243
|
try:
|
|
335
|
-
print(
|
|
336
|
-
f"Attempting to revert version in {pyproject_path} back to {current_version} due to unexpected error..."
|
|
337
|
-
)
|
|
338
|
-
# (Same revert logic as above)
|
|
244
|
+
print(f"Attempting to revert version back to {current_version}...")
|
|
339
245
|
with open(pyproject_path, "r") as f:
|
|
340
246
|
content_revert = f.read()
|
|
341
247
|
pattern_revert = re.compile(
|
|
342
|
-
f'^version
|
|
248
|
+
f'^version\\s*=\\s*"{re.escape(new_version)}"', re.MULTILINE
|
|
343
249
|
)
|
|
344
250
|
reverted_content, num_revert = pattern_revert.subn(
|
|
345
251
|
f'version = "{current_version}"', content_revert
|
|
@@ -347,921 +253,10 @@ def build_and_upload_wheel(bump_part: str = "patch"):
|
|
|
347
253
|
if num_revert > 0:
|
|
348
254
|
with open(pyproject_path, "w") as f:
|
|
349
255
|
f.write(reverted_content)
|
|
350
|
-
print(
|
|
256
|
+
print("Successfully reverted version in pyproject.toml.")
|
|
351
257
|
else:
|
|
352
|
-
print(
|
|
353
|
-
f"Warning: Could not find version {new_version} to revert in {pyproject_path}."
|
|
354
|
-
)
|
|
355
|
-
# Also revert other platform manifests
|
|
356
|
-
for candidate in [
|
|
357
|
-
"pyproject.aws.toml",
|
|
358
|
-
"pyproject.mac.toml",
|
|
359
|
-
"pyproject.workstation.toml",
|
|
360
|
-
]:
|
|
361
|
-
if Path(candidate).exists() and candidate != pyproject_path:
|
|
362
|
-
try:
|
|
363
|
-
content_revert = Path(candidate).read_text()
|
|
364
|
-
reverted, num = pattern_revert.subn(
|
|
365
|
-
f'version = "{current_version}"', content_revert
|
|
366
|
-
)
|
|
367
|
-
if num > 0:
|
|
368
|
-
Path(candidate).write_text(reverted)
|
|
369
|
-
print(f"Successfully reverted version in {candidate}.")
|
|
370
|
-
except Exception as e2:
|
|
371
|
-
print(
|
|
372
|
-
f"Warning: Failed to revert version change in {candidate}: {e2}"
|
|
373
|
-
)
|
|
258
|
+
print(f"Warning: Could not find version {new_version} to revert.")
|
|
374
259
|
except Exception as revert_e:
|
|
375
|
-
print(
|
|
376
|
-
f"Warning: Failed to revert version change in {pyproject_path}: {revert_e}"
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
# --- Dependency Management Commands ---
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
def sync_with_toml(
|
|
384
|
-
install_project: bool = typer.Option(
|
|
385
|
-
False,
|
|
386
|
-
"--install-project",
|
|
387
|
-
"-p",
|
|
388
|
-
help="Install the local project package itself (with 'full' extras) into the environment.",
|
|
389
|
-
),
|
|
390
|
-
):
|
|
391
|
-
"""Sync environment with platform-specific TOML manifest (install/update dependencies).
|
|
392
|
-
|
|
393
|
-
Behavior by platform:
|
|
394
|
-
- Workstation (STUDIO_PLATFORM=workstation) with pyproject.workstation.toml:
|
|
395
|
-
* Uses pip with constraints.txt to preserve NGC PyTorch
|
|
396
|
-
* Parses dependencies directly from pyproject.workstation.toml
|
|
397
|
-
* Installs into .venv_workstation with --system-site-packages
|
|
398
|
-
- Mac (STUDIO_PLATFORM=mac) with pyproject.mac.toml:
|
|
399
|
-
* Ensure `.mac_uv_project/pyproject.toml` is a copy of `pyproject.mac.toml`
|
|
400
|
-
* Run `uv lock` and `uv sync` in `.mac_uv_project` targeting active venv with `--active`
|
|
401
|
-
* If `install_project` is true, install the project from repo root into the active env (editable, [full])
|
|
402
|
-
- AWS (default) with pyproject.aws.toml:
|
|
403
|
-
* Uses UV in temp directory `.aws_uv_project` similar to Mac
|
|
404
|
-
* Run `uv lock` and `uv sync` targeting active venv
|
|
405
|
-
"""
|
|
406
|
-
# ANSI color codes
|
|
407
|
-
BLUE = "\033[94m"
|
|
408
|
-
RESET = "\033[0m"
|
|
409
|
-
|
|
410
|
-
try:
|
|
411
|
-
platform = os.environ.get("STUDIO_PLATFORM", "aws")
|
|
412
|
-
|
|
413
|
-
# Workstation platform: use pip with constraints
|
|
414
|
-
if platform == "workstation" and Path("pyproject.workstation.toml").exists():
|
|
415
|
-
print(
|
|
416
|
-
"Installing dependencies for workstation platform (using pip + constraints)..."
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
# Check for constraints.txt
|
|
420
|
-
if not Path("constraints.txt").exists():
|
|
421
|
-
print(
|
|
422
|
-
"Error: constraints.txt not found. Run direnv to generate it first."
|
|
423
|
-
)
|
|
424
|
-
sys.exit(1)
|
|
425
|
-
|
|
426
|
-
# Parse and install dependencies from pyproject.workstation.toml
|
|
427
|
-
import re
|
|
428
|
-
|
|
429
|
-
with open("pyproject.workstation.toml", "r") as f:
|
|
430
|
-
content = f.read()
|
|
431
|
-
|
|
432
|
-
# Extract dependencies list using line-by-line parsing to handle [] in package names
|
|
433
|
-
lines = content.split("\n")
|
|
434
|
-
in_deps = False
|
|
435
|
-
deps_lines = []
|
|
436
|
-
|
|
437
|
-
for line in lines:
|
|
438
|
-
if re.match(r"\s*dependencies\s*=\s*\[", line):
|
|
439
|
-
in_deps = True
|
|
440
|
-
continue
|
|
441
|
-
if in_deps:
|
|
442
|
-
if re.match(r"^\s*\]\s*$", line):
|
|
443
|
-
break
|
|
444
|
-
deps_lines.append(line)
|
|
445
|
-
|
|
446
|
-
deps = []
|
|
447
|
-
for line in deps_lines:
|
|
448
|
-
line = line.strip()
|
|
449
|
-
if line.startswith('"') or line.startswith("'"):
|
|
450
|
-
dep = re.sub(r'["\']', "", line)
|
|
451
|
-
dep = re.sub(r",?\s*#.*$", "", dep)
|
|
452
|
-
dep = dep.strip().rstrip(",")
|
|
453
|
-
if dep:
|
|
454
|
-
deps.append(dep)
|
|
455
|
-
|
|
456
|
-
if deps:
|
|
457
|
-
pip_cmd = (
|
|
458
|
-
[sys.executable, "-m", "pip", "install"]
|
|
459
|
-
+ deps
|
|
460
|
-
+ ["-c", "constraints.txt"]
|
|
461
|
-
)
|
|
462
|
-
print(
|
|
463
|
-
f"Running command: {BLUE}{' '.join(pip_cmd[:5])} ... -c constraints.txt{RESET}"
|
|
464
|
-
)
|
|
465
|
-
subprocess.run(pip_cmd, check=True)
|
|
466
|
-
|
|
467
|
-
# Install dev dependencies using line-by-line parsing
|
|
468
|
-
in_dev_groups = False
|
|
469
|
-
in_dev_list = False
|
|
470
|
-
dev_lines = []
|
|
471
|
-
|
|
472
|
-
for line in lines:
|
|
473
|
-
if re.match(r"\s*\[dependency-groups\]", line):
|
|
474
|
-
in_dev_groups = True
|
|
475
|
-
continue
|
|
476
|
-
if in_dev_groups and re.match(r"\s*dev\s*=\s*\[", line):
|
|
477
|
-
in_dev_list = True
|
|
478
|
-
continue
|
|
479
|
-
if in_dev_list:
|
|
480
|
-
if re.match(r"^\s*\]\s*$", line):
|
|
481
|
-
break
|
|
482
|
-
dev_lines.append(line)
|
|
483
|
-
|
|
484
|
-
dev_deps = []
|
|
485
|
-
for line in dev_lines:
|
|
486
|
-
line = line.strip()
|
|
487
|
-
if line.startswith('"') or line.startswith("'"):
|
|
488
|
-
dep = re.sub(r'["\']', "", line)
|
|
489
|
-
dep = re.sub(r",?\s*#.*$", "", dep)
|
|
490
|
-
dep = dep.strip().rstrip(",")
|
|
491
|
-
if dep:
|
|
492
|
-
dev_deps.append(dep)
|
|
493
|
-
|
|
494
|
-
if dev_deps:
|
|
495
|
-
print("Installing dev dependencies...")
|
|
496
|
-
pip_cmd = (
|
|
497
|
-
[sys.executable, "-m", "pip", "install"]
|
|
498
|
-
+ dev_deps
|
|
499
|
-
+ ["-c", "constraints.txt"]
|
|
500
|
-
)
|
|
501
|
-
print(
|
|
502
|
-
f"Running command: {BLUE}{' '.join(pip_cmd[:5])} ... -c constraints.txt{RESET}"
|
|
503
|
-
)
|
|
504
|
-
subprocess.run(pip_cmd, check=True)
|
|
505
|
-
|
|
506
|
-
# Install project if requested
|
|
507
|
-
if install_project:
|
|
508
|
-
repo_name = Path.cwd().name
|
|
509
|
-
if repo_name == "dayhoff-tools":
|
|
510
|
-
pip_cmd = [
|
|
511
|
-
sys.executable,
|
|
512
|
-
"-m",
|
|
513
|
-
"pip",
|
|
514
|
-
"install",
|
|
515
|
-
"-e",
|
|
516
|
-
".[full]",
|
|
517
|
-
"-c",
|
|
518
|
-
"constraints.txt",
|
|
519
|
-
]
|
|
520
|
-
else:
|
|
521
|
-
pip_cmd = [
|
|
522
|
-
sys.executable,
|
|
523
|
-
"-m",
|
|
524
|
-
"pip",
|
|
525
|
-
"install",
|
|
526
|
-
"-e",
|
|
527
|
-
".",
|
|
528
|
-
"-c",
|
|
529
|
-
"constraints.txt",
|
|
530
|
-
]
|
|
531
|
-
print(f"Running command: {BLUE}{' '.join(pip_cmd)}{RESET}")
|
|
532
|
-
subprocess.run(pip_cmd, check=True)
|
|
533
|
-
|
|
534
|
-
print("✅ Dependencies installed successfully (workstation)")
|
|
535
|
-
return
|
|
536
|
-
|
|
537
|
-
# Mac platform: use UV with pyproject.mac.toml
|
|
538
|
-
is_mac = platform == "mac"
|
|
539
|
-
mac_manifest = Path("pyproject.mac.toml")
|
|
540
|
-
if is_mac and mac_manifest.exists():
|
|
541
|
-
# Mac devcontainer flow
|
|
542
|
-
mac_uv_dir = Path(".mac_uv_project")
|
|
543
|
-
mac_uv_dir.mkdir(parents=True, exist_ok=True)
|
|
544
|
-
mac_pyproject = mac_uv_dir / "pyproject.toml"
|
|
545
|
-
mac_pyproject.write_text(mac_manifest.read_text())
|
|
546
|
-
|
|
547
|
-
# Copy README.md if it exists (required by some build backends)
|
|
548
|
-
if Path("README.md").exists():
|
|
549
|
-
(mac_uv_dir / "README.md").write_text(Path("README.md").read_text())
|
|
550
|
-
|
|
551
|
-
# Ensure lock matches manifest (in mac temp dir)
|
|
552
|
-
print("Ensuring lock file matches pyproject.mac.toml (Mac devcon)…")
|
|
553
|
-
lock_cmd = ["uv", "lock"]
|
|
554
|
-
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
|
555
|
-
subprocess.run(
|
|
556
|
-
lock_cmd, check=True, capture_output=True, cwd=str(mac_uv_dir)
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
# Sync into the active environment
|
|
560
|
-
if install_project:
|
|
561
|
-
print(
|
|
562
|
-
"Syncing dependencies into ACTIVE env (project installed separately)…"
|
|
563
|
-
)
|
|
564
|
-
sync_cmd = [
|
|
565
|
-
"uv",
|
|
566
|
-
"sync",
|
|
567
|
-
"--all-groups",
|
|
568
|
-
"--no-install-project",
|
|
569
|
-
"--active",
|
|
570
|
-
]
|
|
571
|
-
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
|
572
|
-
subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
|
|
573
|
-
|
|
574
|
-
# Install project from repo root (where source code actually is)
|
|
575
|
-
# Temporarily create pyproject.toml at repo root for UV
|
|
576
|
-
print("Installing project with 'full' extras from repo root…")
|
|
577
|
-
temp_pyproject = False
|
|
578
|
-
backup_created = False
|
|
579
|
-
try:
|
|
580
|
-
if not Path("pyproject.toml").exists():
|
|
581
|
-
# Create temp pyproject.toml from platform manifest
|
|
582
|
-
Path("pyproject.toml").write_text(mac_manifest.read_text())
|
|
583
|
-
temp_pyproject = True
|
|
584
|
-
elif Path("pyproject.toml").is_symlink():
|
|
585
|
-
# Backup existing symlink
|
|
586
|
-
Path("pyproject.toml").rename("pyproject.toml.sync.bak")
|
|
587
|
-
Path("pyproject.toml").write_text(mac_manifest.read_text())
|
|
588
|
-
backup_created = True
|
|
589
|
-
|
|
590
|
-
pip_install_cmd = ["uv", "pip", "install", "-e", ".[full]"]
|
|
591
|
-
print(f"Running command: {BLUE}{' '.join(pip_install_cmd)}{RESET}")
|
|
592
|
-
subprocess.run(pip_install_cmd, check=True)
|
|
593
|
-
print("Project installed with 'full' extras successfully.")
|
|
594
|
-
finally:
|
|
595
|
-
# Clean up temp pyproject.toml
|
|
596
|
-
if temp_pyproject and Path("pyproject.toml").exists():
|
|
597
|
-
Path("pyproject.toml").unlink()
|
|
598
|
-
if backup_created and Path("pyproject.toml.sync.bak").exists():
|
|
599
|
-
Path("pyproject.toml.sync.bak").rename("pyproject.toml")
|
|
600
|
-
else:
|
|
601
|
-
print("Syncing dependencies into ACTIVE env (project not installed)…")
|
|
602
|
-
sync_cmd = [
|
|
603
|
-
"uv",
|
|
604
|
-
"sync",
|
|
605
|
-
"--all-groups",
|
|
606
|
-
"--no-install-project",
|
|
607
|
-
"--active",
|
|
608
|
-
]
|
|
609
|
-
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
|
610
|
-
subprocess.run(sync_cmd, check=True, cwd=str(mac_uv_dir))
|
|
611
|
-
print("Dependencies synced successfully (project not installed).")
|
|
612
|
-
else:
|
|
613
|
-
# AWS platform (or fallback): use UV with pyproject.aws.toml
|
|
614
|
-
aws_manifest = Path("pyproject.aws.toml")
|
|
615
|
-
if aws_manifest.exists():
|
|
616
|
-
# AWS devcontainer flow (similar to Mac)
|
|
617
|
-
aws_uv_dir = Path(".aws_uv_project")
|
|
618
|
-
aws_uv_dir.mkdir(parents=True, exist_ok=True)
|
|
619
|
-
aws_pyproject = aws_uv_dir / "pyproject.toml"
|
|
620
|
-
aws_pyproject.write_text(aws_manifest.read_text())
|
|
621
|
-
|
|
622
|
-
# Copy README.md if it exists (required by some build backends)
|
|
623
|
-
if Path("README.md").exists():
|
|
624
|
-
(aws_uv_dir / "README.md").write_text(Path("README.md").read_text())
|
|
625
|
-
|
|
626
|
-
# Ensure lock matches manifest (in aws temp dir)
|
|
627
|
-
print("Ensuring lock file matches pyproject.aws.toml (AWS devcon)…")
|
|
628
|
-
lock_cmd = ["uv", "lock"]
|
|
629
|
-
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
|
630
|
-
subprocess.run(
|
|
631
|
-
lock_cmd, check=True, capture_output=True, cwd=str(aws_uv_dir)
|
|
632
|
-
)
|
|
633
|
-
|
|
634
|
-
# Sync into the active environment
|
|
635
|
-
if install_project:
|
|
636
|
-
print(
|
|
637
|
-
"Syncing dependencies into ACTIVE env (project installed separately)…"
|
|
638
|
-
)
|
|
639
|
-
sync_cmd = [
|
|
640
|
-
"uv",
|
|
641
|
-
"sync",
|
|
642
|
-
"--all-groups",
|
|
643
|
-
"--no-install-project",
|
|
644
|
-
"--active",
|
|
645
|
-
]
|
|
646
|
-
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
|
647
|
-
subprocess.run(sync_cmd, check=True, cwd=str(aws_uv_dir))
|
|
648
|
-
|
|
649
|
-
# Install project from repo root (where source code actually is)
|
|
650
|
-
# Temporarily create pyproject.toml at repo root for UV
|
|
651
|
-
print("Installing project with 'full' extras from repo root…")
|
|
652
|
-
temp_pyproject = False
|
|
653
|
-
backup_created = False
|
|
654
|
-
try:
|
|
655
|
-
if not Path("pyproject.toml").exists():
|
|
656
|
-
# Create temp pyproject.toml from platform manifest
|
|
657
|
-
Path("pyproject.toml").write_text(aws_manifest.read_text())
|
|
658
|
-
temp_pyproject = True
|
|
659
|
-
elif Path("pyproject.toml").is_symlink():
|
|
660
|
-
# Backup existing symlink
|
|
661
|
-
Path("pyproject.toml").rename("pyproject.toml.sync.bak")
|
|
662
|
-
Path("pyproject.toml").write_text(aws_manifest.read_text())
|
|
663
|
-
backup_created = True
|
|
664
|
-
|
|
665
|
-
pip_install_cmd = ["uv", "pip", "install", "-e", ".[full]"]
|
|
666
|
-
print(
|
|
667
|
-
f"Running command: {BLUE}{' '.join(pip_install_cmd)}{RESET}"
|
|
668
|
-
)
|
|
669
|
-
subprocess.run(pip_install_cmd, check=True)
|
|
670
|
-
print("Project installed with 'full' extras successfully.")
|
|
671
|
-
finally:
|
|
672
|
-
# Clean up temp pyproject.toml
|
|
673
|
-
if temp_pyproject and Path("pyproject.toml").exists():
|
|
674
|
-
Path("pyproject.toml").unlink()
|
|
675
|
-
if backup_created and Path("pyproject.toml.sync.bak").exists():
|
|
676
|
-
Path("pyproject.toml.sync.bak").rename("pyproject.toml")
|
|
677
|
-
else:
|
|
678
|
-
print(
|
|
679
|
-
"Syncing dependencies into ACTIVE env (project not installed)…"
|
|
680
|
-
)
|
|
681
|
-
sync_cmd = [
|
|
682
|
-
"uv",
|
|
683
|
-
"sync",
|
|
684
|
-
"--all-groups",
|
|
685
|
-
"--no-install-project",
|
|
686
|
-
"--active",
|
|
687
|
-
]
|
|
688
|
-
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
|
689
|
-
subprocess.run(sync_cmd, check=True, cwd=str(aws_uv_dir))
|
|
690
|
-
print("Dependencies synced successfully (project not installed).")
|
|
691
|
-
else:
|
|
692
|
-
print(
|
|
693
|
-
"Error: No platform-specific manifest found (pyproject.aws.toml, pyproject.mac.toml, or pyproject.workstation.toml)"
|
|
694
|
-
)
|
|
695
|
-
sys.exit(1)
|
|
696
|
-
|
|
697
|
-
except subprocess.CalledProcessError as e:
|
|
698
|
-
stderr_output = e.stderr.decode() if e.stderr else "No stderr output."
|
|
699
|
-
print(f"Error occurred during dependency installation/sync: {e}")
|
|
700
|
-
print(f"Stderr: {stderr_output}")
|
|
701
|
-
if "NoSolution" in stderr_output:
|
|
702
|
-
print(
|
|
703
|
-
"\nHint: Could not find a compatible set of dependencies. Check constraints in pyproject.toml."
|
|
704
|
-
)
|
|
705
|
-
sys.exit(1)
|
|
706
|
-
except FileNotFoundError:
|
|
707
|
-
print("Error: 'uv' command not found. Is uv installed and in PATH?")
|
|
708
|
-
sys.exit(1)
|
|
709
|
-
except Exception as e:
|
|
710
|
-
print(f"An unexpected error occurred: {e}")
|
|
711
|
-
sys.exit(1)
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
def _get_all_platform_manifests():
|
|
715
|
-
"""Get list of all platform manifests that exist."""
|
|
716
|
-
manifest_files = []
|
|
717
|
-
for fname in [
|
|
718
|
-
"pyproject.aws.toml",
|
|
719
|
-
"pyproject.mac.toml",
|
|
720
|
-
"pyproject.workstation.toml",
|
|
721
|
-
]:
|
|
722
|
-
if Path(fname).exists():
|
|
723
|
-
manifest_files.append(Path(fname))
|
|
724
|
-
return manifest_files
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
def _resolve_package_version(package_name: str) -> str | None:
|
|
728
|
-
"""Resolve a package version by running uv lock and parsing the lock file.
|
|
729
|
-
|
|
730
|
-
Args:
|
|
731
|
-
package_name: Name of the package to resolve
|
|
732
|
-
|
|
733
|
-
Returns:
|
|
734
|
-
Resolved version string, or None if resolution failed
|
|
735
|
-
"""
|
|
736
|
-
import os
|
|
737
|
-
|
|
738
|
-
try:
|
|
739
|
-
# Determine which manifest to use (prefer Mac, then AWS)
|
|
740
|
-
platform = os.environ.get("STUDIO_PLATFORM", "aws")
|
|
741
|
-
manifest_path = None
|
|
742
|
-
uv_dir = None
|
|
743
|
-
|
|
744
|
-
if platform == "mac" and Path("pyproject.mac.toml").exists():
|
|
745
|
-
manifest_path = Path("pyproject.mac.toml")
|
|
746
|
-
uv_dir = Path(".mac_uv_project")
|
|
747
|
-
elif Path("pyproject.aws.toml").exists():
|
|
748
|
-
manifest_path = Path("pyproject.aws.toml")
|
|
749
|
-
uv_dir = Path(".aws_uv_project")
|
|
750
|
-
elif Path("pyproject.mac.toml").exists():
|
|
751
|
-
# Fallback to Mac if AWS doesn't exist
|
|
752
|
-
manifest_path = Path("pyproject.mac.toml")
|
|
753
|
-
uv_dir = Path(".mac_uv_project")
|
|
754
|
-
else:
|
|
755
|
-
return None
|
|
756
|
-
|
|
757
|
-
# Create temp directory and copy manifest
|
|
758
|
-
uv_dir.mkdir(parents=True, exist_ok=True)
|
|
759
|
-
(uv_dir / "pyproject.toml").write_text(manifest_path.read_text())
|
|
760
|
-
|
|
761
|
-
# Copy README if it exists
|
|
762
|
-
if Path("README.md").exists():
|
|
763
|
-
(uv_dir / "README.md").write_text(Path("README.md").read_text())
|
|
764
|
-
|
|
765
|
-
# Run uv lock (suppress output)
|
|
766
|
-
subprocess.run(["uv", "lock"], cwd=str(uv_dir), check=True, capture_output=True)
|
|
767
|
-
|
|
768
|
-
# Parse lock file
|
|
769
|
-
lock_file = uv_dir / "uv.lock"
|
|
770
|
-
if not lock_file.exists():
|
|
771
|
-
return None
|
|
772
|
-
|
|
773
|
-
lock_data = toml.load(lock_file)
|
|
774
|
-
for package in lock_data.get("package", []):
|
|
775
|
-
if package.get("name") == package_name:
|
|
776
|
-
return package.get("version")
|
|
777
|
-
|
|
778
|
-
return None
|
|
779
|
-
|
|
780
|
-
except Exception as e:
|
|
781
|
-
print(f"Warning: Failed to resolve version: {e}")
|
|
782
|
-
return None
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
def _update_all_manifests_for_dayhoff_tools(new_version: str):
|
|
786
|
-
"""Update dayhoff-tools constraint in all platform manifests."""
|
|
787
|
-
import re
|
|
788
|
-
|
|
789
|
-
manifest_files = _get_all_platform_manifests()
|
|
790
|
-
|
|
791
|
-
if not manifest_files:
|
|
792
|
-
print("Warning: No platform manifests found to update.")
|
|
793
|
-
return
|
|
794
|
-
|
|
795
|
-
package_name = "dayhoff-tools"
|
|
796
|
-
package_name_esc = re.escape(package_name)
|
|
797
|
-
|
|
798
|
-
# Regex to match the dependency line, with optional extras and version spec
|
|
799
|
-
pattern = re.compile(
|
|
800
|
-
rf"^(\s*['\"]){package_name_esc}(\[[^]]+\])?(?:[><=~^][^'\"]*)?(['\"].*)$",
|
|
801
|
-
re.MULTILINE,
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
new_constraint_text = f">={new_version}"
|
|
805
|
-
|
|
806
|
-
def _repl(match: re.Match):
|
|
807
|
-
prefix = match.group(1)
|
|
808
|
-
extras = match.group(2) or ""
|
|
809
|
-
suffix = match.group(3)
|
|
810
|
-
return f"{prefix}{package_name}{extras}{new_constraint_text}{suffix}"
|
|
811
|
-
|
|
812
|
-
# Update all manifest files
|
|
813
|
-
for manifest_file in manifest_files:
|
|
814
|
-
try:
|
|
815
|
-
print(f"Updating {manifest_file} version constraint...")
|
|
816
|
-
content = manifest_file.read_text()
|
|
817
|
-
new_content, num_replacements = pattern.subn(_repl, content)
|
|
818
|
-
if num_replacements > 0:
|
|
819
|
-
manifest_file.write_text(new_content)
|
|
820
|
-
print(
|
|
821
|
-
f"Updated dayhoff-tools constraint in {manifest_file} to '{new_constraint_text}'"
|
|
822
|
-
)
|
|
823
|
-
else:
|
|
824
|
-
print(
|
|
825
|
-
f"Warning: Could not find dayhoff-tools dependency line in {manifest_file}"
|
|
826
|
-
)
|
|
827
|
-
except Exception as e:
|
|
828
|
-
print(f"Error updating {manifest_file}: {e}")
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
def add_dependency(
|
|
832
|
-
package: str,
|
|
833
|
-
dev: bool = typer.Option(
|
|
834
|
-
False, "--dev", "-d", help="Add to dev dependencies instead of main."
|
|
835
|
-
),
|
|
836
|
-
):
|
|
837
|
-
"""Add a dependency to all platform-specific manifests.
|
|
838
|
-
|
|
839
|
-
Args:
|
|
840
|
-
package: Package specification (e.g., "numpy>=1.24.0" or "pandas")
|
|
841
|
-
dev: If True, add to [dependency-groups] dev instead of [project] dependencies
|
|
842
|
-
"""
|
|
843
|
-
import re
|
|
844
|
-
|
|
845
|
-
# ANSI color codes
|
|
846
|
-
BLUE = "\033[94m"
|
|
847
|
-
RESET = "\033[0m"
|
|
848
|
-
|
|
849
|
-
manifest_files = _get_all_platform_manifests()
|
|
850
|
-
|
|
851
|
-
if not manifest_files:
|
|
852
|
-
print(
|
|
853
|
-
"Error: No platform-specific manifests found (pyproject.aws.toml, pyproject.mac.toml, or pyproject.workstation.toml)"
|
|
854
|
-
)
|
|
855
|
-
sys.exit(1)
|
|
856
|
-
|
|
857
|
-
# Determine section to add to
|
|
858
|
-
section_name = "dev dependencies" if dev else "main dependencies"
|
|
859
|
-
print(f"Adding '{package}' to {section_name} in all platform manifests...")
|
|
860
|
-
|
|
861
|
-
# Parse package name to check for duplicates and version specs
|
|
862
|
-
package_name = re.split(r"[<>=~!\[]", package)[0].strip()
|
|
863
|
-
has_version_spec = any(c in package for c in ["<", ">", "=", "~", "!"])
|
|
864
|
-
|
|
865
|
-
added_count = 0
|
|
866
|
-
|
|
867
|
-
for manifest_file in manifest_files:
|
|
868
|
-
try:
|
|
869
|
-
content = manifest_file.read_text()
|
|
870
|
-
|
|
871
|
-
# Check if package already exists
|
|
872
|
-
existing_check = re.search(
|
|
873
|
-
rf'^(\s*["\']){re.escape(package_name)}[<>=~!\[]',
|
|
874
|
-
content,
|
|
875
|
-
re.MULTILINE,
|
|
876
|
-
)
|
|
877
|
-
if existing_check:
|
|
878
|
-
print(
|
|
879
|
-
f"⚠️ Package '{package_name}' already exists in {manifest_file}, skipping"
|
|
880
|
-
)
|
|
881
|
-
continue
|
|
882
|
-
|
|
883
|
-
if dev:
|
|
884
|
-
# Add to [dependency-groups] dev section
|
|
885
|
-
# Use line-by-line parsing to handle [] in dependency names like dayhoff-tools[full]
|
|
886
|
-
lines = content.split("\n")
|
|
887
|
-
in_dev_groups = False
|
|
888
|
-
in_dev_list = False
|
|
889
|
-
dev_start_idx = None
|
|
890
|
-
dev_end_idx = None
|
|
891
|
-
|
|
892
|
-
for idx, line in enumerate(lines):
|
|
893
|
-
if re.match(r"\s*\[dependency-groups\]", line):
|
|
894
|
-
in_dev_groups = True
|
|
895
|
-
continue
|
|
896
|
-
if in_dev_groups and re.match(r"\s*dev\s*=\s*\[", line):
|
|
897
|
-
in_dev_list = True
|
|
898
|
-
dev_start_idx = idx
|
|
899
|
-
continue
|
|
900
|
-
if in_dev_list and re.match(r"^\s*\]\s*$", line):
|
|
901
|
-
dev_end_idx = idx
|
|
902
|
-
break
|
|
903
|
-
|
|
904
|
-
if dev_start_idx is None or dev_end_idx is None:
|
|
905
|
-
print(
|
|
906
|
-
f"Warning: Could not find [dependency-groups] dev section in {manifest_file}"
|
|
907
|
-
)
|
|
908
|
-
continue
|
|
909
|
-
|
|
910
|
-
# Insert new dependency before the closing ]
|
|
911
|
-
new_dep = f' "{package}",'
|
|
912
|
-
lines.insert(dev_end_idx, new_dep)
|
|
913
|
-
new_content = "\n".join(lines)
|
|
914
|
-
else:
|
|
915
|
-
# Add to [project] dependencies section
|
|
916
|
-
# Use line-by-line parsing to handle [] in dependency names like dayhoff-tools[full]
|
|
917
|
-
lines = content.split("\n")
|
|
918
|
-
in_deps = False
|
|
919
|
-
deps_start_idx = None
|
|
920
|
-
deps_end_idx = None
|
|
921
|
-
|
|
922
|
-
for idx, line in enumerate(lines):
|
|
923
|
-
if re.match(r"\s*dependencies\s*=\s*\[", line):
|
|
924
|
-
in_deps = True
|
|
925
|
-
deps_start_idx = idx
|
|
926
|
-
continue
|
|
927
|
-
if in_deps and re.match(r"^\s*\]\s*$", line):
|
|
928
|
-
deps_end_idx = idx
|
|
929
|
-
break
|
|
930
|
-
|
|
931
|
-
if deps_start_idx is None or deps_end_idx is None:
|
|
932
|
-
print(
|
|
933
|
-
f"Warning: Could not find dependencies section in {manifest_file}"
|
|
934
|
-
)
|
|
935
|
-
continue
|
|
936
|
-
|
|
937
|
-
# Insert new dependency before the closing ]
|
|
938
|
-
new_dep = f' "{package}",'
|
|
939
|
-
lines.insert(deps_end_idx, new_dep)
|
|
940
|
-
new_content = "\n".join(lines)
|
|
941
|
-
|
|
942
|
-
manifest_file.write_text(new_content)
|
|
943
|
-
print(f"✅ Added '{package}' to {manifest_file}")
|
|
944
|
-
added_count += 1
|
|
945
|
-
|
|
946
|
-
except Exception as e:
|
|
947
|
-
print(f"Error updating {manifest_file}: {e}")
|
|
948
|
-
|
|
949
|
-
# If nothing was added, exit early
|
|
950
|
-
if added_count == 0:
|
|
951
|
-
print(f"\n⚠️ Package '{package_name}' already exists in all manifests")
|
|
952
|
-
return
|
|
953
|
-
|
|
954
|
-
print(f"\n✅ Added '{package}' to {added_count} platform manifest(s)")
|
|
955
|
-
|
|
956
|
-
# If no version specified, resolve and add version constraint
|
|
957
|
-
if not has_version_spec:
|
|
958
|
-
print(f"\n🔍 Resolving version for '{package_name}'...")
|
|
959
|
-
resolved_version = _resolve_package_version(package_name)
|
|
960
|
-
|
|
961
|
-
if resolved_version:
|
|
962
|
-
print(f"📌 Resolved to version {resolved_version}")
|
|
963
|
-
print(
|
|
964
|
-
f"Updating manifests with version constraint '>={resolved_version}'..."
|
|
965
|
-
)
|
|
966
|
-
|
|
967
|
-
# Update all manifests to add version constraint
|
|
968
|
-
for manifest_file in manifest_files:
|
|
969
|
-
try:
|
|
970
|
-
content = manifest_file.read_text()
|
|
971
|
-
# Replace unversioned package with versioned one
|
|
972
|
-
pattern = re.compile(
|
|
973
|
-
rf'^(\s*["\']){re.escape(package_name)}(["\'],?)(.*)$',
|
|
974
|
-
re.MULTILINE,
|
|
975
|
-
)
|
|
976
|
-
|
|
977
|
-
def replace_with_version(match):
|
|
978
|
-
prefix = match.group(1)
|
|
979
|
-
suffix = match.group(2)
|
|
980
|
-
rest = match.group(3)
|
|
981
|
-
return (
|
|
982
|
-
f"{prefix}{package_name}>={resolved_version}{suffix}{rest}"
|
|
983
|
-
)
|
|
984
|
-
|
|
985
|
-
new_content = pattern.sub(replace_with_version, content)
|
|
986
|
-
manifest_file.write_text(new_content)
|
|
987
|
-
print(f"✅ Updated {manifest_file} with version constraint")
|
|
988
|
-
except Exception as e:
|
|
989
|
-
print(f"Warning: Could not update version in {manifest_file}: {e}")
|
|
990
|
-
|
|
991
|
-
print(
|
|
992
|
-
f"\n✅ Added '{package_name}>={resolved_version}' to {added_count} platform manifest(s)"
|
|
993
|
-
)
|
|
994
|
-
else:
|
|
995
|
-
print(
|
|
996
|
-
f"⚠️ Could not resolve version for '{package_name}', left unversioned"
|
|
997
|
-
)
|
|
998
|
-
|
|
999
|
-
print(
|
|
1000
|
-
f"\nRun {BLUE}dh tomlsync{RESET} to install the new dependency in your environment."
|
|
1001
|
-
)
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
def remove_dependency(
|
|
1005
|
-
package: str,
|
|
1006
|
-
dev: bool = typer.Option(
|
|
1007
|
-
False, "--dev", "-d", help="Remove from dev dependencies instead of main."
|
|
1008
|
-
),
|
|
1009
|
-
):
|
|
1010
|
-
"""Remove a dependency from all platform-specific manifests.
|
|
260
|
+
print(f"Warning: Failed to revert version change: {revert_e}")
|
|
1011
261
|
|
|
1012
|
-
Args:
|
|
1013
|
-
package: Package name (e.g., "numpy" or "pandas")
|
|
1014
|
-
dev: If True, remove from [dependency-groups] dev instead of [project] dependencies
|
|
1015
|
-
"""
|
|
1016
|
-
import re
|
|
1017
|
-
|
|
1018
|
-
# ANSI color codes
|
|
1019
|
-
BLUE = "\033[94m"
|
|
1020
|
-
RESET = "\033[0m"
|
|
1021
262
|
|
|
1022
|
-
manifest_files = _get_all_platform_manifests()
|
|
1023
|
-
|
|
1024
|
-
if not manifest_files:
|
|
1025
|
-
print(
|
|
1026
|
-
"Error: No platform-specific manifests found (pyproject.aws.toml, pyproject.mac.toml, or pyproject.workstation.toml)"
|
|
1027
|
-
)
|
|
1028
|
-
sys.exit(1)
|
|
1029
|
-
|
|
1030
|
-
section_name = "dev dependencies" if dev else "main dependencies"
|
|
1031
|
-
print(f"Removing '{package}' from {section_name} in all platform manifests...")
|
|
1032
|
-
|
|
1033
|
-
# Escape package name for regex
|
|
1034
|
-
package_esc = re.escape(package)
|
|
1035
|
-
|
|
1036
|
-
removed_count = 0
|
|
1037
|
-
for manifest_file in manifest_files:
|
|
1038
|
-
try:
|
|
1039
|
-
content = manifest_file.read_text()
|
|
1040
|
-
|
|
1041
|
-
# Pattern to match the dependency line (with optional version spec)
|
|
1042
|
-
# Matches: "package...", or "package...",\n (including the newline)
|
|
1043
|
-
pattern = re.compile(
|
|
1044
|
-
rf'^\s*["\']({package_esc}[<>=~!\[].+?|{package_esc})["\'],?\s*(?:#.*)?$\n?',
|
|
1045
|
-
re.MULTILINE,
|
|
1046
|
-
)
|
|
1047
|
-
|
|
1048
|
-
new_content, num_removed = pattern.subn("", content)
|
|
1049
|
-
|
|
1050
|
-
if num_removed > 0:
|
|
1051
|
-
# Clean up any consecutive blank lines (more than one)
|
|
1052
|
-
new_content = re.sub(r"\n\n\n+", "\n\n", new_content)
|
|
1053
|
-
# Also clean up trailing whitespace on lines
|
|
1054
|
-
new_content = re.sub(r"[ \t]+$", "", new_content, flags=re.MULTILINE)
|
|
1055
|
-
manifest_file.write_text(new_content)
|
|
1056
|
-
print(f"✅ Removed '{package}' from {manifest_file}")
|
|
1057
|
-
removed_count += 1
|
|
1058
|
-
else:
|
|
1059
|
-
print(f"⚠️ Package '{package}' not found in {manifest_file}")
|
|
1060
|
-
|
|
1061
|
-
except Exception as e:
|
|
1062
|
-
print(f"Error updating {manifest_file}: {e}")
|
|
1063
|
-
|
|
1064
|
-
if removed_count > 0:
|
|
1065
|
-
print(f"\n✅ Removed '{package}' from {removed_count} platform manifest(s)")
|
|
1066
|
-
print(
|
|
1067
|
-
f"\nRun {BLUE}dh tomlsync{RESET} to uninstall the dependency from your environment."
|
|
1068
|
-
)
|
|
1069
|
-
else:
|
|
1070
|
-
print(f"\n⚠️ Package '{package}' was not found in any manifests")
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
def update_dependencies(
|
|
1074
|
-
update_all: bool = typer.Option(
|
|
1075
|
-
False,
|
|
1076
|
-
"--all",
|
|
1077
|
-
"-a",
|
|
1078
|
-
help="Update all dependencies instead of just dayhoff-tools.",
|
|
1079
|
-
),
|
|
1080
|
-
):
|
|
1081
|
-
"""Update dependencies to newer versions (platform-aware).
|
|
1082
|
-
|
|
1083
|
-
- Default Action (no flags): Updates only 'dayhoff-tools' package to latest,
|
|
1084
|
-
updates ALL manifest files with the version constraint, and syncs.
|
|
1085
|
-
- Flags:
|
|
1086
|
-
--all/-a: Updates all dependencies and syncs.
|
|
1087
|
-
|
|
1088
|
-
Cross-platform behavior:
|
|
1089
|
-
- Workstation: Uses pip to upgrade packages, regenerates constraints.txt
|
|
1090
|
-
- Mac/AWS: Uses UV with platform-specific manifests (.mac_uv_project or .aws_uv_project)
|
|
1091
|
-
- Always updates ALL platform manifests (pyproject.aws.toml, pyproject.mac.toml,
|
|
1092
|
-
pyproject.workstation.toml) to ensure version consistency
|
|
1093
|
-
"""
|
|
1094
|
-
# ANSI color codes
|
|
1095
|
-
BLUE = "\033[94m"
|
|
1096
|
-
RESET = "\033[0m"
|
|
1097
|
-
|
|
1098
|
-
platform = os.environ.get("STUDIO_PLATFORM", "aws")
|
|
1099
|
-
|
|
1100
|
-
# Workstation platform: use pip upgrade
|
|
1101
|
-
if platform == "workstation" and Path("pyproject.workstation.toml").exists():
|
|
1102
|
-
print("Updating dependencies for workstation platform (using pip)...")
|
|
1103
|
-
|
|
1104
|
-
if update_all:
|
|
1105
|
-
print("Error: --all flag not supported on workstation platform yet.")
|
|
1106
|
-
print("Use 'pip install --upgrade <package>' manually for now.")
|
|
1107
|
-
sys.exit(1)
|
|
1108
|
-
|
|
1109
|
-
# Update dayhoff-tools only (default behavior)
|
|
1110
|
-
print("Upgrading dayhoff-tools to latest version...")
|
|
1111
|
-
upgrade_cmd = [
|
|
1112
|
-
sys.executable,
|
|
1113
|
-
"-m",
|
|
1114
|
-
"pip",
|
|
1115
|
-
"install",
|
|
1116
|
-
"--upgrade",
|
|
1117
|
-
"dayhoff-tools[full]",
|
|
1118
|
-
]
|
|
1119
|
-
print(f"Running command: {BLUE}{' '.join(upgrade_cmd)}{RESET}")
|
|
1120
|
-
subprocess.run(upgrade_cmd, check=True)
|
|
1121
|
-
|
|
1122
|
-
# Get new version
|
|
1123
|
-
result = subprocess.run(
|
|
1124
|
-
[sys.executable, "-m", "pip", "show", "dayhoff-tools"],
|
|
1125
|
-
capture_output=True,
|
|
1126
|
-
text=True,
|
|
1127
|
-
check=True,
|
|
1128
|
-
)
|
|
1129
|
-
version_line = [
|
|
1130
|
-
l for l in result.stdout.split("\n") if l.startswith("Version:")
|
|
1131
|
-
]
|
|
1132
|
-
if version_line:
|
|
1133
|
-
new_version = version_line[0].split(":", 1)[1].strip()
|
|
1134
|
-
print(f"Updated to dayhoff-tools {new_version}")
|
|
1135
|
-
|
|
1136
|
-
# Update all platform manifests with new constraint
|
|
1137
|
-
_update_all_manifests_for_dayhoff_tools(new_version)
|
|
1138
|
-
|
|
1139
|
-
print("✅ Dependencies updated successfully (workstation)")
|
|
1140
|
-
return
|
|
1141
|
-
|
|
1142
|
-
# Mac/AWS platforms: use UV
|
|
1143
|
-
mac_manifest = Path("pyproject.mac.toml")
|
|
1144
|
-
aws_manifest = Path("pyproject.aws.toml")
|
|
1145
|
-
mac_uv_dir = Path(".mac_uv_project")
|
|
1146
|
-
aws_uv_dir = Path(".aws_uv_project")
|
|
1147
|
-
lock_file_path = Path("uv.lock")
|
|
1148
|
-
|
|
1149
|
-
# Determine action based on flags
|
|
1150
|
-
lock_cmd = ["uv", "lock"]
|
|
1151
|
-
action_description = ""
|
|
1152
|
-
run_pyproject_update = False
|
|
1153
|
-
|
|
1154
|
-
if update_all:
|
|
1155
|
-
lock_cmd.append("--upgrade")
|
|
1156
|
-
action_description = (
|
|
1157
|
-
"Updating lock file for all dependencies to latest versions..."
|
|
1158
|
-
)
|
|
1159
|
-
else: # Default behavior: update dayhoff-tools
|
|
1160
|
-
lock_cmd.extend(["--upgrade-package", "dayhoff-tools"])
|
|
1161
|
-
action_description = (
|
|
1162
|
-
"Updating dayhoff-tools lock and pyproject.toml (default behavior)..."
|
|
1163
|
-
)
|
|
1164
|
-
run_pyproject_update = (
|
|
1165
|
-
True # Only update pyproject if we are doing the dayhoff update
|
|
1166
|
-
)
|
|
1167
|
-
|
|
1168
|
-
try:
|
|
1169
|
-
# Choose working directory for uv operations based on platform
|
|
1170
|
-
uv_cwd = None
|
|
1171
|
-
manifest_path_for_constraint = None
|
|
1172
|
-
|
|
1173
|
-
if platform == "mac" and mac_manifest.exists():
|
|
1174
|
-
mac_uv_dir.mkdir(parents=True, exist_ok=True)
|
|
1175
|
-
(mac_uv_dir / "pyproject.toml").write_text(mac_manifest.read_text())
|
|
1176
|
-
# Copy README.md if it exists (required by some build backends)
|
|
1177
|
-
if Path("README.md").exists():
|
|
1178
|
-
(mac_uv_dir / "README.md").write_text(Path("README.md").read_text())
|
|
1179
|
-
uv_cwd = str(mac_uv_dir)
|
|
1180
|
-
lock_file_path = mac_uv_dir / "uv.lock"
|
|
1181
|
-
manifest_path_for_constraint = mac_manifest
|
|
1182
|
-
elif aws_manifest.exists():
|
|
1183
|
-
# AWS platform (default)
|
|
1184
|
-
aws_uv_dir.mkdir(parents=True, exist_ok=True)
|
|
1185
|
-
(aws_uv_dir / "pyproject.toml").write_text(aws_manifest.read_text())
|
|
1186
|
-
# Copy README.md if it exists (required by some build backends)
|
|
1187
|
-
if Path("README.md").exists():
|
|
1188
|
-
(aws_uv_dir / "README.md").write_text(Path("README.md").read_text())
|
|
1189
|
-
uv_cwd = str(aws_uv_dir)
|
|
1190
|
-
lock_file_path = aws_uv_dir / "uv.lock"
|
|
1191
|
-
manifest_path_for_constraint = aws_manifest
|
|
1192
|
-
else:
|
|
1193
|
-
print(
|
|
1194
|
-
"Error: No platform-specific manifest found (pyproject.aws.toml or pyproject.mac.toml)"
|
|
1195
|
-
)
|
|
1196
|
-
sys.exit(1)
|
|
1197
|
-
|
|
1198
|
-
# Step 1: Run the update lock command
|
|
1199
|
-
print(action_description)
|
|
1200
|
-
print(f"Running command: {BLUE}{' '.join(lock_cmd)}{RESET}")
|
|
1201
|
-
subprocess.run(lock_cmd, check=True, capture_output=True, cwd=uv_cwd)
|
|
1202
|
-
|
|
1203
|
-
# Step 2: Update both manifest files if doing the dayhoff update (default)
|
|
1204
|
-
if run_pyproject_update:
|
|
1205
|
-
print(f"Reading {lock_file_path} to find new dayhoff-tools version...")
|
|
1206
|
-
if not lock_file_path.exists():
|
|
1207
|
-
print(f"Error: {lock_file_path} not found after lock command.")
|
|
1208
|
-
return
|
|
1209
|
-
locked_version = None
|
|
1210
|
-
try:
|
|
1211
|
-
lock_data = toml.load(lock_file_path)
|
|
1212
|
-
for package in lock_data.get("package", []):
|
|
1213
|
-
if package.get("name") == "dayhoff-tools":
|
|
1214
|
-
locked_version = package.get("version")
|
|
1215
|
-
break
|
|
1216
|
-
except toml.TomlDecodeError as e:
|
|
1217
|
-
print(f"Error parsing {lock_file_path}: {e}")
|
|
1218
|
-
return
|
|
1219
|
-
except Exception as e:
|
|
1220
|
-
print(f"Error reading lock file: {e}")
|
|
1221
|
-
return
|
|
1222
|
-
|
|
1223
|
-
if not locked_version:
|
|
1224
|
-
print(
|
|
1225
|
-
f"Error: Could not find dayhoff-tools version in {lock_file_path}."
|
|
1226
|
-
)
|
|
1227
|
-
return
|
|
1228
|
-
|
|
1229
|
-
print(f"Found dayhoff-tools version {locked_version} in lock file.")
|
|
1230
|
-
|
|
1231
|
-
# Update all platform manifest files to ensure consistency
|
|
1232
|
-
_update_all_manifests_for_dayhoff_tools(locked_version)
|
|
1233
|
-
|
|
1234
|
-
# Step 3: Sync environment
|
|
1235
|
-
print("Syncing environment with updated lock file...")
|
|
1236
|
-
# Always use --no-install-project for updates
|
|
1237
|
-
sync_cmd = ["uv", "sync", "--all-groups", "--no-install-project", "--active"]
|
|
1238
|
-
print(f"Running command: {BLUE}{' '.join(sync_cmd)}{RESET}")
|
|
1239
|
-
subprocess.run(sync_cmd, check=True, cwd=uv_cwd)
|
|
1240
|
-
|
|
1241
|
-
# Final status message
|
|
1242
|
-
if update_all:
|
|
1243
|
-
print("All dependencies updated and environment synced successfully.")
|
|
1244
|
-
else: # Default case (dayhoff update)
|
|
1245
|
-
print(
|
|
1246
|
-
"dayhoff-tools updated, manifest files modified, and environment synced successfully."
|
|
1247
|
-
)
|
|
1248
|
-
|
|
1249
|
-
except subprocess.CalledProcessError as e:
|
|
1250
|
-
stderr_output = e.stderr.decode() if e.stderr else "No stderr output."
|
|
1251
|
-
print(f"Error occurred during dependency update/sync: {e}")
|
|
1252
|
-
print(f"Stderr: {stderr_output}")
|
|
1253
|
-
if "NoSolution" in stderr_output:
|
|
1254
|
-
print(
|
|
1255
|
-
"\nHint: Could not find a compatible set of dependencies. Check constraints in pyproject.toml."
|
|
1256
|
-
)
|
|
1257
|
-
elif "unrecognized arguments: --upgrade" in stderr_output:
|
|
1258
|
-
print(
|
|
1259
|
-
"\nHint: Your version of 'uv' might be too old to support '--upgrade'. Try updating uv."
|
|
1260
|
-
)
|
|
1261
|
-
sys.exit(1)
|
|
1262
|
-
except FileNotFoundError:
|
|
1263
|
-
print("Error: 'uv' command not found. Is uv installed and in PATH?")
|
|
1264
|
-
sys.exit(1)
|
|
1265
|
-
except Exception as e:
|
|
1266
|
-
print(f"An unexpected error occurred: {e}")
|
|
1267
|
-
sys.exit(1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dayhoff-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.0
|
|
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
|
|
@@ -25,7 +25,7 @@ Requires-Dist: fair-esm (>=2.0.0) ; extra == "full"
|
|
|
25
25
|
Requires-Dist: firebase-admin (>=6.5.0)
|
|
26
26
|
Requires-Dist: h5py (>=3.11.0) ; extra == "full"
|
|
27
27
|
Requires-Dist: h5py (>=3.13.0) ; extra == "embedders"
|
|
28
|
-
Requires-Dist: numpy (>=1.26.4) ; extra == "embedders"
|
|
28
|
+
Requires-Dist: numpy (>=1.26.4,<2.0) ; extra == "embedders"
|
|
29
29
|
Requires-Dist: pandas (>=2.2.0,<2.2.3) ; extra == "embedders"
|
|
30
30
|
Requires-Dist: pandas (>=2.2.0,<2.2.3) ; extra == "full"
|
|
31
31
|
Requires-Dist: pydantic (>=2.0.0) ; extra == "batch"
|
|
@@ -38,7 +38,6 @@ Requires-Dist: sentencepiece (>=0.2.0) ; extra == "embedders"
|
|
|
38
38
|
Requires-Dist: sentencepiece (>=0.2.0) ; extra == "full"
|
|
39
39
|
Requires-Dist: sqlalchemy (>=2.0.40,<3.0.0) ; extra == "full"
|
|
40
40
|
Requires-Dist: toml (>=0.10)
|
|
41
|
-
Requires-Dist: torch (>=2.4.0) ; extra == "embedders"
|
|
42
41
|
Requires-Dist: tqdm (>=4.67.1) ; extra == "embedders"
|
|
43
42
|
Requires-Dist: tqdm (>=4.67.1) ; extra == "full"
|
|
44
43
|
Requires-Dist: transformers (==4.36.2) ; extra == "full"
|
|
@@ -48,9 +48,9 @@ dayhoff_tools/cli/engines_studios/simulators/studio_status_simulator.py,sha256=6
|
|
|
48
48
|
dayhoff_tools/cli/engines_studios/ssh_config.py,sha256=UCv-jf_zSuk7FUStkCQBAJz1QkxiSEwZbdSrwt_9SMU,2932
|
|
49
49
|
dayhoff_tools/cli/engines_studios/studio_commands.py,sha256=KGSNZQS8MmM_DfQzT9SRZvuR3OK6NdIdOrqI2wJFyes,25984
|
|
50
50
|
dayhoff_tools/cli/github_commands.py,sha256=pfrxI68LObGm_gtPlQN-gHPahHV4l9k9T4GqO99NNL0,8948
|
|
51
|
-
dayhoff_tools/cli/main.py,sha256=
|
|
51
|
+
dayhoff_tools/cli/main.py,sha256=E4UXtOBRRAhcrJ3RgLCQFpJdamFN-p9xViMkX45kNgw,6456
|
|
52
52
|
dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
|
|
53
|
-
dayhoff_tools/cli/utility_commands.py,sha256=
|
|
53
|
+
dayhoff_tools/cli/utility_commands.py,sha256=IMFd20OZrl0vIGL7Xrg3OoHNxOhG5KdeeieAUctolUk,9834
|
|
54
54
|
dayhoff_tools/deployment/base.py,sha256=uZnFvnPQx6pH_HmJbdThweAs3BrxMaDohpE3iX_-yk4,18377
|
|
55
55
|
dayhoff_tools/deployment/deploy_aws.py,sha256=1j16aE4hmln4pQVtcSGuIGVWbOBfWwveytvihjofADo,21519
|
|
56
56
|
dayhoff_tools/deployment/deploy_gcp.py,sha256=xgaOVsUDmP6wSEMYNkm1yRNcVskfdz80qJtCulkBIAM,8860
|
|
@@ -71,7 +71,7 @@ dayhoff_tools/intake/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJq
|
|
|
71
71
|
dayhoff_tools/logs.py,sha256=DKdeP0k0kliRcilwvX0mUB2eipO5BdWUeHwh-VnsICs,838
|
|
72
72
|
dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
|
|
73
73
|
dayhoff_tools/warehouse.py,sha256=UETBtZD3r7WgvURqfGbyHlT7cxoiVq8isjzMuerKw8I,24475
|
|
74
|
-
dayhoff_tools-1.
|
|
75
|
-
dayhoff_tools-1.
|
|
76
|
-
dayhoff_tools-1.
|
|
77
|
-
dayhoff_tools-1.
|
|
74
|
+
dayhoff_tools-1.15.0.dist-info/METADATA,sha256=YvxzOOhTeXVfO3NVsZ16ciDQME_kK3YYaNwbRKkIBso,3135
|
|
75
|
+
dayhoff_tools-1.15.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
76
|
+
dayhoff_tools-1.15.0.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
|
|
77
|
+
dayhoff_tools-1.15.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|