lamindb 0.76.6__py3-none-any.whl → 0.76.8__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.
Files changed (61) hide show
  1. lamindb/__init__.py +113 -113
  2. lamindb/_artifact.py +1205 -1174
  3. lamindb/_can_validate.py +579 -579
  4. lamindb/_collection.py +387 -382
  5. lamindb/_curate.py +1601 -1601
  6. lamindb/_feature.py +155 -155
  7. lamindb/_feature_set.py +242 -242
  8. lamindb/_filter.py +23 -23
  9. lamindb/_finish.py +256 -256
  10. lamindb/_from_values.py +382 -382
  11. lamindb/_is_versioned.py +40 -40
  12. lamindb/_parents.py +476 -476
  13. lamindb/_query_manager.py +125 -125
  14. lamindb/_query_set.py +362 -362
  15. lamindb/_record.py +649 -649
  16. lamindb/_run.py +57 -57
  17. lamindb/_save.py +308 -295
  18. lamindb/_storage.py +14 -14
  19. lamindb/_transform.py +127 -127
  20. lamindb/_ulabel.py +56 -56
  21. lamindb/_utils.py +9 -9
  22. lamindb/_view.py +72 -72
  23. lamindb/core/__init__.py +94 -93
  24. lamindb/core/_context.py +574 -558
  25. lamindb/core/_data.py +438 -438
  26. lamindb/core/_feature_manager.py +867 -866
  27. lamindb/core/_label_manager.py +253 -252
  28. lamindb/core/_mapped_collection.py +597 -597
  29. lamindb/core/_settings.py +187 -187
  30. lamindb/core/_sync_git.py +138 -138
  31. lamindb/core/_track_environment.py +27 -27
  32. lamindb/core/datasets/__init__.py +59 -59
  33. lamindb/core/datasets/_core.py +571 -571
  34. lamindb/core/datasets/_fake.py +36 -36
  35. lamindb/core/exceptions.py +90 -77
  36. lamindb/core/fields.py +12 -12
  37. lamindb/core/loaders.py +164 -0
  38. lamindb/core/schema.py +56 -56
  39. lamindb/core/storage/__init__.py +25 -25
  40. lamindb/core/storage/_anndata_accessor.py +740 -740
  41. lamindb/core/storage/_anndata_sizes.py +41 -41
  42. lamindb/core/storage/_backed_access.py +98 -98
  43. lamindb/core/storage/_tiledbsoma.py +204 -196
  44. lamindb/core/storage/_valid_suffixes.py +21 -21
  45. lamindb/core/storage/_zarr.py +110 -110
  46. lamindb/core/storage/objects.py +62 -62
  47. lamindb/core/storage/paths.py +172 -245
  48. lamindb/core/subsettings/__init__.py +12 -12
  49. lamindb/core/subsettings/_creation_settings.py +38 -38
  50. lamindb/core/subsettings/_transform_settings.py +21 -21
  51. lamindb/core/types.py +19 -19
  52. lamindb/core/versioning.py +158 -158
  53. lamindb/integrations/__init__.py +12 -12
  54. lamindb/integrations/_vitessce.py +107 -107
  55. lamindb/setup/__init__.py +14 -14
  56. lamindb/setup/core/__init__.py +4 -4
  57. {lamindb-0.76.6.dist-info → lamindb-0.76.8.dist-info}/LICENSE +201 -201
  58. {lamindb-0.76.6.dist-info → lamindb-0.76.8.dist-info}/METADATA +5 -5
  59. lamindb-0.76.8.dist-info/RECORD +60 -0
  60. {lamindb-0.76.6.dist-info → lamindb-0.76.8.dist-info}/WHEEL +1 -1
  61. lamindb-0.76.6.dist-info/RECORD +0 -59
