lamin_cli 1.2.0__py2.py3-none-any.whl → 1.3.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 +1 -1
- lamin_cli/__main__.py +69 -24
- lamin_cli/_save.py +29 -2
- lamin_cli/compute/__init__.py +0 -0
- lamin_cli/compute/modal.py +175 -0
- {lamin_cli-1.2.0.dist-info → lamin_cli-1.3.0.dist-info}/METADATA +1 -1
- lamin_cli-1.3.0.dist-info/RECORD +14 -0
- lamin_cli-1.2.0.dist-info/RECORD +0 -12
- {lamin_cli-1.2.0.dist-info → lamin_cli-1.3.0.dist-info}/LICENSE +0 -0
- {lamin_cli-1.2.0.dist-info → lamin_cli-1.3.0.dist-info}/WHEEL +0 -0
- {lamin_cli-1.2.0.dist-info → lamin_cli-1.3.0.dist-info}/entry_points.txt +0 -0
lamin_cli/__init__.py
CHANGED
lamin_cli/__main__.py
CHANGED
|
@@ -9,6 +9,13 @@ from functools import wraps
|
|
|
9
9
|
from importlib.metadata import PackageNotFoundError, version
|
|
10
10
|
from typing import TYPE_CHECKING
|
|
11
11
|
|
|
12
|
+
from lamindb_setup._init_instance import (
|
|
13
|
+
DOC_DB,
|
|
14
|
+
DOC_INSTANCE_NAME,
|
|
15
|
+
DOC_MODULES,
|
|
16
|
+
DOC_STORAGE_ARG,
|
|
17
|
+
)
|
|
18
|
+
|
|
12
19
|
if TYPE_CHECKING:
|
|
13
20
|
from collections.abc import Mapping
|
|
14
21
|
|
|
@@ -41,12 +48,7 @@ else:
|
|
|
41
48
|
"lamin": [
|
|
42
49
|
{
|
|
43
50
|
"name": "Connect to an instance",
|
|
44
|
-
"commands": [
|
|
45
|
-
"connect",
|
|
46
|
-
"disconnect",
|
|
47
|
-
"info",
|
|
48
|
-
"init",
|
|
49
|
-
],
|
|
51
|
+
"commands": ["connect", "disconnect", "info", "init", "run"],
|
|
50
52
|
},
|
|
51
53
|
{
|
|
52
54
|
"name": "Read & write data",
|
|
@@ -155,24 +157,20 @@ def schema_to_modules_callback(ctx, param, value):
|
|
|
155
157
|
|
|
156
158
|
# fmt: off
|
|
157
159
|
@main.command()
|
|
158
|
-
@click.option("--storage", type=str,
|
|
159
|
-
@click.option("--
|
|
160
|
-
@click.option("--
|
|
161
|
-
@click.option("--
|
|
162
|
-
@click.option("--schema", type=str, default=None, help="[DEPRECATED] Use --modules instead.", callback=schema_to_modules_callback)
|
|
160
|
+
@click.option("--storage", type=str, default = ".", help=DOC_STORAGE_ARG)
|
|
161
|
+
@click.option("--name", type=str, default=None, help=DOC_INSTANCE_NAME)
|
|
162
|
+
@click.option("--db", type=str, default=None, help=DOC_DB)
|
|
163
|
+
@click.option("--modules", type=str, default=None, help=DOC_MODULES)
|
|
163
164
|
# fmt: on
|
|
164
165
|
def init(
|
|
165
166
|
storage: str,
|
|
167
|
+
name: str | None,
|
|
166
168
|
db: str | None,
|
|
167
169
|
modules: str | None,
|
|
168
|
-
name: str | None,
|
|
169
|
-
schema: str | None,
|
|
170
170
|
):
|
|
171
171
|
"""Init an instance."""
|
|
172
172
|
from lamindb_setup._init_instance import init as init_
|
|
173
173
|
|
|
174
|
-
modules = modules if modules is not None else schema
|
|
175
|
-
|
|
176
174
|
return init_(storage=storage, db=db, modules=modules, name=name)
|
|
177
175
|
|
|
178
176
|
|
|
@@ -188,6 +186,8 @@ def connect(instance: str):
|
|
|
188
186
|
`lamin connect` switches
|
|
189
187
|
{attr}`~lamindb.setup.core.SetupSettings.auto_connect` to `True` so that you
|
|
190
188
|
auto-connect in a Python session upon importing `lamindb`.
|
|
189
|
+
|
|
190
|
+
For manually connecting in a Python session, use {func}`~lamindb.connect`.
|
|
191
191
|
"""
|
|
192
192
|
from lamindb_setup import connect as connect_
|
|
193
193
|
from lamindb_setup import settings as settings_
|
|
@@ -307,30 +307,75 @@ def get(entity: str, uid: str | None = None, key: str | None = None):
|
|
|
307
307
|
|
|
308
308
|
|
|
309
309
|
@main.command()
|
|
310
|
-
@click.argument("
|
|
310
|
+
@click.argument("path", type=click.Path(exists=True, dir_okay=True, file_okay=True))
|
|
311
311
|
@click.option("--key", type=str, default=None)
|
|
312
312
|
@click.option("--description", type=str, default=None)
|
|
313
|
+
@click.option("--stem-uid", type=str, default=None)
|
|
313
314
|
@click.option("--registry", type=str, default=None)
|
|
314
|
-
def save(
|
|
315
|
+
def save(path: str, key: str, description: str, stem_uid: str, registry: str):
|
|
315
316
|
"""Save a file or folder.
|
|
316
317
|
|
|
317
|
-
Defaults to saving `.py
|
|
318
|
-
other file types and folders as {class}`~lamindb.Artifact`.
|
|
319
|
-
|
|
320
|
-
You can save a `.py` or `.ipynb` file as an {class}`~lamindb.Artifact` by
|
|
321
|
-
passing `--registry artifact`.
|
|
318
|
+
Defaults to saving `.py`, `.ipynb`, `.R`, `.Rmd`, and `.qmd` as {class}`~lamindb.Transform` and
|
|
319
|
+
other file types and folders as {class}`~lamindb.Artifact`. You can save a `.py` or `.ipynb` file as
|
|
320
|
+
an {class}`~lamindb.Artifact` by passing `--registry artifact`.
|
|
322
321
|
"""
|
|
323
322
|
from lamin_cli._save import save_from_filepath_cli
|
|
324
323
|
|
|
325
|
-
if save_from_filepath_cli(
|
|
324
|
+
if save_from_filepath_cli(path, key, description, stem_uid, registry) is not None:
|
|
326
325
|
sys.exit(1)
|
|
327
326
|
|
|
328
327
|
|
|
328
|
+
@main.command()
|
|
329
|
+
@click.argument("filepath", type=str)
|
|
330
|
+
@click.option("--project", type=str, default=None, help="A valid project name or uid. When running on Modal, creates an app with the same name.", required=True)
|
|
331
|
+
@click.option("--image-url", type=str, default=None, help="A URL to the base docker image to use.")
|
|
332
|
+
@click.option("--packages", type=str, default="lamindb", help="A comma-separated list of additional packages to install.")
|
|
333
|
+
@click.option("--cpu", type=float, default=None, help="Configuration for the CPU.")
|
|
334
|
+
@click.option("--gpu", type=str, default=None, help="The type of GPU to use (only compatible with cuda images).")
|
|
335
|
+
def run(filepath: str, project: str, image_url: str, packages: str, cpu: int, gpu: str | None):
|
|
336
|
+
"""Run a compute job in the cloud.
|
|
337
|
+
|
|
338
|
+
This is an EXPERIMENTAL feature that enables to run a script on Modal.
|
|
339
|
+
|
|
340
|
+
Example: Given a valid project name "my_project".
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
lamin run my_script.py --project my_project
|
|
344
|
+
```
|
|
345
|
+
"""
|
|
346
|
+
import shutil
|
|
347
|
+
from pathlib import Path
|
|
348
|
+
|
|
349
|
+
from lamin_cli.compute.modal import Runner
|
|
350
|
+
|
|
351
|
+
default_mount_dir = Path('./modal_mount_dir')
|
|
352
|
+
if not default_mount_dir.is_dir():
|
|
353
|
+
default_mount_dir.mkdir(parents=True, exist_ok=True)
|
|
354
|
+
|
|
355
|
+
shutil.copy(filepath, default_mount_dir)
|
|
356
|
+
|
|
357
|
+
filepath_in_mount_dir = Path(default_mount_dir) / Path(filepath).name
|
|
358
|
+
|
|
359
|
+
package_list = []
|
|
360
|
+
if packages:
|
|
361
|
+
package_list = [package.strip() for package in packages.split(',')]
|
|
362
|
+
|
|
363
|
+
runner = Runner(
|
|
364
|
+
local_mount_dir=default_mount_dir,
|
|
365
|
+
app_name=project,
|
|
366
|
+
packages=package_list,
|
|
367
|
+
image_url=image_url,
|
|
368
|
+
cpu=cpu,
|
|
369
|
+
gpu=gpu
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
runner.run(filepath_in_mount_dir)
|
|
373
|
+
|
|
374
|
+
|
|
329
375
|
main.add_command(settings)
|
|
330
376
|
main.add_command(cache)
|
|
331
377
|
main.add_command(migrate)
|
|
332
378
|
|
|
333
|
-
|
|
334
379
|
# https://stackoverflow.com/questions/57810659/automatically-generate-all-help-documentation-for-click-commands
|
|
335
380
|
# https://claude.ai/chat/73c28487-bec3-4073-8110-50d1a2dd6b84
|
|
336
381
|
def _generate_help():
|
lamin_cli/_save.py
CHANGED
|
@@ -46,6 +46,7 @@ def save_from_filepath_cli(
|
|
|
46
46
|
filepath: str | Path,
|
|
47
47
|
key: str | None,
|
|
48
48
|
description: str | None,
|
|
49
|
+
stem_uid: str | None,
|
|
49
50
|
registry: str | None,
|
|
50
51
|
) -> str | None:
|
|
51
52
|
import lamindb_setup as ln_setup
|
|
@@ -99,10 +100,21 @@ def save_from_filepath_cli(
|
|
|
99
100
|
|
|
100
101
|
if registry == "artifact":
|
|
101
102
|
ln.settings.creation.artifact_silence_missing_run_warning = True
|
|
102
|
-
|
|
103
|
+
revises = None
|
|
104
|
+
if stem_uid is not None:
|
|
105
|
+
revises = (
|
|
106
|
+
ln.Artifact.filter(uid__startswith=stem_uid)
|
|
107
|
+
.order_by("-created_at")
|
|
108
|
+
.first()
|
|
109
|
+
)
|
|
110
|
+
if revises is None:
|
|
111
|
+
raise ln.errors.InvalidArgument("The stem uid is not found.")
|
|
112
|
+
elif key is None and description is None:
|
|
103
113
|
logger.error("Please pass a key or description via --key or --description")
|
|
104
114
|
return "missing-key-or-description"
|
|
105
|
-
artifact = ln.Artifact(
|
|
115
|
+
artifact = ln.Artifact(
|
|
116
|
+
filepath, key=key, description=description, revises=revises
|
|
117
|
+
).save()
|
|
106
118
|
logger.important(f"saved: {artifact}")
|
|
107
119
|
logger.important(f"storage path: {artifact.path}")
|
|
108
120
|
if ln_setup.settings.storage.type == "s3":
|
|
@@ -125,6 +137,15 @@ def save_from_filepath_cli(
|
|
|
125
137
|
)
|
|
126
138
|
return "not-tracked-in-transform-registry"
|
|
127
139
|
else:
|
|
140
|
+
revises = None
|
|
141
|
+
if stem_uid is not None:
|
|
142
|
+
revises = (
|
|
143
|
+
ln.Transform.filter(uid__startswith=stem_uid)
|
|
144
|
+
.order_by("-created_at")
|
|
145
|
+
.first()
|
|
146
|
+
)
|
|
147
|
+
if revises is None:
|
|
148
|
+
raise ln.errors.InvalidArgument("The stem uid is not found.")
|
|
128
149
|
# TODO: build in the logic that queries for relative file paths
|
|
129
150
|
# we have in Context; add tests for multiple versions
|
|
130
151
|
transform = ln.Transform.filter(
|
|
@@ -135,6 +156,7 @@ def save_from_filepath_cli(
|
|
|
135
156
|
description=filepath.name,
|
|
136
157
|
key=filepath.name,
|
|
137
158
|
type="script" if filepath.suffix in {".R", ".py"} else "notebook",
|
|
159
|
+
revises=revises,
|
|
138
160
|
).save()
|
|
139
161
|
logger.important(f"created Transform('{transform.uid}')")
|
|
140
162
|
# latest run of this transform by user
|
|
@@ -147,6 +169,11 @@ def save_from_filepath_cli(
|
|
|
147
169
|
)
|
|
148
170
|
if response != "y":
|
|
149
171
|
return "aborted-save-notebook-created-by-different-user"
|
|
172
|
+
if run is None and transform.key.endswith(".ipynb"):
|
|
173
|
+
run = ln.Run(transform=transform).save()
|
|
174
|
+
logger.important(
|
|
175
|
+
f"found no run, creating Run('{run.uid}') to display the html"
|
|
176
|
+
)
|
|
150
177
|
return_code = save_context_core(
|
|
151
178
|
run=run,
|
|
152
179
|
transform=transform,
|
|
File without changes
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import lamindb_setup as ln_setup
|
|
8
|
+
import modal
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_script(path: Path) -> dict:
|
|
12
|
+
"""Takes a path to a script for running it as a function through Modal."""
|
|
13
|
+
result = {"success": False, "output": "", "error": ""}
|
|
14
|
+
|
|
15
|
+
def stream_output(stream, capture_list):
|
|
16
|
+
"""Read from stream line by line and print in real-time while also capturing to a list."""
|
|
17
|
+
for line in iter(stream.readline, ""):
|
|
18
|
+
print(line, end="") # Print in real-time
|
|
19
|
+
capture_list.append(line)
|
|
20
|
+
stream.close()
|
|
21
|
+
|
|
22
|
+
if not path.exists():
|
|
23
|
+
raise FileNotFoundError(f"Script file not found: {path}")
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Run the script using subprocess
|
|
27
|
+
process = subprocess.Popen(
|
|
28
|
+
[sys.executable, path.as_posix()],
|
|
29
|
+
stdout=subprocess.PIPE,
|
|
30
|
+
stderr=subprocess.PIPE,
|
|
31
|
+
text=True,
|
|
32
|
+
bufsize=1, # Line buffered
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Capture output and error while streaming stdout in real-time
|
|
36
|
+
stdout_lines: list[str] = []
|
|
37
|
+
stderr_lines: list[str] = []
|
|
38
|
+
|
|
39
|
+
# Create threads to handle stdout and stderr streams
|
|
40
|
+
stdout_thread = threading.Thread(
|
|
41
|
+
target=stream_output, args=(process.stdout, stdout_lines)
|
|
42
|
+
)
|
|
43
|
+
stderr_thread = threading.Thread(
|
|
44
|
+
target=stream_output, args=(process.stderr, stderr_lines)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Set as daemon threads so they exit when the main program exits
|
|
48
|
+
stdout_thread.daemon = True
|
|
49
|
+
stderr_thread.daemon = True
|
|
50
|
+
|
|
51
|
+
# Start the threads
|
|
52
|
+
stdout_thread.start()
|
|
53
|
+
stderr_thread.start()
|
|
54
|
+
|
|
55
|
+
# Wait for the process to complete
|
|
56
|
+
return_code = process.wait()
|
|
57
|
+
|
|
58
|
+
# Wait for the threads to finish
|
|
59
|
+
stdout_thread.join()
|
|
60
|
+
stderr_thread.join()
|
|
61
|
+
|
|
62
|
+
# Join the captured output
|
|
63
|
+
stdout_output = "".join(stdout_lines)
|
|
64
|
+
stderr_output = "".join(stderr_lines)
|
|
65
|
+
|
|
66
|
+
# Check return code
|
|
67
|
+
if return_code == 0:
|
|
68
|
+
result["success"] = True
|
|
69
|
+
result["output"] = stdout_output
|
|
70
|
+
else:
|
|
71
|
+
result["error"] = stderr_output
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
import traceback
|
|
75
|
+
|
|
76
|
+
result["error"] = str(e) + "\n" + traceback.format_exc()
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Runner:
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
app_name: str,
|
|
84
|
+
local_mount_dir: str | Path = "./scripts",
|
|
85
|
+
remote_mount_dir: str | Path = "/scripts",
|
|
86
|
+
image_url: str | None = None,
|
|
87
|
+
packages: list[str] | None = None,
|
|
88
|
+
cpu: float | None = None,
|
|
89
|
+
gpu: str | None = None,
|
|
90
|
+
):
|
|
91
|
+
self.app_name = app_name # we use the LaminDB project name as the app name
|
|
92
|
+
self.app = self.create_modal_app(app_name)
|
|
93
|
+
|
|
94
|
+
self.local_mount_dir = local_mount_dir
|
|
95
|
+
self.remote_mount_dir = remote_mount_dir
|
|
96
|
+
|
|
97
|
+
self.image = self.create_modal_image(
|
|
98
|
+
local_dir=local_mount_dir, packages=packages, image_url=image_url
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
local_secrets = self._configure_local_secrets()
|
|
102
|
+
|
|
103
|
+
self.modal_function = self.app.function(
|
|
104
|
+
image=self.image, cpu=cpu, gpu=gpu, secrets=[local_secrets]
|
|
105
|
+
)(run_script)
|
|
106
|
+
|
|
107
|
+
def run(self, script_local_path: Path) -> None:
|
|
108
|
+
script_remote_path = self.local_to_remote_path(str(script_local_path))
|
|
109
|
+
with modal.enable_output(show_progress=True): # Prints out modal logs
|
|
110
|
+
with self.app.run():
|
|
111
|
+
self.modal_function.remote(Path(script_remote_path))
|
|
112
|
+
|
|
113
|
+
def create_modal_app(self, app_name: str) -> modal.App:
|
|
114
|
+
app = modal.App(app_name)
|
|
115
|
+
return app
|
|
116
|
+
|
|
117
|
+
def local_to_remote_path(self, local_path: str | Path) -> str:
|
|
118
|
+
local_path = Path(local_path).absolute()
|
|
119
|
+
local_mount_dir = Path(self.local_mount_dir).absolute()
|
|
120
|
+
remote_mount_dir = Path(self.remote_mount_dir)
|
|
121
|
+
|
|
122
|
+
# Check if local_path is inside local_mount_dir
|
|
123
|
+
try:
|
|
124
|
+
# This will raise ValueError if local_path is not relative to local_mount_dir
|
|
125
|
+
relative_path = local_path.relative_to(local_mount_dir)
|
|
126
|
+
except ValueError as err:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f"Local path '{local_path}' is not inside the mount directory '{local_mount_dir}'"
|
|
129
|
+
) from err
|
|
130
|
+
|
|
131
|
+
# Join remote_mount_dir with the relative path
|
|
132
|
+
remote_path = remote_mount_dir / relative_path
|
|
133
|
+
|
|
134
|
+
# Return as string with normalized separators
|
|
135
|
+
return remote_path.as_posix()
|
|
136
|
+
|
|
137
|
+
def _configure_local_secrets(self) -> dict:
|
|
138
|
+
if ln_setup.settings.user.api_key is None:
|
|
139
|
+
raise ValueError("Please authenticate via: lamin login")
|
|
140
|
+
|
|
141
|
+
all_env_variables = {
|
|
142
|
+
"LAMIN_API_KEY": ln_setup.settings.user.api_key,
|
|
143
|
+
"LAMIN_CURRENT_PROJECT": self.app_name,
|
|
144
|
+
"LAMIN_CURRENT_INSTANCE": ln_setup.settings.instance.slug,
|
|
145
|
+
}
|
|
146
|
+
local_secrets = modal.Secret.from_dict(all_env_variables)
|
|
147
|
+
return local_secrets
|
|
148
|
+
|
|
149
|
+
def create_modal_image(
|
|
150
|
+
self,
|
|
151
|
+
python_version: str = "3.12",
|
|
152
|
+
packages: list[str] | None = None,
|
|
153
|
+
local_dir: str | Path = "./scripts",
|
|
154
|
+
remote_dir: str = "/scripts/",
|
|
155
|
+
image_url: str | None = None,
|
|
156
|
+
env_variables: dict | None = None,
|
|
157
|
+
) -> modal.Image:
|
|
158
|
+
if env_variables is None:
|
|
159
|
+
env_variables = {}
|
|
160
|
+
if packages is None:
|
|
161
|
+
packages = ["lamindb"]
|
|
162
|
+
else:
|
|
163
|
+
packages.append("lamindb") # Append lamindb to the list of packages
|
|
164
|
+
|
|
165
|
+
if image_url is None:
|
|
166
|
+
image = modal.Image.debian_slim(python_version=python_version)
|
|
167
|
+
else:
|
|
168
|
+
image = modal.Image.from_registry(image_url, add_python=python_version)
|
|
169
|
+
return (
|
|
170
|
+
image.pip_install(packages)
|
|
171
|
+
.env(env_variables)
|
|
172
|
+
.add_local_python_source("lamindb", "lamindb_setup", copy=True)
|
|
173
|
+
.run_commands("lamin settings set auto-connect true")
|
|
174
|
+
.add_local_dir(local_dir, remote_dir)
|
|
175
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
lamin_cli/__init__.py,sha256=M6lfPWq9Iw7_uUcLoHjPp1SVuvhsIU0NkQQDt-EYpIc,40
|
|
2
|
+
lamin_cli/__main__.py,sha256=2CZdjlQK0nHrMyDpNvKn7iQ34Owlbwhl4mmavc5yXgc,12392
|
|
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=JQQxby2PP6SfUb1FZhnwNnp0fkBqTj-UnbGql4oPt8g,6948
|
|
7
|
+
lamin_cli/_settings.py,sha256=O2tecCf5EIZu98ima4DTJujo4KuywckOLgw8c-Ke3dY,1142
|
|
8
|
+
lamin_cli/compute/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
lamin_cli/compute/modal.py,sha256=QnR7GyyvWWWkLnou95HxS9xxSQfw1k-SiefM_qRVnU0,6010
|
|
10
|
+
lamin_cli-1.3.0.dist-info/entry_points.txt,sha256=Qms85i9cZPlu-U7RnVZhFsF7vJ9gaLZUFkCjcGcXTpg,49
|
|
11
|
+
lamin_cli-1.3.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
12
|
+
lamin_cli-1.3.0.dist-info/WHEEL,sha256=ssQ84EZ5gH1pCOujd3iW7HClo_O_aDaClUbX4B8bjKY,100
|
|
13
|
+
lamin_cli-1.3.0.dist-info/METADATA,sha256=m6_U_cQGfZ-2HjU-u_1frMM-Q6ATjQCObBkiebqNzMs,337
|
|
14
|
+
lamin_cli-1.3.0.dist-info/RECORD,,
|
lamin_cli-1.2.0.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|