lamin_cli 1.11.0__py2.py3-none-any.whl → 1.12.0__py2.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.
lamin_cli/__init__.py CHANGED
@@ -6,7 +6,7 @@ The interface is defined in `__main__.py`.
6
6
  The root API here is used by LaminR to replicate the CLI functionality.
7
7
  """
8
8
 
9
- __version__ = "1.11.0"
9
+ __version__ = "1.12.0"
10
10
 
11
11
  from lamindb_setup import disconnect, logout
12
12
  from lamindb_setup._connect_instance import _connect_cli as connect
lamin_cli/__main__.py CHANGED
@@ -53,9 +53,7 @@ COMMAND_GROUPS = {
53
53
  {
54
54
  "name": "Configure",
55
55
  "commands": [
56
- "checkout",
57
56
  "switch",
58
- "cache",
59
57
  "settings",
60
58
  "migrate",
61
59
  ],
@@ -112,7 +110,6 @@ else:
112
110
 
113
111
  from lamindb_setup._silence_loggers import silence_loggers
114
112
 
115
- from lamin_cli._cache import cache
116
113
  from lamin_cli._io import io
117
114
  from lamin_cli._migration import migrate
118
115
  from lamin_cli._settings import settings
@@ -146,7 +143,7 @@ def login(user: str, key: str | None):
146
143
 
147
144
  After authenticating once, you can re-authenticate and switch between accounts via `lamin login myhandle`.
148
145
 
149
- See also: Login in a Python session via {func}`~lamindb.setup.login`.
146
+ Python/R alternative: {func}`~lamindb.setup.login`
150
147
  """
151
148
  return login_(user, key=key)
152
149
 
@@ -181,9 +178,19 @@ def init(
181
178
  db: str | None,
182
179
  modules: str | None,
183
180
  ):
184
- """Init a LaminDB instance.
181
+ """Initialize an instance.
185
182
 
186
- See also: Init in a Python session via {func}`~lamindb.setup.init`.
183
+ This initializes a LaminDB instance, for example:
184
+
185
+ ```
186
+ lamin init --storage ./mydata
187
+ lamin init --storage s3://my-bucket
188
+ lamin init --storage gs://my-bucket
189
+ lamin init --storage ./mydata --modules bionty
190
+ lamin init --storage ./mydata --modules bionty,pertdb
191
+ ```
192
+
193
+ → Python/R alternative: {func}`~lamindb.setup.init`
187
194
  """
188
195
  return init_(storage=storage, db=db, modules=modules, name=name)
189
196
 
@@ -191,73 +198,108 @@ def init(
191
198
  # fmt: off
192
199
  @main.command()
193
200
  @click.argument("instance", type=str)
194
- @click.option("--use_proxy_db", is_flag=True, help="Use proxy database connection.")
195
201
  # fmt: on
196
- def connect(instance: str, use_proxy_db: bool):
197
- """Configure default instance for connections.
202
+ def connect(instance: str):
203
+ """Set a default instance for auto-connection.
198
204
 
199
- Python/R sessions and CLI commands will then auto-connect to the configured instance.
205
+ Python/R sessions and CLI commands will then auto-connect to this LaminDB instance.
200
206
 
201
- Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`).
207
+ Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`), for example:
208
+
209
+ ```
210
+ lamin connect laminlabs/cellxgene
211
+ lamin connect https://lamin.ai/laminlabs/cellxgene
212
+ ```
202
213
 
203
- See also: Connect in a Python session via {func}`~lamindb.connect`.
214
+ Python/R alternative: {func}`~lamindb.connect` the global default database or a database object via {class}`~lamindb.DB`
204
215
  """
205
- return connect_(instance, use_proxy_db=use_proxy_db)
216
+ return connect_(instance)
206
217
 
207
218
 
208
219
  @main.command()
209
220
  def disconnect():
210
- """Clear default instance configuration.
221
+ """Unset the default instance for auto-connection.
211
222
 
212
- See also: Disconnect in a Python session via {func}`~lamindb.setup.disconnect`.
223
+ Python/R sessions and CLI commands will no longer auto-connect to a LaminDB instance.
224
+
225
+ For example:
226
+
227
+ ```
228
+ lamin disconnect
229
+ ```
230
+
231
+ → Python/R alternative: {func}`~lamindb.setup.disconnect`
213
232
  """
214
233
  return disconnect_()
215
234
 
216
235
 
217
236
  # fmt: off
218
237
  @main.command()
219
- @click.argument("entity", type=str)
220
- @click.option("--name", type=str, default=None, help="A name.")
238
+ @click.argument("registry", type=click.Choice(["branch", "project"]))
239
+ @click.argument("name", type=str, required=False)
240
+ # below is deprecated, for backward compatibility
241
+ @click.option("--name", "name_opt", type=str, default=None, hidden=True, help="A name.")
221
242
  # fmt: on
222
- def create(entity: Literal["branch"], name: str | None = None):
223
- """Create a record for an entity.
243
+ def create(
244
+ registry: Literal["branch", "project"],
245
+ name: str | None,
246
+ name_opt: str | None,
247
+ ):
248
+ """Create an object.
224
249
 
225
250
  Currently only supports creating branches and projects.
226
251
 
227
252
  ```
228
- lamin create branch --name my_branch
229
- lamin create project --name my_project
253
+ lamin create branch my_branch
254
+ lamin create project my_project
230
255
  ```
256
+
257
+ → Python/R alternative: {class}`~lamindb.Branch` and {class}`~lamindb.Project`.
231
258
  """
259
+ resolved_name = name if name is not None else name_opt
260
+ if resolved_name is None:
261
+ raise click.UsageError(
262
+ "Specify a name. Examples: lamin create branch my_branch, lamin create project my_project"
263
+ )
264
+ if name_opt is not None:
265
+ warnings.warn(
266
+ "lamin create --name is deprecated; use 'lamin create <registry> <name>' instead, e.g. lamin create branch my_branch.",
267
+ DeprecationWarning,
268
+ stacklevel=2,
269
+ )
270
+
232
271
  from lamindb.models import Branch, Project
233
272
 
234
- if entity == "branch":
235
- record = Branch(name=name).save()
236
- elif entity == "project":
237
- record = Project(name=name).save()
273
+ if registry == "branch":
274
+ record = Branch(name=resolved_name).save()
275
+ elif registry == "project":
276
+ record = Project(name=resolved_name).save()
238
277
  else:
239
- raise NotImplementedError(f"Creating {entity} is not implemented.")
240
- logger.important(f"created {entity}: {record.name}")
278
+ raise NotImplementedError(f"Creating {registry} object is not implemented.")
279
+ logger.important(f"created {registry}: {record.name}")
241
280
 
242
281
 
243
282
  # fmt: off
244
283
  @main.command(name="list")
245
- @click.argument("entity", type=str)
246
- @click.option("--name", type=str, default=None, help="A name.")
284
+ @click.argument("registry", type=str)
247
285
  # fmt: on
