lamin_cli 0.21.4__py2.py3-none-any.whl → 1.0a1__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__ = "0.21.4"
3
+ __version__ = "1.0a1"
lamin_cli/__main__.py CHANGED
@@ -165,7 +165,7 @@ def connect(instance: str):
165
165
  from lamindb_setup import settings as settings_, connect as connect_
166
166
 
167
167
  settings_.auto_connect = True
168
- return connect_(slug=instance)
168
+ return connect_(slug=instance, _reload_lamindb=False)
169
169
 
170
170
 
171
171
  @main.command()
@@ -239,7 +239,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
239
239
  # f"! please use: lamin connect {entity}"
240
240
  # )
241
241
  settings_.auto_connect = True
242
- return connect(slug=entity)
242
+ return connect(slug=entity, _reload_lamindb=False)
243
243
  else:
244
244
  from lamin_cli._load import load as load_
245
245
 
lamin_cli/_load.py CHANGED
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  from typing import Tuple
3
3
  from lamin_utils import logger
4
4
  import shutil
5
+ import re
5
6
  from pathlib import Path
6
7
 
7
8
 
@@ -27,7 +28,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
27
28
  instance = ln_setup.settings.instance.slug
28
29
 
29
30
  ln_setup.connect(instance)
30
- from lnschema_core import models as ln
31
+ import lamindb as ln
31
32
 
32
33
  def script_to_notebook(
33
34
  transform: ln.Transform, notebook_path: Path, bump_revision: bool = False
@@ -35,16 +36,48 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
35
36
  import jupytext
36
37
  from lamin_utils._base62 import increment_base62
37
38
 
38
- py_content = transform.source_code.replace(
39
- "# # transform.name", f"# # {transform.name}"
40
- )
39
+ if notebook_path.suffix == ".ipynb":
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
+ )
50
+ else: # R notebook
51
+ # Pattern to match title only within YAML header section
52
+ title_pattern = r'^---\n.*?title:\s*"([^"]*)".*?---'
53
+ title_match = re.search(
54
+ title_pattern, transform.source_code, flags=re.DOTALL | re.MULTILINE
55
+ )
56
+ new_content = transform.source_code
57
+ if title_match:
58
+ current_title = title_match.group(1)
59
+ if current_title != transform.description:
60
+ pattern = r'^(---\n.*?title:\s*)"([^"]*)"(.*?---)'
61
+ replacement = f'\\1"{transform.description}"\\3'
62
+ new_content = re.sub(
63
+ pattern,
64
+ replacement,
65
+ new_content,
66
+ flags=re.DOTALL | re.MULTILINE,
67
+ )
68
+ logger.important(
69
+ f"fixed title: {current_title} → {transform.description}"
70
+ )
41
71
  if bump_revision:
42
72
  uid = transform.uid
43
73
  new_uid = f"{uid[:-4]}{increment_base62(uid[-4:])}"
44
- py_content = py_content.replace(uid, new_uid)
74
+ new_content = new_content.replace(uid, new_uid)
45
75
  logger.important(f"updated uid: {uid} → {new_uid}")
46
- notebook = jupytext.reads(py_content, fmt="py:percent")
47
- jupytext.write(notebook, notebook_path)
76
+ if notebook_path.suffix == ".ipynb":
77
+ notebook = jupytext.reads(new_content, fmt="py:percent")
78
+ jupytext.write(notebook, notebook_path)
79
+ else:
80
+ notebook_path.write_text(new_content)
48
81
 
49
82
  query_by_uid = uid is not None
50
83
 
@@ -61,42 +94,36 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
61
94
  transforms = ln.Transform.objects.filter(key=key, source_code__isnull=False)
62
95
 
63
96
  if (n_transforms := len(transforms)) == 0:
64
- err_msg = (
65
- f"uid strating with {uid}"
66
- if query_by_uid
67
- else f"key={key} and source_code"
68
- )
97
+ err_msg = f"uid {uid}" if query_by_uid else f"key={key} and source_code"
69
98
  raise SystemExit(f"Transform with {err_msg} does not exist.")
70
99
 
71
100
  if n_transforms > 1:
72
101
  transforms = transforms.order_by("-created_at")
73
102
  transform = transforms.first()
74
103
 
75
- target_filename = transform.key
76
- if Path(target_filename).exists():
77
- response = input(f"! {target_filename} exists: replace? (y/n)")
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)")
78
113
  if response != "y":
79
114
  raise SystemExit("Aborted.")
