lamin_cli 0.17.3__py2.py3-none-any.whl → 0.17.4__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.17.3"
3
+ __version__ = "0.17.4"
lamin_cli/__main__.py CHANGED
@@ -1,303 +1,303 @@
1
- from __future__ import annotations
2
- import os
3
- import sys
4
- from collections import OrderedDict
5
- import inspect
6
- from importlib.metadata import PackageNotFoundError, version
7
- from typing import Optional, Mapping
8
- from functools import wraps
9
-
10
- # https://github.com/ewels/rich-click/issues/19
11
- # Otherwise rich-click takes over the formatting.
12
- if os.environ.get("NO_RICH"):
13
- import click as click
14
-
15
- class OrderedGroup(click.Group):
16
- """Overwrites list_commands to return commands in order of definition."""
17
-
18
- def __init__(
19
- self,
20
- name: Optional[str] = None,
21
- commands: Optional[Mapping[str, click.Command]] = None,
22
- **kwargs,
23
- ):
24
- super(OrderedGroup, self).__init__(name, commands, **kwargs)
25
- self.commands = commands or OrderedDict()
26
-
27
- def list_commands(self, ctx: click.Context) -> Mapping[str, click.Command]:
28
- return self.commands
29
-
30
- lamin_group_decorator = click.group(cls=OrderedGroup)
31
-
32
- else:
33
- import rich_click as click
34
-
35
- COMMAND_GROUPS = {
36
- "lamin": [
37
- {
38
- "name": "Main commands",
39
- "commands": [
40
- "login",
41
- "init",
42
- "load",
43
- "info",
44
- "delete",
45
- ],
46
- },
47
- {
48
- "name": "Data commands",
49
- "commands": ["get", "save"],
50
- },
51
- {
52
- "name": "Configuration commands",
53
- "commands": ["cache", "set"],
54
- },
55
- {
56
- "name": "Schema migration",
57
- "commands": ["migrate"],
58
- },
59
- ]
60
- }
61
-
62
- def lamin_group_decorator(f):
63
- @click.rich_config(
64
- help_config=click.RichHelpConfiguration(
65
- command_groups=COMMAND_GROUPS,
66
- style_commands_table_column_width_ratio=(1, 13),
67
- )
68
- )
69
- @click.group()
70
- @wraps(f)
71
- def wrapper(*args, **kwargs):
72
- return f(*args, **kwargs)
73
-
74
- return wrapper
75
-
76
-
77
- from click import Command, Context
78
- from lamindb_setup._silence_loggers import silence_loggers
79
-
80
- from lamin_cli._cache import cache
81
- from lamin_cli._migration import migrate
82
-
83
- try:
84
- lamindb_version = version("lamindb")
85
- except PackageNotFoundError:
86
- lamindb_version = "lamindb installation not found"
87
-
88
-
89
- @lamin_group_decorator
90
- @click.version_option(version=lamindb_version, prog_name="lamindb")
91
- def main():
92
- """Configure LaminDB and perform simple actions."""
93
- silence_loggers()
94
-
95
-
96
- @main.command()
97
- @click.argument("user", type=str, default=None, required=False)
98
- @click.option("--key", type=str, default=None, help="The API key.")
99
- @click.option("--logout", is_flag=True, help="Logout instead of logging in.")
100
- def login(user: str, key: Optional[str], logout: bool = False):
101
- """Log into LaminHub.
102
-
103
- Upon logging in the first time, you need to pass your API key via:
104
-
105
- ```
106
- lamin login myemail@acme.com --key YOUR_API_KEY
107
- ```
108
-
109
- You'll find your API key on LaminHub in the top right corner under "Settings".
110
-
111
- After this, you can either use `lamin login myhandle` or `lamin login myemail@acme.com`
112
-
113
- You can also call this without arguments:
114
-
115
- ```
116
- lamin login
117
- ```
118
-
119
- You will be prompted for your Beta API key unless you set an environment variable `LAMIN_API_KEY`.
120
- """
121
- if logout:
122
- from lamindb_setup._setup_user import logout as logout_func
123
-
124
- return logout_func()
125
- else:
126
- from lamindb_setup._setup_user import login
127
-
128
- if user is None:
129
- if "LAMIN_API_KEY" in os.environ:
130
- api_key = os.environ["LAMIN_API_KEY"]
131
- else:
132
- api_key = input("Your API key: ")
133
- else:
134
- api_key = None
135
-
136
- return login(user, key=key, api_key=api_key)
137
-
138
-
139
- # fmt: off
140
- @main.command()
141
- @click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.") # noqa: E501
142
- @click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.") # noqa: E501
143
- @click.option("--schema", type=str, default=None, help="Comma-separated string of schema modules.") # noqa: E501
144
- @click.option("--name", type=str, default=None, help="The instance name.")
145
- # fmt: on
146
- def init(storage: str, db: Optional[str], schema: Optional[str], name: Optional[str]):
147
- """Init a LaminDB instance."""
148
- from lamindb_setup._init_instance import init as init_
149
-
150
- return init_(storage=storage, db=db, schema=schema, name=name)
151
-
152
-
153
- # fmt: off
154
- @main.command()
155
- @click.argument("instance", type=str, default=None)
156
- @click.option("--db", type=str, default=None, help="Update database URL.") # noqa: E501
157
- @click.option("--storage", type=str, default=None, help="Update storage while loading.")
158
- @click.option("--unload", is_flag=True, help="Unload the current instance.")
159
- # fmt: on
160
- def load(
161
- instance: Optional[str], db: Optional[str], storage: Optional[str], unload: bool
162
- ):
163
- """Load an instance for auto-connection.
164
-
165
- Pass a slug (`account/name`) or URL
166
- (`https://lamin.ai/account/name`).
167
- """
168
- if unload:
169
- from lamindb_setup._close import close as close_
170
-
171
- return close_()
172
- else:
173
- if instance is None:
174
- raise click.UsageError("INSTANCE is required when loading an instance.")
175
- from lamindb_setup import settings, connect
176
-
177
- settings.auto_connect = True
178
- return connect(slug=instance, db=db, storage=storage)
179
-
180
-
181
- @main.command()
182
- @click.option("--schema", is_flag=True, help="View schema.")
183
- def info(schema: bool):
184
- """Show info about current instance."""
185
- if schema:
186
- from lamindb_setup._schema import view
187
-
188
- print("Open in browser: http://127.0.0.1:8000/schema/")
189
- return view()
190
- else:
191
- import lamindb_setup
192
-
193
- print(lamindb_setup.settings)
194
-
195
-
196
- # fmt: off
197
- @main.command()
198
- @click.argument("instance", type=str, default=None)
199
- @click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.") # noqa: E501
200
- # fmt: on
201
- def delete(instance: str, force: bool = False):
202
- """Delete an instance."""
203
- from lamindb_setup._delete import delete
204
-
205
- return delete(instance, force=force)
206
-
207
-
208
- @main.command()
209
- @click.argument("entity", type=str)
210
- @click.option("--uid", help="The uid for the entity.")
211
- @click.option("--key", help="The key for the entity.")
212
- @click.option(
213
- "--with-env", is_flag=True, help="Also return the environment for a tranform."
214
- )
215
- def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
216
- """Query an entity.
217
-
218
- Pass a URL, `artifact`, or `transform`. For example:
219
-
220
- ```
221
- lamin get https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
222
- lamin get artifact --key mydatasets/mytable.parquet
223
- lamin get artifact --uid e2G7k9EVul4JbfsEYAy5
224
- lamin get transform --key analysis.ipynb
225
- lamin get transform --uid Vul4JbfsEYAy5
226
- lamin get transform --uid Vul4JbfsEYAy5 --with-env
227
- ```
228
- """
229
- from lamin_cli._get import get
230
-
231
- return get(entity, uid=uid, key=key, with_env=with_env)
232
-
233
-
234
- @main.command()
235
- @click.argument(
236
- "filepath", type=click.Path(exists=True, dir_okay=False, file_okay=True)
237
- )
238
- @click.option("--key", type=str, default=None)
239
- @click.option("--description", type=str, default=None)
240
- def save(filepath: str, key: str, description: str):
241
- """Save file or folder."""
242
- from lamin_cli._save import save_from_filepath_cli
243
-
244
- if save_from_filepath_cli(filepath, key, description) is not None:
245
- sys.exit(1)
246
-
247
-
248
- main.add_command(cache)
249
-
250
-
251
- @main.command(name="set")
252
- @click.argument(
253
- "setting",
254
- type=click.Choice(["auto-connect", "private-django-api"], case_sensitive=False),
255
- )
256
- @click.argument("value", type=click.BOOL)
257
- def set_(setting: str, value: bool):
258
- """Update settings.
259
-
260
- - `auto-connect` → {attr}`~lamindb.setup.core.SetupSettings.auto_connect`
261
- - `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
262
- """
263
- from lamindb_setup import settings
264
-
265
- if setting == "auto-connect":
266
- settings.auto_connect = value
267
- if setting == "private-django-api":
268
- settings.private_django_api = value
269
-
270
-
271
- main.add_command(migrate)
272
-
273
-
274
- # https://stackoverflow.com/questions/57810659/automatically-generate-all-help-documentation-for-click-commands
275
- # https://claude.ai/chat/73c28487-bec3-4073-8110-50d1a2dd6b84
276
- def _generate_help():
277
- out: dict[str, dict[str, str | None]] = {}
278
-
279
- def recursive_help(
280
- cmd: Command, parent: Optional[Context] = None, name: tuple[str, ...] = ()
281
- ):
282
- ctx = click.Context(cmd, info_name=cmd.name, parent=parent)
283
- assert cmd.name
284
- name = (*name, cmd.name)
285
- command_name = " ".join(name)
286
-
287
- docstring = inspect.getdoc(cmd.callback)
288
- usage = cmd.get_help(ctx).split("\n")[0]
289
- options = cmd.get_help(ctx).split("Options:")[1]
290
- out[command_name] = {
291
- "help": usage + "\n\nOptions:" + options,
292
- "docstring": docstring,
293
- }
294
-
295
- for sub in getattr(cmd, "commands", {}).values():
296
- recursive_help(sub, ctx, name=name)
297
-
298
- recursive_help(main)
299
- return out
300
-
301
-
302
- if __name__ == "__main__":
303
- main()
1
+ from __future__ import annotations
2
+ import os
3
+ import sys
4
+ from collections import OrderedDict
5
+ import inspect
6
+ from importlib.metadata import PackageNotFoundError, version
7
+ from typing import Optional, Mapping
8
+ from functools import wraps
9
+
10
+ # https://github.com/ewels/rich-click/issues/19
11
+ # Otherwise rich-click takes over the formatting.
12
+ if os.environ.get("NO_RICH"):
13
+ import click as click
14
+
15
+ class OrderedGroup(click.Group):
16
+ """Overwrites list_commands to return commands in order of definition."""
17
+
18
+ def __init__(
19
+ self,
20
+ name: Optional[str] = None,
21
+ commands: Optional[Mapping[str, click.Command]] = None,
22
+ **kwargs,
23
+ ):
24
+ super(OrderedGroup, self).__init__(name, commands, **kwargs)
25
+ self.commands = commands or OrderedDict()
26
+
27
+ def list_commands(self, ctx: click.Context) -> Mapping[str, click.Command]:
28
+ return self.commands
29
+
30
+ lamin_group_decorator = click.group(cls=OrderedGroup)
31
+
32
+ else:
33
+ import rich_click as click
34
+
35
+ COMMAND_GROUPS = {
36
+ "lamin": [
37
+ {
38
+ "name": "Main commands",
39
+ "commands": [
40
+ "login",
41
+ "init",
42
+ "load",
43
+ "info",
44
+ "delete",
45
+ ],
46
+ },
47
+ {
48
+ "name": "Data commands",
49
+ "commands": ["get", "save"],
50
+ },
51
+ {
52
+ "name": "Configuration commands",
53
+ "commands": ["cache", "set"],
54
+ },
55
+ {
56
+ "name": "Schema migration",
57
+ "commands": ["migrate"],
58
+ },
59
+ ]
60
+ }
61
+
62
+ def lamin_group_decorator(f):
63
+ @click.rich_config(
64
+ help_config=click.RichHelpConfiguration(
65
+ command_groups=COMMAND_GROUPS,
66
+ style_commands_table_column_width_ratio=(1, 13),
67
+ )
68
+ )
69
+ @click.group()
70
+ @wraps(f)
71
+ def wrapper(*args, **kwargs):
72
+ return f(*args, **kwargs)
73
+
74
+ return wrapper
75
+
76
+
77
+ from click import Command, Context
78
+ from lamindb_setup._silence_loggers import silence_loggers
79
+
80
+ from lamin_cli._cache import cache
81
+ from lamin_cli._migration import migrate
82
+
83
+ try:
84
+ lamindb_version = version("lamindb")
85
+ except PackageNotFoundError:
86
+ lamindb_version = "lamindb installation not found"
87
+
88
+
89
+ @lamin_group_decorator
90
+ @click.version_option(version=lamindb_version, prog_name="lamindb")
91
+ def main():
92
+ """Configure LaminDB and perform simple actions."""
93
+ silence_loggers()
94
+
95
+
96
+ @main.command()
97
+ @click.argument("user", type=str, default=None, required=False)
98
+ @click.option("--key", type=str, default=None, help="The API key.")
99
+ @click.option("--logout", is_flag=True, help="Logout instead of logging in.")
100
+ def login(user: str, key: Optional[str], logout: bool = False):
101
+ """Log into LaminHub.
102
+
103
+ Upon logging in the first time, you need to pass your API key via:
104
+
105
+ ```
106
+ lamin login myemail@acme.com --key YOUR_API_KEY
107
+ ```
108
+
109
+ You'll find your API key on LaminHub in the top right corner under "Settings".
110
+
111
+ After this, you can either use `lamin login myhandle` or `lamin login myemail@acme.com`
112
+
113
+ You can also call this without arguments:
114
+
115
+ ```
116
+ lamin login
117
+ ```
118
+
119
+ You will be prompted for your Beta API key unless you set an environment variable `LAMIN_API_KEY`.
120
+ """
121
+ if logout:
122
+ from lamindb_setup._setup_user import logout as logout_func
123
+
124
+ return logout_func()
125
+ else:
126
+ from lamindb_setup._setup_user import login
127
+
128
+ if user is None:
129
+ if "LAMIN_API_KEY" in os.environ:
130
+ api_key = os.environ["LAMIN_API_KEY"]
131
+ else:
132
+ api_key = input("Your API key: ")
133
+ else:
134
+ api_key = None
135
+
136
+ return login(user, key=key, api_key=api_key)
137
+
138
+
139
+ # fmt: off
140
+ @main.command()
141
+ @click.option("--storage", type=str, help="Local directory, s3://bucket_name, gs://bucket_name.") # noqa: E501
142
+ @click.option("--db", type=str, default=None, help="Postgres database connection URL, do not pass for SQLite.") # noqa: E501
143
+ @click.option("--schema", type=str, default=None, help="Comma-separated string of schema modules.") # noqa: E501
144
+ @click.option("--name", type=str, default=None, help="The instance name.")
145
+ # fmt: on
146
+ def init(storage: str, db: Optional[str], schema: Optional[str], name: Optional[str]):
147
+ """Init a LaminDB instance."""
148
+ from lamindb_setup._init_instance import init as init_
149
+
150
+ return init_(storage=storage, db=db, schema=schema, name=name)
151
+
152
+
153
+ # fmt: off
154
+ @main.command()
155
+ @click.argument("instance", type=str, default=None, required=False)
156
+ @click.option("--db", type=str, default=None, help="Update database URL.") # noqa: E501
157
+ @click.option("--storage", type=str, default=None, help="Update storage while loading.")
158
+ @click.option("--unload", is_flag=True, help="Unload the current instance.")
159
+ # fmt: on
160
+ def load(
161
+ instance: Optional[str], db: Optional[str], storage: Optional[str], unload: bool
162
+ ):
163
+ """Load an instance for auto-connection.
164
+
165
+ Pass a slug (`account/name`) or URL
166
+ (`https://lamin.ai/account/name`).
167
+ """
168
+ if unload:
169
+ from lamindb_setup._close import close as close_
170
+
171
+ return close_()
172
+ else:
173
+ if instance is None:
174
+ raise click.UsageError("INSTANCE is required when loading an instance.")
175
+ from lamindb_setup import settings, connect
176
+
177
+ settings.auto_connect = True
178
+ return connect(slug=instance, db=db, storage=storage)
179
+
180
+
181
+ @main.command()
182
+ @click.option("--schema", is_flag=True, help="View schema.")
183
+ def info(schema: bool):
184
+ """Show info about current instance."""
185
+ if schema:
186
+ from lamindb_setup._schema import view
187
+
188
+ print("Open in browser: http://127.0.0.1:8000/schema/")
189
+ return view()
190
+ else:
191
+ import lamindb_setup
192
+
193
+ print(lamindb_setup.settings)
194
+
195
+
196
+ # fmt: off
197
+ @main.command()
198
+ @click.argument("instance", type=str, default=None)
199
+ @click.option("--force", is_flag=True, default=False, help="Do not ask for confirmation.") # noqa: E501
200
+ # fmt: on
201
+ def delete(instance: str, force: bool = False):
202
+ """Delete an instance."""
203
+ from lamindb_setup._delete import delete
204
+
205
+ return delete(instance, force=force)
206
+
207
+
208
+ @main.command()
209
+ @click.argument("entity", type=str)
210
+ @click.option("--uid", help="The uid for the entity.")
211
+ @click.option("--key", help="The key for the entity.")
212
+ @click.option(
213
+ "--with-env", is_flag=True, help="Also return the environment for a tranform."
214
+ )
215
+ def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
216
+ """Query an entity.
217
+
218
+ Pass a URL, `artifact`, or `transform`. For example:
219
+
220
+ ```
221
+ lamin get https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
222
+ lamin get artifact --key mydatasets/mytable.parquet
223
+ lamin get artifact --uid e2G7k9EVul4JbfsEYAy5
224
+ lamin get transform --key analysis.ipynb
225
+ lamin get transform --uid Vul4JbfsEYAy5
226
+ lamin get transform --uid Vul4JbfsEYAy5 --with-env
227
+ ```
228
+ """
229
+ from lamin_cli._get import get
230
+
231
+ return get(entity, uid=uid, key=key, with_env=with_env)
232
+
233
+
234
+ @main.command()
235
+ @click.argument(
236
+ "filepath", type=click.Path(exists=True, dir_okay=False, file_okay=True)
237
+ )
238
+ @click.option("--key", type=str, default=None)
239
+ @click.option("--description", type=str, default=None)
240
+ def save(filepath: str, key: str, description: str):
241
+ """Save file or folder."""
242
+ from lamin_cli._save import save_from_filepath_cli
243
+
244
+ if save_from_filepath_cli(filepath, key, description) is not None:
245
+ sys.exit(1)
246
+
247
+
248
+ main.add_command(cache)
249
+
250
+
251
+ @main.command(name="set")
252
+ @click.argument(
253
+ "setting",
254
+ type=click.Choice(["auto-connect", "private-django-api"], case_sensitive=False),
255
+ )
256
+ @click.argument("value", type=click.BOOL)
257
+ def set_(setting: str, value: bool):
258
+ """Update settings.
259
+
260
+ - `auto-connect` → {attr}`~lamindb.setup.core.SetupSettings.auto_connect`
261
+ - `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
262
+ """
263
+ from lamindb_setup import settings
264
+
265
+ if setting == "auto-connect":
266
+ settings.auto_connect = value
267
+ if setting == "private-django-api":
268
+ settings.private_django_api = value
269
+
270
+
271
+ main.add_command(migrate)
272
+
273
+
274
+ # https://stackoverflow.com/questions/57810659/automatically-generate-all-help-documentation-for-click-commands
275
+ # https://claude.ai/chat/73c28487-bec3-4073-8110-50d1a2dd6b84
276
+ def _generate_help():
277
+ out: dict[str, dict[str, str | None]] = {}
278
+
279
+ def recursive_help(
280
+ cmd: Command, parent: Optional[Context] = None, name: tuple[str, ...] = ()
281
+ ):
282
+ ctx = click.Context(cmd, info_name=cmd.name, parent=parent)
283
+ assert cmd.name
284
+ name = (*name, cmd.name)
285
+ command_name = " ".join(name)
286
+
287
+ docstring = inspect.getdoc(cmd.callback)
288
+ usage = cmd.get_help(ctx).split("\n")[0]
289
+ options = cmd.get_help(ctx).split("Options:")[1]
290
+ out[command_name] = {
291
+ "help": usage + "\n\nOptions:" + options,
292
+ "docstring": docstring,
293
+ }
294
+
295
+ for sub in getattr(cmd, "commands", {}).values():
296
+ recursive_help(sub, ctx, name=name)
297
+
298
+ recursive_help(main)
299
+ return out
300
+
301
+
302
+ if __name__ == "__main__":
303
+ main()