lamindb 0.77.3__py3-none-any.whl → 1.0rc1__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.
- lamindb/__init__.py +39 -32
- lamindb/_artifact.py +95 -64
- lamindb/_can_curate.py +13 -6
- lamindb/_collection.py +51 -49
- lamindb/_feature.py +9 -9
- lamindb/_finish.py +92 -79
- lamindb/_from_values.py +13 -10
- lamindb/_is_versioned.py +2 -1
- lamindb/_parents.py +23 -16
- lamindb/_query_manager.py +3 -3
- lamindb/_query_set.py +85 -18
- lamindb/_record.py +114 -41
- lamindb/_run.py +3 -3
- lamindb/_save.py +5 -6
- lamindb/{_feature_set.py → _schema.py} +34 -31
- lamindb/_storage.py +2 -1
- lamindb/_transform.py +51 -23
- lamindb/_ulabel.py +17 -8
- lamindb/_view.py +13 -13
- lamindb/base/__init__.py +24 -0
- lamindb/base/fields.py +281 -0
- lamindb/base/ids.py +103 -0
- lamindb/base/types.py +51 -0
- lamindb/base/users.py +30 -0
- lamindb/base/validation.py +67 -0
- lamindb/core/__init__.py +18 -15
- lamindb/core/_context.py +295 -224
- lamindb/core/_data.py +44 -49
- lamindb/core/_describe.py +41 -31
- lamindb/core/_django.py +29 -27
- lamindb/core/_feature_manager.py +130 -129
- lamindb/core/_label_manager.py +7 -8
- lamindb/core/_mapped_collection.py +17 -14
- lamindb/core/_settings.py +1 -12
- lamindb/core/_sync_git.py +56 -9
- lamindb/core/_track_environment.py +1 -1
- lamindb/core/datasets/_core.py +5 -6
- lamindb/core/exceptions.py +0 -7
- lamindb/core/fields.py +1 -1
- lamindb/core/loaders.py +0 -1
- lamindb/core/{schema.py → relations.py} +22 -19
- lamindb/core/storage/_anndata_accessor.py +1 -2
- lamindb/core/storage/_backed_access.py +2 -1
- lamindb/core/storage/_tiledbsoma.py +38 -13
- lamindb/core/storage/objects.py +1 -1
- lamindb/core/storage/paths.py +13 -8
- lamindb/core/subsettings/__init__.py +0 -2
- lamindb/core/types.py +2 -23
- lamindb/core/versioning.py +11 -7
- lamindb/{_curate.py → curators/__init__.py} +122 -23
- lamindb/curators/_spatial.py +528 -0
- lamindb/integrations/_vitessce.py +1 -3
- lamindb/migrations/0052_squashed.py +1261 -0
- lamindb/migrations/0053_alter_featureset_hash_alter_paramvalue_created_by_and_more.py +57 -0
- lamindb/migrations/0054_alter_feature_previous_runs_and_more.py +35 -0
- lamindb/migrations/0055_artifact_type_artifactparamvalue_and_more.py +61 -0
- lamindb/migrations/0056_rename_ulabel_ref_is_name_artifactulabel_label_ref_is_name_and_more.py +22 -0
- lamindb/migrations/0057_link_models_latest_report_and_others.py +356 -0
- lamindb/migrations/0058_artifact__actions_collection__actions.py +22 -0
- lamindb/migrations/0059_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +31 -0
- lamindb/migrations/0060_alter_artifact__actions.py +22 -0
- lamindb/migrations/0061_alter_collection_meta_artifact_alter_run_environment_and_more.py +45 -0
- lamindb/migrations/0062_add_is_latest_field.py +32 -0
- lamindb/migrations/0063_populate_latest_field.py +45 -0
- lamindb/migrations/0064_alter_artifact_version_alter_collection_version_and_more.py +33 -0
- lamindb/migrations/0065_remove_collection_feature_sets_and_more.py +22 -0
- lamindb/migrations/0066_alter_artifact__feature_values_and_more.py +352 -0
- lamindb/migrations/0067_alter_featurevalue_unique_together_and_more.py +20 -0
- lamindb/migrations/0068_alter_artifactulabel_unique_together_and_more.py +20 -0
- lamindb/migrations/0069_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +1294 -0
- lamindb/migrations/0069_squashed.py +1770 -0
- lamindb/migrations/0070_lamindbv1_migrate_data.py +78 -0
- lamindb/migrations/0071_lamindbv1_migrate_schema.py +741 -0
- lamindb/migrations/0072_remove_user__branch_code_remove_user_aux_and_more.py +148 -0
- lamindb/migrations/0073_merge_ourprojects.py +945 -0
- lamindb/migrations/0074_lamindbv1_part4.py +374 -0
- lamindb/migrations/0075_lamindbv1_part5.py +276 -0
- lamindb/migrations/0076_lamindbv1_part6.py +621 -0
- lamindb/migrations/0077_lamindbv1_part6b.py +228 -0
- lamindb/migrations/0078_lamindbv1_part6c.py +468 -0
- lamindb/migrations/0079_alter_rundata_value_json_and_more.py +36 -0
- lamindb/migrations/__init__.py +0 -0
- lamindb/models.py +4064 -0
- {lamindb-0.77.3.dist-info → lamindb-1.0rc1.dist-info}/METADATA +13 -19
- lamindb-1.0rc1.dist-info/RECORD +100 -0
- {lamindb-0.77.3.dist-info → lamindb-1.0rc1.dist-info}/WHEEL +1 -1
- lamindb/core/subsettings/_transform_settings.py +0 -21
- lamindb-0.77.3.dist-info/RECORD +0 -63
- {lamindb-0.77.3.dist-info → lamindb-1.0rc1.dist-info}/LICENSE +0 -0
lamindb/base/fields.py
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
from django.db import models
|
2
|
+
|
3
|
+
|
4
|
+
class CharField(models.CharField):
|
5
|
+
"""Custom `CharField` with default values for `blank`, `default`, and `max_length`.
|
6
|
+
|
7
|
+
Django default values for `CharField` are `blank=False`, `default=""`, undefined `max_length`.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, max_length: int = 255, **kwargs):
|
11
|
+
kwargs["max_length"] = max_length # Set max_length in kwargs
|
12
|
+
kwargs.setdefault("blank", True)
|
13
|
+
kwargs.setdefault("default", None)
|
14
|
+
super().__init__(**kwargs) # Pass all arguments as kwargs
|
15
|
+
|
16
|
+
|
17
|
+
class TextField(models.TextField):
|
18
|
+
"""Custom `TextField` with default values for `blank` and `default`.
|
19
|
+
|
20
|
+
Django default values for `TextField` are `blank=False`, `default=''`.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, *args, **kwargs):
|
24
|
+
kwargs.setdefault("blank", True)
|
25
|
+
kwargs.setdefault("default", None)
|
26
|
+
super().__init__(*args, **kwargs)
|
27
|
+
|
28
|
+
|
29
|
+
class ForeignKey(models.ForeignKey):
|
30
|
+
"""Custom `ForeignKey` with default values for `blank`.
|
31
|
+
|
32
|
+
Django default value for `ForeignKey` `blank=False`.
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, *args, **kwargs):
|
36
|
+
kwargs.setdefault("blank", True)
|
37
|
+
super().__init__(*args, **kwargs)
|
38
|
+
|
39
|
+
|
40
|
+
class BooleanField(models.BooleanField):
|
41
|
+
"""Custom `BooleanField` with default values for `blank` and `default`.
|
42
|
+
|
43
|
+
Django default values for `BooleanField` are `blank=False`, `default=False`.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init__(self, *args, **kwargs):
|
47
|
+
kwargs.setdefault("blank", True)
|
48
|
+
kwargs.setdefault("default", None)
|
49
|
+
super().__init__(*args, **kwargs)
|
50
|
+
|
51
|
+
|
52
|
+
class DateField(models.DateField):
|
53
|
+
"""Custom `DateField` with default values for `blank`.
|
54
|
+
|
55
|
+
Django default values for `DateField` are `blank=False`.
|
56
|
+
"""
|
57
|
+
|
58
|
+
def __init__(self, *args, **kwargs):
|
59
|
+
kwargs.setdefault("blank", True)
|
60
|
+
super().__init__(*args, **kwargs)
|
61
|
+
|
62
|
+
|
63
|
+
class DateTimeField(models.DateTimeField):
|
64
|
+
"""Custom `DateTimeField` with default values for `blank`.
|
65
|
+
|
66
|
+
Django default values for `DateTimeField` are `blank=False`.
|
67
|
+
"""
|
68
|
+
|
69
|
+
def __init__(self, *args, **kwargs):
|
70
|
+
kwargs.setdefault("blank", True)
|
71
|
+
super().__init__(*args, **kwargs)
|
72
|
+
|
73
|
+
|
74
|
+
class BigIntegerField(models.BigIntegerField):
|
75
|
+
"""Custom `BigIntegerField` with default values for `blank`.
|
76
|
+
|
77
|
+
Django default values for `BigIntegerField` are `blank=False`.
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(self, *args, **kwargs):
|
81
|
+
kwargs.setdefault("blank", True)
|
82
|
+
kwargs.setdefault("default", None)
|
83
|
+
super().__init__(*args, **kwargs)
|
84
|
+
|
85
|
+
|
86
|
+
class IntegerField(models.IntegerField):
|
87
|
+
"""Custom `IntegerField` with default values for `blank`.
|
88
|
+
|
89
|
+
Django default values for `IntegerField` are `blank=False`.
|
90
|
+
"""
|
91
|
+
|
92
|
+
def __init__(self, *args, **kwargs):
|
93
|
+
kwargs.setdefault("blank", True)
|
94
|
+
super().__init__(*args, **kwargs)
|
95
|
+
|
96
|
+
|
97
|
+
class OneToOneField(models.OneToOneField):
|
98
|
+
"""Custom `OneToOneField` with default values for `blank`.
|
99
|
+
|
100
|
+
Django default values for `OneToOneField` are `blank=False`.
|
101
|
+
"""
|
102
|
+
|
103
|
+
def __init__(self, *args, **kwargs):
|
104
|
+
kwargs.setdefault("blank", True)
|
105
|
+
super().__init__(*args, **kwargs)
|
106
|
+
|
107
|
+
|
108
|
+
class FloatField(models.FloatField):
|
109
|
+
"""Custom `FloatField` with default values for `blank`.
|
110
|
+
|
111
|
+
Django default values for `FloatField` are `blank=False`.
|
112
|
+
"""
|
113
|
+
|
114
|
+
def __init__(self, *args, **kwargs):
|
115
|
+
kwargs.setdefault("blank", True)
|
116
|
+
super().__init__(*args, **kwargs)
|
117
|
+
|
118
|
+
|
119
|
+
class DecimalField(models.DecimalField):
|
120
|
+
"""Custom `DecimalField` with default values for `blank`.
|
121
|
+
|
122
|
+
Django default values for `DecimalField` are `blank=False`.
|
123
|
+
"""
|
124
|
+
|
125
|
+
def __init__(self, *args, **kwargs):
|
126
|
+
kwargs.setdefault("blank", True)
|
127
|
+
super().__init__(*args, **kwargs)
|
128
|
+
|
129
|
+
|
130
|
+
class JSONField(models.JSONField):
|
131
|
+
"""Custom `JSONField` with default values for `blank`.
|
132
|
+
|
133
|
+
Django default values for `JSONField` are `blank=False`.
|
134
|
+
"""
|
135
|
+
|
136
|
+
def __init__(self, *args, **kwargs):
|
137
|
+
kwargs.setdefault("blank", True)
|
138
|
+
super().__init__(*args, **kwargs)
|
139
|
+
|
140
|
+
|
141
|
+
class DurationField(models.DurationField):
|
142
|
+
"""Custom `DurationField` with default values for `blank`.
|
143
|
+
|
144
|
+
Django default values for `DurationField` are `blank=False`.
|
145
|
+
"""
|
146
|
+
|
147
|
+
def __init__(self, *args, **kwargs):
|
148
|
+
kwargs.setdefault("blank", True)
|
149
|
+
super().__init__(*args, **kwargs)
|
150
|
+
|
151
|
+
|
152
|
+
class URLField(models.URLField):
|
153
|
+
"""Custom `URLField` with default values for `blank`.
|
154
|
+
|
155
|
+
Django default values for `URLField` are `blank=False`.
|
156
|
+
"""
|
157
|
+
|
158
|
+
def __init__(self, *args, **kwargs):
|
159
|
+
kwargs.setdefault("blank", True)
|
160
|
+
super().__init__(*args, **kwargs)
|
161
|
+
|
162
|
+
|
163
|
+
class EmailField(models.EmailField):
|
164
|
+
"""Custom `EmailField` with default values for `blank`.
|
165
|
+
|
166
|
+
Django default values for `EmailField` are `blank=False`.
|
167
|
+
"""
|
168
|
+
|
169
|
+
def __init__(self, *args, **kwargs):
|
170
|
+
kwargs.setdefault("blank", True)
|
171
|
+
super().__init__(*args, **kwargs)
|
172
|
+
|
173
|
+
|
174
|
+
class TimeField(models.TimeField):
|
175
|
+
"""Custom `TimeField` with default values for `blank`.
|
176
|
+
|
177
|
+
Django default values for `TimeField` are `blank=False`.
|
178
|
+
"""
|
179
|
+
|
180
|
+
def __init__(self, *args, **kwargs):
|
181
|
+
kwargs.setdefault("blank", True)
|
182
|
+
super().__init__(*args, **kwargs)
|
183
|
+
|
184
|
+
|
185
|
+
class SlugField(models.SlugField):
|
186
|
+
"""Custom `SlugField` with default values for `blank`.
|
187
|
+
|
188
|
+
Django default values for `SlugField` are `blank=False`.
|
189
|
+
"""
|
190
|
+
|
191
|
+
def __init__(self, *args, **kwargs):
|
192
|
+
kwargs.setdefault("blank", True)
|
193
|
+
super().__init__(*args, **kwargs)
|
194
|
+
|
195
|
+
|
196
|
+
class UUIDField(models.UUIDField):
|
197
|
+
"""Custom `UUIDField` with default values for `blank`.
|
198
|
+
|
199
|
+
Django default values for `UUIDField` are `blank=False`.
|
200
|
+
"""
|
201
|
+
|
202
|
+
def __init__(self, *args, **kwargs):
|
203
|
+
kwargs.setdefault("blank", True)
|
204
|
+
super().__init__(*args, **kwargs)
|
205
|
+
|
206
|
+
|
207
|
+
class PositiveIntegerField(models.PositiveIntegerField):
|
208
|
+
"""Custom `PositiveIntegerField` with default values for `blank`.
|
209
|
+
|
210
|
+
Django default values for `PositiveIntegerField` are `blank=False`.
|
211
|
+
"""
|
212
|
+
|
213
|
+
def __init__(self, *args, **kwargs):
|
214
|
+
kwargs.setdefault("blank", True)
|
215
|
+
super().__init__(*args, **kwargs)
|
216
|
+
|
217
|
+
|
218
|
+
class PositiveSmallIntegerField(models.PositiveSmallIntegerField):
|
219
|
+
"""Custom `PositiveSmallIntegerField` with default values for `blank`.
|
220
|
+
|
221
|
+
Django default values for `PositiveSmallIntegerField` are `blank=False`.
|
222
|
+
"""
|
223
|
+
|
224
|
+
def __init__(self, *args, **kwargs):
|
225
|
+
kwargs.setdefault("blank", True)
|
226
|
+
super().__init__(*args, **kwargs)
|
227
|
+
|
228
|
+
|
229
|
+
class SmallIntegerField(models.SmallIntegerField):
|
230
|
+
"""Custom `SmallIntegerField` with default values for `blank`.
|
231
|
+
|
232
|
+
Django default values for `SmallIntegerField` are `blank=False`.
|
233
|
+
"""
|
234
|
+
|
235
|
+
def __init__(self, *args, **kwargs):
|
236
|
+
kwargs.setdefault("blank", True)
|
237
|
+
super().__init__(*args, **kwargs)
|
238
|
+
|
239
|
+
|
240
|
+
class BinaryField(models.BinaryField):
|
241
|
+
"""Custom `BinaryField` with default values for `blank`.
|
242
|
+
|
243
|
+
Django default values for `BinaryField` are `blank=False`.
|
244
|
+
"""
|
245
|
+
|
246
|
+
def __init__(self, *args, **kwargs):
|
247
|
+
kwargs.setdefault("blank", True)
|
248
|
+
super().__init__(*args, **kwargs)
|
249
|
+
|
250
|
+
|
251
|
+
class GenericIPAddressField(models.GenericIPAddressField):
|
252
|
+
"""Custom `GenericIPAddressField` with default values for `blank`.
|
253
|
+
|
254
|
+
Django default values for `GenericIPAddressField` are `blank=False`.
|
255
|
+
"""
|
256
|
+
|
257
|
+
def __init__(self, *args, **kwargs):
|
258
|
+
kwargs.setdefault("blank", True)
|
259
|
+
super().__init__(*args, **kwargs)
|
260
|
+
|
261
|
+
|
262
|
+
class FileField(models.FileField):
|
263
|
+
"""Custom `FileField` with default values for `blank`.
|
264
|
+
|
265
|
+
Django default values for `FileField` are `blank=False`.
|
266
|
+
"""
|
267
|
+
|
268
|
+
def __init__(self, *args, **kwargs):
|
269
|
+
kwargs.setdefault("blank", True)
|
270
|
+
super().__init__(*args, **kwargs)
|
271
|
+
|
272
|
+
|
273
|
+
class ImageField(models.ImageField):
|
274
|
+
"""Custom `ImageField` with default values for `blank`.
|
275
|
+
|
276
|
+
Django default values for `ImageField` are `blank=False`.
|
277
|
+
"""
|
278
|
+
|
279
|
+
def __init__(self, *args, **kwargs):
|
280
|
+
kwargs.setdefault("blank", True)
|
281
|
+
super().__init__(*args, **kwargs)
|
lamindb/base/ids.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
"""IDs.
|
2
|
+
|
3
|
+
Base generators:
|
4
|
+
|
5
|
+
.. autosummary::
|
6
|
+
:toctree: .
|
7
|
+
|
8
|
+
base26
|
9
|
+
base62
|
10
|
+
base64
|
11
|
+
|
12
|
+
8 base62 characters:
|
13
|
+
|
14
|
+
======= ===========
|
15
|
+
n p_collision
|
16
|
+
======= ===========
|
17
|
+
100k 2e-05
|
18
|
+
1M 2e-03
|
19
|
+
======= ===========
|
20
|
+
|
21
|
+
12 base62 characters:
|
22
|
+
|
23
|
+
======= ===========
|
24
|
+
n p_collision
|
25
|
+
======= ===========
|
26
|
+
100M 2e-06
|
27
|
+
1B 2e-04
|
28
|
+
======= ===========
|
29
|
+
|
30
|
+
20 base62 characters (62**20=7e+35) roughly matches UUID (2*122=5e+36):
|
31
|
+
|
32
|
+
======= ===========
|
33
|
+
n p_collision
|
34
|
+
======= ===========
|
35
|
+
3e15 1e-6
|
36
|
+
======= ===========
|
37
|
+
|
38
|
+
"""
|
39
|
+
|
40
|
+
import secrets
|
41
|
+
import string
|
42
|
+
|
43
|
+
|
44
|
+
def base64(n_char: int) -> str:
|
45
|
+
"""Random Base64 string."""
|
46
|
+
alphabet = string.digits + string.ascii_letters.swapcase() + "_" + "-"
|
47
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
48
|
+
return id
|
49
|
+
|
50
|
+
|
51
|
+
def base62(n_char: int) -> str:
|
52
|
+
"""Random Base62 string."""
|
53
|
+
alphabet = string.digits + string.ascii_letters.swapcase()
|
54
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
55
|
+
return id
|
56
|
+
|
57
|
+
|
58
|
+
# the following cannot be serialized by Django
|
59
|
+
# class Base62:
|
60
|
+
# def __init__(self, n_char: int):
|
61
|
+
# self.n_char = n_char
|
62
|
+
|
63
|
+
# def __call__(self):
|
64
|
+
# return base62(self.n_char)
|
65
|
+
|
66
|
+
|
67
|
+
def base26(n_char: int):
|
68
|
+
"""ASCII lowercase."""
|
69
|
+
alphabet = string.ascii_lowercase
|
70
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
71
|
+
return id
|
72
|
+
|
73
|
+
|
74
|
+
def base62_4() -> str:
|
75
|
+
return base62(4)
|
76
|
+
|
77
|
+
|
78
|
+
def base62_8() -> str:
|
79
|
+
return base62(8)
|
80
|
+
|
81
|
+
|
82
|
+
def base62_12() -> str:
|
83
|
+
return base62(12)
|
84
|
+
|
85
|
+
|
86
|
+
def base62_14() -> str:
|
87
|
+
return base62(14)
|
88
|
+
|
89
|
+
|
90
|
+
def base62_16() -> str:
|
91
|
+
return base62(16)
|
92
|
+
|
93
|
+
|
94
|
+
def base62_18() -> str:
|
95
|
+
return base62(18)
|
96
|
+
|
97
|
+
|
98
|
+
def base62_20() -> str:
|
99
|
+
return base62(20)
|
100
|
+
|
101
|
+
|
102
|
+
def base62_24() -> str:
|
103
|
+
return base62(24)
|
lamindb/base/types.py
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
"""Types.
|
2
|
+
|
3
|
+
Central object types.
|
4
|
+
|
5
|
+
.. autosummary::
|
6
|
+
:toctree: .
|
7
|
+
|
8
|
+
ArtifactKind
|
9
|
+
TransformType
|
10
|
+
FeatureDtype
|
11
|
+
|
12
|
+
Basic types.
|
13
|
+
|
14
|
+
.. autosummary::
|
15
|
+
:toctree: .
|
16
|
+
|
17
|
+
UPathStr
|
18
|
+
StrField
|
19
|
+
ListLike
|
20
|
+
FieldAttr
|
21
|
+
"""
|
22
|
+
|
23
|
+
from __future__ import annotations
|
24
|
+
|
25
|
+
from typing import Literal, Union
|
26
|
+
|
27
|
+
import numpy as np
|
28
|
+
import pandas as pd
|
29
|
+
from django.db.models.query_utils import DeferredAttribute as FieldAttr
|
30
|
+
from lamindb_setup.core.types import UPathStr # noqa: F401
|
31
|
+
|
32
|
+
# need to use Union because __future__.annotations doesn't do the job here <3.10
|
33
|
+
# typing.TypeAlias, >3.10 on but already deprecated
|
34
|
+
ListLike = Union[list[str], pd.Series, np.array]
|
35
|
+
StrField = Union[str, FieldAttr] # typing.TypeAlias
|
36
|
+
|
37
|
+
TransformType = Literal[
|
38
|
+
"pipeline", "notebook", "upload", "script", "function", "linker"
|
39
|
+
]
|
40
|
+
ArtifactKind = Literal["dataset", "model"]
|
41
|
+
FeatureDtype = Literal[
|
42
|
+
"cat", # categorical variables
|
43
|
+
"num", # numerical variables
|
44
|
+
"str", # string variables
|
45
|
+
"int", # integer variables
|
46
|
+
"float", # float variables
|
47
|
+
"bool", # boolean variables
|
48
|
+
"date", # date variables
|
49
|
+
"datetime", # datetime variables
|
50
|
+
"object", # this is a pandas type, we're only using it for complicated types, not for strings
|
51
|
+
]
|
lamindb/base/users.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
user_id_cache = {}
|
2
|
+
|
3
|
+
|
4
|
+
def current_user_id() -> int:
|
5
|
+
import lamindb_setup as ln_setup
|
6
|
+
from lamindb_setup import settings
|
7
|
+
from lamindb_setup._init_instance import register_user
|
8
|
+
|
9
|
+
from lamindb.models import User
|
10
|
+
|
11
|
+
def query_user_id():
|
12
|
+
if ln_setup.core.django.IS_MIGRATING:
|
13
|
+
return 1
|
14
|
+
else:
|
15
|
+
exc_attr = (
|
16
|
+
"DoesNotExist" if hasattr(User, "DoesNotExist") else "_DoesNotExist"
|
17
|
+
)
|
18
|
+
try:
|
19
|
+
user_id = User.objects.get(uid=settings.user.uid).id
|
20
|
+
except getattr(User, exc_attr):
|
21
|
+
register_user(settings.user)
|
22
|
+
user_id = User.objects.get(uid=settings.user.uid).id
|
23
|
+
return user_id
|
24
|
+
|
25
|
+
if settings._instance_exists:
|
26
|
+
if settings.instance.slug not in user_id_cache:
|
27
|
+
user_id_cache[settings.instance.slug] = query_user_id()
|
28
|
+
return user_id_cache[settings.instance.slug]
|
29
|
+
else:
|
30
|
+
return query_user_id()
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from typing import TYPE_CHECKING, Literal, Union, get_args, get_origin, get_type_hints
|
2
|
+
|
3
|
+
from lamin_utils import colors
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from .models import Record
|
7
|
+
|
8
|
+
|
9
|
+
class FieldValidationError(SystemExit):
|
10
|
+
"""Field validation error."""
|
11
|
+
|
12
|
+
pass
|
13
|
+
|
14
|
+
|
15
|
+
def validate_literal_fields(record: "Record", kwargs) -> None:
|
16
|
+
"""Validate all Literal type fields in a record.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
record: record being validated
|
20
|
+
|
21
|
+
Raises:
|
22
|
+
ValidationError: If any field value is not in its Literal's allowed values
|
23
|
+
"""
|
24
|
+
# check is based on string to avoid circular imports
|
25
|
+
if record.__class__.__name__ == "Feature":
|
26
|
+
# the FeatureDtype is more complicated than a simple literal
|
27
|
+
# because it allows constructs like cat[ULabel] etc.
|
28
|
+
# the User model is used at startup and throws a datetime-related error otherwise
|
29
|
+
# simmilar for Storage & Source
|
30
|
+
return None
|
31
|
+
try:
|
32
|
+
type_hints = get_type_hints(record.__class__)
|
33
|
+
except TypeError:
|
34
|
+
# for 3.9, get_type_hints errors with | in type hints
|
35
|
+
return
|
36
|
+
errors = {}
|
37
|
+
|
38
|
+
for field_name, field_type in type_hints.items():
|
39
|
+
# Handle both plain Literal and Union/Optional Literal types
|
40
|
+
origin = get_origin(field_type)
|
41
|
+
if origin is Union:
|
42
|
+
# For Optional/Union types, find the Literal type if it exists
|
43
|
+
literal_type = next(
|
44
|
+
(t for t in get_args(field_type) if get_origin(t) is Literal), None
|
45
|
+
)
|
46
|
+
else:
|
47
|
+
# For plain types, check if it's a Literal
|
48
|
+
literal_type = field_type if origin is Literal else None
|
49
|
+
|
50
|
+
# Skip if no Literal type found
|
51
|
+
if literal_type is None:
|
52
|
+
continue
|
53
|
+
|
54
|
+
value = kwargs.get(field_name)
|
55
|
+
if value is not None:
|
56
|
+
valid_values = set(get_args(literal_type))
|
57
|
+
if value not in valid_values:
|
58
|
+
errors[field_name] = (
|
59
|
+
f"{field_name}: {colors.yellow(value)} is not a valid value"
|
60
|
+
f"\n → Valid values are: {colors.green(', '.join(sorted(valid_values)))}"
|
61
|
+
)
|
62
|
+
|
63
|
+
if errors:
|
64
|
+
message = "\n "
|
65
|
+
for _, error in errors.items():
|
66
|
+
message += error + "\n "
|
67
|
+
raise FieldValidationError(message)
|
lamindb/core/__init__.py
CHANGED
@@ -5,10 +5,12 @@ Registries:
|
|
5
5
|
.. autosummary::
|
6
6
|
:toctree: .
|
7
7
|
|
8
|
+
BasicRecord
|
8
9
|
Record
|
9
10
|
Registry
|
10
11
|
QuerySet
|
11
12
|
QueryManager
|
13
|
+
Schema
|
12
14
|
RecordList
|
13
15
|
FeatureManager
|
14
16
|
ParamManager
|
@@ -59,7 +61,6 @@ Modules:
|
|
59
61
|
loaders
|
60
62
|
datasets
|
61
63
|
storage
|
62
|
-
types
|
63
64
|
exceptions
|
64
65
|
subsettings
|
65
66
|
logger
|
@@ -68,7 +69,21 @@ Modules:
|
|
68
69
|
|
69
70
|
from lamin_utils import logger
|
70
71
|
from lamin_utils._inspect import InspectResult
|
71
|
-
|
72
|
+
|
73
|
+
from lamindb._query_manager import QueryManager
|
74
|
+
from lamindb._query_set import QuerySet, RecordList
|
75
|
+
from lamindb.core._feature_manager import FeatureManager, ParamManager
|
76
|
+
from lamindb.core._label_manager import LabelManager
|
77
|
+
from lamindb.curators import (
|
78
|
+
AnnDataCurator,
|
79
|
+
BaseCurator,
|
80
|
+
CurateLookup,
|
81
|
+
DataFrameCurator,
|
82
|
+
MuDataCurator,
|
83
|
+
SOMACurator,
|
84
|
+
)
|
85
|
+
from lamindb.models import (
|
86
|
+
BasicRecord,
|
72
87
|
CanCurate,
|
73
88
|
FeatureValue,
|
74
89
|
HasParents,
|
@@ -76,24 +91,12 @@ from lnschema_core.models import (
|
|
76
91
|
ParamValue,
|
77
92
|
Record,
|
78
93
|
Registry,
|
94
|
+
Schema,
|
79
95
|
TracksRun,
|
80
96
|
TracksUpdates,
|
81
97
|
ValidateFields,
|
82
98
|
)
|
83
99
|
|
84
|
-
from lamindb._curate import (
|
85
|
-
AnnDataCurator,
|
86
|
-
BaseCurator,
|
87
|
-
CurateLookup,
|
88
|
-
DataFrameCurator,
|
89
|
-
MuDataCurator,
|
90
|
-
SOMACurator,
|
91
|
-
)
|
92
|
-
from lamindb._query_manager import QueryManager
|
93
|
-
from lamindb._query_set import QuerySet, RecordList
|
94
|
-
from lamindb.core._feature_manager import FeatureManager, ParamManager
|
95
|
-
from lamindb.core._label_manager import LabelManager
|
96
|
-
|
97
100
|
from . import _data, datasets, exceptions, fields, loaders, subsettings, types
|
98
101
|
from ._context import Context
|
99
102
|
from ._mapped_collection import MappedCollection
|