80
- if transform._source_code_artifact_id is not None: # backward compat
81
- # need lamindb here to have .cache() available
82
- import lamindb as ln
83
115
 
84
- ln.settings.track_run_inputs = False
85
- filepath_cache = transform._source_code_artifact.cache()
86
- if not target_filename.endswith(transform._source_code_artifact.suffix):
87
- target_filename += transform._source_code_artifact.suffix
88
- shutil.move(filepath_cache, target_filename)
89
- elif transform.source_code is not None:
90
- if transform.key.endswith(".ipynb"):
91
- script_to_notebook(transform, 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)
92
119
  else:
93
- Path(target_filename).write_text(transform.source_code)
120
+ target_relpath.write_text(transform.source_code)
94
121
  else:
95
122
  raise SystemExit("No source code available for this transform.")
96
- logger.important(f"{transform.type} is here: {target_filename}")
97
- if with_env:
98
- import lamindb as ln
99
123
 
124
+ logger.important(f"{transform.type} is here: {target_relpath}")
125
+
126
+ if with_env:
100
127
  ln.settings.track_run_inputs = False
101
128
  if (
102
129
  transform.latest_run is not None
@@ -104,16 +131,15 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
104
131
  ):
105
132
  filepath_env_cache = transform.latest_run.environment.cache()
106
133
  target_env_filename = (
107
- ".".join(target_filename.split(".")[:-1]) + "__requirements.txt"
134
+ target_relpath.parent / f"{target_relpath.stem}__requirements.txt"
108
135
  )
109
136
  shutil.move(filepath_env_cache, target_env_filename)
110
137
  logger.important(f"environment is here: {target_env_filename}")
111
138
  else:
112
139
  logger.warning("latest transform run with environment doesn't exist")
113
- return target_filename
114
- elif entity == "artifact":
115
- import lamindb as ln
116
140
 
141
+ return target_relpath
142
+ elif entity == "artifact":
117
143
  ln.settings.track_run_inputs = False
118
144
 
119
145
  if query_by_uid:
@@ -124,7 +150,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
124
150
  artifacts = ln.Artifact.filter(key=key)
125
151
 
126
152
  if (n_artifacts := len(artifacts)) == 0:
127
- err_msg = f"uid strating with {uid}" if query_by_uid else f"key={key}"
153
+ err_msg = f"uid={uid}" if query_by_uid else f"key={key}"
128
154
  raise SystemExit(f"Artifact with {err_msg} does not exist.")
129
155
 
130
156
  if n_artifacts > 1:
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
-
64
- if uid is None and (stem_uid is None or version is None):
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
39
 
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
 
@@ -94,14 +66,23 @@ def save_from_filepath_cli(
94
66
  "R": set([".R", ".qmd", ".Rmd"]),
95
67
  }
96
68
 
97
- if (
98
- filepath.suffix in {".qmd", ".Rmd"}
99
- and not filepath.with_suffix(".html").exists()
100
- ):
101
- raise SystemExit(
102
- f"Please export your {filepath.suffix} file as an html file here"
103
- f" {filepath.with_suffix('.html')}"
104
- )
69
+ if filepath.suffix in {".qmd", ".Rmd"}:
70
+ if not (
71
+ filepath.with_suffix(".html").exists()
72
+ or filepath.with_suffix(".nb.html").exists()
73
+ ):
74
+ raise SystemExit(
75
+ f"Please export your {filepath.suffix} file as an html file here"
76
+ f" {filepath.with_suffix('.html')}"
77
+ )
78
+ if (
79
+ filepath.with_suffix(".html").exists()
80
+ and filepath.with_suffix(".nb.html").exists()
81
+ ):
82
+ raise SystemExit(
83
+ f'Please delete one of\n - {filepath.with_suffix(".html")}\n -'
84
+ f' {filepath.with_suffix(".nb.html")}'
85
+ )
105
86
 
106
87
  if registry is None:
