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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Lamin CLI."""
2
2
 
3
- __version__ = "1.1.0"
3
+ __version__ = "1.2.0"
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
- @click.option(
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 & transform and behaves like `lamin load`.
285
+ Currently only works for artifact.
289
286
  """
290
- from lamin_cli._load import load as load_
287
+ import lamindb_setup as ln_setup
288
+
289
+ from ._load import decompose_url
291
290
 
292
- click.echo(f"! to load a file or folder, please use: lamin load {entity}")
293
- return load_(entity, uid=uid, key=key, with_env=with_env)
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 "transform" in url or "artifact" in url
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("Entity has to be a laminhub URL or 'artifact' or 'transform'")
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
- if entity == "transform":
92
- if query_by_uid:
93
- # we don't use .get here because DoesNotExist is hard to catch
94
- # due to private django API
95
- # here full uid is not expected anymore as before
96
- # via ln.Transform.objects.get(uid=uid)
97
- transforms = ln.Transform.objects.filter(uid__startswith=uid)
98
- else:
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
- target_relpath.write_text(transform.source_code)
128
- else:
129
- raise SystemExit("No source code available for this transform.")
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
- logger.important(f"{transform.type} is here: {target_relpath}")
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
- if with_env:
134
- ln.settings.track_run_inputs = False
135
- if (
136
- transform.latest_run is not None
137
- and transform.latest_run.environment is not None
138
- ):
139
- filepath_env_cache = transform.latest_run.environment.cache()
140
- target_env_filename = (
141
- target_relpath.parent / f"{target_relpath.stem}__requirements.txt"
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
- shutil.move(filepath_env_cache, target_env_filename)
144
- logger.important(f"environment is here: {target_env_filename}")
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
- logger.warning("latest transform run with environment doesn't exist")
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
- return target_relpath
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
- artifacts = ln.Artifact.filter(uid__startswith=uid)
156
- else:
157
- artifacts = ln.Artifact.filter(key=key)
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
- if (n_artifacts := len(artifacts)) == 0:
160
- err_msg = f"uid={uid}" if query_by_uid else f"key={key}"
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
- if n_artifacts > 1:
164
- artifacts = artifacts.order_by("-created_at")
165
- artifact = artifacts.first()
178
+ entity_obj = entities.first()
179
+ cache_path = entity_obj.cache()
166
180
 
167
- cache_path = artifact.cache()
168
- logger.important(f"artifact is here: {cache_path}")
169
- return cache_path
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}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lamin_cli
3
- Version: 1.1.0
3
+ Version: 1.2.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,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,,
@@ -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,,