lamin_cli 1.0.7__py2.py3-none-any.whl → 1.2.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 +1 -1
- lamin_cli/__main__.py +53 -31
- lamin_cli/_cache.py +1 -0
- lamin_cli/_load.py +109 -81
- lamin_cli/_migration.py +4 -3
- lamin_cli/_save.py +12 -7
- lamin_cli/_settings.py +1 -0
- {lamin_cli-1.0.7.dist-info → lamin_cli-1.2.0.dist-info}/METADATA +1 -1
- lamin_cli-1.2.0.dist-info/RECORD +12 -0
- lamin_cli-1.0.7.dist-info/RECORD +0 -12
- {lamin_cli-1.0.7.dist-info → lamin_cli-1.2.0.dist-info}/LICENSE +0 -0
- {lamin_cli-1.0.7.dist-info → lamin_cli-1.2.0.dist-info}/WHEEL +0 -0
- {lamin_cli-1.0.7.dist-info → lamin_cli-1.2.0.dist-info}/entry_points.txt +0 -0
lamin_cli/__init__.py
CHANGED
lamin_cli/__main__.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
2
4
|
import os
|
|
3
5
|
import sys
|
|
6
|
+
import warnings
|
|
4
7
|
from collections import OrderedDict
|
|
5
|
-
import inspect
|
|
6
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
-
from typing import Optional, Mapping
|
|
8
8
|
from functools import wraps
|
|
9
|
-
import
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Mapping
|
|
10
14
|
|
|
11
15
|
# https://github.com/ewels/rich-click/issues/19
|
|
12
16
|
# Otherwise rich-click takes over the formatting.
|
|
@@ -18,11 +22,11 @@ if os.environ.get("NO_RICH"):
|
|
|
18
22
|
|
|
19
23
|
def __init__(
|
|
20
24
|
self,
|
|
21
|
-
name:
|
|
22
|
-
commands:
|
|
25
|
+
name: str | None = None,
|
|
26
|
+
commands: Mapping[str, click.Command] | None = None,
|
|
23
27
|
**kwargs,
|
|
24
28
|
):
|
|
25
|
-
super(
|
|
29
|
+
super().__init__(name, commands, **kwargs)
|
|
26
30
|
self.commands = commands or OrderedDict()
|
|
27
31
|
|
|
28
32
|
def list_commands(self, ctx: click.Context) -> Mapping[str, click.Command]:
|
|
@@ -77,12 +81,14 @@ else:
|
|
|
77
81
|
return wrapper
|
|
78
82
|
|
|
79
83
|
|
|
80
|
-
from click import Command, Context
|
|
81
84
|
from lamindb_setup._silence_loggers import silence_loggers
|
|
82
85
|
|
|
83
|
-
from lamin_cli._settings import settings
|
|
84
86
|
from lamin_cli._cache import cache
|
|
85
87
|
from lamin_cli._migration import migrate
|
|
88
|
+
from lamin_cli._settings import settings
|
|
89
|
+
|
|
90
|
+
if TYPE_CHECKING:
|
|
91
|
+
from click import Command, Context
|
|
86
92
|
|
|
87
93
|
try:
|
|
88
94
|
lamindb_version = version("lamindb")
|
|
@@ -100,7 +106,7 @@ def main():
|
|
|
100
106
|
@main.command()
|
|
101
107
|
@click.argument("user", type=str, default=None, required=False)
|
|
102
108
|
@click.option("--key", type=str, default=None, help="The legacy API key.")
|
|
103
|
-
def login(user: str, key:
|
|
109
|
+
def login(user: str, key: str | None):
|
|
104
110
|
"""Log into LaminHub.
|
|
105
111
|
|
|
106
112
|
`lamin login` prompts for your API key unless you set it via environment variable `LAMIN_API_KEY`.
|
|
@@ -142,24 +148,25 @@ def schema_to_modules_callback(ctx, param, value):
|
|
|
142
148
|
"The --schema option is deprecated and will be removed in a future version."
|
|
143
149
|
" Please use --modules instead.",
|
|
144
150
|
DeprecationWarning,
|
|
151
|
+
stacklevel=2,
|
|
145
152
|
)
|
|
146
153
|
return value
|
|
147
154
|
|
|
148
155
|
|
|
149
156
|
# fmt: off
|
|
150
157
|
@main.command()
|
|
151
|
-
@click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.")
|
|
152
|
-
@click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.")
|
|
153
|
-
@click.option("--modules", type=str, default=None, help="Comma-separated string of modules.")
|
|
158
|
+
@click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.")
|
|
159
|
+
@click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.")
|
|
160
|
+
@click.option("--modules", type=str, default=None, help="Comma-separated string of schema modules.")
|
|
154
161
|
@click.option("--name", type=str, default=None, help="The instance name.")
|
|
155
|
-
@click.option("--schema", type=str, default=None, help="[DEPRECATED] Use --modules instead.", callback=schema_to_modules_callback)
|
|
162
|
+
@click.option("--schema", type=str, default=None, help="[DEPRECATED] Use --modules instead.", callback=schema_to_modules_callback)
|
|
156
163
|
# fmt: on
|
|
157
164
|
def init(
|
|
158
165
|
storage: str,
|
|
159
|
-
db:
|
|
160
|
-
modules:
|
|
161
|
-
name:
|
|
162
|
-
schema:
|
|
166
|
+
db: str | None,
|
|
167
|
+
modules: str | None,
|
|
168
|
+
name: str | None,
|
|
169
|
+
schema: str | None,
|
|
163
170
|
):
|
|
164
171
|
"""Init an instance."""
|
|
165
172
|
from lamindb_setup._init_instance import init as init_
|
|
@@ -182,7 +189,8 @@ def connect(instance: str):
|
|
|
182
189
|
{attr}`~lamindb.setup.core.SetupSettings.auto_connect` to `True` so that you
|
|
183
190
|
auto-connect in a Python session upon importing `lamindb`.
|
|
184
191
|
"""
|
|
185
|
-
from lamindb_setup import
|
|
192
|
+
from lamindb_setup import connect as connect_
|
|
193
|
+
from lamindb_setup import settings as settings_
|
|
186
194
|
|
|
187
195
|
settings_.auto_connect = True
|
|
188
196
|
return connect_(instance, _reload_lamindb=False)
|
|
@@ -217,7 +225,7 @@ def info(schema: bool):
|
|
|
217
225
|
# fmt: off
|
|
218
226
|
@main.command()
|
|
219
227
|
@click.argument("instance", type=str, default=None)
|
|
220
|
-
@click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.")
|
|
228
|
+
@click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.")
|
|
221
229
|
# fmt: on
|
|
222
230
|
def delete(instance: str, force: bool = False):
|
|
223
231
|
"""Delete an entity.
|
|
@@ -236,7 +244,7 @@ def delete(instance: str, force: bool = False):
|
|
|
236
244
|
@click.option(
|
|
237
245
|
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
238
246
|
)
|
|
239
|
-
def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
247
|
+
def load(entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False):
|
|
240
248
|
"""Load a file or folder.
|
|
241
249
|
|
|
242
250
|
Pass a URL, `artifact`, or `transform`. For example:
|
|
@@ -252,7 +260,8 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
252
260
|
"""
|
|
253
261
|
is_slug = entity.count("/") == 1
|
|
254
262
|
if is_slug:
|
|
255
|
-
from lamindb_setup import
|
|
263
|
+
from lamindb_setup import connect
|
|
264
|
+
from lamindb_setup import settings as settings_
|
|
256
265
|
|
|
257
266
|
# can decide whether we want to actually deprecate
|
|
258
267
|
# click.echo(
|
|
@@ -270,18 +279,31 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
270
279
|
@click.argument("entity", type=str)
|
|
271
280
|
@click.option("--uid", help="The uid for the entity.")
|
|
272
281
|
@click.option("--key", help="The key for the entity.")
|
|
273
|
-
|
|
274
|
-
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
275
|
-
)
|
|
276
|
-
def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
282
|
+
def get(entity: str, uid: str | None = None, key: str | None = None):
|
|
277
283
|
"""Query metadata about an entity.
|
|
278
284
|
|
|
279
|
-
Currently only works for artifact
|
|
285
|
+
Currently only works for artifact.
|
|
280
286
|
"""
|
|
281
|
-
|
|
287
|
+
import lamindb_setup as ln_setup
|
|
282
288
|
|
|
283
|
-
|
|
284
|
-
|
|
289
|
+
from ._load import decompose_url
|
|
290
|
+
|
|
291
|
+
if entity.startswith("https://") and "lamin" in entity:
|
|
292
|
+
url = entity
|
|
293
|
+
instance, entity, uid = decompose_url(url)
|
|
294
|
+
elif entity not in {"artifact"}:
|
|
295
|
+
raise SystemExit("Entity has to be a laminhub URL or 'artifact'")
|
|
296
|
+
else:
|
|
297
|
+
instance = ln_setup.settings.instance.slug
|
|
298
|
+
|
|
299
|
+
ln_setup.connect(instance)
|
|
300
|
+
import lamindb as ln
|
|
301
|
+
|
|
302
|
+
if uid is not None:
|
|
303
|
+
artifact = ln.Artifact.get(uid)
|
|
304
|
+
else:
|
|
305
|
+
artifact = ln.Artifact.get(key=key)
|
|
306
|
+
artifact.describe()
|
|
285
307
|
|
|
286
308
|
|
|
287
309
|
@main.command()
|
|
@@ -315,7 +337,7 @@ def _generate_help():
|
|
|
315
337
|
out: dict[str, dict[str, str | None]] = {}
|
|
316
338
|
|
|
317
339
|
def recursive_help(
|
|
318
|
-
cmd: Command, parent:
|
|
340
|
+
cmd: Command, parent: Context | None = None, name: tuple[str, ...] = ()
|
|
319
341
|
):
|
|
320
342
|
ctx = click.Context(cmd, info_name=cmd.name, parent=parent)
|
|
321
343
|
assert cmd.name
|
lamin_cli/_cache.py
CHANGED
lamin_cli/_load.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from lamin_utils import logger
|
|
4
|
-
import shutil
|
|
2
|
+
|
|
5
3
|
import re
|
|
4
|
+
import shutil
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
|
|
7
|
+
from lamin_utils import logger
|
|
8
|
+
|
|
8
9
|
|
|
9
|
-
def decompose_url(url: str) ->
|
|
10
|
-
assert
|
|
11
|
-
for entity in ["transform", "artifact"]:
|
|
10
|
+
def decompose_url(url: str) -> tuple[str, str, str]:
|
|
11
|
+
assert any(keyword in url for keyword in ["transform", "artifact", "collection"])
|
|
12
|
+
for entity in ["transform", "artifact", "collection"]:
|
|
12
13
|
if entity in url:
|
|
13
14
|
break
|
|
14
15
|
uid = url.split(f"{entity}/")[1]
|
|
@@ -16,14 +17,18 @@ def decompose_url(url: str) -> Tuple[str, str, str]:
|
|
|
16
17
|
return instance_slug, entity, uid
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def load(
|
|
20
|
+
def load(
|
|
21
|
+
entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False
|
|
22
|
+
):
|
|
20
23
|
import lamindb_setup as ln_setup
|
|
21
24
|
|
|
22
25
|
if entity.startswith("https://") and "lamin" in entity:
|
|
23
26
|
url = entity
|
|
24
27
|
instance, entity, uid = decompose_url(url)
|
|
25
|
-
elif entity not in {"artifact", "transform"}:
|
|
26
|
-
raise SystemExit(
|
|
28
|
+
elif entity not in {"artifact", "transform", "collection"}:
|
|
29
|
+
raise SystemExit(
|
|
30
|
+
"Entity has to be a laminhub URL or 'artifact', 'collection', or 'transform'"
|
|
31
|
+
)
|
|
27
32
|
else:
|
|
28
33
|
instance = ln_setup.settings.instance.slug
|
|
29
34
|
|
|
@@ -42,11 +47,13 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
42
47
|
new_content = transform.source_code.replace(
|
|
43
48
|
"# # transform.name", f"# # {transform.description}"
|
|
44
49
|
)
|
|
45
|
-
elif transform.source_code.startswith("# %% [markdown]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
f"#
|
|
49
|
-
)
|
|
50
|
+
elif transform.source_code.startswith("# %% [markdown]"):
|
|
51
|
+
source_code_split = transform.source_code.split("\n")
|
|
52
|
+
if source_code_split[1] == "#":
|
|
53
|
+
source_code_split[1] = f"# # {transform.description}"
|
|
54
|
+
new_content = "\n".join(source_code_split)
|
|
55
|
+
else:
|
|
56
|
+
new_content = transform.source_code
|
|
50
57
|
else: # R notebook
|
|
51
58
|
# Pattern to match title only within YAML header section
|
|
52
59
|
title_pattern = r'^---\n.*?title:\s*"([^"]*)".*?---'
|
|
@@ -83,82 +90,103 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
83
90
|
|
|
84
91
|
query_by_uid = uid is not None
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# if below, we take is_latest=True as the criterion, we might get draft notebooks
|
|
95
|
-
# hence, we use source_code__isnull=False and order by created_at instead
|
|
96
|
-
transforms = ln.Transform.objects.filter(key=key, source_code__isnull=False)
|
|
97
|
-
|
|
98
|
-
if (n_transforms := len(transforms)) == 0:
|
|
99
|
-
err_msg = f"uid {uid}" if query_by_uid else f"key={key} and source_code"
|
|
100
|
-
raise SystemExit(f"Transform with {err_msg} does not exist.")
|
|
101
|
-
|
|
102
|
-
if n_transforms > 1:
|
|
103
|
-
transforms = transforms.order_by("-created_at")
|
|
104
|
-
transform = transforms.first()
|
|
105
|
-
|
|
106
|
-
target_relpath = Path(transform.key)
|
|
107
|
-
if len(target_relpath.parents) > 1:
|
|
108
|
-
logger.important(
|
|
109
|
-
"preserve the folder structure for versioning:"
|
|
110
|
-
f" {target_relpath.parent}/"
|
|
111
|
-
)
|
|
112
|
-
target_relpath.parent.mkdir(parents=True, exist_ok=True)
|
|
113
|
-
if target_relpath.exists():
|
|
114
|
-
response = input(f"! {target_relpath} exists: replace? (y/n)")
|
|
115
|
-
if response != "y":
|
|
116
|
-
raise SystemExit("Aborted.")
|
|
117
|
-
|
|
118
|
-
if transform.source_code is not None:
|
|
119
|
-
if target_relpath.suffix in (".ipynb", ".Rmd", ".qmd"):
|
|
120
|
-
script_to_notebook(transform, target_relpath, bump_revision=True)
|
|
93
|
+
match entity:
|
|
94
|
+
case "transform":
|
|
95
|
+
if query_by_uid:
|
|
96
|
+
# we don't use .get here because DoesNotExist is hard to catch
|
|
97
|
+
# due to private django API
|
|
98
|
+
# here full uid is not expected anymore as before
|
|
99
|
+
# via ln.Transform.objects.get(uid=uid)
|
|
100
|
+
transforms = ln.Transform.objects.filter(uid__startswith=uid)
|
|
121
101
|
else:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
102
|
+
# if below, we take is_latest=True as the criterion, we might get draft notebooks
|
|
103
|
+
# hence, we use source_code__isnull=False and order by created_at instead
|
|
104
|
+
transforms = ln.Transform.objects.filter(
|
|
105
|
+
key=key, source_code__isnull=False
|
|
106
|
+
)
|
|
125
107
|
|
|
126
|
-
|
|
108
|
+
if (n_transforms := len(transforms)) == 0:
|
|
109
|
+
err_msg = f"uid {uid}" if query_by_uid else f"key={key} and source_code"
|
|
110
|
+
raise SystemExit(f"Transform with {err_msg} does not exist.")
|
|
127
111
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
):
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
112
|
+
if n_transforms > 1:
|
|
113
|
+
transforms = transforms.order_by("-created_at")
|
|
114
|
+
transform = transforms.first()
|
|
115
|
+
|
|
116
|
+
target_relpath = Path(transform.key)
|
|
117
|
+
if len(target_relpath.parents) > 1:
|
|
118
|
+
logger.important(
|
|
119
|
+
"preserve the folder structure for versioning:"
|
|
120
|
+
f" {target_relpath.parent}/"
|
|
137
121
|
)
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
target_relpath.parent.mkdir(parents=True, exist_ok=True)
|
|
123
|
+
if target_relpath.exists():
|
|
124
|
+
response = input(f"! {target_relpath} exists: replace? (y/n)")
|
|
125
|
+
if response != "y":
|
|
126
|
+
raise SystemExit("Aborted.")
|
|
127
|
+
|
|
128
|
+
if transform.source_code is not None:
|
|
129
|
+
if target_relpath.suffix in (".ipynb", ".Rmd", ".qmd"):
|
|
130
|
+
script_to_notebook(transform, target_relpath, bump_revision=True)
|
|
131
|
+
else:
|
|
132
|
+
target_relpath.write_text(transform.source_code)
|
|
140
133
|
else:
|
|
141
|
-
|
|
134
|
+
raise SystemExit("No source code available for this transform.")
|
|
135
|
+
|
|
136
|
+
logger.important(f"{transform.type} is here: {target_relpath}")
|
|
137
|
+
|
|
138
|
+
if with_env:
|
|
139
|
+
ln.settings.track_run_inputs = False
|
|
140
|
+
if (
|
|
141
|
+
transform.latest_run is not None
|
|
142
|
+
and transform.latest_run.environment is not None
|
|
143
|
+
):
|
|
144
|
+
filepath_env_cache = transform.latest_run.environment.cache()
|
|
145
|
+
target_env_filename = (
|
|
146
|
+
target_relpath.parent
|
|
147
|
+
/ f"{target_relpath.stem}__requirements.txt"
|
|
148
|
+
)
|
|
149
|
+
shutil.move(filepath_env_cache, target_env_filename)
|
|
150
|
+
logger.important(f"environment is here: {target_env_filename}")
|
|
151
|
+
else:
|
|
152
|
+
logger.warning(
|
|
153
|
+
"latest transform run with environment doesn't exist"
|
|
154
|
+
)
|
|
142
155
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
156
|
+
return target_relpath
|
|
157
|
+
case "artifact" | "collection":
|
|
158
|
+
ln.settings.track_run_inputs = False
|
|
159
|
+
|
|
160
|
+
EntityClass = ln.Artifact if entity == "artifact" else ln.Collection
|
|
146
161
|
|
|
147
|
-
if query_by_uid:
|
|
148
162
|
# we don't use .get here because DoesNotExist is hard to catch
|
|
149
163
|
# due to private django API
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
164
|
+
if query_by_uid:
|
|
165
|
+
entities = EntityClass.filter(uid__startswith=uid)
|
|
166
|
+
else:
|
|
167
|
+
entities = EntityClass.filter(key=key)
|
|
153
168
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
169
|
+
if (n_entities := len(entities)) == 0:
|
|
170
|
+
err_msg = f"uid={uid}" if query_by_uid else f"key={key}"
|
|
171
|
+
raise SystemExit(
|
|
172
|
+
f"{entity.capitalize()} with {err_msg} does not exist."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if n_entities > 1:
|
|
176
|
+
entities = entities.order_by("-created_at")
|
|
157
177
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
artifact = artifacts.first()
|
|
178
|
+
entity_obj = entities.first()
|
|
179
|
+
cache_path = entity_obj.cache()
|
|
161
180
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
# collection gives us a list of paths
|
|
182
|
+
if isinstance(cache_path, list):
|
|
183
|
+
logger.important(f"{entity} paths ({len(cache_path)} files):")
|
|
184
|
+
for i, path in enumerate(cache_path):
|
|
185
|
+
if i < 5 or i >= len(cache_path) - 5:
|
|
186
|
+
logger.important(f" [{i + 1}/{len(cache_path)}] {path}")
|
|
187
|
+
elif i == 5:
|
|
188
|
+
logger.important(f" ... {len(cache_path) - 10} more files ...")
|
|
189
|
+
else:
|
|
190
|
+
logger.important(f"{entity} is here: {cache_path}")
|
|
191
|
+
case _:
|
|
192
|
+
raise AssertionError(f"unknown entity {entity}")
|
lamin_cli/_migration.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
@@ -34,9 +35,9 @@ def deploy():
|
|
|
34
35
|
@click.option("--end-number", type=str, default=None)
|
|
35
36
|
@click.option("--start-number", type=str, default=None)
|
|
36
37
|
def squash(
|
|
37
|
-
package_name:
|
|
38
|
-
end_number:
|
|
39
|
-
start_number:
|
|
38
|
+
package_name: str | None,
|
|
39
|
+
end_number: str | None,
|
|
40
|
+
start_number: str | None,
|
|
40
41
|
):
|
|
41
42
|
"""Squash migrations."""
|
|
42
43
|
from lamindb_setup._migrate import migrate
|
lamin_cli/_save.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
2
5
|
from pathlib import Path
|
|
3
|
-
|
|
6
|
+
|
|
4
7
|
from lamin_utils import logger
|
|
5
|
-
import re
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
@@ -41,7 +43,7 @@ def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
def save_from_filepath_cli(
|
|
44
|
-
filepath:
|
|
46
|
+
filepath: str | Path,
|
|
45
47
|
key: str | None,
|
|
46
48
|
description: str | None,
|
|
47
49
|
registry: str | None,
|
|
@@ -57,13 +59,16 @@ def save_from_filepath_cli(
|
|
|
57
59
|
ln_setup.settings.auto_connect = True
|
|
58
60
|
|
|
59
61
|
import lamindb as ln
|
|
62
|
+
|
|
63
|
+
if not ln.setup.core.django.IS_SETUP:
|
|
64
|
+
sys.exit(-1)
|
|
60
65
|
from lamindb._finish import save_context_core
|
|
61
66
|
|
|
62
67
|
ln_setup.settings.auto_connect = auto_connect_state
|
|
63
68
|
|
|
64
69
|
suffixes_transform = {
|
|
65
|
-
"py":
|
|
66
|
-
"R":
|
|
70
|
+
"py": {".py", ".ipynb"},
|
|
71
|
+
"R": {".R", ".qmd", ".Rmd"},
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
if filepath.suffix in {".qmd", ".Rmd"}:
|
|
@@ -80,8 +85,8 @@ def save_from_filepath_cli(
|
|
|
80
85
|
and filepath.with_suffix(".nb.html").exists()
|
|
81
86
|
):
|
|
82
87
|
raise SystemExit(
|
|
83
|
-
f
|
|
84
|
-
f
|
|
88
|
+
f"Please delete one of\n - {filepath.with_suffix('.html')}\n -"
|
|
89
|
+
f" {filepath.with_suffix('.nb.html')}"
|
|
85
90
|
)
|
|
86
91
|
|
|
87
92
|
if registry is None:
|
lamin_cli/_settings.py
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
lamin_cli/__init__.py,sha256=2kLtbQt2_3KEeiaePHI1brXcTblfz2N7yIGKsKcAtb4,40
|
|
2
|
+
lamin_cli/__main__.py,sha256=a14UZj5xS66nZXdL_dUkU9M5AAWvs5unQ9lHJx1S5fI,10839
|
|
3
|
+
lamin_cli/_cache.py,sha256=oplwE8AcS_9PYptQUZxff2qTIdNFS81clGPkJNWk098,800
|
|
4
|
+
lamin_cli/_load.py,sha256=lMhV9AMkybjvj4VChJE_v7IMy6qGqisFlo40BJUibsA,8087
|
|
5
|
+
lamin_cli/_migration.py,sha256=xQi6mwnpBzY5wcv1-TJhveD7a3XJIlpiYx6Z3AJ1NF0,1063
|
|
6
|
+
lamin_cli/_save.py,sha256=bt873beNgog5naWITjPb61cjy00aeEtIv9lwqQttRGI,5908
|
|
7
|
+
lamin_cli/_settings.py,sha256=O2tecCf5EIZu98ima4DTJujo4KuywckOLgw8c-Ke3dY,1142
|
|
8
|
+
lamin_cli-1.2.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
9
|
+
lamin_cli-1.2.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
+
lamin_cli-1.2.0.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
11
|
+
lamin_cli-1.2.0.dist-info/METADATA,sha256=22dRlSUzNnNcaapFD-faPqMrNG5Fi9KZLjuHVe06hEc,337
|
|
12
|
+
lamin_cli-1.2.0.dist-info/RECORD,,
|
lamin_cli-1.0.7.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
lamin_cli/__init__.py,sha256=At34nwmlcGUb1vGmsRLVLk2f_sJhCINkEHKoIbl8lHc,40
|
|
2
|
-
lamin_cli/__main__.py,sha256=nmGl8kj9iC1ohjy6JTJc00ZAAkzAWAHVLk5AMFrJDXU,10523
|
|
3
|
-
lamin_cli/_cache.py,sha256=kW8rqlMwQeOngm9uq2gjzPVl3EBrwh6W2F2AvyBFABY,799
|
|
4
|
-
lamin_cli/_load.py,sha256=fOA2lxEWp2l4oOwr_Y7XXDLqbIs9ivF6kBXU7I1eNww,6792
|
|
5
|
-
lamin_cli/_migration.py,sha256=KH0aVRs72ej6ieyM49JaQw1SbT8z24H2heTFnWFcgy4,1071
|
|
6
|
-
lamin_cli/_save.py,sha256=gig2BNgE0RfWXTVzv8Y1QJ5UA5fRS1xE5en6Dx0EwX8,5872
|
|
7
|
-
lamin_cli/_settings.py,sha256=iS37mcQUHKRWxi2sHnAojEI6sWk3w232qwG-GeY2_Qc,1141
|
|
8
|
-
lamin_cli-1.0.7.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
9
|
-
lamin_cli-1.0.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
-
lamin_cli-1.0.7.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
11
|
-
lamin_cli-1.0.7.dist-info/METADATA,sha256=h1HDG-taHw9WZ9fR-g2KcqioTwkKIPwxhm2JxclLOD0,337
|
|
12
|
-
lamin_cli-1.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|