107
88
  registry = (
@@ -128,9 +109,9 @@ def save_from_filepath_cli(
128
109
  elif registry == "transform":
129
110
  with open(filepath) as file:
130
111
  content = file.read()
131
- uid, stem_uid, version = parse_uid_from_code(content, filepath.suffix)
132
- logger.important(f"mapped '{filepath}' on uid '{uid}'")
112
+ uid = parse_uid_from_code(content, filepath.suffix)
133
113
  if uid is not None:
114
+ logger.important(f"mapped '{filepath}' on uid '{uid}'")
134
115
  transform = ln.Transform.filter(uid=uid).one_or_none()
135
116
  if transform is None:
136
117
  logger.error(
@@ -139,10 +120,17 @@ def save_from_filepath_cli(
139
120
  )
140
121
  return "not-tracked-in-transform-registry"
141
122
  else:
142
- transform = ln.Transform.get(uid__startswith=stem_uid, version=version)
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}')")
143
131
  # latest run of this transform by user
144
132
  run = ln.Run.filter(transform=transform).order_by("-started_at").first()
145
- 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:
146
134
  response = input(
147
135
  "You are trying to save a transform created by another user: Source"
148
136
  " and report files will be tagged with *your* user id. Proceed?"
@@ -156,16 +144,6 @@ def save_from_filepath_cli(
156
144
  filepath=filepath,
157
145
  from_cli=True,
158
146
  )
159
- if filepath.suffix in {".qmd", ".Rmd"}:
160
- report_file = ln.Artifact(
161
- filepath.with_suffix(".html"), # validated at the top that this exists
162
- description=f"Report of run {run.uid}",
163
- visibility=0, # hidden file
164
- run=False,
165
- )
166
- report_file.save(upload=True, print_progress=False)
167
- run.report = report_file
168
- run.save()
169
147
  return return_code
170
148
  else:
171
149
  raise SystemExit("Allowed values for '--registry' are: 'artifact', 'transform'")
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: lamin_cli
3
- Version: 0.21.4
3
+ Version: 1.0a1
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=ygApcHBvEoSGZBbdy7cdCV_MnVUaYbGis_FDxgtNxm4,40
2
+ lamin_cli/__main__.py,sha256=CnwhcWeSJwLdq4esnewNKc6Z-T4AuZ8EMDmILfpBZF0,9946
3
+ lamin_cli/_cache.py,sha256=kW8rqlMwQeOngm9uq2gjzPVl3EBrwh6W2F2AvyBFABY,799
4
+ lamin_cli/_load.py,sha256=I2UGifZdIbbLm0JBOsuoR2CZeq5_uQR1BwIujDq8WfA,6694
5
+ lamin_cli/_migration.py,sha256=xTbad6aDUwuK0QvWCmDW3f8-xaqwisS-Cqf-LbD1WRI,1071
6
+ lamin_cli/_save.py,sha256=PrvZSL02jU1BWLUEVTTQsn2MLi942RKOyN3Qd3qfl6s,5677
7
+ lamin_cli/_settings.py,sha256=iS37mcQUHKRWxi2sHnAojEI6sWk3w232qwG-GeY2_Qc,1141
8
+ lamin_cli-1.0a1.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
9
+ lamin_cli-1.0a1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
10
+ lamin_cli-1.0a1.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
11
+ lamin_cli-1.0a1.dist-info/METADATA,sha256=4t0BVXkfQyWzahzefrlFl2vG0MRyYmxfNoT6rvQdcJg,337
12
+ lamin_cli-1.0a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.9.0
2
+ Generator: flit 3.10.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,12 +0,0 @@
1
- lamin_cli/__init__.py,sha256=TdeUIkMA4v5UQztsiYP7y--juAZplFXt-MOJAiS-1ao,41
2
- lamin_cli/__main__.py,sha256=-QAfgrYKcPjl0qkiWr8Tfm5wRaaZ99rW2atjIBnBn9Y,9900
3
- lamin_cli/_cache.py,sha256=kW8rqlMwQeOngm9uq2gjzPVl3EBrwh6W2F2AvyBFABY,799
4
- lamin_cli/_load.py,sha256=n0_qHTCXsZQIUy87rVdPvT89fx60N-KZgn_dL8nsvEA,5523
5
- lamin_cli/_migration.py,sha256=xTbad6aDUwuK0QvWCmDW3f8-xaqwisS-Cqf-LbD1WRI,1071
6
- lamin_cli/_save.py,sha256=X5Rn1rmvtoKz10oZXWWDK3XmNchFhruQ_is-sE8Q-BM,6575
7
- lamin_cli/_settings.py,sha256=iS37mcQUHKRWxi2sHnAojEI6sWk3w232qwG-GeY2_Qc,1141
8
- lamin_cli-0.21.4.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
9
- lamin_cli-0.21.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
10
- lamin_cli-0.21.4.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
11
- lamin_cli-0.21.4.dist-info/METADATA,sha256=tWTkIflVJ0CM_3pwig0RnfF0eddeR6LntJKyf2_GI5c,338
12
- lamin_cli-0.21.4.dist-info/RECORD,,