lamin_cli 1.10.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/_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/_context.py ADDED
@@ -0,0 +1,76 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import click
6
+ from lamin_utils import logger
7
+ from lamindb_setup.core._settings_store import settings_dir
8
+
9
+
10
+ def get_current_run_file() -> Path:
11
+ """Get the path to the file storing the current run UID."""
12
+ return settings_dir / "current_shell_run.txt"
13
+
14
+
15
+ def is_interactive_shell() -> bool:
16
+ """Check if running in an interactive terminal."""
17
+ return sys.stdin.isatty() and sys.stdout.isatty() and os.isatty(0)
18
+
19
+
20
+ def get_script_filename() -> Path:
21
+ """Try to get the filename of the calling shell script."""
22
+ import psutil
23
+
24
+ parent = psutil.Process(os.getppid())
25
+ cmdline = parent.cmdline()
26
+
27
+ # For shells like bash, sh, zsh
28
+ if parent.name() in ["bash", "sh", "zsh", "dash"]:
29
+ # cmdline is typically: ['/bin/bash', 'script.sh', ...]
30
+ if len(cmdline) > 1 and not cmdline[1].startswith("-"):
31
+ return Path(cmdline[1])
32
+ raise click.ClickException(
33
+ "Cannot determine script filename. Please run in an interactive shell."
34
+ )
35
+
36
+
37
+ def track():
38
+ import lamindb as ln
39
+
40
+ if not ln.setup.settings._instance_exists:
41
+ raise click.ClickException(
42
+ "Not connected to an instance. Please run: lamin connect account/name"
43
+ )
44
+ path = get_script_filename()
45
+ source_code = path.read_text()
46
+ transform = ln.Transform(
47
+ key=path.name, source_code=source_code, type="script"
48
+ ).save()
49
+ run = ln.Run(transform=transform).save()
50
+ current_run_file = get_current_run_file()
51
+ current_run_file.parent.mkdir(parents=True, exist_ok=True)
52
+ current_run_file.write_text(run.uid)
53
+ logger.important(f"started tracking shell run: {run.uid}")
54
+
55
+
56
+ def finish():
57
+ from datetime import datetime, timezone
58
+
59
+ import lamindb as ln
60
+
61
+ if not ln.setup.settings._instance_exists:
62
+ raise click.ClickException(
63
+ "Not connected to an instance. Please run: lamin connect account/name"
64
+ )
65
+
66
+ current_run_file = get_current_run_file()
67
+ if not current_run_file.exists():
68
+ raise click.ClickException(
69
+ "No active run to finish. Please run `lamin track` first."
70
+ )
71
+ run = ln.Run.get(uid=current_run_file.read_text().strip())
72
+ run._status_code = 0
73
+ run.finished_at = datetime.now(timezone.utc)
74
+ run.save()
75
+ current_run_file.unlink()
76
+ logger.important(f"finished tracking shell run: {run.uid}")
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
@@ -30,7 +30,8 @@ def io():
30
30
  @click.option("--track/--no-track", is_flag=True, help="Whether to track snapshot generation.", default=True)
31
31
  # fmt: on
32
32
  def snapshot(upload: bool, track: bool) -> None:
33
- """Create and optionally upload a SQLite snapshot of the current instance."""
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
 
@@ -86,8 +87,8 @@ def snapshot(upload: bool, track: bool) -> None:
86
87
  for table, (orig, clone) in mismatches.items()]
87
88
  )
88
89
  raise click.ClickException(error_msg)
89
- except json.JSONDecodeError:
90
- error_msg = f"Clone verification failed:\n{result.stderr}"
90
+ except (json.JSONDecodeError, AttributeError, ValueError, TypeError):
91
+ raise click.ClickException(f"Clone verification failed:\n{result.stderr}") from None
91
92
 
92
93
 
93
94
  ln_setup.connect(f"{instance_owner}/{instance_name}", use_root_db_user=True)
@@ -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/_load.py CHANGED
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
 
7
7
  from lamin_utils import logger
8
8
 
9
+ from ._context import get_current_run_file
9
10
  from ._save import infer_registry_from_path, parse_title_r_notebook
10
11
  from .urls import decompose_url
11
12
 
