lamin_cli 1.7.2__tar.gz → 1.8.0__tar.gz

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.
Files changed (45) hide show
  1. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/PKG-INFO +1 -1
  2. lamin_cli-1.8.0/lamin_cli/__init__.py +18 -0
  3. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/__main__.py +25 -57
  4. lamin_cli-1.8.0/lamin_cli/_delete.py +43 -0
  5. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/_save.py +7 -7
  6. lamin_cli-1.8.0/lamin_cli/_settings.py +96 -0
  7. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/conftest.py +0 -1
  8. lamin_cli-1.7.2/tests/core/test_create_switch_delete_list.py → lamin_cli-1.8.0/tests/core/test_create_switch_delete_list_settings.py +41 -0
  9. lamin_cli-1.7.2/lamin_cli/__init__.py +0 -12
  10. lamin_cli-1.7.2/lamin_cli/_settings.py +0 -62
  11. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/.github/workflows/build.yml +0 -0
  12. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/.github/workflows/doc-changes.yml +0 -0
  13. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/.gitignore +0 -0
  14. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/.pre-commit-config.yaml +0 -0
  15. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/LICENSE +0 -0
  16. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/README.md +0 -0
  17. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/_annotate.py +0 -0
  18. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/_cache.py +0 -0
  19. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/_load.py +0 -0
  20. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/_migration.py +0 -0
  21. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/compute/__init__.py +0 -0
  22. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/compute/modal.py +0 -0
  23. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/lamin_cli/urls.py +0 -0
  24. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/noxfile.py +0 -0
  25. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/pyproject.toml +0 -0
  26. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_load.py +0 -0
  27. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_login.py +0 -0
  28. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_migrate.py +0 -0
  29. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_multi_process.py +0 -0
  30. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_parse_uid_from_code.py +0 -0
  31. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_save_annotate_files.py +0 -0
  32. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_save_annotate_scripts.py +0 -0
  33. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_save_notebooks.py +0 -0
  34. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/core/test_save_r_code.py +0 -0
  35. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/modal/test_modal.py +0 -0
  36. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/notebooks/not-initialized.ipynb +0 -0
  37. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
  38. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
  39. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/merely-import-lamindb.py +0 -0
  40. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/run-track-and-finish-sync-git.py +0 -0
  41. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/run-track-and-finish.py +0 -0
  42. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/run-track-with-params.py +0 -0
  43. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/run-track.R +0 -0
  44. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/run-track.qmd +0 -0
  45. {lamin_cli-1.7.2 → lamin_cli-1.8.0}/tests/scripts/testscript.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lamin_cli
3
- Version: 1.7.2
3
+ Version: 1.8.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,18 @@
1
+ """Lamin CLI.
2
+
3
+ This is the command line interface for interacting with LaminDB & LaminHub.
4
+
5
+ The interface is defined in `__main__.py`. The root API here is used by LaminR to replicate the CLI functionality.
6
+ """
7
+
8
+ __version__ = "1.8.0"
9
+
10
+ from lamindb_setup import disconnect, logout
11
+ from lamindb_setup._connect_instance import _connect_cli as connect
12
+ from lamindb_setup._init_instance import init
13
+ from lamindb_setup._setup_user import login
14
+
15
+ from ._delete import delete
16
+ from ._save import save
17
+
18
+ __all__ = ["save", "init", "connect", "delete", "login", "disconnect"]
@@ -11,6 +11,7 @@ from importlib.metadata import PackageNotFoundError, version
11
11
  from pathlib import Path
12
12
  from typing import TYPE_CHECKING, Literal
13
13
 
14
+ import lamindb_setup as ln_setup
14
15
  from lamin_utils import logger