@@ -1,245 +1,172 @@
1
- from __future__ import annotations
2
-
3
- import builtins
4
- import re
5
- import shutil
6
- from pathlib import Path
7
- from typing import TYPE_CHECKING
8
-
9
- import anndata as ad
10
- import pandas as pd
11
- from lamin_utils import logger
12
- from lamindb_setup import settings as setup_settings
13
- from lamindb_setup.core import StorageSettings
14
- from lamindb_setup.core.upath import (
15
- LocalPathClasses,
16
- UPath,
17
- create_path,
18
- infer_filesystem,
19
- )
20
- from lnschema_core.models import Artifact, Storage
21
-
22
- from lamindb.core._settings import settings
23
-
24
- if TYPE_CHECKING:
25
- import mudata as md
26
- from lamindb_setup.core.types import UPathStr
27
-
28
- try:
29
- from ._zarr import read_adata_zarr
30
- except ImportError:
31
-
32
- def read_adata_zarr(storepath): # type: ignore
33
- raise ImportError("Please install zarr: pip install zarr")
34
-
35
-
36
- AUTO_KEY_PREFIX = ".lamindb/"
37
- is_run_from_ipython = getattr(builtins, "__IPYTHON__", False)
38
-
39
-
40
- # add type annotations back asap when re-organizing the module
41
- def auto_storage_key_from_artifact(artifact: Artifact):
42
- if artifact.key is None or artifact._key_is_virtual:
43
- is_dir = artifact.n_objects is not None
44
- return auto_storage_key_from_artifact_uid(artifact.uid, artifact.suffix, is_dir)
45
- else:
46
- return artifact.key
47
-
48
-
49
- def auto_storage_key_from_artifact_uid(uid: str, suffix: str, is_dir: bool) -> str:
50
- assert isinstance(suffix, str) # noqa: S101 Suffix cannot be None.
51
- if is_dir:
52
- uid_storage = uid[:16] # 16 chars, leave 4 chars for versioning
53
- else:
54
- uid_storage = uid
55
- storage_key = f"{AUTO_KEY_PREFIX}{uid_storage}{suffix}"
56
- return storage_key
57
-
58
-
59
- def check_path_is_child_of_root(path: Path | UPath, root: Path | UPath | None) -> bool:
60
- # str is needed to eliminate UPath storage_options
61
- # from the equality checks below
62
- path = UPath(str(path))
63
- root = UPath(str(root))
64
- return root.resolve() in path.resolve().parents
65
-
66
-
67
- def attempt_accessing_path(
68
- artifact: Artifact,
69
- storage_key: str,
70
- using_key: str | None = None,
71
- access_token: str | None = None,
72
- ):
73
- # check whether the file is in the default db and whether storage
74
- # matches default storage
75
- if (
76
- artifact._state.db in ("default", None)
77
- and artifact.storage_id == settings._storage_settings.id
78
- ):
79
- if access_token is None:
80
- storage_settings = settings._storage_settings
81
- else:
82
- storage_settings = StorageSettings(
83
- settings.storage.root, access_token=access_token
84
- )
85
- else:
86
- if artifact._state.db not in ("default", None) and using_key is None:
87
- storage = Storage.using(artifact._state.db).get(id=artifact.storage_id)
88
- else:
89
- storage = Storage.objects.using(using_key).get(id=artifact.storage_id)
90
- # find a better way than passing None to instance_settings in the future!
91
- storage_settings = StorageSettings(storage.root, access_token=access_token)
92
- path = storage_settings.key_to_filepath(storage_key)
93
- return path
94
-
95
-
96
- # add type annotations back asap when re-organizing the module
97
- def filepath_from_artifact(artifact: Artifact, using_key: str | None = None):
98
- if hasattr(artifact, "_local_filepath") and artifact._local_filepath is not None:
99
- return artifact._local_filepath.resolve()
100
- storage_key = auto_storage_key_from_artifact(artifact)
101
- path = attempt_accessing_path(artifact, storage_key, using_key=using_key)
102
- return path
103
-
104
-
105
- def read_adata_h5ad(filepath, **kwargs) -> ad.AnnData:
106
- fs, filepath = infer_filesystem(filepath)
107
-
108
- with fs.open(filepath, mode="rb") as file:
109
- adata = ad.read_h5ad(file, backed=False, **kwargs)
110
- return adata
111
-
112
-
113
- def store_file_or_folder(
114
- local_path: UPathStr, storage_path: UPath, print_progress: bool = True
115
- ) -> None:
116
- """Store file or folder (localpath) at storagepath."""
117
- local_path = Path(local_path)
118
- if not isinstance(storage_path, LocalPathClasses):
119
- # this uploads files and directories
120
- create_folder = False if local_path.is_dir() else None
121
- storage_path.upload_from(
122
- local_path, create_folder=create_folder, print_progress=print_progress
123
- )
124
- else: # storage path is local
125
- storage_path.parent.mkdir(parents=True, exist_ok=True)
126
- if local_path.is_file():
127
- try:
128
- shutil.copyfile(local_path, storage_path)
129
- except shutil.SameFileError:
130
- pass
131
- else:
132
- if storage_path.exists():
133
- shutil.rmtree(storage_path)
134
- shutil.copytree(local_path, storage_path)
135
-
136
-
137
- def delete_storage_using_key(
138
- artifact: Artifact, storage_key: str, using_key: str | None
139
- ):
140
- filepath = attempt_accessing_path(artifact, storage_key, using_key=using_key)
141
- delete_storage(filepath)
142
-
143
-
144
- def delete_storage(
145
- storagepath: Path, raise_file_not_found_error: bool = True
146
- ) -> None | str:
147
- """Delete arbitrary artifact."""
148
- if storagepath.is_file():
149
- storagepath.unlink()
150
- elif storagepath.is_dir():
151
- if isinstance(storagepath, LocalPathClasses) or not isinstance(
152
- storagepath, UPath
153
- ):
154
- shutil.rmtree(storagepath)
155
- else:
156
- storagepath.rmdir()
157
- elif raise_file_not_found_error:
158
- raise FileNotFoundError(f"{storagepath} is not an existing path!")
159
- else:
160
- logger.warning(f"{storagepath} is not an existing path!")
161
- return None
162
-
163
-
164
- # tested in lamin-usecases
165
- def read_fcs(*args, **kwargs):
166
- try:
167
- import readfcs
168
- except ImportError: # pragma: no cover
169
- raise ImportError("Please install readfcs: pip install readfcs") from None
170
- return readfcs.read(*args, **kwargs)
171
-
172
-
173
- def read_tsv(path: UPathStr, **kwargs) -> pd.DataFrame:
174
- path_sanitized = Path(path)
175
- return pd.read_csv(path_sanitized, sep="\t", **kwargs)
176
-
177
-
178
- def read_mdata_h5mu(filepath: UPathStr, **kwargs) -> md.MuData:
179
- import mudata as md
180
-
181
- path_sanitized = Path(filepath)
182
- return md.read_h5mu(path_sanitized, **kwargs)
183
-
184
-
185
- def load_html(path: UPathStr):
186
- if is_run_from_ipython:
187
- with open(path, encoding="utf-8") as f:
188
- html_content = f.read()
189
- # Extract the body content using regular expressions
190
- body_content = re.findall(
191
- r"<body(?:.*?)>(?:.*?)</body>", html_content, re.DOTALL
192
- )
193
- # Remove any empty body tags
194
- if body_content:
195
- body_content = body_content[0]
196
- body_content = body_content.strip() # type: ignore
197
- from IPython.display import HTML, display
198
-
199
- display(HTML(data=body_content))
200
- else:
201
- return path
202
-
203
-
204
- def load_json(path: UPathStr):
205
- import json
206
-
207
- with open(path) as f:
208
- data = json.load(f)
209
- return data
210
-
211
-
212
- def load_to_memory(filepath: UPathStr, stream: bool = False, **kwargs):
213
- """Load a file into memory.
214
-
215
- Returns the filepath if no in-memory form is found.
216
- """
217
- filepath = create_path(filepath)
218
-
219
- if filepath.suffix not in {".h5ad", ".zarr"}:
220
- stream = False
221
-
222
- if not stream:
223
- # caching happens here if filename is a UPath
224
- # todo: make it safe when filepath is just Path
225
- filepath = settings._storage_settings.cloud_to_local(
226
- filepath, print_progress=True
227
- )
228
-
229
- READER_FUNCS = {
230
- ".csv": pd.read_csv,
231
- ".tsv": read_tsv,
232
- ".h5ad": read_adata_h5ad,
233
- ".parquet": pd.read_parquet,
234
- ".fcs": read_fcs,
235
- ".zarr": read_adata_zarr,
236
- ".html": load_html,
237
- ".json": load_json,
238
- ".h5mu": read_mdata_h5mu,
239
- }
240
-
241
- reader = READER_FUNCS.get(filepath.suffix)
242
- if reader is None:
243
- return filepath
244
- else:
245
- return reader(filepath, **kwargs)
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ from typing import TYPE_CHECKING
5
+
6
+ import anndata as ad
7
+ import pandas as pd
8
+ from lamin_utils import logger
9
+ from lamindb_setup.core import StorageSettings
10
+ from lamindb_setup.core.upath import (
11
+ LocalPathClasses,
12
+ UPath,
13
+ create_path,
14
+ infer_filesystem,
15
+ )
16
+ from lnschema_core.models import Artifact, Storage
17
+
18
+ from lamindb.core._settings import settings
19
+
20
+ if TYPE_CHECKING:
21
+ from pathlib import Path
22
+
23
+ from lamindb_setup.core.types import UPathStr
24
+
25
+
26
+ AUTO_KEY_PREFIX = ".lamindb/"
27
+
28
+
29
+ # add type annotations back asap when re-organizing the module
30
+ def auto_storage_key_from_artifact(artifact: Artifact):
31
+ if artifact.key is None or artifact._key_is_virtual:
32
+ is_dir = artifact.n_objects is not None
33
+ return auto_storage_key_from_artifact_uid(artifact.uid, artifact.suffix, is_dir)
34
+ else:
35
+ return artifact.key
36
+
37
+
38
+ def auto_storage_key_from_artifact_uid(uid: str, suffix: str, is_dir: bool) -> str:
39
+ assert isinstance(suffix, str) # noqa: S101 Suffix cannot be None.
40
+ if is_dir:
41
+ uid_storage = uid[:16] # 16 chars, leave 4 chars for versioning
42
+ else:
43
+ uid_storage = uid
44
+ storage_key = f"{AUTO_KEY_PREFIX}{uid_storage}{suffix}"
45
+ return storage_key
46
+
47
+
48
+ def check_path_is_child_of_root(path: Path | UPath, root: Path | UPath | None) -> bool:
49
+ # str is needed to eliminate UPath storage_options
50
+ # from the equality checks below
51
+ path = UPath(str(path))
52
+ root = UPath(str(root))
53
+ return root.resolve() in path.resolve().parents
54
+
55
+
56
+ # returns filepath and root of the storage
57
+ def attempt_accessing_path(
58
+ artifact: Artifact,
59
+ storage_key: str,
60
+ using_key: str | None = None,
61
+ access_token: str | None = None,
62
+ ) -> tuple[UPath, StorageSettings]:
63
+ # check whether the file is in the default db and whether storage
64
+ # matches default storage
65
+ if (
66
+ artifact._state.db in ("default", None)
67
+ and artifact.storage_id == settings._storage_settings.id
68
+ ):
69
+ if access_token is None:
70
+ storage_settings = settings._storage_settings
71
+ else:
72
+ storage_settings = StorageSettings(
73
+ settings.storage.root, access_token=access_token
74
+ )
75
+ else:
76
+ if artifact._state.db not in ("default", None) and using_key is None:
77
+ storage = Storage.using(artifact._state.db).get(id=artifact.storage_id)
78
+ else:
79
+ storage = Storage.objects.using(using_key).get(id=artifact.storage_id)
80
+ # find a better way than passing None to instance_settings in the future!
81
+ storage_settings = StorageSettings(storage.root, access_token=access_token)
82
+ path = storage_settings.key_to_filepath(storage_key)
83
+ return path, storage_settings
84
+
85
+
86
+ def filepath_from_artifact(
87
+ artifact: Artifact, using_key: str | None = None
88
+ ) -> tuple[UPath, StorageSettings | None]:
89
+ if hasattr(artifact, "_local_filepath") and artifact._local_filepath is not None:
90
+ return artifact._local_filepath.resolve(), None
91
+ storage_key = auto_storage_key_from_artifact(artifact)
92
+ path, storage_settings = attempt_accessing_path(
93
+ artifact, storage_key, using_key=using_key
94
+ )
95
+ return path, storage_settings
96
+
97
+
98
+ # virtual key is taken into consideration
99
+ # only if the version is latest
100
+ def _cache_key_from_artifact_storage(
101
+ artifact: Artifact, storage_settings: StorageSettings | None
102
+ ):
103
+ cache_key = None
104
+ if (
105
+ artifact._key_is_virtual
106
+ and artifact.key is not None
107
+ and storage_settings is not None
108
+ and artifact.is_latest
109
+ ):
110
+ cache_key = (storage_settings.root / artifact.key).path
111
+ return cache_key
112
+
113
+
114
+ # return filepath and cache_key if needed
115
+ def filepath_cache_key_from_artifact(
116
+ artifact: Artifact, using_key: str | None = None
117
+ ) -> tuple[UPath, str | None]:
118
+ filepath, storage_settings = filepath_from_artifact(artifact, using_key)
119
+ if isinstance(filepath, LocalPathClasses):
120
+ return filepath, None
121
+ cache_key = _cache_key_from_artifact_storage(artifact, storage_settings)
122
+ return filepath, cache_key
123
+
124
+
125
+ def store_file_or_folder(
126
+ local_path: UPathStr, storage_path: UPath, print_progress: bool = True
127
+ ) -> None:
128
+ """Store file or folder (localpath) at storagepath."""
129
+ local_path = UPath(local_path)
130
+ if not isinstance(storage_path, LocalPathClasses):
131
+ # this uploads files and directories
132
+ create_folder = False if local_path.is_dir() else None
133
+ storage_path.upload_from(
134
+ local_path, create_folder=create_folder, print_progress=print_progress
135
+ )
136
+ else: # storage path is local
137
+ if local_path.resolve().as_posix() == storage_path.resolve().as_posix():
138
+ return None
139
+ storage_path.parent.mkdir(parents=True, exist_ok=True)
140
+ if local_path.is_file():
141
+ shutil.copyfile(local_path, storage_path)
142
+ else:
143
+ if storage_path.exists():
144
+ shutil.rmtree(storage_path)
145
+ shutil.copytree(local_path, storage_path)
146
+
147
+
148
+ def delete_storage_using_key(
149
+ artifact: Artifact, storage_key: str, using_key: str | None
150
+ ):
151
+ filepath, _ = attempt_accessing_path(artifact, storage_key, using_key=using_key)
152
+ delete_storage(filepath)
153
+
154
+
155
+ def delete_storage(
156
+ storagepath: Path, raise_file_not_found_error: bool = True
157
+ ) -> None | str:
158
+ """Delete arbitrary artifact."""
159
+ if storagepath.is_file():
160
+ storagepath.unlink()
161
+ elif storagepath.is_dir():
162
+ if isinstance(storagepath, LocalPathClasses) or not isinstance(
163
+ storagepath, UPath
164
+ ):
165
+ shutil.rmtree(storagepath)
166
+ else:
167
+ storagepath.rmdir()
168
+ elif raise_file_not_found_error:
169
+ raise FileNotFoundError(f"{storagepath} is not an existing path!")
170
+ else:
171
+ logger.warning(f"{storagepath} is not an existing path!")
172
+ return None
@@ -1,12 +1,12 @@
1
- """Sub settings.
2
-
3
- .. autosummary::
4
- :toctree: .
5
-
6
- TransformSettings
7
- CreationSettings
8
-
9
- """
10
-
11
- from ._creation_settings import CreationSettings
12
- from ._transform_settings import TransformSettings
1
+ """Sub settings.
2
+
3
+ .. autosummary::
4
+ :toctree: .
5
+
6
+ TransformSettings
7
+ CreationSettings
8
+
9
+ """
10
+
11
+ from ._creation_settings import CreationSettings
12
+ from ._transform_settings import TransformSettings
@@ -1,38 +1,38 @@
1
- from typing import Literal
2
-
3
-
4
- class CreationSettings:
5
- artifact_if_hash_exists: Literal[
6
- "warn_return_existing", "error", "warn_create_new"
7
- ] = "warn_return_existing"
8
- """Behavior if file hash exists (default `"warn_return_existing"`).
9
-
10
- One of `["warn_return_existing", "error", "warn_create_new"]`.
11
-
12
- FAQ: :doc:`/faq/idempotency`
13
- """
14
- artifact_skip_size_hash: bool = False
15
- """To speed up registering high numbers of files (default `False`).
16
-
17
- This bypasses queries for size and hash to AWS & GCP.
18
-
19
- It speeds up file creation by about a factor 100.
20
- """
21
- search_names: bool = True
22
- """To speed up creating records (default `True`).
23
-
24
- If `True`, search for alternative names.
25
-
26
- FAQ: :doc:`/faq/idempotency`
27
- """
28
- artifact_silence_missing_run_warning: bool = False
29
- """Silence warning about missing run & transform during artifact creation."""
30
- _artifact_use_virtual_keys: bool = True
31
- """Treat `key` parameter in :class:`~lamindb.Artifact` as virtual.
32
-
33
- If `True`, the `key` is **not** used to construct file paths, but file paths are
34
- based on the `uid` of artifact.
35
- """
36
-
37
-
38
- creation_settings = CreationSettings()
1
+ from typing import Literal
2
+
3
+
4
+ class CreationSettings:
5
+ artifact_if_hash_exists: Literal[
6
+ "warn_return_existing", "error", "warn_create_new"
7
+ ] = "warn_return_existing"
8
+ """Behavior if file hash exists (default `"warn_return_existing"`).
9
+
10
+ One of `["warn_return_existing", "error", "warn_create_new"]`.
11
+
12
+ FAQ: :doc:`/faq/idempotency`
13
+ """
14
+ artifact_skip_size_hash: bool = False
15
+ """To speed up registering high numbers of files (default `False`).
16
+
17
+ This bypasses queries for size and hash to AWS & GCP.
18
+
19
+ It speeds up file creation by about a factor 100.
20
+ """
21
+ search_names: bool = True
22
+ """To speed up creating records (default `True`).
23
+
24
+ If `True`, search for alternative names.
25
+
26
+ FAQ: :doc:`/faq/idempotency`
27
+ """
28
+ artifact_silence_missing_run_warning: bool = False
29
+ """Silence warning about missing run & transform during artifact creation."""
30
+ _artifact_use_virtual_keys: bool = True
31
+ """Treat `key` parameter in :class:`~lamindb.Artifact` as virtual.
32
+
33
+ If `True`, the `key` is **not** used to construct file paths, but file paths are
34
+ based on the `uid` of artifact.
35
+ """
36
+
37
+
38
+ creation_settings = CreationSettings()
@@ -1,21 +1,21 @@
1
- from __future__ import annotations
2
-
3
-
4
- class TransformSettings:
5
- stem_uid: str | None = None
6
- """Defines the version family of the transform.
7
-
8
- For example, all notebooks of the same family have a uid that starts with
9
- `"FPnfDtJz8qbE"`.
10
-
11
- The full uids of the notebooks in this family are of form
12
- `"{stem_uid}{suffix_uid}"` where the `suffix_uid` encodes the semantic
13
- `version`.
14
- """
15
- version: str | None = None
16
- """The version."""
17
- name: str | None = None
18
- """A name like a notebook or script title."""
19
-
20
-
21
- transform_settings = TransformSettings()
1
+ from __future__ import annotations
2
+
3
+
4
+ class TransformSettings:
5
+ stem_uid: str | None = None
6
+ """Defines the version family of the transform.
7
+
8
+ For example, all notebooks of the same family have a uid that starts with
9
+ `"FPnfDtJz8qbE"`.
10
+
11
+ The full uids of the notebooks in this family are of form
12
+ `"{stem_uid}{suffix_uid}"` where the `suffix_uid` encodes the semantic
13
+ `version`.
14
+ """
15
+ version: str | None = None
16
+ """The version."""
17
+ name: str | None = None
18
+ """A name like a notebook or script title."""
19
+
20
+
21
+ transform_settings = TransformSettings()
lamindb/core/types.py CHANGED
@@ -1,19 +1,19 @@
1
- """Types.
2
-
3
- .. autosummary::
4
- :toctree: .
5
-
6
- UPathStr
7
- StrField
8
- ListLike
9
- TransformType
10
- ArtifactType
11
- """
12
-
13
- from lamindb_setup.core.types import UPathStr
14
- from lnschema_core.types import (
15
- ArtifactType,
16
- ListLike,
17
- StrField,
18
- TransformType,
19
- )
1
+ """Types.
2
+
3
+ .. autosummary::
4
+ :toctree: .
5
+
6
+ UPathStr
7
+ StrField
8
+ ListLike
9
+ TransformType
10
+ ArtifactType
11
+ """
12
+
13
+ from lamindb_setup.core.types import UPathStr
14
+ from lnschema_core.types import (
15
+ ArtifactType,
16
+ ListLike,
17
+ StrField,
18
+ TransformType,
19
+ )