248
- def list_(entity: Literal["branch"], name: str | None = None):
249
- """List records for an entity.
286
+ def list_(registry: Literal["branch", "space"]):
287
+ """List objects.
288
+
289
+ For example:
250
290
 
251
291
  ```
252
292
  lamin list branch
253
293
  lamin list space
254
294
  ```
295
+
296
+ → Python/R alternative: {method}`~lamindb.Branch.to_dataframe()`
255
297
  """
256
- assert entity in {"branch", "space"}, "Currently only supports listing branches and spaces."
298
+ assert registry in {"branch", "space"}, "Currently only supports listing branches and spaces."
257
299
 
258
300
  from lamindb.models import Branch, Space
259
301
 
260
- if entity == "branch":
302
+ if registry == "branch":
261
303
  print(Branch.to_dataframe())
262
304
  else:
263
305
  print(Space.to_dataframe())
@@ -265,28 +307,56 @@ def list_(entity: Literal["branch"], name: str | None = None):
265
307
 
266
308
  # fmt: off
267
309
  @main.command()
268
- @click.option("--branch", type=str, default=None, help="A valid branch name or uid.")
269
- @click.option("--space", type=str, default=None, help="A valid branch name or uid.")
310
+ @click.argument("registry", type=click.Choice(["branch", "space"]), required=False)
311
+ @click.argument("name", type=str, required=False)
312
+ # below are deprecated, for backward compatibility
313
+ @click.option("--branch", type=str, default=None, hidden=True, help="A valid branch name or uid.")
314
+ @click.option("--space", type=str, default=None, hidden=True, help="A valid space name or uid.")
270
315
  # fmt: on
271
- def switch(branch: str | None = None, space: str | None = None):
316
+ def switch(
317
+ registry: Literal["branch", "space"] | None,
318
+ name: str | None,
319
+ branch: str | None,
320
+ space: str | None,
321
+ ):
272
322
  """Switch between branches or spaces.
273
323
 
324
+ Python/R sessions and CLI commands will use the current default branch or space, for example:
325
+
274
326
  ```
275
- lamin switch --branch my_branch
276
- lamin switch --space our_space
327
+ lamin switch branch my_branch
328
+ lamin switch space our_space
277
329
  ```
330
+
331
+ → Python/R alternative: {attr}`~lamindb.setup.core.SetupSettings.branch` and {attr}`~lamindb.setup.core.SetupSettings.space`
278
332
  """
333
+ if registry is not None and name is not None:
334
+ branch = name if registry == "branch" else None
335
+ space = name if registry == "space" else None
336
+ elif branch is None and space is None:
337
+ raise click.UsageError(
338
+ "Specify branch or space. Examples: lamin switch branch my_branch, lamin switch space our_space"
339
+ )
340
+ else:
341
+ warnings.warn(
342
+ "lamin switch --branch and --space are deprecated; use 'lamin switch branch <name>' or 'lamin switch space <name>' instead.",
343
+ DeprecationWarning,
344
+ stacklevel=2,
345
+ )
346
+
279
347
  from lamindb.setup import switch as switch_
280
348
 
281
349
  switch_(branch=branch, space=space)
282
350
 
283
351
 
284
352
  @main.command()
285
- @click.option("--schema", is_flag=True, help="View database schema.")
353
+ @click.option("--schema", is_flag=True, help="View database schema via Django plugin.")
286
354
  def info(schema: bool):
287
- """Show info about the environment, instance, branch, space, and user.
355
+ """Show info about the instance, development & cache directories, branch, space, and user.
288
356
 
289
- See also: Print the instance settings in a Python session via {func}`~lamindb.setup.settings`.
357
+ Manage settings via [lamin settings](https://docs.lamin.ai/cli#settings).
358
+
359
+ → Python/R alternative: {func}`~lamindb.setup.settings`
290
360
  """
291
361
  if schema:
292
362
  from lamindb_setup._schema import view
@@ -301,31 +371,42 @@ def info(schema: bool):
301
371
 
302
372
  # fmt: off
303
373
  @main.command()
374
+ # entity can be a registry or an object in the registry
304
375
  @click.argument("entity", type=str)
305
376
  @click.option("--name", type=str, default=None)
306
377
  @click.option("--uid", type=str, default=None)
307
- @click.option("--slug", type=str, default=None)
378
+ @click.option("--key", type=str, default=None, help="The key for the entity (artifact, transform).")
308
379
  @click.option("--permanent", is_flag=True, default=None, help="Permanently delete the entity where applicable, e.g., for artifact, transform, collection.")
309
380
  @click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation (only relevant for instance).")
310
381
  # fmt: on
311
- def delete(entity: str, name: str | None = None, uid: str | None = None, slug: str | None = None, permanent: bool | None = None, force: bool = False):
312
- """Delete an entity.
382
+ def delete(entity: str, name: str | None = None, uid: str | None = None, key: str | None = None, slug: str | None = None, permanent: bool | None = None, force: bool = False):
383
+ """Delete an object.
313
384
 
314
385
  Currently supported: `branch`, `artifact`, `transform`, `collection`, and `instance`. For example:
315
386
 
316
387
  ```
317
- lamin delete https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
318
- lamin delete https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5 --permanent
388
+ # via --key or --name
389
+ lamin delete artifact --key mydatasets/mytable.parquet
390
+ lamin delete transform --key myanalyses/analysis.ipynb
319
391
  lamin delete branch --name my_branch
320
392
  lamin delete instance --slug account/name
393
+ # via registry and --uid
394
+ lamin delete artifact --uid e2G7k9EVul4JbfsE
395
+ lamin delete transform --uid Vul4JbfsEYAy5
396
+ # via URL
397
+ lamin delete https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
398
+ lamin delete https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5 --permanent
321
399
  ```
400
+
401
+ → Python/R alternative: {method}`~lamindb.SQLRecord.delete` and {func}`~lamindb.setup.delete`
322
402
  """
323
403
  from lamin_cli._delete import delete as delete_
324
404
 
325
- return delete_(entity=entity, name=name, uid=uid, slug=slug, permanent=permanent, force=force)
405
+ return delete_(entity=entity, name=name, uid=uid, key=key, permanent=permanent, force=force)
326
406
 
327
407
 
328
408
  @main.command()
409
+ # entity can be a registry or an object in the registry
329
410
  @click.argument("entity", type=str, required=False)
330
411
  @click.option("--uid", help="The uid for the entity.")
331
412
  @click.option("--key", help="The key for the entity.")
@@ -333,23 +414,23 @@ def delete(entity: str, name: str | None = None, uid: str | None = None, slug: s
333
414
  "--with-env", is_flag=True, help="Also return the environment for a tranform."
334
415
  )
335
416
  def load(entity: str | None = None, uid: str | None = None, key: str | None = None, with_env: bool = False):
336
- """Load a file or folder into the cache or working directory.
417
+ """Sync a file/folder into a local cache (artifacts) or development directory (transforms).
337
418
 
338
419
  Pass a URL or `--key`. For example:
339
420
 
340
421
  ```
341
- lamin load https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsE
422
+ # via key
342
423
  lamin load --key mydatasets/mytable.parquet
343
424
  lamin load --key analysis.ipynb
344
425
  lamin load --key myanalyses/analysis.ipynb --with-env
345
- ```
346
-
347
- You can also pass a uid and the entity type:
348
-
349
- ```
426
+ # via registry and --uid
350
427
  lamin load artifact --uid e2G7k9EVul4JbfsE
351
428
  lamin load transform --uid Vul4JbfsEYAy5
429
+ # via URL
430
+ lamin load https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsE
352
431
  ```
432
+
433
+ → Python/R alternative: {func}`~lamindb.Artifact.load`, no equivalent for transforms
353
434
  """
354
435
  from lamin_cli._load import load as load_
355
436
  if entity is not None:
@@ -381,30 +462,36 @@ def _describe(entity: str = "artifact", uid: str | None = None, key: str | None
381
462
 
382
463
 
383
464
  @main.command()
465
+ # entity can be a registry or an object in the registry
384
466
  @click.argument("entity", type=str, default="artifact")
385
467
  @click.option("--uid", help="The uid for the entity.")
386
468
  @click.option("--key", help="The key for the entity.")
387
469
  def describe(entity: str = "artifact", uid: str | None = None, key: str | None = None):
388
- """Describe an artifact.
470
+ """Describe an object.
389
471
 
390
472
  Examples:
391
473
 
392
474
  ```
475
+ # via --key
393
476
  lamin describe --key example_datasets/mini_immuno/dataset1.h5ad
477
+ # via registry and --uid
478
+ lamin describe artifact --uid e2G7k9EVul4JbfsE
479
+ # via URL
394
480
  lamin describe https://lamin.ai/laminlabs/lamin-site-assets/artifact/6sofuDVvTANB0f48
395
481
  ```
396
482
 
397
- See also: Describe an artifact in a Python session via {func}`~lamindb.Artifact.describe`.
483
+ Python/R alternative: {meth}`~lamindb.Artifact.describe`
398
484
  """
399
485
  _describe(entity=entity, uid=uid, key=key)
400
486
 
401
487
 
402
488
  @main.command()
489
+ # entity can be a registry or an object in the registry
403
490
  @click.argument("entity", type=str, default="artifact")
404
491
  @click.option("--uid", help="The uid for the entity.")
405
492
  @click.option("--key", help="The key for the entity.")
406
493
  def get(entity: str = "artifact", uid: str | None = None, key: str | None = None):
407
- """Query metadata about an entity.
494
+ """Query metadata about an object.
408
495
 
409
496
  Currently equivalent to `lamin describe`.
410
497
  """
@@ -420,11 +507,25 @@ def get(entity: str = "artifact", uid: str | None = None, key: str | None = None
420
507
  @click.option("--project", type=str, default=None, help="A valid project name or uid.")
421
508
  @click.option("--space", type=str, default=None, help="A valid space name or uid.")
422
509
  @click.option("--branch", type=str, default=None, help="A valid branch name or uid.")
423
- @click.option("--registry", type=str, default=None, help="Either 'artifact' or 'transform'. If not passed, chooses based on path suffix.")
424
- def save(path: str, key: str, description: str, stem_uid: str, project: str, space: str, branch: str, registry: str):
425
- """Save a file or folder.
510
+ @click.option(
511
+ "--registry",
512
+ type=click.Choice(["artifact", "transform"]),
513
+ default=None,
514
+ help="Either 'artifact' or 'transform'. If not passed, chooses based on path suffix.",
515
+ )
516
+ def save(
517
+ path: str,
518
+ key: str,
519
+ description: str,
520
+ stem_uid: str,
521
+ project: str,
522
+ space: str,
523
+ branch: str,
524
+ registry: Literal["artifact", "transform"] | None,
525
+ ):
526
+ """Save a file or folder as an artifact or transform.
426
527
 
427
- Example: Given a valid project name "my_project",
528
+ Example:
428
529
 
429
530
  ```
430
531
  lamin save my_table.csv --key my_tables/my_table.csv --project my_project
@@ -433,9 +534,11 @@ def save(path: str, key: str, description: str, stem_uid: str, project: str, spa
433
534
  By passing a `--project` identifier, the artifact will be labeled with the corresponding project.
434
535
  If you pass a `--space` or `--branch` identifier, you save the artifact in the corresponding {class}`~lamindb.Space` or on the corresponding {class}`~lamindb.Branch`.
435
536
 
436
- Note: Defaults to saving `.py`, `.ipynb`, `.R`, `.Rmd`, and `.qmd` as {class}`~lamindb.Transform` and
537
+ Defaults to saving `.py`, `.ipynb`, `.R`, `.Rmd`, and `.qmd` as {class}`~lamindb.Transform` and
437
538
  other file types and folders as {class}`~lamindb.Artifact`. You can enforce saving a file as
438
539
  an {class}`~lamindb.Artifact` by passing `--registry artifact`.
540
+
541
+ → Python/R alternative: {class}`~lamindb.Artifact` and {class}`~lamindb.Transform`
439
542
  """
440
543
  if save_(path=path, key=key, description=description, stem_uid=stem_uid, project=project, space=space, branch=branch, registry=registry) is not None:
441
544
  sys.exit(1)
@@ -461,6 +564,8 @@ def track():
461
564
  ```
462
565
  sh my_script.sh
463
566
  ```
567
+
568
+ → Python/R alternative: {func}`~lamindb.track` and {func}`~lamindb.finish` for (non-shell) scripts or notebooks
464
569
  """
465
570
  from lamin_cli._context import track as track_
466
571
  return track_()
@@ -468,61 +573,85 @@ def track():
468
573
 
469
574
  @main.command()
470
575
  def finish():
471
- """Finish the current tracked run of a shell script.
576
+ """Finish a currently tracked run of a shell script.
472
577
 
473
- This command works like {func}`~lamindb.finish()` in a Python session.
578
+ Python/R alternative: {func}`~lamindb.finish()`
474
579
  """
475
580
  from lamin_cli._context import finish as finish_
476
581
  return finish_()
477
582
 
478
583
 
479
584
  @main.command()
585
+ # entity can be a registry or an object in the registry
480
586
  @click.argument("entity", type=str, default=None, required=False)
481
587
  @click.option("--key", type=str, default=None, help="The key of an artifact or transform.")
482
588
  @click.option("--uid", type=str, default=None, help="The uid of an artifact or transform.")
483
589
  @click.option("--project", type=str, default=None, help="A valid project name or uid.")
590
+ @click.option("--ulabel", type=str, default=None, help="A valid ulabel name or uid.")
591
+ @click.option("--record", type=str, default=None, help="A valid record name or uid.")
484
592
  @click.option("--features", multiple=True, help="Feature annotations. Supports: feature=value, feature=val1,val2, or feature=\"val1\",\"val2\"")
485
- def annotate(entity: str | None, key: str, uid: str, project: str, features: tuple):
593
+ def annotate(entity: str | None, key: str, uid: str, project: str, ulabel: str, record: str, features: tuple):
486
594
  """Annotate an artifact or transform.
487
595
 
488
- Entity is either 'artifact' or 'transform'. If not passed, chooses based on key suffix.
489
-
490
- You can annotate with projects and valid features & values. For example,
596
+ You can annotate with projects, ulabels, records, and valid features & values. For example,
491
597
 
492
598
  ```
599
+ # via --key
493
600
  lamin annotate --key raw/sample.fastq --project "My Project"
601
+ lamin annotate --key raw/sample.fastq --ulabel "My ULabel" --record "Experiment 1"
494
602
  lamin annotate --key raw/sample.fastq --features perturbation=IFNG,DMSO cell_line=HEK297
495
603
  lamin annotate --key my-notebook.ipynb --project "My Project"
604
+ # via registry and --uid
605
+ lamin annotate artifact --uid e2G7k9EVul4JbfsE --project "My Project"
606
+ # via URL
607
+ lamin annotate https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsE --project "My Project"
496
608
  ```
497
- """
498
- import lamindb as ln
499
609
 
610
+ → Python/R alternative: `artifact.features.add_values()` via {meth}`~lamindb.models.FeatureManager.add_values` and `artifact.projects.add()`, `artifact.ulabels.add()`, `artifact.records.add()`, ... via {meth}`~lamindb.models.RelatedManager.add`
611
+ """
500
612
  from lamin_cli._annotate import _parse_features_list
501
613
  from lamin_cli._save import infer_registry_from_path
502
614
 
503
- # once we enable passing the URL as entity, then we don't need to throw this error
504
- if not ln.setup.settings._instance_exists:
505
- raise click.ClickException("Not connected to an instance. Please run: lamin connect account/name")
506
-
507
- if entity is None:
508
- if key is not None:
509
- registry = infer_registry_from_path(key)
510
- else:
511
- registry = "artifact"
615
+ # Handle URL: decompose and connect (same pattern as load/delete)
616
+ if entity is not None and entity.startswith("https://"):
617
+ url = entity
618
+ instance, registry, uid = decompose_url(url)
619
+ if registry not in {"artifact", "transform"}:
620
+ raise click.ClickException(
621
+ f"Annotate does not support {registry}. Use artifact or transform URLs."
622
+ )
623
+ ln_setup.connect(instance)
512
624
  else:
513
- registry = entity
625
+ if not ln_setup.settings._instance_exists:
626
+ raise click.ClickException(
627
+ "Not connected to an instance. Please run: lamin connect account/name"
628
+ )
629
+ if entity is None:
630
+ registry = infer_registry_from_path(key) if key is not None else "artifact"
631
+ else:
632
+ registry = entity
633
+ if registry not in {"artifact", "transform"}:
634
+ raise click.ClickException(
635
+ f"Annotate does not support {registry}. Use artifact or transform URLs."
636
+ )
637
+
638
+ # import lamindb after connect went through
639
+ import lamindb as ln
640
+
514
641
  if registry == "artifact":
515
642
  model = ln.Artifact
516
643
  else:
517
644
  model = ln.Transform
518
645
 
519
- # Get the artifact
646
+ # Get the artifact or transform
520
647
  if key is not None:
521
648
  artifact = model.get(key=key)
522
649
  elif uid is not None:
523
650
  artifact = model.get(uid) # do not use uid=uid, because then no truncated uids would work
524
651
  else:
525
- raise ln.errors.InvalidArgument("Either --key or --uid must be provided")
652
+ raise ln.errors.InvalidArgument(
653
+ "Either pass a URL as entity or provide --key or --uid"
654
+ )
526
655
 
527
656
  # Handle project annotation
528
657
  if project is not None:
@@ -535,6 +664,28 @@ def annotate(entity: str | None, key: str, uid: str, project: str, features: tup
535
664
  )
536
665
  artifact.projects.add(project_record)
537
666
 
667
+ # Handle ulabel annotation
668
+ if ulabel is not None:
669
+ ulabel_record = ln.ULabel.filter(
670
+ ln.Q(name=ulabel) | ln.Q(uid=ulabel)
671
+ ).one_or_none()
672
+ if ulabel_record is None:
673
+ raise ln.errors.InvalidArgument(
674
+ f"ULabel '{ulabel}' not found, either create it with `ln.ULabel(name='...').save()` or fix typos."
675
+ )
676
+ artifact.ulabels.add(ulabel_record)
677
+
678
+ # Handle record annotation
679
+ if record is not None:
680
+ record_record = ln.Record.filter(
681
+ ln.Q(name=record) | ln.Q(uid=record)
682
+ ).one_or_none()
683
+ if record_record is None:
684
+ raise ln.errors.InvalidArgument(
685
+ f"Record '{record}' not found, either create it with `ln.Record(name='...').save()` or fix typos."
686
+ )
687
+ artifact.records.add(record_record)
688
+
538
689
  # Handle feature annotations
539
690
  if features:
540
691
  feature_dict = _parse_features_list(features)
@@ -548,7 +699,7 @@ def annotate(entity: str | None, key: str, uid: str, project: str, features: tup
548
699
  @click.argument("filepath", type=str)
549
700
  @click.option("--project", type=str, default=None, help="A valid project name or uid. When running on Modal, creates an app with the same name.", required=True)
550
701
  @click.option("--image-url", type=str, default=None, help="A URL to the base docker image to use.")
551
- @click.option("--packages", type=str, default="lamindb", help="A comma-separated list of additional packages to install.")
702
+ @click.option("--packages", type=str, default=None, help="A comma-separated list of additional packages to install.")
552
703
  @click.option("--cpu", type=float, default=None, help="Configuration for the CPU.")
553
704
  @click.option("--gpu", type=str, default=None, help="The type of GPU to use (only compatible with cuda images).")
554
705
  def run(filepath: str, project: str, image_url: str, packages: str, cpu: int, gpu: str | None):
@@ -561,6 +712,8 @@ def run(filepath: str, project: str, image_url: str, packages: str, cpu: int, gp
561
712
  ```
562
713
  lamin run my_script.py --project my_project
563
714
  ```
715
+
716
+ → Python/R alternative: no equivalent
564
717
  """
565
718
  from lamin_cli.compute.modal import Runner
566
719
 
@@ -589,10 +742,54 @@ def run(filepath: str, project: str, image_url: str, packages: str, cpu: int, gp
589
742
 
590
743
 
591
744
  main.add_command(settings)
592
- main.add_command(cache)
593
745
  main.add_command(migrate)
594
746
  main.add_command(io)
595
747
 
748
+
749
+ def _deprecated_cache_set(cache_dir: str) -> None:
750
+ logger.warning("'lamin cache' is deprecated. Use 'lamin settings cache-dir' instead.")
751
+ from lamindb_setup._cache import set_cache_dir
752
+
753
+ set_cache_dir(cache_dir)
754
+
755
+
756
+ def _deprecated_cache_clear() -> None:
757
+ logger.warning("'lamin cache' is deprecated. Use 'lamin settings cache-dir' instead.")
758
+ from lamindb_setup._cache import clear_cache_dir
759
+
760
+ clear_cache_dir()
761
+
762
+
763
+ def _deprecated_cache_get() -> None:
764
+ logger.warning("'lamin cache' is deprecated. Use 'lamin settings cache-dir' instead.")
765
+ from lamindb_setup._cache import get_cache_dir
766
+
767
+ click.echo(f"The cache directory is {get_cache_dir()}")
768
+
769
+
770
+ @main.group("cache", hidden=True)
771
+ def deprecated_cache():
772
+ """Deprecated. Use 'lamin settings cache-dir' instead."""
773
+
774
+
775
+ @deprecated_cache.command("set")
776
+ @click.argument(
777
+ "cache_dir",
778
+ type=click.Path(dir_okay=True, file_okay=False),
779
+ )
780
+ def _deprecated_cache_set_cmd(cache_dir: str) -> None:
781
+ _deprecated_cache_set(cache_dir)
782
+
783
+
784
+ @deprecated_cache.command("clear")
785
+ def _deprecated_cache_clear_cmd() -> None:
786
+ _deprecated_cache_clear()
787
+
788
+
789
+ @deprecated_cache.command("get")
790
+ def _deprecated_cache_get_cmd() -> None:
791
+ _deprecated_cache_get()
792
+
596
793
  # https://stackoverflow.com/questions/57810659/automatically-generate-all-help-documentation-for-click-commands
597
794
  # https://claude.ai/chat/73c28487-bec3-4073-8110-50d1a2dd6b84
598
795
  def _generate_help():
@@ -601,6 +798,8 @@ def _generate_help():
601
798
  def recursive_help(
602
799
  cmd: Command, parent: Context | None = None, name: tuple[str, ...] = ()
603
800
  ):
801
+ if getattr(cmd, "hidden", False):
802
+ return
604
803
  ctx = click.Context(cmd, info_name=cmd.name, parent=parent)
605
804
  assert cmd.name
606
805
  name = (*name, cmd.name)
@@ -615,6 +814,8 @@ def _generate_help():
615
814
  }
616
815
 
617
816
  for sub in getattr(cmd, "commands", {}).values():
817
+ if getattr(sub, "hidden", False):
818
+ continue
618
819
  recursive_help(sub, ctx, name=name)
619
820
 
620
821
  recursive_help(main)
lamin_cli/_cache.py CHANGED
@@ -10,7 +10,7 @@ else:
10
10
 
11
11
  @click.group()
12
12
  def cache():
13
- """Manage cache."""
13
+ """Get, set, or clear the cache directory."""
14
14
 
15
15
 
16
16
  @cache.command("set")
lamin_cli/_delete.py CHANGED
@@ -1,5 +1,7 @@
1
+ import click
1
2
  from lamindb_setup import connect
2
3
  from lamindb_setup import delete as delete_instance
4
+ from lamindb_setup.errors import StorageNotEmpty
3
5
 
4
6
  from .urls import decompose_url
5
7
 
@@ -8,7 +10,7 @@ def delete(
8
10
  entity: str,
9
11
  name: str | None = None,
10
12
  uid: str | None = None,
11
- slug: str | None = None,
13
+ key: str | None = None,
12
14
  permanent: bool | None = None,
13
15
  force: bool = False,
14
16
  ):
@@ -24,21 +26,60 @@ def delete(
24
26
 
25
27
  Branch.get(name=name).delete(permanent=permanent)
26
28
  elif entity == "artifact":
27
- assert uid is not None, "You have to pass a uid for deleting an artifact."
29
+ assert uid is not None or key is not None, (
30
+ "You have to pass a uid or key for deleting an artifact."
31
+ )
28
32
  from lamindb import Artifact
29
33
 
30
- Artifact.get(uid).delete(permanent=permanent)
34
+ if key is not None:
35
+ record = Artifact.objects.filter(key=key).order_by("-created_at").first()
36
+ if record is None:
37
+ raise SystemExit(f"Artifact with key={key} does not exist.")
38
+ else:
39
+ record = Artifact.get(uid)
40
+ record.delete(permanent=permanent)
31
41
  elif entity == "transform":
32
- assert uid is not None, "You have to pass a uid for deleting an transform."
42
+ assert uid is not None or key is not None, (
43
+ "You have to pass a uid or key for deleting a transform."
44
+ )
33
45
  from lamindb import Transform
34
46
 
35
- Transform.get(uid).delete(permanent=permanent)
47
+ if key is not None:
48
+ record = Transform.objects.filter(key=key).order_by("-created_at").first()
49
+ if record is None:
50
+ raise SystemExit(f"Transform with key={key} does not exist.")
51
+ else:
52
+ record = Transform.get(uid)
53
+ record.delete(permanent=permanent)
36
54
  elif entity == "collection":
37
- assert uid is not None, "You have to pass a uid for deleting an collection."
55
+ assert uid is not None or key is not None, (
56
+ "You have to pass a uid or key for deleting a collection."
57
+ )
38
58
  from lamindb import Collection
39
59
 
40
- Collection.get(uid).delete(permanent=permanent)
41
- elif entity == "instance":
42
- return delete_instance(slug, force=force)
43
- else: # backwards compatibility
44
- return delete_instance(entity, force=force)
60
+ if key is not None:
61
+ record = Collection.objects.filter(key=key).order_by("-created_at").first()
62
+ if record is None:
63
+ raise SystemExit(f"Collection with key={key} does not exist.")
64
+ else:
65
+ record = Collection.get(uid)
66
+ record.delete(permanent=permanent)
67
+ elif entity == "record":
68
+ assert uid is not None or name is not None, (
69
+ "You have to pass a uid or name for deleting a record."
70
+ )
71
+ from lamindb import Record
72
+
73
+ if name is not None:
74
+ record = Record.objects.get(name=name)
75
+ if record is None:
76
+ raise SystemExit(f"Record with name={name} does not exist.")
77
+ else:
78
+ record = Record.get(uid)
79
+ record.delete(permanent=permanent)
80
+ else:
81
+ # could introduce "db" as an entity
82
+ try:
83
+ return delete_instance(entity, force=force)
84
+ except StorageNotEmpty as e:
85
+ raise click.ClickException(str(e)) from e
lamin_cli/_io.py CHANGED
@@ -31,6 +31,7 @@ def io():
31
31
  # fmt: on
32
32
  def snapshot(upload: bool, track: bool) -> None:
33
33
  """Create a SQLite snapshot of the connected instance."""
34
+ from lamindb_setup.io import export_db
34
35
  if not ln_setup.settings._instance_exists:
35
36
  raise click.ClickException(
36
37
  "Not connected to an instance. Please run: lamin connect account/name"
@@ -52,8 +53,8 @@ def snapshot(upload: bool, track: bool) -> None:
52
53
 
53
54
  with tempfile.TemporaryDirectory() as export_dir:
54
55
  if track:
55
- ln.track()
56
- ln_setup.io.export_db(module_names=modules_complete, output_dir=export_dir)
56
+ ln.track("o39ljgTzvFew", key="__lamin_io_snapshot__.py")
57
+ export_db(module_names=modules_complete, output_dir=export_dir)
57
58
  if track:
58
59
  ln.finish()
59
60
 
@@ -109,13 +110,14 @@ def snapshot(upload: bool, track: bool) -> None:
109
110
  # fmt: on
110
111
  def exportdb(modules: str | None, output_dir: str, max_workers: int, chunk_size: int):
111
112
  """Export registry tables to parquet files."""
113
+ from lamindb_setup.io import export_db
112
114
  if not ln_setup.settings._instance_exists:
113
115
  raise click.ClickException(
114
116
  "Not connected to an instance. Please run: lamin connect account/name"
115
117
  )
116
118
 
117
119
  module_list = modules.split(",") if modules else None
118
- ln_setup.io.export_db(
120
+ export_db(
119
121
  module_names=module_list,
120
122
  output_dir=output_dir,
121
123
  max_workers=max_workers,
@@ -131,13 +133,14 @@ def exportdb(modules: str | None, output_dir: str, max_workers: int, chunk_size:
131
133
  # fmt: on
132
134
  def importdb(modules: str | None, input_dir: str, if_exists: str):
133
135
  """Import registry tables from parquet files."""
136
+ from lamindb_setup.io import import_db
134
137
  if not ln_setup.settings._instance_exists:
135
138
  raise click.ClickException(
136
139
  "Not connected to an instance. Please run: lamin connect account/name"
137
140
  )
138
141
 
139
142
  module_list = modules.split(",") if modules else None
140
- ln_setup.io.import_db(
143
+ import_db(
141
144
  module_names=module_list,
142
145
  input_dir=input_dir,
143
146
  if_exists=if_exists,
lamin_cli/_save.py CHANGED
@@ -51,7 +51,7 @@ def parse_uid_from_code(content: str, suffix: str) -> str | None:
51
51
  elif suffix == ".sh":
52
52
  return None
53
53
  else:
54
- raise SystemExit(
54
+ raise click.ClickException(
55
55
  "Only .py, .ipynb, .R, .qmd, .Rmd, .sh files are supported for saving"
56
56
  " transforms."
57
57
  )
@@ -113,14 +113,14 @@ def save(
113
113
  ln.Q(name=project) | ln.Q(uid=project)
114
114
  ).one_or_none()
115
115
  if project_record is None:
116
- raise ln.errors.InvalidArgument(
116
+ raise click.ClickException(
117
117
  f"Project '{project}' not found, either create it with `ln.Project(name='...').save()` or fix typos."
118
118
  )
119
119
  space_record = None
120
120
  if space is not None:
121
121
  space_record = ln.Space.filter(ln.Q(name=space) | ln.Q(uid=space)).one_or_none()
122
122
  if space_record is None:
123
- raise ln.errors.InvalidArgument(
123
+ raise click.ClickException(
124
124
  f"Space '{space}' not found, either create it on LaminHub or fix typos."
125
125
  )
126
126
  branch_record = None
@@ -129,7 +129,7 @@ def save(
129
129
  ln.Q(name=branch) | ln.Q(uid=branch)
130
130
  ).one_or_none()
131
131
  if branch_record is None:
132
- raise ln.errors.InvalidArgument(
132
+ raise click.ClickException(
133
133
  f"Branch '{branch}' not found, either create it with `ln.Branch(name='...').save()` or fix typos."
134
134
  )
135
135
 
@@ -145,7 +145,7 @@ def save(
145
145
  .first()
146
146
  )
147
147
  if revises is None:
148
- raise ln.errors.InvalidArgument("The stem uid is not found.")
148
+ raise click.ClickException("The stem uid is not found.")
149
149
 
150
150
  if is_cloud_path:
151
151
  if key is not None:
@@ -266,7 +266,30 @@ def save(
266
266
  .first()
267
267
  )
268
268
  if revises is None:
269
- raise ln.errors.InvalidArgument("The stem uid is not found.")
269
+ # check if the transform is on other branches
270
+ revises_other_branches = (
271
+ ln.Transform.filter(uid__startswith=stem_uid, branch=None)
272
+ .order_by("-created_at")
273
+ .first()
274
+ )
275
+ if revises_other_branches is not None:
276
+ if revises_other_branches.branch_id == -1:
277
+ raise click.ClickException(
278
+ "Transform is in the trash, please restore it before running `lamin save`!"
279
+ )
280
+ elif revises_other_branches.branch_id == 0:
281
+ raise click.ClickException(
282
+ "Transform is in the archive, please restore it before running `lamin save`!"
283
+ )
284
+ elif (
285
+ revises_other_branches.branch_id != ln_setup.settings.branch.id
286
+ ):
287
+ raise click.ClickException(
288
+ "Transform is on a different branch"
289
+ f"({revises_other_branches.branch.name}), please switch to the correct branch"
290
+ " before running `lamin save`!"
291
+ )
292
+ raise click.ClickException("The stem uid is not found.")
270
293
  if transform is None:
271
294
  if ppath.suffix == ".ipynb":
272
295
  from nbproject.dev import read_notebook
@@ -322,4 +345,6 @@ def save(
322
345
  )
323
346
  return return_code
324
347
  else:
325
- raise SystemExit("Allowed values for '--registry' are: 'artifact', 'transform'")
348
+ raise click.ClickException(
349
+ "Allowed values for '--registry' are: 'artifact', 'transform'"
350
+ )
lamin_cli/_settings.py CHANGED
@@ -12,35 +12,38 @@ else:
12
12
  @click.group(invoke_without_command=True)
13
13
  @click.pass_context
14
14
  def settings(ctx):
15
- """Manage settings.
15
+ """Manage development & cache directories, branch, and space settings.
16
16
 
17
- Call without subcommands and options to show settings:
17
+ Get or set a setting by name:
18
18
 
19
- ```
20
- lamin settings
21
- ```
19
+ - `dev-dir` → development directory {attr}`~lamindb.setup.core.SetupSettings.dev_dir`
20
+ - `cache-dir` → cache directory {attr}`~lamindb.setup.core.SetupSettings.cache_dir`
21
+ - `branch` → branch {attr}`~lamindb.setup.core.SetupSettings.branch`
22
+ - `space` → space {attr}`~lamindb.setup.core.SetupSettings.space`
22
23
 
23
- Allows to get and set these settings:
24
+ Display via [lamin info](https://docs.lamin.ai/cli#info)
24
25
 
25
- - `dev-dir` → {attr}`~lamindb.setup.core.SetupSettings.dev_dir`
26
- - `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
27
- - `branch` → current branch (use `lamin switch --branch` to change)
28
- - `space` → current space (use `lamin switch --space` to change)
29
-
30
- Examples for getting a setting:
26
+ Examples:
31
27
 
32
28
  ```
33
- lamin settings get dev-dir
34
- lamin settings get branch
29
+ # dev-dir
30
+ lamin settings dev-dir get
31
+ lamin settings dev-dir set . # set to current directory
32
+ lamin settings dev-dir set ~/my-project
33
+ lamin settings dev-dir unset
34
+ # cache-dir
35
+ lamin settings cache-dir get
36
+ lamin settings cache-dir set /path/to/cache
37
+ lamin settings cache-dir clear
38
+ # branch
39
+ lamin settings branch get
40
+ lamin settings branch set main
41
+ # space
42
+ lamin settings space get
43
+ lamin settings space set all
35
44
  ```
36
45
 
37
- Examples for setting the working directory:
38
-
39
- ```
40
- lamin settings set dev-dir . # set dev-dir to current directory
41
- lamin settings set dev-dir ~/my-project # set dev-dir to ~/my-project
42
- lamin settings set dev-dir none # unset dev-dir
43
- ```
46
+ Python/R alternative: {attr}`~lamindb.setup.core.SetupSettings.dev_dir`, {attr}`~lamindb.setup.core.SetupSettings.cache_dir`, {attr}`~lamindb.setup.core.SetupSettings.branch`, and {attr}`~lamindb.setup.core.SetupSettings.space`
44
47
  """
45
48
  if ctx.invoked_subcommand is None:
46
49
  from lamindb_setup import settings as settings_
@@ -49,7 +52,53 @@ def settings(ctx):
49
52
  click.echo(settings_)
50
53
 
51
54
 
52
- @settings.command("set")
55
+ # -----------------------------------------------------------------------------
56
+ # dev-dir group (pattern: lamin settings dev-dir get/set)
57
+ # -----------------------------------------------------------------------------
58
+
59
+
60
+ @click.group("dev-dir")
61
+ def dev_dir_group():
62
+ """Get or set the development directory."""
63
+
64
+
65
+ @dev_dir_group.command("get")
66
+ def dev_dir_get():
67
+ """Show the current development directory."""
68
+ from lamindb_setup import settings as settings_
69
+
70
+ value = settings_.dev_dir
71
+ click.echo(value if value is not None else "None")
72
+
73
+
74
+ @dev_dir_group.command("set")
75
+ @click.argument("value", type=str)
76
+ def dev_dir_set(value: str):
77
+ """Set the development directory."""
78
+ from lamindb_setup import settings as settings_
79
+
80
+ if value.lower() == "none":
81
+ value = None # type: ignore[assignment]
82
+ settings_.dev_dir = value
83
+
84
+
85
+ @dev_dir_group.command("unset")
86
+ def dev_dir_unset():
87
+ """Unset the development directory."""
88
+ from lamindb_setup import settings as settings_
89
+
90
+ settings_.dev_dir = None
91
+
92
+
93
+ settings.add_command(dev_dir_group)
94
+
95
+
96
+ # -----------------------------------------------------------------------------
97
+ # Legacy get/set (hidden, backward compatibility)
98
+ # -----------------------------------------------------------------------------
99
+
100
+
101
+ @settings.command("set", hidden=True)
53
102
  @click.argument(
54
103
  "setting",
55
104
  type=click.Choice(
@@ -57,8 +106,8 @@ def settings(ctx):
57
106
  ),
58
107
  )
59
108
  @click.argument("value") # No explicit type - let Click handle it
60
- def set(setting: str, value: str):
61
- """Set a setting."""
109
+ def set_legacy(setting: str, value: str):
110
+ """Set a setting (legacy). Use lamin settings <name> set <value> instead."""
62
111
  from lamindb_setup import settings as settings_
63
112
 
64
113
  if setting == "auto-connect":
@@ -71,7 +120,7 @@ def set(setting: str, value: str):
71
120
  settings_.dev_dir = value
72
121
 
73
122
 
74
- @settings.command("get")
123
+ @settings.command("get", hidden=True)
75
124
  @click.argument(
76
125
  "setting",
77
126
  type=click.Choice(
@@ -79,8 +128,8 @@ def set(setting: str, value: str):
79
128
  case_sensitive=False,
80
129
  ),
81
130
  )
82
- def get(setting: str):
83
- """Get a setting."""
131
+ def get_legacy(setting: str):
132
+ """Get a setting (legacy). Use lamin settings <name> get instead."""
84
133
  from lamindb_setup import settings as settings_
85
134
 
86
135
  if setting == "branch":
@@ -94,3 +143,12 @@ def get(setting: str):
94
143
  else:
95
144
  value = getattr(settings_, setting.replace("-", "_"))
96
145
  click.echo(value)
146
+
147
+
148
+ # -----------------------------------------------------------------------------
149
+ # cache-dir (already uses lamin settings cache-dir get/set/clear)
150
+ # -----------------------------------------------------------------------------
151
+
152
+ from lamin_cli._cache import cache
153
+
154
+ settings.add_command(cache, "cache-dir")
@@ -6,6 +6,8 @@ from pathlib import Path
6
6
  import lamindb_setup as ln_setup
7
7
 
8
8
  if __name__ == "__main__":
9
+ from lamindb_setup.io import import_db
10
+
9
11
  sys.path.insert(0, str(Path(__file__).parent.parent))
10
12
 
11
13
  parser = argparse.ArgumentParser()
@@ -25,7 +27,7 @@ if __name__ == "__main__":
25
27
  storage=f"{instance_name}-clone", modules=f"{','.join(modules_without_lamindb)}"
26
28
  )
27
29
 
28
- ln_setup.io.import_db(
30
+ import_db(
29
31
  module_names=list(modules_complete), input_dir=export_dir, if_exists="replace"
30
32
  )
31
33
 
@@ -157,11 +157,11 @@ class Runner:
157
157
  ) -> modal.Image:
158
158
  if env_variables is None:
159
159
  env_variables = {}
160
+ base_packages = ["lamindb", "httpx_retries"]
160
161
  if packages is None:
161
- packages = ["lamindb"]
162
+ packages = base_packages
162
163
  else:
163
- packages.append("lamindb") # Append lamindb to the list of packages
164
-
164
+ packages += base_packages
165
165
  if image_url is None:
166
166
  image = modal.Image.debian_slim(python_version=python_version)
167
167
  else:
@@ -170,6 +170,5 @@ class Runner:
170
170
  image.pip_install(packages)
171
171
  .env(env_variables)
172
172
  .add_local_python_source("lamindb", "lamindb_setup", copy=True)
173
- .run_commands("lamin settings set auto-connect true")
174
173
  .add_local_dir(local_dir, remote_dir)
175
174
  )
lamin_cli/urls.py CHANGED
@@ -1,6 +1,8 @@
1
1
  def decompose_url(url: str) -> tuple[str, str, str]:
2
- assert any(keyword in url for keyword in ["transform", "artifact", "collection"])
3
- for entity in ["transform", "artifact", "collection"]:
2
+ assert any(
3
+ keyword in url for keyword in ["transform", "artifact", "collection", "record"]
4
+ )
5
+ for entity in ["transform", "artifact", "collection", "record"]:
4
6
  if entity in url:
5
7
  break
6
8
  uid = url.split(f"{entity}/")[1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lamin_cli
3
- Version: 1.11.0
3
+ Version: 1.12.0
4
4
  Summary: Lamin CLI.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,22 @@
1
+ lamin_cli/__init__.py,sha256=IJ_B3QJrPMYc1SfgQQ1WgVnE2dEZOf8wSmQIGZhAauw,605
2
+ lamin_cli/__main__.py,sha256=HkZh79zC2mrdmAtpib1uE83T_E5pRUuYrYXqgfrmY8w,27931
3
+ lamin_cli/_annotate.py,sha256=ZD76__K-mQt7UpYqyM1I2lKCs-DraTmnkjsByIHmD-g,1839
4
+ lamin_cli/_cache.py,sha256=SvfgC0pIpsjIGUYm21oPUfE_RCOG2_b0cvDscLtzbYo,826
5
+ lamin_cli/_context.py,sha256=JLhv98isasX_M_o6tIDkeBWcNH8ryRrl3nk2rmwsuD4,2392
6
+ lamin_cli/_delete.py,sha256=TQV-CJOV3tYBGm6vCj0JKymptrXWhBvJ8CZPfDVyzAw,3061
7
+ lamin_cli/_io.py,sha256=HPFpASrTa3zLsIXmTpZ69l34t3ZoYIa_SNHOclJnd-o,5175
8
+ lamin_cli/_load.py,sha256=v7PCB7sYqGs6jF186aN191lTV2Mr5jdayHKSmdgsw_k,8445
9
+ lamin_cli/_migration.py,sha256=w-TLYC2q6sqcfNtIUewqRW_69_xy_71X3eSV7Ud58jk,1239
10
+ lamin_cli/_save.py,sha256=PiBFlTgPMPdFg4eMqRPYOiF4rl7R6LA3e3RTpZc_gaM,13806
11
+ lamin_cli/_settings.py,sha256=vuKZeEToEh0pb_R4ECzWpe6FeJgmIACLEupPYxQ60k4,4781
12
+ lamin_cli/urls.py,sha256=6yTBFByrzdWIntFjupC5Dt8hYkl4agR31D4g1zjzPK8,401
13
+ lamin_cli/clone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ lamin_cli/clone/_clone_verification.py,sha256=8hRFZt_1Z0i2wMqKBkrioq01RJEjwHy9cYmw6LV4PXI,1835
15
+ lamin_cli/clone/create_sqlite_clone_and_import_db.py,sha256=m4Lzm1UvqNjYR2WKE0u_g-RrKv77pdVwHyFOKdR0exQ,1576
16
+ lamin_cli/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ lamin_cli/compute/modal.py,sha256=-QBnqxjI8AJDjkF7-Q30o4m3EpSeJ2_n5wYxedaPiLA,5955
18
+ lamin_cli-1.12.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
19
+ lamin_cli-1.12.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
+ lamin_cli-1.12.0.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
21
+ lamin_cli-1.12.0.dist-info/METADATA,sha256=JN15Pvzdjb2Eshdqrp5lh6NM-kkmccfy-STokqEFNGY,338
22
+ lamin_cli-1.12.0.dist-info/RECORD,,
@@ -1,22 +0,0 @@
1
- lamin_cli/__init__.py,sha256=bd6la8_wxlClB7o8h3FNAjQ6z8HPcZ4QqxvVp4Cvycg,605
2
- lamin_cli/__main__.py,sha256=DEifHuxyyffXHVpBjn9-LVpVUsuSnRhicxTfsuZ0YUw,20791
3
- lamin_cli/_annotate.py,sha256=ZD76__K-mQt7UpYqyM1I2lKCs-DraTmnkjsByIHmD-g,1839
4
- lamin_cli/_cache.py,sha256=oplwE8AcS_9PYptQUZxff2qTIdNFS81clGPkJNWk098,800
5
- lamin_cli/_context.py,sha256=JLhv98isasX_M_o6tIDkeBWcNH8ryRrl3nk2rmwsuD4,2392
6
- lamin_cli/_delete.py,sha256=1wp8LQdiWHJznRrm4abxEhApB9derBWEm25ufrzjvIg,1531
7
- lamin_cli/_io.py,sha256=DboDoecZN5OLCHe5vJzOLuwztW8hxbdRffWj1kmJ9WI,5036
8
- lamin_cli/_load.py,sha256=v7PCB7sYqGs6jF186aN191lTV2Mr5jdayHKSmdgsw_k,8445
9
- lamin_cli/_migration.py,sha256=w-TLYC2q6sqcfNtIUewqRW_69_xy_71X3eSV7Ud58jk,1239
10
- lamin_cli/_save.py,sha256=8dM4blz6caKJtbqND-6xJXUMlGIvo2FwvSu1CDcBPig,12517
11
- lamin_cli/_settings.py,sha256=nqVM8FO9kjL0SXYqP8x-Xfww6Qth4I86HKKeTirsgc4,2657
12
- lamin_cli/urls.py,sha256=gc72s4SpaAQA8J50CtCIWlr49DWOSL_a6OM9lXfPouM,367
13
- lamin_cli/clone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- lamin_cli/clone/_clone_verification.py,sha256=8hRFZt_1Z0i2wMqKBkrioq01RJEjwHy9cYmw6LV4PXI,1835
15
- lamin_cli/clone/create_sqlite_clone_and_import_db.py,sha256=yv2mLkHdRUao_IkZO_4Swu5w4Nieh1gMrBwPbyn4c1o,1544
16
- lamin_cli/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- lamin_cli/compute/modal.py,sha256=QnR7GyyvWWWkLnou95HxS9xxSQfw1k-SiefM_qRVnU0,6010
18
- lamin_cli-1.11.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
19
- lamin_cli-1.11.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
20
- lamin_cli-1.11.0.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
21
- lamin_cli-1.11.0.dist-info/METADATA,sha256=4TK6hWuABuBxr1PYF_iBpm7KP8xNH7g4LjMWUkHgDKE,338
22
- lamin_cli-1.11.0.dist-info/RECORD,,