@@ -48,6 +49,10 @@ def load(
48
49
  ln_setup.connect(instance)
49
50
  import lamindb as ln
50
51
 
52
+ current_run = None
53
+ if get_current_run_file().exists():
54
+ current_run = ln.Run.get(uid=get_current_run_file().read_text().strip())
55
+
51
56
  def script_to_notebook(
52
57
  transform: ln.Transform, notebook_path: Path, bump_revision: bool = False
53
58
  ) -> None:
@@ -182,7 +187,7 @@ def load(
182
187
  entities = entities.order_by("-created_at")
183
188
 
184
189
  entity_obj = entities.first()
185
- cache_path = entity_obj.cache()
190
+ cache_path = entity_obj.cache(is_run_input=current_run)
186
191
 
187
192
  # collection gives us a list of paths
188
193
  if isinstance(cache_path, list):
lamin_cli/_save.py CHANGED
@@ -9,17 +9,23 @@ import lamindb_setup as ln_setup
9
9
  from lamin_utils import logger
10
10
  from lamindb_setup.core.hashing import hash_file
11
11
 
12
+ from lamin_cli._context import get_current_run_file
13
+
12
14
 
13
15
  def infer_registry_from_path(path: Path | str) -> str:
14
16
  suffixes_transform = {
15
17
  "py": {".py", ".ipynb"},
16
18
  "R": {".R", ".qmd", ".Rmd"},
19
+ "sh": {".sh"},
17
20
  }
18
21
  if isinstance(path, str):
19
22
  path = Path(path)
20
23
  registry = (
21
24
  "transform"
22
- if path.suffix in suffixes_transform["py"].union(suffixes_transform["R"])
25
+ if path.suffix
26
+ in suffixes_transform["py"]
27
+ .union(suffixes_transform["R"])
28
+ .union(suffixes_transform["sh"])
23
29
  else "artifact"
24
30
  )
25
31
  return registry
@@ -42,9 +48,11 @@ def parse_uid_from_code(content: str, suffix: str) -> str | None:
42
48
  r'track\(\s*(?:transform\s*=\s*)?([\'"])([a-zA-Z0-9]{12,16})\1'
43
49
  )
44
50
  uid_pattern = None
51
+ elif suffix == ".sh":
52
+ return None
45
53
  else:
46
- raise SystemExit(
47
- "Only .py, .ipynb, .R, .qmd, .Rmd files are supported for saving"
54
+ raise click.ClickException(
55
+ "Only .py, .ipynb, .R, .qmd, .Rmd, .sh files are supported for saving"
48
56
  " transforms."
49
57
  )
50
58
 
@@ -82,8 +90,13 @@ def save(
82
90
  ) -> str | None:
83
91
  import lamindb as ln
84
92
  from lamindb._finish import save_context_core
93
+ from lamindb_setup.core._settings_store import settings_dir
85
94
  from lamindb_setup.core.upath import LocalPathClasses, UPath, create_path
86
95
 
96
+ current_run = None
97
+ if get_current_run_file().exists():
98
+ current_run = ln.Run.get(uid=get_current_run_file().read_text().strip())
99
+
87
100
  # this allows to have the correct treatment of credentials in case of cloud paths
88
101
  ppath = create_path(path)
89
102
  # isinstance is needed to cast the type of path to UPath
@@ -100,14 +113,14 @@ def save(
100
113
  ln.Q(name=project) | ln.Q(uid=project)
101
114
  ).one_or_none()
102
115
  if project_record is None:
103
- raise ln.errors.InvalidArgument(
116
+ raise click.ClickException(
104
117
  f"Project '{project}' not found, either create it with `ln.Project(name='...').save()` or fix typos."
105
118
  )
106
119
  space_record = None
107
120
  if space is not None:
108
121
  space_record = ln.Space.filter(ln.Q(name=space) | ln.Q(uid=space)).one_or_none()
109
122
  if space_record is None:
110
- raise ln.errors.InvalidArgument(
123
+ raise click.ClickException(
111
124
  f"Space '{space}' not found, either create it on LaminHub or fix typos."
112
125
  )
113
126
  branch_record = None
@@ -116,7 +129,7 @@ def save(
116
129
  ln.Q(name=branch) | ln.Q(uid=branch)
117
130
  ).one_or_none()
118
131
  if branch_record is None:
119
- raise ln.errors.InvalidArgument(
132
+ raise click.ClickException(
120
133
  f"Branch '{branch}' not found, either create it with `ln.Branch(name='...').save()` or fix typos."
121
134
  )
122
135
 
@@ -132,7 +145,7 @@ def save(
132
145
  .first()
133
146
  )
134
147
  if revises is None:
135
- raise ln.errors.InvalidArgument("The stem uid is not found.")
148
+ raise click.ClickException("The stem uid is not found.")
136
149
 
137
150
  if is_cloud_path:
138
151
  if key is not None:
@@ -149,6 +162,7 @@ def save(
149
162
  revises=revises,
150
163
  branch=branch_record,
151
164
  space=space_record,
165
+ run=current_run,
152
166
  ).save()
153
167
  logger.important(f"saved: {artifact}")
154
168
  logger.important(f"storage path: {artifact.path}")
@@ -219,7 +233,7 @@ def save(
219
233
  if transform is None:
220
234
  uid = f"{stem_uid}0000"
221
235
  else:
222
- transform_hash, _ = hash_file(ppath)
236
+ _, transform_hash, _ = hash_file(ppath)
223
237
  transform = ln.Transform.filter(hash=transform_hash).first()
224
238
  if transform is not None and transform.hash is not None:
225
239
  if transform.hash == transform_hash:
@@ -252,7 +266,30 @@ def save(
252
266
  .first()
253
267
  )
254
268
  if revises is None:
255
- 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.")
256
293
  if transform is None:
257
294
  if ppath.suffix == ".ipynb":
258
295
  from nbproject.dev import read_notebook
@@ -268,7 +305,7 @@ def save(
268
305
  uid=uid,
269
306
  description=description,
270
307
  key=key,
271
- type="script" if ppath.suffix in {".R", ".py"} else "notebook",
308
+ type="script" if ppath.suffix in {".R", ".py", ".sh"} else "notebook",
272
309
  revises=revises,
273
310
  )
274
311
  if space is not None:
@@ -308,4 +345,6 @@ def save(
308
345
  )
309
346
  return return_code
310
347
  else:
311
- 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,10 +1,9 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.3
2
2
  Name: lamin_cli
3
- Version: 1.10.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
7
- License-File: LICENSE
8
7
  Requires-Dist: rich-click>=1.7
9
8
  Project-URL: Home, https://github.com/laminlabs/lamin-cli
10
9
 
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.12.0
2
+ Generator: flit 3.10.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any