lamin_cli 1.1.0__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 +21 -8
- lamin_cli/_load.py +94 -71
- {lamin_cli-1.1.0.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.1.0.dist-info/RECORD +0 -12
- {lamin_cli-1.1.0.dist-info → lamin_cli-1.2.0.dist-info}/LICENSE +0 -0
- {lamin_cli-1.1.0.dist-info → lamin_cli-1.2.0.dist-info}/WHEEL +0 -0
- {lamin_cli-1.1.0.dist-info → lamin_cli-1.2.0.dist-info}/entry_points.txt +0 -0
lamin_cli/__init__.py
CHANGED
lamin_cli/__main__.py
CHANGED
|
@@ -279,18 +279,31 @@ def load(entity: str, uid: str | None = None, key: str | None = None, with_env:
|
|
|
279
279
|
@click.argument("entity", type=str)
|
|
280
280
|
@click.option("--uid", help="The uid for the entity.")
|
|
281
281
|
@click.option("--key", help="The key for the entity.")
|
|
282
|
-
|
|
283
|
-
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
284
|
-
)
|
|
285
|
-
def get(entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False):
|
|
282
|
+
def get(entity: str, uid: str | None = None, key: str | None = None):
|
|
286
283
|
"""Query metadata about an entity.
|
|
287
284
|
|
|
288
|
-
Currently only works for artifact
|
|
285
|
+
Currently only works for artifact.
|
|
289
286
|
"""
|
|
290
|
-
|
|
287
|
+
import lamindb_setup as ln_setup
|
|
288
|
+
|
|
289
|
+
from ._load import decompose_url
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
|
|
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()
|
|
294
307
|
|
|
295
308
|
|
|
296
309
|
@main.command()
|
lamin_cli/_load.py
CHANGED
|
@@ -8,8 +8,8 @@ from lamin_utils import logger
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def decompose_url(url: str) -> tuple[str, str, str]:
|
|
11
|
-
assert
|
|
12
|
-
for entity in ["transform", "artifact"]:
|
|
11
|
+
assert any(keyword in url for keyword in ["transform", "artifact", "collection"])
|
|
12
|
+
for entity in ["transform", "artifact", "collection"]:
|
|
13
13
|
if entity in url:
|
|
14
14
|
break
|
|
15
15
|
uid = url.split(f"{entity}/")[1]
|
|
@@ -25,8 +25,10 @@ def load(
|
|
|
25
25
|
if entity.startswith("https://") and "lamin" in entity:
|
|
26
26
|
url = entity
|
|
27
27
|
instance, entity, uid = decompose_url(url)
|
|
28
|
-
elif entity not in {"artifact", "transform"}:
|
|
29
|
-
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
|
+
)
|
|
30
32
|
else:
|
|
31
33
|
instance = ln_setup.settings.instance.slug
|
|
32
34
|
|
|
@@ -88,82 +90,103 @@ def load(
|
|
|
88
90
|
|
|
89
91
|
query_by_uid = uid is not None
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
# if below, we take is_latest=True as the criterion, we might get draft notebooks
|
|
100
|
-
# hence, we use source_code__isnull=False and order by created_at instead
|
|
101
|
-
transforms = ln.Transform.objects.filter(key=key, source_code__isnull=False)
|
|
102
|
-
|
|
103
|
-
if (n_transforms := len(transforms)) == 0:
|
|
104
|
-
err_msg = f"uid {uid}" if query_by_uid else f"key={key} and source_code"
|
|
105
|
-
raise SystemExit(f"Transform with {err_msg} does not exist.")
|
|
106
|
-
|
|
107
|
-
if n_transforms > 1:
|
|
108
|
-
transforms = transforms.order_by("-created_at")
|
|
109
|
-
transform = transforms.first()
|
|
110
|
-
|
|
111
|
-
target_relpath = Path(transform.key)
|
|
112
|
-
if len(target_relpath.parents) > 1:
|
|
113
|
-
logger.important(
|
|
114
|
-
"preserve the folder structure for versioning:"
|
|
115
|
-
f" {target_relpath.parent}/"
|
|
116
|
-
)
|
|
117
|
-
target_relpath.parent.mkdir(parents=True, exist_ok=True)
|
|
118
|
-
if target_relpath.exists():
|
|
119
|
-
response = input(f"! {target_relpath} exists: replace? (y/n)")
|
|
120
|
-
if response != "y":
|
|
121
|
-
raise SystemExit("Aborted.")
|
|
122
|
-
|
|
123
|
-
if transform.source_code is not None:
|
|
124
|
-
if target_relpath.suffix in (".ipynb", ".Rmd", ".qmd"):
|
|
125
|
-
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)
|
|
126
101
|
else:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
+
)
|
|
130
107
|
|
|
131
|
-
|
|
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.")
|
|
132
111
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
):
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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}/"
|
|
142
121
|
)
|
|
143
|
-
|
|
144
|
-
|
|
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)
|
|
145
133
|
else:
|
|
146
|
-
|
|
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
|
+
)
|
|
155
|
+
|
|
156
|
+
return target_relpath
|
|
157
|
+
case "artifact" | "collection":
|
|
158
|
+
ln.settings.track_run_inputs = False
|
|
147
159
|
|
|
148
|
-
|
|
149
|
-
elif entity == "artifact":
|
|
150
|
-
ln.settings.track_run_inputs = False
|
|
160
|
+
EntityClass = ln.Artifact if entity == "artifact" else ln.Collection
|
|
151
161
|
|
|
152
|
-
if query_by_uid:
|
|
153
162
|
# we don't use .get here because DoesNotExist is hard to catch
|
|
154
163
|
# due to private django API
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
if query_by_uid:
|
|
165
|
+
entities = EntityClass.filter(uid__startswith=uid)
|
|
166
|
+
else:
|
|
167
|
+
entities = EntityClass.filter(key=key)
|
|
168
|
+
|
|
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
|
+
)
|
|
158
174
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
raise SystemExit(f"Artifact with {err_msg} does not exist.")
|
|
175
|
+
if n_entities > 1:
|
|
176
|
+
entities = entities.order_by("-created_at")
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
artifact = artifacts.first()
|
|
178
|
+
entity_obj = entities.first()
|
|
179
|
+
cache_path = entity_obj.cache()
|
|
166
180
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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}")
|
|
@@ -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.1.0.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
lamin_cli/__init__.py,sha256=UV55rBrtkCqAG7s-dBM8P0bBJbmz8LGqaj801UwAnps,40
|
|
2
|
-
lamin_cli/__main__.py,sha256=qxmIIzZBt90N8VNgcGd96CNaLl7hj_mfJ719jLzqFLU,10613
|
|
3
|
-
lamin_cli/_cache.py,sha256=oplwE8AcS_9PYptQUZxff2qTIdNFS81clGPkJNWk098,800
|
|
4
|
-
lamin_cli/_load.py,sha256=6pHzBrG6Zbs4aw631cDo28C5XeKwuJxa-LPXxV3Uwm8,6911
|
|
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.1.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
9
|
-
lamin_cli-1.1.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
-
lamin_cli-1.1.0.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
11
|
-
lamin_cli-1.1.0.dist-info/METADATA,sha256=nV4-YhAA3afoijViSF5Un_FZrLWPToCAqUctmHE-tAc,337
|
|
12
|
-
lamin_cli-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|