lamin_cli 0.22.0__py2.py3-none-any.whl → 1.0a5__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 +27 -9
- lamin_cli/_load.py +36 -30
- lamin_cli/_migration.py +1 -1
- lamin_cli/_save.py +15 -36
- {lamin_cli-0.22.0.dist-info → lamin_cli-1.0a5.dist-info}/METADATA +2 -2
- lamin_cli-1.0a5.dist-info/RECORD +12 -0
- {lamin_cli-0.22.0.dist-info → lamin_cli-1.0a5.dist-info}/WHEEL +1 -1
- lamin_cli-0.22.0.dist-info/RECORD +0 -12
- {lamin_cli-0.22.0.dist-info → lamin_cli-1.0a5.dist-info}/LICENSE +0 -0
- {lamin_cli-0.22.0.dist-info → lamin_cli-1.0a5.dist-info}/entry_points.txt +0 -0
lamin_cli/__init__.py
CHANGED
lamin_cli/__main__.py
CHANGED
|
@@ -6,6 +6,7 @@ import inspect
|
|
|
6
6
|
from importlib.metadata import PackageNotFoundError, version
|
|
7
7
|
from typing import Optional, Mapping
|
|
8
8
|
from functools import wraps
|
|
9
|
+
import warnings
|
|
9
10
|
|
|
10
11
|
# https://github.com/ewels/rich-click/issues/19
|
|
11
12
|
# Otherwise rich-click takes over the formatting.
|
|
@@ -135,18 +136,37 @@ def logout():
|
|
|
135
136
|
return logout_()
|
|
136
137
|
|
|
137
138
|
|
|
139
|
+
def schema_to_modules_callback(ctx, param, value):
|
|
140
|
+
if param.name == "schema" and value is not None:
|
|
141
|
+
warnings.warn(
|
|
142
|
+
"The --schema option is deprecated and will be removed in a future version."
|
|
143
|
+
" Please use --modules instead.",
|
|
144
|
+
DeprecationWarning,
|
|
145
|
+
)
|
|
146
|
+
return value
|
|
147
|
+
|
|
148
|
+
|
|
138
149
|
# fmt: off
|
|
139
150
|
@main.command()
|
|
140
151
|
@click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.") # noqa: E501
|
|
141
152
|
@click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.") # noqa: E501
|
|
142
|
-
@click.option("--
|
|
153
|
+
@click.option("--modules", type=str, default=None, help="Comma-separated string of modules.") # noqa: E501
|
|
143
154
|
@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) # noqa: E501
|
|
144
156
|
# fmt: on
|
|
145
|
-
def init(
|
|
157
|
+
def init(
|
|
158
|
+
storage: str,
|
|
159
|
+
db: Optional[str],
|
|
160
|
+
modules: Optional[str],
|
|
161
|
+
name: Optional[str],
|
|
162
|
+
schema: Optional[str],
|
|
163
|
+
):
|
|
146
164
|
"""Init an instance."""
|
|
147
165
|
from lamindb_setup._init_instance import init as init_
|
|
148
166
|
|
|
149
|
-
|
|
167
|
+
modules = modules if modules is not None else schema
|
|
168
|
+
|
|
169
|
+
return init_(storage=storage, db=db, modules=modules, name=name)
|
|
150
170
|
|
|
151
171
|
|
|
152
172
|
# fmt: off
|
|
@@ -165,7 +185,7 @@ def connect(instance: str):
|
|
|
165
185
|
from lamindb_setup import settings as settings_, connect as connect_
|
|
166
186
|
|
|
167
187
|
settings_.auto_connect = True
|
|
168
|
-
return connect_(slug=instance)
|
|
188
|
+
return connect_(slug=instance, _reload_lamindb=False)
|
|
169
189
|
|
|
170
190
|
|
|
171
191
|
@main.command()
|
|
@@ -180,7 +200,7 @@ def disconnect():
|
|
|
180
200
|
|
|
181
201
|
|
|
182
202
|
@main.command()
|
|
183
|
-
@click.option("--schema", is_flag=True, help="View schema.")
|
|
203
|
+
@click.option("--schema", is_flag=True, help="View database schema.")
|
|
184
204
|
def info(schema: bool):
|
|
185
205
|
"""Show info about current instance."""
|
|
186
206
|
if schema:
|
|
@@ -239,7 +259,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
239
259
|
# f"! please use: lamin connect {entity}"
|
|
240
260
|
# )
|
|
241
261
|
settings_.auto_connect = True
|
|
242
|
-
return connect(slug=entity)
|
|
262
|
+
return connect(slug=entity, _reload_lamindb=False)
|
|
243
263
|
else:
|
|
244
264
|
from lamin_cli._load import load as load_
|
|
245
265
|
|
|
@@ -265,9 +285,7 @@ def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
265
285
|
|
|
266
286
|
|
|
267
287
|
@main.command()
|
|
268
|
-
@click.argument(
|
|
269
|
-
"filepath", type=click.Path(exists=True, dir_okay=False, file_okay=True)
|
|
270
|
-
)
|
|
288
|
+
@click.argument("filepath", type=click.Path(exists=True, dir_okay=True, file_okay=True))
|
|
271
289
|
@click.option("--key", type=str, default=None)
|
|
272
290
|
@click.option("--description", type=str, default=None)
|
|
273
291
|
@click.option("--registry", type=str, default=None)
|
lamin_cli/_load.py
CHANGED
|
@@ -28,7 +28,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
28
28
|
instance = ln_setup.settings.instance.slug
|
|
29
29
|
|
|
30
30
|
ln_setup.connect(instance)
|
|
31
|
-
|
|
31
|
+
import lamindb as ln
|
|
32
32
|
|
|
33
33
|
def script_to_notebook(
|
|
34
34
|
transform: ln.Transform, notebook_path: Path, bump_revision: bool = False
|
|
@@ -37,9 +37,16 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
37
37
|
from lamin_utils._base62 import increment_base62
|
|
38
38
|
|
|
39
39
|
if notebook_path.suffix == ".ipynb":
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
# below is backward compat
|
|
41
|
+
if "# # transform.name" in transform.source_code:
|
|
42
|
+
new_content = transform.source_code.replace(
|
|
43
|
+
"# # transform.name", f"# # {transform.description}"
|
|
44
|
+
)
|
|
45
|
+
elif transform.source_code.startswith("# %% [markdown]\n#\n"):
|
|
46
|
+
new_content = transform.source_code.replace(
|
|
47
|
+
"# %% [markdown]\n#\n",
|
|
48
|
+
f"# %% [markdown]\n# # {transform.description}\n",
|
|
49
|
+
)
|
|
43
50
|
else: # R notebook
|
|
44
51
|
# Pattern to match title only within YAML header section
|
|
45
52
|
title_pattern = r'^---\n.*?title:\s*"([^"]*)".*?---'
|
|
@@ -49,16 +56,18 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
49
56
|
new_content = transform.source_code
|
|
50
57
|
if title_match:
|
|
51
58
|
current_title = title_match.group(1)
|
|
52
|
-
if current_title != transform.
|
|
59
|
+
if current_title != transform.description:
|
|
53
60
|
pattern = r'^(---\n.*?title:\s*)"([^"]*)"(.*?---)'
|
|
54
|
-
replacement = f'\\1"{transform.
|
|
61
|
+
replacement = f'\\1"{transform.description}"\\3'
|
|
55
62
|
new_content = re.sub(
|
|
56
63
|
pattern,
|
|
57
64
|
replacement,
|
|
58
65
|
new_content,
|
|
59
66
|
flags=re.DOTALL | re.MULTILINE,
|
|
60
67
|
)
|
|
61
|
-
logger.important(
|
|
68
|
+
logger.important(
|
|
69
|
+
f"fixed title: {current_title} → {transform.description}"
|
|
70
|
+
)
|
|
62
71
|
if bump_revision:
|
|
63
72
|
uid = transform.uid
|
|
64
73
|
new_uid = f"{uid[:-4]}{increment_base62(uid[-4:])}"
|
|
@@ -92,31 +101,29 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
92
101
|
transforms = transforms.order_by("-created_at")
|
|
93
102
|
transform = transforms.first()
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
if
|
|
97
|
-
|
|
104
|
+
target_relpath = Path(transform.key)
|
|
105
|
+
if len(target_relpath.parents) > 1:
|
|
106
|
+
logger.important(
|
|
107
|
+
"preserve the folder structure for versioning:"
|
|
108
|
+
f" {target_relpath.parent}/"
|
|
109
|
+
)
|
|
110
|
+
target_relpath.parent.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
if target_relpath.exists():
|
|
112
|
+
response = input(f"! {target_relpath} exists: replace? (y/n)")
|
|
98
113
|
if response != "y":
|
|
99
114
|
raise SystemExit("Aborted.")
|
|
100
|
-
if transform._source_code_artifact_id is not None: # backward compat
|
|
101
|
-
# need lamindb here to have .cache() available
|
|
102
|
-
import lamindb as ln
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
target_filename += transform._source_code_artifact.suffix
|
|
108
|
-
shutil.move(filepath_cache, target_filename)
|
|
109
|
-
elif transform.source_code is not None:
|
|
110
|
-
if transform.key.endswith((".ipynb", ".Rmd", ".qmd")):
|
|
111
|
-
script_to_notebook(transform, Path(target_filename), bump_revision=True)
|
|
116
|
+
if transform.source_code is not None:
|
|
117
|
+
if target_relpath.suffix in (".ipynb", ".Rmd", ".qmd"):
|
|
118
|
+
script_to_notebook(transform, target_relpath, bump_revision=True)
|
|
112
119
|
else:
|
|
113
|
-
|
|
120
|
+
target_relpath.write_text(transform.source_code)
|
|
114
121
|
else:
|
|
115
122
|
raise SystemExit("No source code available for this transform.")
|
|
116
|
-
logger.important(f"{transform.type} is here: {target_filename}")
|
|
117
|
-
if with_env:
|
|
118
|
-
import lamindb as ln
|
|
119
123
|
|
|
124
|
+
logger.important(f"{transform.type} is here: {target_relpath}")
|
|
125
|
+
|
|
126
|
+
if with_env:
|
|
120
127
|
ln.settings.track_run_inputs = False
|
|
121
128
|
if (
|
|
122
129
|
transform.latest_run is not None
|
|
@@ -124,16 +131,15 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
124
131
|
):
|
|
125
132
|
filepath_env_cache = transform.latest_run.environment.cache()
|
|
126
133
|
target_env_filename = (
|
|
127
|
-
|
|
134
|
+
target_relpath.parent / f"{target_relpath.stem}__requirements.txt"
|
|
128
135
|
)
|
|
129
136
|
shutil.move(filepath_env_cache, target_env_filename)
|
|
130
137
|
logger.important(f"environment is here: {target_env_filename}")
|
|
131
138
|
else:
|
|
132
139
|
logger.warning("latest transform run with environment doesn't exist")
|
|
133
|
-
return target_filename
|
|
134
|
-
elif entity == "artifact":
|
|
135
|
-
import lamindb as ln
|
|
136
140
|
|
|
141
|
+
return target_relpath
|
|
142
|
+
elif entity == "artifact":
|
|
137
143
|
ln.settings.track_run_inputs = False
|
|
138
144
|
|
|
139
145
|
if query_by_uid:
|
|
@@ -144,7 +150,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
144
150
|
artifacts = ln.Artifact.filter(key=key)
|
|
145
151
|
|
|
146
152
|
if (n_artifacts := len(artifacts)) == 0:
|
|
147
|
-
err_msg = f"uid
|
|
153
|
+
err_msg = f"uid={uid}" if query_by_uid else f"key={key}"
|
|
148
154
|
raise SystemExit(f"Artifact with {err_msg} does not exist.")
|
|
149
155
|
|
|
150
156
|
if n_artifacts > 1:
|
lamin_cli/_migration.py
CHANGED
lamin_cli/_save.py
CHANGED
|
@@ -1,43 +1,27 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import Union
|
|
4
|
-
import lamindb_setup as ln_setup
|
|
5
4
|
from lamin_utils import logger
|
|
6
5
|
import re
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
def parse_uid_from_code(
|
|
10
|
-
content: str, suffix: str
|
|
11
|
-
) -> tuple[str | None, str | None, str | None]:
|
|
8
|
+
def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
12
9
|
if suffix == ".py":
|
|
13
10
|
track_pattern = re.compile(
|
|
14
11
|
r'ln\.track\(\s*(?:transform\s*=\s*)?(["\'])([a-zA-Z0-9]{16})\1'
|
|
15
12
|
)
|
|
16
|
-
# backward compat
|
|
17
13
|
uid_pattern = re.compile(r'\.context\.uid\s*=\s*["\']([^"\']+)["\']')
|
|
18
|
-
stem_uid_pattern = re.compile(
|
|
19
|
-
r'\.transform\.stem_uid\s*=\s*["\']([^"\']+)["\']'
|
|
20
|
-
)
|
|
21
|
-
version_pattern = re.compile(r'\.transform\.version\s*=\s*["\']([^"\']+)["\']')
|
|
22
14
|
elif suffix == ".ipynb":
|
|
23
15
|
track_pattern = re.compile(
|
|
24
16
|
r'ln\.track\(\s*(?:transform\s*=\s*)?(?:\\"|\')([a-zA-Z0-9]{16})(?:\\"|\')'
|
|
25
17
|
)
|
|
26
18
|
# backward compat
|
|
27
19
|
uid_pattern = re.compile(r'\.context\.uid\s*=\s*\\["\']([^"\']+)\\["\']')
|
|
28
|
-
stem_uid_pattern = re.compile(
|
|
29
|
-
r'\.transform\.stem_uid\s*=\s*\\["\']([^"\']+)\\["\']'
|
|
30
|
-
)
|
|
31
|
-
version_pattern = re.compile(
|
|
32
|
-
r'\.transform\.version\s*=\s*\\["\']([^"\']+)\\["\']'
|
|
33
|
-
)
|
|
34
20
|
elif suffix in {".R", ".qmd", ".Rmd"}:
|
|
35
21
|
track_pattern = re.compile(
|
|
36
22
|
r'track\(\s*(?:transform\s*=\s*)?([\'"])([a-zA-Z0-9]{16})\1'
|
|
37
23
|
)
|
|
38
24
|
uid_pattern = None
|
|
39
|
-
stem_uid_pattern = None
|
|
40
|
-
version_pattern = None
|
|
41
25
|
else:
|
|
42
26
|
raise SystemExit(
|
|
43
27
|
"Only .py, .ipynb, .R, .qmd, .Rmd files are supported for saving"
|
|
@@ -48,26 +32,12 @@ def parse_uid_from_code(
|
|
|
48
32
|
uid_match = track_pattern.search(content)
|
|
49
33
|
group_index = 1 if suffix == ".ipynb" else 2
|
|
50
34
|
uid = uid_match.group(group_index) if uid_match else None
|
|
51
|
-
stem_uid = None
|
|
52
|
-
version = None
|
|
53
35
|
|
|
54
36
|
if uid_pattern is not None and uid is None:
|
|
55
37
|
uid_match = uid_pattern.search(content)
|
|
56
38
|
uid = uid_match.group(1) if uid_match else None
|
|
57
|
-
if stem_uid_pattern is not None:
|
|
58
|
-
stem_uid_match = stem_uid_pattern.search(content)
|
|
59
|
-
stem_uid = stem_uid_match.group(1) if stem_uid_match else None
|
|
60
|
-
if version_pattern is not None:
|
|
61
|
-
version_match = version_pattern.search(content)
|
|
62
|
-
version = version_match.group(1) if version_match else None
|
|
63
39
|
|
|
64
|
-
|
|
65
|
-
target = "script" if suffix in {".py", ".R"} else "notebook"
|
|
66
|
-
raise SystemExit(
|
|
67
|
-
f"Cannot infer transform uid. Did you run `ln.track()` in your {target}?"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
return uid, stem_uid, version
|
|
40
|
+
return uid
|
|
71
41
|
|
|
72
42
|
|
|
73
43
|
def save_from_filepath_cli(
|
|
@@ -76,6 +46,8 @@ def save_from_filepath_cli(
|
|
|
76
46
|
description: str | None,
|
|
77
47
|
registry: str | None,
|
|
78
48
|
) -> str | None:
|
|
49
|
+
import lamindb_setup as ln_setup
|
|
50
|
+
|
|
79
51
|
if not isinstance(filepath, Path):
|
|
80
52
|
filepath = Path(filepath)
|
|
81
53
|
|
|
@@ -137,9 +109,9 @@ def save_from_filepath_cli(
|
|
|
137
109
|
elif registry == "transform":
|
|
138
110
|
with open(filepath) as file:
|
|
139
111
|
content = file.read()
|
|
140
|
-
uid
|
|
141
|
-
logger.important(f"mapped '{filepath}' on uid '{uid}'")
|
|
112
|
+
uid = parse_uid_from_code(content, filepath.suffix)
|
|
142
113
|
if uid is not None:
|
|
114
|
+
logger.important(f"mapped '{filepath}' on uid '{uid}'")
|
|
143
115
|
transform = ln.Transform.filter(uid=uid).one_or_none()
|
|
144
116
|
if transform is None:
|
|
145
117
|
logger.error(
|
|
@@ -148,10 +120,17 @@ def save_from_filepath_cli(
|
|
|
148
120
|
)
|
|
149
121
|
return "not-tracked-in-transform-registry"
|
|
150
122
|
else:
|
|
151
|
-
transform = ln.Transform.
|
|
123
|
+
transform = ln.Transform.filter(key=filepath.name).one_or_none()
|
|
124
|
+
if transform is None:
|
|
125
|
+
transform = ln.Transform(
|
|
126
|
+
name=filepath.name,
|
|
127
|
+
key=filepath.name,
|
|
128
|
+
type="script" if filepath.suffix in {".R", ".py"} else "notebook",
|
|
129
|
+
).save()
|
|
130
|
+
logger.important(f"created Transform('{transform.uid}')")
|
|
152
131
|
# latest run of this transform by user
|
|
153
132
|
run = ln.Run.filter(transform=transform).order_by("-started_at").first()
|
|
154
|
-
if run.created_by.id != ln_setup.settings.user.id:
|
|
133
|
+
if run is not None and run.created_by.id != ln_setup.settings.user.id:
|
|
155
134
|
response = input(
|
|
156
135
|
"You are trying to save a transform created by another user: Source"
|
|
157
136
|
" and report files will be tagged with *your* user id. Proceed?"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
lamin_cli/__init__.py,sha256=xQ-vwh6TYcH_M4iuwgYH8WhhgOTCWlFZGu-0xqkGsXM,40
|
|
2
|
+
lamin_cli/__main__.py,sha256=T2EnywSV4coW0CuXbohrY2WfU88dehAocTlDwvS8YqE,10533
|
|
3
|
+
lamin_cli/_cache.py,sha256=kW8rqlMwQeOngm9uq2gjzPVl3EBrwh6W2F2AvyBFABY,799
|
|
4
|
+
lamin_cli/_load.py,sha256=I2UGifZdIbbLm0JBOsuoR2CZeq5_uQR1BwIujDq8WfA,6694
|
|
5
|
+
lamin_cli/_migration.py,sha256=KH0aVRs72ej6ieyM49JaQw1SbT8z24H2heTFnWFcgy4,1071
|
|
6
|
+
lamin_cli/_save.py,sha256=PrvZSL02jU1BWLUEVTTQsn2MLi942RKOyN3Qd3qfl6s,5677
|
|
7
|
+
lamin_cli/_settings.py,sha256=iS37mcQUHKRWxi2sHnAojEI6sWk3w232qwG-GeY2_Qc,1141
|
|
8
|
+
lamin_cli-1.0a5.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
9
|
+
lamin_cli-1.0a5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
+
lamin_cli-1.0a5.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
11
|
+
lamin_cli-1.0a5.dist-info/METADATA,sha256=cLzAMN0WoMGHmsWMOIH-ByOdCK83qd1jJcWubPNYmNQ,337
|
|
12
|
+
lamin_cli-1.0a5.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
lamin_cli/__init__.py,sha256=51k8L5nYnqtxhh4IaBMawVfAYftmB7jjvbS-gCQY704,41
|
|
2
|
-
lamin_cli/__main__.py,sha256=-QAfgrYKcPjl0qkiWr8Tfm5wRaaZ99rW2atjIBnBn9Y,9900
|
|
3
|
-
lamin_cli/_cache.py,sha256=kW8rqlMwQeOngm9uq2gjzPVl3EBrwh6W2F2AvyBFABY,799
|
|
4
|
-
lamin_cli/_load.py,sha256=Voj8bx9WFAR3FmPhrvF91RA0QKJVVdtYbj8ADsdDt7E,6587
|
|
5
|
-
lamin_cli/_migration.py,sha256=xTbad6aDUwuK0QvWCmDW3f8-xaqwisS-Cqf-LbD1WRI,1071
|
|
6
|
-
lamin_cli/_save.py,sha256=NiDQRfo_A_Kvdh5A7n9dzJevbfIhmGZaBsjqcKdWHiw,6527
|
|
7
|
-
lamin_cli/_settings.py,sha256=iS37mcQUHKRWxi2sHnAojEI6sWk3w232qwG-GeY2_Qc,1141
|
|
8
|
-
lamin_cli-0.22.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
9
|
-
lamin_cli-0.22.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
-
lamin_cli-0.22.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
|
11
|
-
lamin_cli-0.22.0.dist-info/METADATA,sha256=a3pMYtacHsXYf1HOS5p529b54Mz17G422rffSnU6YHc,338
|
|
12
|
-
lamin_cli-0.22.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|