lamin_cli 0.21.0__tar.gz → 0.21.2__tar.gz

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.
Files changed (34) hide show
  1. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/PKG-INFO +2 -2
  2. lamin_cli-0.21.2/lamin_cli/__init__.py +3 -0
  3. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/_save.py +13 -7
  4. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/pyproject.toml +1 -1
  5. lamin_cli-0.21.2/tests/test_parse_uid_from_code.py +122 -0
  6. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_save_notebooks.py +1 -1
  7. lamin_cli-0.21.0/lamin_cli/__init__.py +0 -3
  8. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/.github/workflows/doc-changes.yml +0 -0
  9. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/.gitignore +0 -0
  10. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/.pre-commit-config.yaml +0 -0
  11. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/LICENSE +0 -0
  12. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/README.md +0 -0
  13. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/__main__.py +0 -0
  14. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/_cache.py +0 -0
  15. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/_load.py +0 -0
  16. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/_migration.py +0 -0
  17. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/lamin_cli/_settings.py +0 -0
  18. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/conftest.py +0 -0
  19. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/notebooks/not-initialized.ipynb +0 -0
  20. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
  21. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
  22. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/merely-import-lamindb.py +0 -0
  23. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/run-track-and-finish-sync-git.py +0 -0
  24. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/run-track-and-finish.py +0 -0
  25. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/run-track-with-params.py +0 -0
  26. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/run-track.R +0 -0
  27. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/scripts/run-track.qmd +0 -0
  28. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_cli.py +0 -0
  29. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_load.py +0 -0
  30. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_migrate.py +0 -0
  31. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_multi_process.py +0 -0
  32. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_save_files.py +0 -0
  33. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_save_r_code.py +0 -0
  34. {lamin_cli-0.21.0 → lamin_cli-0.21.2}/tests/test_save_scripts.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lamin_cli
3
- Version: 0.21.0
3
+ Version: 0.21.2
4
4
  Summary: Lamin CLI.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: lamindb_setup==0.80.0
7
+ Requires-Dist: lamindb_setup==0.81.0
8
8
  Requires-Dist: rich-click>=1.7
9
9
  Project-URL: Home, https://github.com/laminlabs/lamin-cli
10
10
 
@@ -0,0 +1,3 @@
1
+ """Lamin CLI."""
2
+
3
+ __version__ = "0.21.2"
@@ -10,7 +10,10 @@ def parse_uid_from_code(
10
10
  content: str, suffix: str
11
11
  ) -> tuple[str | None, str | None, str | None]:
12
12
  if suffix == ".py":
13
- track_pattern = re.compile(r'ln\.track\(\s*(?:uid\s*=\s*)?["\']([^"\']+)["\']')
13
+ track_pattern = re.compile(
14
+ r'ln\.track\(\s*(?:transform\s*=\s*)?(["\'])([a-zA-Z0-9]{16})\1'
15
+ )
16
+ # backward compat
14
17
  uid_pattern = re.compile(r'\.context\.uid\s*=\s*["\']([^"\']+)["\']')
15
18
  stem_uid_pattern = re.compile(
16
19
  r'\.transform\.stem_uid\s*=\s*["\']([^"\']+)["\']'
@@ -18,8 +21,9 @@ def parse_uid_from_code(
18
21
  version_pattern = re.compile(r'\.transform\.version\s*=\s*["\']([^"\']+)["\']')
19
22
  elif suffix == ".ipynb":
20
23
  track_pattern = re.compile(
21
- r'ln\.track\(\s*(?:uid\s*=\s*)?\\["\']([^"\']+)\\["\']'
24
+ r'ln\.track\(\s*(?:transform\s*=\s*)?(?:\\"|\')([a-zA-Z0-9]{16})(?:\\"|\')'
22
25
  )
26
+ # backward compat
23
27
  uid_pattern = re.compile(r'\.context\.uid\s*=\s*\\["\']([^"\']+)\\["\']')
24
28
  stem_uid_pattern = re.compile(
25
29
  r'\.transform\.stem_uid\s*=\s*\\["\']([^"\']+)\\["\']'
@@ -28,7 +32,9 @@ def parse_uid_from_code(
28
32
  r'\.transform\.version\s*=\s*\\["\']([^"\']+)\\["\']'
29
33
  )
30
34
  elif suffix in {".R", ".qmd", ".Rmd"}:
31
- track_pattern = re.compile(r'track\(\s*[\'"]([a-zA-Z0-9]{16})[\'"]')
35
+ track_pattern = re.compile(
36
+ r'track\(\s*(?:transform\s*=\s*)?([\'"])([a-zA-Z0-9]{16})\1'
37
+ )
32
38
  uid_pattern = None
33
39
  stem_uid_pattern = None
34
40
  version_pattern = None
@@ -40,7 +46,8 @@ def parse_uid_from_code(
40
46
 
41
47
  # Search for matches in the entire file content
42
48
  uid_match = track_pattern.search(content)
43
- uid = uid_match.group(1) if uid_match else None
49
+ group_index = 1 if suffix == ".ipynb" else 2
50
+ uid = uid_match.group(group_index) if uid_match else None
44
51
  stem_uid = None
45
52
  version = None
46
53
 
@@ -55,10 +62,9 @@ def parse_uid_from_code(
55
62
  version = version_match.group(1) if version_match else None
56
63
 
57
64
  if uid is None and (stem_uid is None or version is None):
58
- target = "script" if suffix == ".py" else "notebook"
65
+ target = "script" if suffix in {".py", ".R"} else "notebook"
59
66
  raise SystemExit(
60
- "Cannot infer transform uid."
61
- f"\nCall `ln.track()` and copy/paste the output into the {target}."
67
+ f"Cannot infer transform uid. Did you run `ln.track()` in your {target}?"
62
68
  )
63
69
 
64
70
  return uid, stem_uid, version
@@ -10,7 +10,7 @@ dynamic = ["version", "description"]
10
10
  dependencies = [
11
11
  # lamindb-setup is only pinned here
12
12
  # NOT in lamindb
13
- "lamindb_setup==0.80.0",
13
+ "lamindb_setup==0.81.0",
14
14
  "rich-click>=1.7",
15
15
  ]
16
16
 
@@ -0,0 +1,122 @@
1
+ import pytest
2
+ from lamin_cli._save import parse_uid_from_code
3
+
4
+
5
+ def test_python_track_pattern():
6
+ valid_cases = [
7
+ # Basic cases with 16 character requirement
8
+ ('ln.track("abcd123456789xyz")', "abcd123456789xyz"),
9
+ ("ln.track('abcd123456789xyz')", "abcd123456789xyz"),
10
+ # With transform parameter
11
+ ('ln.track(transform="abcd123456789xyz")', "abcd123456789xyz"),
12
+ ("ln.track(transform='abcd123456789xyz')", "abcd123456789xyz"),
13
+ # With whitespace variations
14
+ ('ln.track( "abcd123456789xyz" )', "abcd123456789xyz"),
15
+ ('ln.track( transform ="abcd123456789xyz")', "abcd123456789xyz"),
16
+ ('ln.track(\n "abcd123456789xyz"\n)', "abcd123456789xyz"),
17
+ ]
18
+
19
+ invalid_cases = [
20
+ # Mismatched quotes
21
+ "ln.track(\"abcd123456789xyz')",
22
+ "ln.track('abcd123456789xyz\")",
23
+ # Wrong length
24
+ 'ln.track("abc123")', # Too short
25
+ 'ln.track("abcd123456789xyz0")', # Too long
26
+ # Invalid characters
27
+ 'ln.track("abcd-123456789xyz")', # Contains hyphen
28
+ 'ln.track("abcd_123456789xyz")', # Contains underscore
29
+ 'ln.track("abcd!123456789xyz")', # Contains special character
30
+ # Old uid parameter
31
+ 'ln.track(uid="abcd123456789xyz")',
32
+ ]
33
+
34
+ # Test valid cases
35
+ for content, expected_uid in valid_cases:
36
+ uid, _, _ = parse_uid_from_code(content, ".py")
37
+ assert uid == expected_uid, f"Failed for valid content: {content}"
38
+
39
+ # Test invalid cases
40
+ for content in invalid_cases:
41
+ with pytest.raises(SystemExit):
42
+ uid, _, _ = parse_uid_from_code(content, ".py")
43
+
44
+
45
+ def test_jupyter_track_pattern():
46
+ valid_cases = [
47
+ # Basic cases
48
+ (r"ln.track(\"abcd123456789xyz\")", "abcd123456789xyz"),
49
+ ("ln.track('abcd123456789xyz')", "abcd123456789xyz"),
50
+ # With transform parameter
51
+ (r"ln.track(transform=\"abcd123456789xyz\")", "abcd123456789xyz"),
52
+ ("ln.track(transform='abcd123456789xyz')", "abcd123456789xyz"),
53
+ # With whitespace variations
54
+ (r"ln.track( \"abcd123456789xyz\" )", "abcd123456789xyz"),
55
+ (r"ln.track( transform =\"abcd123456789xyz\")", "abcd123456789xyz"),
56
+ ]
57
+
58
+ invalid_cases = [
59
+ # Mismatched quotes
60
+ r"ln.track(\"abcd123456789xyz')",
61
+ # Wrong length
62
+ r"ln.track(\"abc123\")", # Too short
63
+ r"ln.track(\"abcd123456789xyz0\")", # Too long
64
+ # Invalid characters
65
+ r"ln.track(\"abcd-123456789xyz\")", # Contains hyphen
66
+ r"ln.track(\"abcd_123456789xyz\")", # Contains underscore
67
+ # Old uid parameter
68
+ r"ln.track(uid=\"abcd123456789xyz\")",
69
+ ]
70
+
71
+ # Test valid cases
72
+ for content, expected_uid in valid_cases:
73
+ uid, _, _ = parse_uid_from_code(content, ".ipynb")
74
+ assert uid == expected_uid, f"Failed for valid content: {content}"
75
+
76
+ # Test invalid cases
77
+ for content in invalid_cases:
78
+ print(content)
79
+ with pytest.raises(SystemExit):
80
+ uid, _, _ = parse_uid_from_code(content, ".py")
81
+
82
+
83
+ def test_edge_cases():
84
+ test_cases = [
85
+ # Multiple track calls (should match first valid one)
86
+ (
87
+ 'ln.track("abcd123456789xyz")\nln.track("efgh123456789xyz")',
88
+ "abcd123456789xyz",
89
+ ".py",
90
+ ),
91
+ # Comments in code -- this will fail
92
+ # ('# ln.track("abcd123456789xyz")\nln.track("real123456789xyz")', "real123456789xyz", ".py"),
93
+ # Mixed valid and invalid cases
94
+ (
95
+ 'ln.track("invalid")\nln.track("abcd123456789xyz")',
96
+ "abcd123456789xyz",
97
+ ".py",
98
+ ),
99
+ ]
100
+
101
+ for content, expected_uid, suffix in test_cases:
102
+ print(content)
103
+ uid, _, _ = parse_uid_from_code(content, suffix)
104
+ assert uid == expected_uid, f"Failed for content: {content}"
105
+
106
+
107
+ def test_r_track_pattern():
108
+ suffixes = [".R", ".qmd", ".Rmd"]
109
+
110
+ valid_cases = [
111
+ # Basic cases with 16 character requirement
112
+ ('track("abcd123456789xyz")', "abcd123456789xyz"),
113
+ ("track('abcd123456789xyz')", "abcd123456789xyz"),
114
+ ]
115
+
116
+ # Test valid cases across all R-related suffixes
117
+ for suffix in suffixes:
118
+ for content, expected_uid in valid_cases:
119
+ uid, _, _ = parse_uid_from_code(content, suffix)
120
+ assert (
121
+ uid == expected_uid
122
+ ), f"Failed for valid content with {suffix}: {content}"
@@ -22,7 +22,7 @@ def test_save_not_initialized():
22
22
  )
23
23
  assert result.returncode == 1
24
24
  assert (
25
- "Call `ln.track()` and copy/paste the output into the notebook"
25
+ "Cannot infer transform uid. Did you run `ln.track()` in your notebook?"
26
26
  in result.stderr.decode()
27
27
  )
28
28
 
@@ -1,3 +0,0 @@
1
- """Lamin CLI."""
2
-
3
- __version__ = "0.21.0"
File without changes
File without changes
File without changes
File without changes
File without changes