15
16
  from lamindb_setup._init_instance import (
16
17
  DOC_DB,
@@ -19,6 +20,13 @@ from lamindb_setup._init_instance import (
19
20
  DOC_STORAGE_ARG,
20
21
  )
21
22
 
23
+ from lamin_cli import connect as connect_
24
+ from lamin_cli import disconnect as disconnect_
25
+ from lamin_cli import init as init_
26
+ from lamin_cli import login as login_
27
+ from lamin_cli import logout as logout_
28
+ from lamin_cli import save as save_
29
+
22
30
  from .urls import decompose_url
23
31
 
24
32
  if TYPE_CHECKING:
@@ -129,16 +137,12 @@ def login(user: str, key: str | None):
129
137
 
130
138
  See also: Login in a Python session via {func}`~lamindb.setup.login`.
131
139
  """
132
- from lamindb_setup._setup_user import login as login_
133
-
134
140
  return login_(user, key=key)
135
141
 
136
142
 
137
143
  @main.command()
138
144
  def logout():
139
145
  """Log out of LaminHub."""
140
- from lamindb_setup import logout as logout_
141
-
142
146
  return logout_()
143
147
 
144
148
 
@@ -170,8 +174,6 @@ def init(
170
174
 
171
175
  See also: Init in a Python session via {func}`~lamindb.setup.init`.
172
176
  """
173
- from lamindb_setup._init_instance import init as init_
174
-
175
177
  return init_(storage=storage, db=db, modules=modules, name=name)
176
178
 
177
179
 
@@ -188,8 +190,7 @@ def connect(instance: str):
188
190
 
189
191
  See also: Connect in a Python session via {func}`~lamindb.connect`.
190
192
  """
191
- from lamindb_setup._connect_instance import _connect_cli
192
- return _connect_cli(instance)
193
+ return connect_(instance)
193
194
 
194
195
 
195
196
  @main.command()
@@ -198,8 +199,6 @@ def disconnect():
198
199
 
199
200
  See also: Disconnect in a Python session via {func}`~lamindb.setup.disconnect`.
200
201
  """
201
- from lamindb_setup import disconnect as disconnect_
202
-
203
202
  return disconnect_()
204
203
 
205
204
 
@@ -211,18 +210,22 @@ def disconnect():
211
210
  def create(entity: Literal["branch"], name: str | None = None):
212
211
  """Create a record for an entity.
213
212
 
214
- Currently only supports creating a branch.
213
+ Currently only supports creating branches and projects.
215
214
 
216
215
  ```
217
216
  lamin create branch --name my_branch
217
+ lamin create project --name my_project
218
218
  ```
219
219
  """
220
- assert entity == "branch", "Currently only supports creating a branch."
220
+ from lamindb.models import Branch, Project
221
221
 
222
- from lamindb.models import Branch
223
-
224
- branch = Branch(name=name).save()
225
- logger.important(f"created branch: {branch.name}")
222
+ if entity == "branch":
223
+ record = Branch(name=name).save()
224
+ elif entity == "project":
225
+ record = Project(name=name).save()
226
+ else:
227
+ raise NotImplementedError(f"Creating {entity} is not implemented.")
228
+ logger.important(f"created {entity}: {record.name}")
226
229
 
227
230
 
228
231
  # fmt: off
@@ -243,9 +246,9 @@ def list_(entity: Literal["branch"], name: str | None = None):
243
246
  from lamindb.models import Branch, Space
244
247
 
245
248
  if entity == "branch":
246
- print(Branch.df())
249
+ print(Branch.to_dataframe())
247
250
  else:
248
- print(Space.df())
251
+ print(Space.to_dataframe())
249
252
 
250
253
 
251
254
  # fmt: off
@@ -303,38 +306,9 @@ def delete(entity: str, name: str | None = None, uid: str | None = None, slug: s
303
306
  lamin delete instance --slug account/name
304
307
  ```
305
308
  """
306
- from lamindb_setup import connect, delete
307
-
308
- # TODO: refactor to abstract getting and deleting across entities
309
- if entity.startswith("https://") and "lamin" in entity:
310
- url = entity
311
- instance, entity, uid = decompose_url(url)
312
- connect(instance)
313
-
314
- if entity == "branch":
315
- assert name is not None, "You have to pass a name for deleting a branch."
316
- from lamindb import Branch
317
-
318
- Branch.get(name=name).delete()
319
- elif entity == "artifact":
320
- assert uid is not None, "You have to pass a uid for deleting an artifact."
321
- from lamindb import Artifact
309
+ from lamin_cli._delete import delete as delete_
322
310
 
323
- Artifact.get(uid).delete()
324
- elif entity == "transform":
325
- assert uid is not None, "You have to pass a uid for deleting an transform."
326
- from lamindb import Transform
327
-
328
- Transform.get(uid).delete()
329
- elif entity == "collection":
330
- assert uid is not None, "You have to pass a uid for deleting an collection."
331
- from lamindb import Collection
332
-
333
- Collection.get(uid).delete()
334
- elif entity == "instance":
335
- return delete(slug, force=force)
336
- else: # backwars compatibility
337
- return delete(entity, force=force)
311
+ return delete_(entity=entity, name=name, uid=uid, slug=slug, force=force)
338
312
 
339
313
 
340
314
  @main.command()
@@ -350,9 +324,9 @@ def load(entity: str, uid: str | None = None, key: str | None = None, with_env:
350
324
  Pass a URL, `artifact`, or `transform`. For example:
351
325
 
352
326
  ```
353
- lamin load https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
327
+ lamin load https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsE
354
328
  lamin load artifact --key mydatasets/mytable.parquet
355
- lamin load artifact --uid e2G7k9EVul4JbfsEYAy5
329
+ lamin load artifact --uid e2G7k9EVul4JbfsE
356
330
  lamin load transform --key analysis.ipynb
357
331
  lamin load transform --uid Vul4JbfsEYAy5
358
332
  lamin load transform --uid Vul4JbfsEYAy5 --with-env
@@ -370,10 +344,6 @@ def load(entity: str, uid: str | None = None, key: str | None = None, with_env:
370
344
 
371
345
 
372
346
  def _describe(entity: str = "artifact", uid: str | None = None, key: str | None = None):
373
- import lamindb_setup as ln_setup
374
-
375
- from ._load import decompose_url
376
-
377
347
  if entity.startswith("https://") and "lamin" in entity:
378
348
  url = entity
379
349
  instance, entity, uid = decompose_url(url)
@@ -449,8 +419,6 @@ def save(path: str, key: str, description: str, stem_uid: str, project: str, spa
449
419
  other file types and folders as {class}`~lamindb.Artifact`. You can enforce saving a file as
450
420
  an {class}`~lamindb.Artifact` by passing `--registry artifact`.
451
421
  """
452
- from lamin_cli import save as save_
453
-
454
422
  if save_(path=path, key=key, description=description, stem_uid=stem_uid, project=project, space=space, branch=branch, registry=registry) is not None:
455
423
  sys.exit(1)
456
424
 
@@ -0,0 +1,43 @@
1
+ from lamindb_setup import connect
2
+ from lamindb_setup import delete as delete_instance
3
+
4
+ from .urls import decompose_url
5
+
6
+
7
+ def delete(
8
+ entity: str,
9
+ name: str | None = None,
10
+ uid: str | None = None,
11
+ slug: str | None = None,
12
+ force: bool = False,
13
+ ):
14
+ # TODO: refactor to abstract getting and deleting across entities
15
+ if entity.startswith("https://") and "lamin" in entity:
16
+ url = entity
17
+ instance, entity, uid = decompose_url(url)
18
+ connect(instance)
19
+
20
+ if entity == "branch":
21
+ assert name is not None, "You have to pass a name for deleting a branch."
22
+ from lamindb import Branch
23
+
24
+ Branch.get(name=name).delete()
25
+ elif entity == "artifact":
26
+ assert uid is not None, "You have to pass a uid for deleting an artifact."
27
+ from lamindb import Artifact
28
+
29
+ Artifact.get(uid).delete()
30
+ elif entity == "transform":
31
+ assert uid is not None, "You have to pass a uid for deleting an transform."
32
+ from lamindb import Transform
33
+
34
+ Transform.get(uid).delete()
35
+ elif entity == "collection":
36
+ assert uid is not None, "You have to pass a uid for deleting an collection."
37
+ from lamindb import Collection
38
+
39
+ Collection.get(uid).delete()
40
+ elif entity == "instance":
41
+ return delete_instance(slug, force=force)
42
+ else: # backwards compatibility
43
+ return delete_instance(entity, force=force)
@@ -73,13 +73,13 @@ def parse_title_r_notebook(content: str) -> str | None:
73
73
 
74
74
  def save(
75
75
  path: Path | str,
76
- key: str | None,
77
- description: str | None,
78
- stem_uid: str | None,
79
- project: str | None,
80
- space: str | None,
81
- branch: str | None,
82
- registry: str | None,
76
+ key: str | None = None,
77
+ description: str | None = None,
78
+ stem_uid: str | None = None,
79
+ project: str | None = None,
80
+ space: str | None = None,
81
+ branch: str | None = None,
82
+ registry: str | None = None,
83
83
  ) -> str | None:
84
84
  import lamindb as ln
85
85
  from lamindb._finish import save_context_core
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ if os.environ.get("NO_RICH"):
7
+ import click as click
8
+ else:
9
+ import rich_click as click
10
+
11
+
12
+ @click.group(invoke_without_command=True)
13
+ @click.pass_context
14
+ def settings(ctx):
15
+ """Manage settings.
16
+
17
+ Call without subcommands and options to show settings:
18
+
19
+ ```
20
+ lamin settings
21
+ ```
22
+
23
+ Allows to get and set these settings:
24
+
25
+ - `work-dir` → {attr}`~lamindb.setup.core.SetupSettings.work_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:
31
+
32
+ ```
33
+ lamin settings get work-dir
34
+ lamin settings get branch
35
+ ```
36
+
37
+ Examples for setting the working directory:
38
+
39
+ ```
40
+ lamin settings set work-dir . # set work-dir to current directory
41
+ lamin settings set work-dir ~/my-project # set work-dir to ~/my-project
42
+ lamin settings set work-dir none # unset work-dir
43
+ ```
44
+ """
45
+ if ctx.invoked_subcommand is None:
46
+ from lamindb_setup import settings as settings_
47
+
48
+ click.echo("Configure: see `lamin settings --help`")
49
+ click.echo(settings_)
50
+
51
+
52
+ @settings.command("set")
53
+ @click.argument(
54
+ "setting",
55
+ type=click.Choice(
56
+ ["auto-connect", "private-django-api", "work-dir"], case_sensitive=False
57
+ ),
58
+ )
59
+ @click.argument("value") # No explicit type - let Click handle it
60
+ def set(setting: str, value: str):
61
+ """Set a setting."""
62
+ from lamindb_setup import settings as settings_
63
+
64
+ if setting == "auto-connect":
65
+ settings_.auto_connect = click.BOOL(value)
66
+ if setting == "private-django-api":
67
+ settings_.private_django_api = click.BOOL(value)
68
+ if setting == "work-dir":
69
+ if value.lower() == "none":
70
+ value = None # type: ignore[assignment]
71
+ settings_.work_dir = value
72
+
73
+
74
+ @settings.command("get")
75
+ @click.argument(
76
+ "setting",
77
+ type=click.Choice(
78
+ ["auto-connect", "private-django-api", "space", "branch", "work-dir"],
79
+ case_sensitive=False,
80
+ ),
81
+ )
82
+ def get(setting: str):
83
+ """Get a setting."""
84
+ from lamindb_setup import settings as settings_
85
+
86
+ if setting == "branch":
87
+ _, value = settings_._read_branch_idlike_name()
88
+ elif setting == "space":
89
+ _, value = settings_._read_space_idlike_name()
90
+ elif setting == "work-dir":
91
+ value = settings_.work_dir
92
+ if value is None:
93
+ value = "None"
94
+ else:
95
+ value = getattr(settings_, setting.replace("-", "_"))
96
+ click.echo(value)
@@ -10,7 +10,6 @@ def pytest_sessionstart(session: pytest.Session):
10
10
  storage="./default_storage_cli",
11
11
  name="lamin-cli-unit-tests",
12
12
  )
13
- ln.setup.settings.auto_connect = True
14
13
 
15
14
 
16
15
  def pytest_sessionfinish(session: pytest.Session):
@@ -1,10 +1,16 @@
1
1
  import os
2
2
  import subprocess
3
+ from pathlib import Path
3
4
 
4
5
  import lamindb as ln
5
6
  import lamindb_setup as ln_setup
6
7
 
7
8
 
9
+ def test_create_project():
10
+ exit_status = os.system("lamin create project --name testproject")
11
+ assert exit_status == 0
12
+
13
+
8
14
  def test_branch():
9
15
  exit_status = os.system("lamin switch --branch archive")
10
16
  assert exit_status == 0
@@ -40,3 +46,38 @@ def test_space():
40
46
  exit_status = os.system("lamin switch --space all")
41
47
  assert exit_status == 0
42
48
  assert ln_setup.settings.space.uid == 12 * "a"
49
+
50
+
51
+ def test_work_dir():
52
+ # default work-dir is None
53
+ result = subprocess.run(
54
+ "lamin settings get work-dir",
55
+ capture_output=True,
56
+ text=True,
57
+ shell=True,
58
+ )
59
+ assert result.stdout.strip() == "None"
60
+ assert ln_setup.settings.work_dir is None
61
+ # set work-dir to tmp_path
62
+ this_path = Path(__file__).resolve()
63
+ exit_status = os.system(f"lamin settings set work-dir {this_path.parent}")
64
+ assert exit_status == 0
65
+ result = subprocess.run(
66
+ "lamin settings get work-dir",
67
+ capture_output=True,
68
+ text=True,
69
+ shell=True,
70
+ )
71
+ assert result.stdout.strip() == str(this_path.parent)
72
+ assert ln_setup.settings.work_dir == this_path.parent
73
+ # unset work-dir
74
+ exit_status = os.system("lamin settings set work-dir none")
75
+ assert exit_status == 0
76
+ result = subprocess.run(
77
+ "lamin settings get work-dir",
78
+ capture_output=True,
79
+ text=True,
80
+ shell=True,
81
+ )
82
+ assert result.stdout.strip() == "None"
83
+ assert ln_setup.settings.work_dir is None
@@ -1,12 +0,0 @@
1
- """Lamin CLI.
2
-
3
- This is the command line interface for interacting with LaminDB & LaminHub.
4
-
5
- The interface is defined in `__main__.py`. The root API here is used by LaminR to replicate the CLI functionality.
6
- """
7
-
8
- __version__ = "1.7.2"
9
-
10
- from ._save import save
11
-
12
- __all__ = ["save"]
@@ -1,62 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os
4
-
5
- if os.environ.get("NO_RICH"):
6
- import click as click
7
- else:
8
- import rich_click as click
9
-
10
-
11
- @click.group(invoke_without_command=True)
12
- @click.pass_context
13
- def settings(ctx):
14
- """Manage settings.
15
-
16
- Call without subcommands and options to show settings.
17
- """
18
- if ctx.invoked_subcommand is None:
19
- from lamindb_setup import settings as settings_
20
-
21
- click.echo("Configure: see `lamin settings --help`")
22
- click.echo(settings_)
23
-
24
-
25
- @settings.command("set")
26
- @click.argument(
27
- "setting",
28
- type=click.Choice(["auto-connect", "private-django-api"], case_sensitive=False),
29
- )
30
- @click.argument("value", type=click.BOOL)
31
- def set(setting: str, value: bool):
32
- """Update settings.
33
-
34
- - `auto-connect` → {attr}`~lamindb.setup.core.SetupSettings.auto_connect`
35
- - `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
36
- """
37
- from lamindb_setup import settings as settings_
38
-
39
- if setting == "auto-connect":
40
- settings_.auto_connect = value
41
- if setting == "private-django-api":
42
- settings_.private_django_api = value
43
-
44
-
45
- @settings.command("get")
46
- @click.argument(
47
- "setting",
48
- type=click.Choice(
49
- ["auto-connect", "private-django-api", "space", "branch"], case_sensitive=False
50
- ),
51
- )
52
- def get(setting: str):
53
- """Get a setting."""
54
- from lamindb_setup import settings as settings_
55
-
56
- if setting == "branch":
57
- _, value = settings_._read_branch_idlike_name()
58
- elif setting == "space":
59
- _, value = settings_._read_space_idlike_name()
60
- else:
61
- value = getattr(settings_, setting.replace("-", "_"))
62
- click.echo(value)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes