lamin_cli 1.0.6__tar.gz → 1.1.0__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.
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/.gitignore +2 -0
- lamin_cli-1.1.0/.pre-commit-config.yaml +40 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/PKG-INFO +1 -1
- lamin_cli-1.1.0/lamin_cli/__init__.py +3 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/__main__.py +33 -24
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/_cache.py +1 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/_load.py +15 -10
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/_migration.py +4 -3
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/_save.py +17 -8
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/lamin_cli/_settings.py +1 -0
- lamin_cli-1.1.0/pyproject.toml +114 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/merely-import-lamindb.py +1 -1
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/run-track-and-finish-sync-git.py +1 -1
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/run-track-with-params.py +1 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_cli.py +1 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_load.py +7 -6
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_parse_uid_from_code.py +3 -3
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_save_files.py +3 -2
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_save_notebooks.py +6 -5
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_save_r_code.py +3 -2
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_save_scripts.py +29 -8
- lamin_cli-1.0.6/.pre-commit-config.yaml +0 -64
- lamin_cli-1.0.6/lamin_cli/__init__.py +0 -3
- lamin_cli-1.0.6/pyproject.toml +0 -21
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/.github/workflows/doc-changes.yml +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/LICENSE +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/README.md +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/conftest.py +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/notebooks/not-initialized.ipynb +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/run-track-and-finish.py +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/run-track.R +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/scripts/run-track.qmd +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_migrate.py +0 -0
- {lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/test_multi_process.py +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v4.5.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
exclude: |
|
|
8
|
+
(?x)(
|
|
9
|
+
.github/workflows/latest-changes.jinja2
|
|
10
|
+
)
|
|
11
|
+
- id: check-yaml
|
|
12
|
+
- id: check-added-large-files
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.9.2
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix, --exit-non-zero-on-fix, --unsafe-fixes]
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
- repo: https://github.com/pre-commit/mirrors-prettier
|
|
20
|
+
rev: v4.0.0-alpha.8
|
|
21
|
+
hooks:
|
|
22
|
+
- id: prettier
|
|
23
|
+
- repo: https://github.com/kynan/nbstripout
|
|
24
|
+
rev: 0.3.9
|
|
25
|
+
hooks:
|
|
26
|
+
- id: nbstripout
|
|
27
|
+
exclude: |
|
|
28
|
+
(?x)(
|
|
29
|
+
docs/examples/|
|
|
30
|
+
docs/notes/|
|
|
31
|
+
tests
|
|
32
|
+
)
|
|
33
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
34
|
+
rev: v1.14.1
|
|
35
|
+
hooks:
|
|
36
|
+
- id: mypy
|
|
37
|
+
exclude: |
|
|
38
|
+
(?x)(
|
|
39
|
+
tests/hub-local/conftest.py
|
|
40
|
+
)
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
2
4
|
import os
|
|
3
5
|
import sys
|
|
6
|
+
import warnings
|
|
4
7
|
from collections import OrderedDict
|
|
5
|
-
import inspect
|
|
6
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
7
|
-
from typing import Optional, Mapping
|
|
8
8
|
from functools import wraps
|
|
9
|
-
import
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from collections.abc import Mapping
|
|
10
14
|
|
|
11
15
|
# https://github.com/ewels/rich-click/issues/19
|
|
12
16
|
# Otherwise rich-click takes over the formatting.
|
|
@@ -18,11 +22,11 @@ if os.environ.get("NO_RICH"):
|
|
|
18
22
|
|
|
19
23
|
def __init__(
|
|
20
24
|
self,
|
|
21
|
-
name:
|
|
22
|
-
commands:
|
|
25
|
+
name: str | None = None,
|
|
26
|
+
commands: Mapping[str, click.Command] | None = None,
|
|
23
27
|
**kwargs,
|
|
24
28
|
):
|
|
25
|
-
super(
|
|
29
|
+
super().__init__(name, commands, **kwargs)
|
|
26
30
|
self.commands = commands or OrderedDict()
|
|
27
31
|
|
|
28
32
|
def list_commands(self, ctx: click.Context) -> Mapping[str, click.Command]:
|
|
@@ -77,12 +81,14 @@ else:
|
|
|
77
81
|
return wrapper
|
|
78
82
|
|
|
79
83
|
|
|
80
|
-
from click import Command, Context
|
|
81
84
|
from lamindb_setup._silence_loggers import silence_loggers
|
|
82
85
|
|
|
83
|
-
from lamin_cli._settings import settings
|
|
84
86
|
from lamin_cli._cache import cache
|
|
85
87
|
from lamin_cli._migration import migrate
|
|
88
|
+
from lamin_cli._settings import settings
|
|
89
|
+
|
|
90
|
+
if TYPE_CHECKING:
|
|
91
|
+
from click import Command, Context
|
|
86
92
|
|
|
87
93
|
try:
|
|
88
94
|
lamindb_version = version("lamindb")
|
|
@@ -100,7 +106,7 @@ def main():
|
|
|
100
106
|
@main.command()
|
|
101
107
|
@click.argument("user", type=str, default=None, required=False)
|
|
102
108
|
@click.option("--key", type=str, default=None, help="The legacy API key.")
|
|
103
|
-
def login(user: str, key:
|
|
109
|
+
def login(user: str, key: str | None):
|
|
104
110
|
"""Log into LaminHub.
|
|
105
111
|
|
|
106
112
|
`lamin login` prompts for your API key unless you set it via environment variable `LAMIN_API_KEY`.
|
|
@@ -142,24 +148,25 @@ def schema_to_modules_callback(ctx, param, value):
|
|
|
142
148
|
"The --schema option is deprecated and will be removed in a future version."
|
|
143
149
|
" Please use --modules instead.",
|
|
144
150
|
DeprecationWarning,
|
|
151
|
+
stacklevel=2,
|
|
145
152
|
)
|
|
146
153
|
return value
|
|
147
154
|
|
|
148
155
|
|
|
149
156
|
# fmt: off
|
|
150
157
|
@main.command()
|
|
151
|
-
@click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.")
|
|
152
|
-
@click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.")
|
|
153
|
-
@click.option("--modules", type=str, default=None, help="Comma-separated string of modules.")
|
|
158
|
+
@click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.")
|
|
159
|
+
@click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.")
|
|
160
|
+
@click.option("--modules", type=str, default=None, help="Comma-separated string of schema modules.")
|
|
154
161
|
@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)
|
|
162
|
+
@click.option("--schema", type=str, default=None, help="[DEPRECATED] Use --modules instead.", callback=schema_to_modules_callback)
|
|
156
163
|
# fmt: on
|
|
157
164
|
def init(
|
|
158
165
|
storage: str,
|
|
159
|
-
db:
|
|
160
|
-
modules:
|
|
161
|
-
name:
|
|
162
|
-
schema:
|
|
166
|
+
db: str | None,
|
|
167
|
+
modules: str | None,
|
|
168
|
+
name: str | None,
|
|
169
|
+
schema: str | None,
|
|
163
170
|
):
|
|
164
171
|
"""Init an instance."""
|
|
165
172
|
from lamindb_setup._init_instance import init as init_
|
|
@@ -182,7 +189,8 @@ def connect(instance: str):
|
|
|
182
189
|
{attr}`~lamindb.setup.core.SetupSettings.auto_connect` to `True` so that you
|
|
183
190
|
auto-connect in a Python session upon importing `lamindb`.
|
|
184
191
|
"""
|
|
185
|
-
from lamindb_setup import
|
|
192
|
+
from lamindb_setup import connect as connect_
|
|
193
|
+
from lamindb_setup import settings as settings_
|
|
186
194
|
|
|
187
195
|
settings_.auto_connect = True
|
|
188
196
|
return connect_(instance, _reload_lamindb=False)
|
|
@@ -217,7 +225,7 @@ def info(schema: bool):
|
|
|
217
225
|
# fmt: off
|
|
218
226
|
@main.command()
|
|
219
227
|
@click.argument("instance", type=str, default=None)
|
|
220
|
-
@click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.")
|
|
228
|
+
@click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.")
|
|
221
229
|
# fmt: on
|
|
222
230
|
def delete(instance: str, force: bool = False):
|
|
223
231
|
"""Delete an entity.
|
|
@@ -236,7 +244,7 @@ def delete(instance: str, force: bool = False):
|
|
|
236
244
|
@click.option(
|
|
237
245
|
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
238
246
|
)
|
|
239
|
-
def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
247
|
+
def load(entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False):
|
|
240
248
|
"""Load a file or folder.
|
|
241
249
|
|
|
242
250
|
Pass a URL, `artifact`, or `transform`. For example:
|
|
@@ -252,7 +260,8 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
252
260
|
"""
|
|
253
261
|
is_slug = entity.count("/") == 1
|
|
254
262
|
if is_slug:
|
|
255
|
-
from lamindb_setup import
|
|
263
|
+
from lamindb_setup import connect
|
|
264
|
+
from lamindb_setup import settings as settings_
|
|
256
265
|
|
|
257
266
|
# can decide whether we want to actually deprecate
|
|
258
267
|
# click.echo(
|
|
@@ -273,7 +282,7 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
273
282
|
@click.option(
|
|
274
283
|
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
275
284
|
)
|
|
276
|
-
def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
285
|
+
def get(entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False):
|
|
277
286
|
"""Query metadata about an entity.
|
|
278
287
|
|
|
279
288
|
Currently only works for artifact & transform and behaves like `lamin load`.
|
|
@@ -315,7 +324,7 @@ def _generate_help():
|
|
|
315
324
|
out: dict[str, dict[str, str | None]] = {}
|
|
316
325
|
|
|
317
326
|
def recursive_help(
|
|
318
|
-
cmd: Command, parent:
|
|
327
|
+
cmd: Command, parent: Context | None = None, name: tuple[str, ...] = ()
|
|
319
328
|
):
|
|
320
329
|
ctx = click.Context(cmd, info_name=cmd.name, parent=parent)
|
|
321
330
|
assert cmd.name
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from lamin_utils import logger
|
|
4
|
-
import shutil
|
|
2
|
+
|
|
5
3
|
import re
|
|
4
|
+
import shutil
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
|
|
7
|
+
from lamin_utils import logger
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
def decompose_url(url: str) -> tuple[str, str, str]:
|
|
10
11
|
assert "transform" in url or "artifact" in url
|
|
11
12
|
for entity in ["transform", "artifact"]:
|
|
12
13
|
if entity in url:
|
|
@@ -16,7 +17,9 @@ def decompose_url(url: str) -> Tuple[str, str, str]:
|
|
|
16
17
|
return instance_slug, entity, uid
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def load(
|
|
20
|
+
def load(
|
|
21
|
+
entity: str, uid: str | None = None, key: str | None = None, with_env: bool = False
|
|
22
|
+
):
|
|
20
23
|
import lamindb_setup as ln_setup
|
|
21
24
|
|
|
22
25
|
if entity.startswith("https://") and "lamin" in entity:
|
|
@@ -42,11 +45,13 @@ def load(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
|
42
45
|
new_content = transform.source_code.replace(
|
|
43
46
|
"# # transform.name", f"# # {transform.description}"
|
|
44
47
|
)
|
|
45
|
-
elif transform.source_code.startswith("# %% [markdown]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
f"#
|
|
49
|
-
)
|
|
48
|
+
elif transform.source_code.startswith("# %% [markdown]"):
|
|
49
|
+
source_code_split = transform.source_code.split("\n")
|
|
50
|
+
if source_code_split[1] == "#":
|
|
51
|
+
source_code_split[1] = f"# # {transform.description}"
|
|
52
|
+
new_content = "\n".join(source_code_split)
|
|
53
|
+
else:
|
|
54
|
+
new_content = transform.source_code
|
|
50
55
|
else: # R notebook
|
|
51
56
|
# Pattern to match title only within YAML header section
|
|
52
57
|
title_pattern = r'^---\n.*?title:\s*"([^"]*)".*?---'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import os
|
|
3
4
|
from typing import Optional
|
|
4
5
|
|
|
@@ -34,9 +35,9 @@ def deploy():
|
|
|
34
35
|
@click.option("--end-number", type=str, default=None)
|
|
35
36
|
@click.option("--start-number", type=str, default=None)
|
|
36
37
|
def squash(
|
|
37
|
-
package_name:
|
|
38
|
-
end_number:
|
|
39
|
-
start_number:
|
|
38
|
+
package_name: str | None,
|
|
39
|
+
end_number: str | None,
|
|
40
|
+
start_number: str | None,
|
|
40
41
|
):
|
|
41
42
|
"""Squash migrations."""
|
|
42
43
|
from lamindb_setup._migrate import migrate
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
2
5
|
from pathlib import Path
|
|
3
|
-
|
|
6
|
+
|
|
4
7
|
from lamin_utils import logger
|
|
5
|
-
import re
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
@@ -41,7 +43,7 @@ def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
def save_from_filepath_cli(
|
|
44
|
-
filepath:
|
|
46
|
+
filepath: str | Path,
|
|
45
47
|
key: str | None,
|
|
46
48
|
description: str | None,
|
|
47
49
|
registry: str | None,
|
|
@@ -57,13 +59,16 @@ def save_from_filepath_cli(
|
|
|
57
59
|
ln_setup.settings.auto_connect = True
|
|
58
60
|
|
|
59
61
|
import lamindb as ln
|
|
62
|
+
|
|
63
|
+
if not ln.setup.core.django.IS_SETUP:
|
|
64
|
+
sys.exit(-1)
|
|
60
65
|
from lamindb._finish import save_context_core
|
|
61
66
|
|
|
62
67
|
ln_setup.settings.auto_connect = auto_connect_state
|
|
63
68
|
|
|
64
69
|
suffixes_transform = {
|
|
65
|
-
"py":
|
|
66
|
-
"R":
|
|
70
|
+
"py": {".py", ".ipynb"},
|
|
71
|
+
"R": {".R", ".qmd", ".Rmd"},
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
if filepath.suffix in {".qmd", ".Rmd"}:
|
|
@@ -80,8 +85,8 @@ def save_from_filepath_cli(
|
|
|
80
85
|
and filepath.with_suffix(".nb.html").exists()
|
|
81
86
|
):
|
|
82
87
|
raise SystemExit(
|
|
83
|
-
f
|
|
84
|
-
f
|
|
88
|
+
f"Please delete one of\n - {filepath.with_suffix('.html')}\n -"
|
|
89
|
+
f" {filepath.with_suffix('.nb.html')}"
|
|
85
90
|
)
|
|
86
91
|
|
|
87
92
|
if registry is None:
|
|
@@ -120,7 +125,11 @@ def save_from_filepath_cli(
|
|
|
120
125
|
)
|
|
121
126
|
return "not-tracked-in-transform-registry"
|
|
122
127
|
else:
|
|
123
|
-
|
|
128
|
+
# TODO: build in the logic that queries for relative file paths
|
|
129
|
+
# we have in Context; add tests for multiple versions
|
|
130
|
+
transform = ln.Transform.filter(
|
|
131
|
+
key=filepath.name, is_latest=True
|
|
132
|
+
).one_or_none()
|
|
124
133
|
if transform is None:
|
|
125
134
|
transform = ln.Transform(
|
|
126
135
|
description=filepath.name,
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["flit_core >=3.2,<4"]
|
|
3
|
+
build-backend = "flit_core.buildapi"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lamin_cli"
|
|
7
|
+
authors = [{name = "Lamin Labs", email = "open-source@lamin.ai"}]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
dynamic = ["version", "description"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"rich-click>=1.7",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.urls]
|
|
15
|
+
Home = "https://github.com/laminlabs/lamin-cli"
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
lamin = "lamin_cli.__main__:main"
|
|
19
|
+
|
|
20
|
+
[tool.ruff]
|
|
21
|
+
src = ["src"]
|
|
22
|
+
line-length = 88
|
|
23
|
+
lint.select = [
|
|
24
|
+
"F", # Errors detected by Pyflakes
|
|
25
|
+
"E", # Error detected by Pycodestyle
|
|
26
|
+
"W", # Warning detected by Pycodestyle
|
|
27
|
+
"I", # isort
|
|
28
|
+
"D", # pydocstyle
|
|
29
|
+
"B", # flake8-bugbear
|
|
30
|
+
"TID", # flake8-tidy-imports
|
|
31
|
+
"C4", # flake8-comprehensions
|
|
32
|
+
"BLE", # flake8-blind-except
|
|
33
|
+
"UP", # pyupgrade
|
|
34
|
+
"RUF100", # Report unused noqa directives
|
|
35
|
+
"TCH", # Typing imports
|
|
36
|
+
"NPY", # Numpy specific rules
|
|
37
|
+
"PTH" # Use pathlib
|
|
38
|
+
]
|
|
39
|
+
lint.ignore = [
|
|
40
|
+
# Do not catch blind exception: `Exception`
|
|
41
|
+
"BLE001",
|
|
42
|
+
# Errors from function calls in argument defaults. These are fine when the result is immutable.
|
|
43
|
+
"B008",
|
|
44
|
+
# line too long -> we accept long comment lines; black gets rid of long code lines
|
|
45
|
+
"E501",
|
|
46
|
+
# Do not assign a lambda expression, use a def -> lambda expression assignments are convenient
|
|
47
|
+
"E731",
|
|
48
|
+
# allow I, O, l as variable names -> I is the identity matrix
|
|
49
|
+
"E741",
|
|
50
|
+
# Missing docstring in public module
|
|
51
|
+
"D100",
|
|
52
|
+
# undocumented-public-class
|
|
53
|
+
"D101",
|
|
54
|
+
# Missing docstring in public method
|
|
55
|
+
"D102",
|
|
56
|
+
# Missing docstring in public function
|
|
57
|
+
"D103",
|
|
58
|
+
# Missing docstring in public package
|
|
59
|
+
"D104",
|
|
60
|
+
# __magic__ methods are are often self-explanatory, allow missing docstrings
|
|
61
|
+
"D105",
|
|
62
|
+
# Missing docstring in public nested class
|
|
63
|
+
"D106",
|
|
64
|
+
# Missing docstring in __init__
|
|
65
|
+
"D107",
|
|
66
|
+
## Disable one in each pair of mutually incompatible rules
|
|
67
|
+
# We don’t want a blank line before a class docstring
|
|
68
|
+
"D203",
|
|
69
|
+
# 1 blank line required after class docstring
|
|
70
|
+
"D204",
|
|
71
|
+
# first line should end with a period [Bug: doesn't work with single-line docstrings]
|
|
72
|
+
# We want docstrings to start immediately after the opening triple quote
|
|
73
|
+
"D213",
|
|
74
|
+
# Section underline is over-indented ("{name}")
|
|
75
|
+
"D215",
|
|
76
|
+
# First line should end with a period
|
|
77
|
+
"D400",
|
|
78
|
+
# First line should be in imperative mood; try rephrasing
|
|
79
|
+
"D401",
|
|
80
|
+
# First word of the first line should be capitalized: {} -> {}
|
|
81
|
+
"D403",
|
|
82
|
+
# First word of the docstring should not be "This"
|
|
83
|
+
"D404",
|
|
84
|
+
# Section name should end with a newline ("{name}")
|
|
85
|
+
"D406",
|
|
86
|
+
# Missing dashed underline after section ("{name}")
|
|
87
|
+
"D407",
|
|
88
|
+
# Section underline should be in the line following the section's name ("{name}")
|
|
89
|
+
"D408",
|
|
90
|
+
# Section underline should match the length of its name ("{name}")
|
|
91
|
+
"D409",
|
|
92
|
+
# No blank lines allowed between a section header and its content ("{name}")
|
|
93
|
+
"D412",
|
|
94
|
+
# Missing blank line after last section ("{name}")
|
|
95
|
+
"D413",
|
|
96
|
+
# Missing argument description
|
|
97
|
+
"D417",
|
|
98
|
+
# Imports unused
|
|
99
|
+
"F401",
|
|
100
|
+
# camcelcase imported as lowercase
|
|
101
|
+
"N813",
|
|
102
|
+
# module import not at top level of file
|
|
103
|
+
"E402",
|
|
104
|
+
# open()` should be replaced by `Path.open()
|
|
105
|
+
"PTH123",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[tool.ruff.lint.pydocstyle]
|
|
109
|
+
convention = "google"
|
|
110
|
+
|
|
111
|
+
[tool.ruff.lint.per-file-ignores]
|
|
112
|
+
"docs/*" = ["I", "B018", "B017"]
|
|
113
|
+
"tests/*" = ["D"]
|
|
114
|
+
"*/__init__.py" = ["F401"]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
from lamin_cli._load import decompose_url
|
|
2
|
-
from pathlib import Path
|
|
3
1
|
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from lamin_cli._load import decompose_url
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
def test_decompose_url():
|
|
7
8
|
urls = [
|
|
8
|
-
"https://lamin.ai/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv",
|
|
9
|
-
"https://lamin.company.com/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv",
|
|
9
|
+
"https://lamin.ai/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv",
|
|
10
|
+
"https://lamin.company.com/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv",
|
|
10
11
|
]
|
|
11
12
|
for url in urls:
|
|
12
13
|
result = decompose_url(url)
|
|
@@ -23,7 +24,7 @@ def test_load_transform():
|
|
|
23
24
|
result = subprocess.run(
|
|
24
25
|
"lamin load"
|
|
25
26
|
" 'https://lamin.ai/laminlabs/lamin-dev/transform/VFYCIuaw2GsX0000'"
|
|
26
|
-
" --with-env",
|
|
27
|
+
" --with-env",
|
|
27
28
|
shell=True,
|
|
28
29
|
capture_output=True,
|
|
29
30
|
)
|
|
@@ -62,7 +63,7 @@ def test_load_transform():
|
|
|
62
63
|
def test_load_artifact():
|
|
63
64
|
result = subprocess.run(
|
|
64
65
|
"lamin load"
|
|
65
|
-
" 'https://lamin.ai/laminlabs/lamin-site-assets/artifact/e2G7k9EVul4JbfsEYAy5'",
|
|
66
|
+
" 'https://lamin.ai/laminlabs/lamin-site-assets/artifact/e2G7k9EVul4JbfsEYAy5'",
|
|
66
67
|
shell=True,
|
|
67
68
|
capture_output=True,
|
|
68
69
|
)
|
|
@@ -112,6 +112,6 @@ def test_r_track_pattern():
|
|
|
112
112
|
for suffix in suffixes:
|
|
113
113
|
for content, expected_uid in valid_cases:
|
|
114
114
|
uid = parse_uid_from_code(content, suffix)
|
|
115
|
-
assert (
|
|
116
|
-
|
|
117
|
-
)
|
|
115
|
+
assert uid == expected_uid, (
|
|
116
|
+
f"Failed for valid content with {suffix}: {content}"
|
|
117
|
+
)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import subprocess
|
|
2
|
-
import lamindb_setup as ln_setup
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
|
|
4
|
+
import lamindb_setup as ln_setup
|
|
5
|
+
|
|
5
6
|
test_file = Path(__file__).parent.parent.resolve() / ".gitignore"
|
|
6
7
|
|
|
7
8
|
|
|
@@ -43,7 +44,7 @@ def test_save_file():
|
|
|
43
44
|
)
|
|
44
45
|
print(result.stdout.decode())
|
|
45
46
|
print(result.stderr.decode())
|
|
46
|
-
assert "
|
|
47
|
+
assert "found artifact with same hash" in result.stdout.decode()
|
|
47
48
|
assert "key='mytest'" in result.stdout.decode()
|
|
48
49
|
assert "storage path:" in result.stdout.decode()
|
|
49
50
|
assert result.returncode == 0
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import os
|
|
2
3
|
import subprocess
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import lamindb as ln
|
|
4
7
|
import nbproject_test
|
|
5
8
|
import pytest
|
|
6
|
-
from nbproject.dev import read_notebook, write_notebook
|
|
7
9
|
from nbclient.exceptions import CellExecutionError
|
|
8
|
-
import
|
|
9
|
-
import lamindb as ln
|
|
10
|
+
from nbproject.dev import read_notebook, write_notebook
|
|
10
11
|
|
|
11
12
|
notebook_dir = "./sub/lamin-cli/tests/notebooks/"
|
|
12
13
|
|
|
@@ -167,13 +168,13 @@ print("my consecutive cell")
|
|
|
167
168
|
# get the the source code via command line
|
|
168
169
|
result = subprocess.run(
|
|
169
170
|
"yes | lamin load"
|
|
170
|
-
f" https://lamin.ai/{ln.setup.settings.user.handle}/laminci-unit-tests/transform/hlsFXswrJjtt0000",
|
|
171
|
+
f" https://lamin.ai/{ln.setup.settings.user.handle}/laminci-unit-tests/transform/hlsFXswrJjtt0000",
|
|
171
172
|
shell=True,
|
|
172
173
|
capture_output=True,
|
|
173
174
|
)
|
|
174
175
|
# print(result.stderr.decode())
|
|
175
176
|
assert Path("./with-title-and-initialized-consecutive.ipynb").exists()
|
|
176
|
-
with open("./with-title-and-initialized-consecutive.ipynb"
|
|
177
|
+
with open("./with-title-and-initialized-consecutive.ipynb") as f:
|
|
177
178
|
json_notebook = json.load(f)
|
|
178
179
|
print(json_notebook["cells"][0])
|
|
179
180
|
assert json_notebook["cells"][0]["source"] == ["# My test notebook (consecutive)"]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
import subprocess
|
|
3
1
|
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
4
5
|
import lamindb as ln
|
|
5
6
|
from lamindb_setup import settings
|
|
6
7
|
|
|
7
|
-
|
|
8
8
|
scripts_dir = Path(__file__).parent.resolve() / "scripts"
|
|
9
9
|
|
|
10
10
|
|
|
@@ -45,17 +45,38 @@ def test_run_save_cache_with_git_and_uid():
|
|
|
45
45
|
shell=True,
|
|
46
46
|
capture_output=True,
|
|
47
47
|
)
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
print(result.stdout.decode())
|
|
49
|
+
print(result.stderr.decode())
|
|
50
50
|
assert result.returncode == 0
|
|
51
51
|
assert "created Transform" in result.stdout.decode()
|
|
52
52
|
assert "m5uCHTTp" in result.stdout.decode()
|
|
53
53
|
assert "started new Run" in result.stdout.decode()
|
|
54
54
|
|
|
55
55
|
transform = ln.Transform.get("m5uCHTTpJnjQ")
|
|
56
|
-
assert transform.hash == "
|
|
56
|
+
assert transform.hash == "VC1oTPcaVSrzNrXUT9p4qw"
|
|
57
57
|
assert transform.latest_run.environment.path.exists()
|
|
58
58
|
|
|
59
|
+
assert (
|
|
60
|
+
transform.source_code
|
|
61
|
+
== """import lamindb as ln
|
|
62
|
+
|
|
63
|
+
ln.settings.sync_git_repo = "https://github.com/laminlabs/lamin-cli"
|
|
64
|
+
ln.context.description = "My good script"
|
|
65
|
+
ln.track("m5uCHTTpJnjQ0000")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
# we're using new_run here to mock the notebook situation
|
|
70
|
+
# and cover the look up of an existing run in the tests
|
|
71
|
+
# new_run = True is trivial
|
|
72
|
+
ln.track(new_run=False)
|
|
73
|
+
|
|
74
|
+
print("hello!")
|
|
75
|
+
|
|
76
|
+
ln.finish()
|
|
77
|
+
"""
|
|
78
|
+
)
|
|
79
|
+
|
|
59
80
|
# you can rerun the same script
|
|
60
81
|
result = subprocess.run(
|
|
61
82
|
f"python {filepath}",
|
|
@@ -160,7 +181,7 @@ def test_run_save_cache_with_git_and_uid():
|
|
|
160
181
|
# try to get the the source code via command line
|
|
161
182
|
result = subprocess.run(
|
|
162
183
|
"yes | lamin load"
|
|
163
|
-
f" https://lamin.ai/{settings.user.handle}/laminci-unit-tests/transform/m5uCHTTpJnjQ0000",
|
|
184
|
+
f" https://lamin.ai/{settings.user.handle}/laminci-unit-tests/transform/m5uCHTTpJnjQ0000",
|
|
164
185
|
shell=True,
|
|
165
186
|
capture_output=True,
|
|
166
187
|
)
|
|
@@ -168,7 +189,7 @@ def test_run_save_cache_with_git_and_uid():
|
|
|
168
189
|
assert result.returncode == 0
|
|
169
190
|
|
|
170
191
|
result = subprocess.run(
|
|
171
|
-
f"yes | lamin load transform --key {filepath.name}",
|
|
192
|
+
f"yes | lamin load transform --key {filepath.name}",
|
|
172
193
|
shell=True,
|
|
173
194
|
capture_output=True,
|
|
174
195
|
)
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
repos:
|
|
2
|
-
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
-
rev: v3.2.0
|
|
4
|
-
hooks:
|
|
5
|
-
- id: trailing-whitespace
|
|
6
|
-
- id: end-of-file-fixer
|
|
7
|
-
exclude: |
|
|
8
|
-
(?x)(
|
|
9
|
-
.github/workflows/latest-changes.jinja2
|
|
10
|
-
)
|
|
11
|
-
- id: check-yaml
|
|
12
|
-
- id: check-added-large-files
|
|
13
|
-
- repo: https://github.com/psf/black
|
|
14
|
-
rev: 22.6.0
|
|
15
|
-
hooks:
|
|
16
|
-
- id: black-jupyter
|
|
17
|
-
- repo: https://github.com/pycqa/flake8
|
|
18
|
-
rev: 4.0.1
|
|
19
|
-
hooks:
|
|
20
|
-
- id: flake8
|
|
21
|
-
additional_dependencies:
|
|
22
|
-
- flake8-black==0.3.3
|
|
23
|
-
- flake8-typing-imports==1.10.0
|
|
24
|
-
language_version: python3
|
|
25
|
-
args:
|
|
26
|
-
- --max-line-length=120
|
|
27
|
-
- --ignore=E203,W503,BLK100,TYP001
|
|
28
|
-
exclude: |
|
|
29
|
-
(?x)(
|
|
30
|
-
__init__.py
|
|
31
|
-
)
|
|
32
|
-
- repo: https://github.com/pre-commit/mirrors-prettier
|
|
33
|
-
rev: v2.6.2
|
|
34
|
-
hooks:
|
|
35
|
-
- id: prettier
|
|
36
|
-
- repo: https://github.com/kynan/nbstripout
|
|
37
|
-
rev: 0.3.9
|
|
38
|
-
hooks:
|
|
39
|
-
- id: nbstripout
|
|
40
|
-
exclude: |
|
|
41
|
-
(?x)(
|
|
42
|
-
docs/examples/|
|
|
43
|
-
docs/notes/|
|
|
44
|
-
tests
|
|
45
|
-
)
|
|
46
|
-
- repo: https://github.com/Lucas-C/pre-commit-hooks
|
|
47
|
-
rev: v1.1.9
|
|
48
|
-
hooks:
|
|
49
|
-
- id: forbid-crlf
|
|
50
|
-
- id: remove-crlf
|
|
51
|
-
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
52
|
-
rev: v0.940
|
|
53
|
-
hooks:
|
|
54
|
-
- id: mypy
|
|
55
|
-
exclude: |
|
|
56
|
-
(?x)(
|
|
57
|
-
tests/hub-local/conftest.py
|
|
58
|
-
)
|
|
59
|
-
- repo: https://github.com/pycqa/pydocstyle
|
|
60
|
-
rev: 6.1.1
|
|
61
|
-
hooks:
|
|
62
|
-
- id: pydocstyle
|
|
63
|
-
args: # google style + __init__, see http://www.pydocstyle.org/en/stable/error_codes.html
|
|
64
|
-
- --ignore=D100,D101,D102,D103,D104,D106,D107,D203,D204,D213,D215,D400,D401,D403,D404,D406,D407,D408,D409,D412,D413
|
lamin_cli-1.0.6/pyproject.toml
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["flit_core >=3.2,<4"]
|
|
3
|
-
build-backend = "flit_core.buildapi"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "lamin_cli"
|
|
7
|
-
authors = [{name = "Lamin Labs", email = "open-source@lamin.ai"}]
|
|
8
|
-
readme = "README.md"
|
|
9
|
-
dynamic = ["version", "description"]
|
|
10
|
-
dependencies = [
|
|
11
|
-
"rich-click>=1.7",
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
[project.urls]
|
|
15
|
-
Home = "https://github.com/laminlabs/lamin-cli"
|
|
16
|
-
|
|
17
|
-
[project.scripts]
|
|
18
|
-
lamin = "lamin_cli.__main__:main"
|
|
19
|
-
|
|
20
|
-
[tool.black]
|
|
21
|
-
preview = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/notebooks/with-title-and-initialized-consecutive.ipynb
RENAMED
|
File without changes
|
{lamin_cli-1.0.6 → lamin_cli-1.1.0}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|