lamindb 1.12.1__py3-none-any.whl → 1.13.0__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.
@@ -1,4 +1,4 @@
1
- # Generated by Django 5.1.12 on 2025-10-06 06:11
1
+ # Generated by Django 5.2 on 2025-10-13 07:43
2
2
 
3
3
  import django.core.validators
4
4
  import django.db.models.deletion
@@ -210,6 +210,7 @@ class Migration(migrations.Migration):
210
210
  ("lamindb", "0131_record_unique_name_type_space"),
211
211
  ("lamindb", "0132_record_parents_record_reference_and_more"),
212
212
  ("lamindb", "0133_artifactuser_artifact_users"),
213
+ ("lamindb", "0134_run_params"),
213
214
  ]
214
215
 
215
216
  dependencies = [] # type: ignore
@@ -2086,6 +2087,7 @@ class Migration(migrations.Migration):
2086
2087
  blank=True, db_index=True, default=None, null=True
2087
2088
  ),
2088
2089
  ),
2090
+ ("params", models.JSONField(null=True)),
2089
2091
  (
2090
2092
  "reference",
2091
2093
  lamindb.base.fields.CharField(
@@ -5098,6 +5100,85 @@ class Migration(migrations.Migration):
5098
5100
  to="lamindb.user",
5099
5101
  ),
5100
5102
  ),
5103
+ migrations.CreateModel(
5104
+ name="ArtifactUser",
5105
+ fields=[
5106
+ (
5107
+ "created_at",
5108
+ lamindb.base.fields.DateTimeField(
5109
+ blank=True,
5110
+ db_default=django.db.models.functions.datetime.Now(),
5111
+ db_index=True,
5112
+ editable=False,
5113
+ ),
5114
+ ),
5115
+ ("id", models.BigAutoField(primary_key=True, serialize=False)),
5116
+ (
5117
+ "label_ref_is_name",
5118
+ lamindb.base.fields.BooleanField(
5119
+ blank=True, default=None, null=True
5120
+ ),
5121
+ ),
5122
+ (
5123
+ "feature_ref_is_name",
5124
+ lamindb.base.fields.BooleanField(
5125
+ blank=True, default=None, null=True
5126
+ ),
5127
+ ),
5128
+ (
5129
+ "artifact",
5130
+ lamindb.base.fields.ForeignKey(
5131
+ blank=True,
5132
+ on_delete=django.db.models.deletion.CASCADE,
5133
+ related_name="links_user",
5134
+ to="lamindb.artifact",
5135
+ ),
5136
+ ),
5137
+ (
5138
+ "feature",
5139
+ lamindb.base.fields.ForeignKey(
5140
+ blank=True,
5141
+ default=None,
5142
+ null=True,
5143
+ on_delete=django.db.models.deletion.PROTECT,
5144
+ related_name="links_artifactuser",
5145
+ to="lamindb.feature",
5146
+ ),
5147
+ ),
5148
+ (
5149
+ "run",
5150
+ lamindb.base.fields.ForeignKey(
5151
+ blank=True,
5152
+ default=lamindb.models.run.current_run,
5153
+ null=True,
5154
+ on_delete=django.db.models.deletion.PROTECT,
5155
+ related_name="+",
5156
+ to="lamindb.run",
5157
+ ),
5158
+ ),
5159
+ (
5160
+ "created_by",
5161
+ lamindb.base.fields.ForeignKey(
5162
+ blank=True,
5163
+ default=lamindb.base.users.current_user_id,
5164
+ editable=False,
5165
+ on_delete=django.db.models.deletion.PROTECT,
5166
+ related_name="+",
5167
+ to="lamindb.user",
5168
+ ),
5169
+ ),
5170
+ (
5171
+ "user",
5172
+ lamindb.base.fields.ForeignKey(
5173
+ blank=True,
5174
+ on_delete=django.db.models.deletion.PROTECT,
5175
+ related_name="links_artifact",
5176
+ to="lamindb.user",
5177
+ ),
5178
+ ),
5179
+ ],
5180
+ bases=(lamindb.models.sqlrecord.IsLink, models.Model),
5181
+ ),
5101
5182
  migrations.AddField(
5102
5183
  model_name="artifactreference",
5103
5184
  name="created_by",
@@ -5215,6 +5296,13 @@ class Migration(migrations.Migration):
5215
5296
  to="lamindb.user",
5216
5297
  ),
5217
5298
  ),
5299
+ migrations.AddField(
5300
+ model_name="artifact",
5301
+ name="users",
5302
+ field=models.ManyToManyField(
5303
+ related_name="+", through="lamindb.ArtifactUser", to="lamindb.user"
5304
+ ),
5305
+ ),
5218
5306
  migrations.AlterUniqueTogether(
5219
5307
  name="artifactrecord",
5220
5308
  unique_together={("artifact", "record", "feature")},
@@ -5346,6 +5434,10 @@ class Migration(migrations.Migration):
5346
5434
  name="unique_branch_name_lower",
5347
5435
  ),
5348
5436
  ),
5437
+ migrations.AlterUniqueTogether(
5438
+ name="artifactuser",
5439
+ unique_together={("artifact", "user", "feature")},
5440
+ ),
5349
5441
  migrations.AlterUniqueTogether(
5350
5442
  name="artifactulabel",
5351
5443
  unique_together={("artifact", "ulabel", "feature")},
@@ -5382,95 +5474,6 @@ class Migration(migrations.Migration):
5382
5474
  name="unique_artifact_storage_hash_null_key",
5383
5475
  ),
5384
5476
  ),
5385
- migrations.CreateModel(
5386
- name="ArtifactUser",
5387
- fields=[
5388
- (
5389
- "created_at",
5390
- lamindb.base.fields.DateTimeField(
5391
- blank=True,
5392
- db_default=django.db.models.functions.datetime.Now(),
5393
- db_index=True,
5394
- editable=False,
5395
- ),
5396
- ),
5397
- ("id", models.BigAutoField(primary_key=True, serialize=False)),
5398
- (
5399
- "label_ref_is_name",
5400
- lamindb.base.fields.BooleanField(
5401
- blank=True, default=None, null=True
5402
- ),
5403
- ),
5404
- (
5405
- "feature_ref_is_name",
5406
- lamindb.base.fields.BooleanField(
5407
- blank=True, default=None, null=True
5408
- ),
5409
- ),
5410
- (
5411
- "artifact",
5412
- lamindb.base.fields.ForeignKey(
5413
- blank=True,
5414
- on_delete=django.db.models.deletion.CASCADE,
5415
- related_name="links_user",
5416
- to="lamindb.artifact",
5417
- ),
5418
- ),
5419
- (
5420
- "created_by",
5421
- lamindb.base.fields.ForeignKey(
5422
- blank=True,
5423
- default=lamindb.base.users.current_user_id,
5424
- editable=False,
5425
- on_delete=django.db.models.deletion.PROTECT,
5426
- related_name="+",
5427
- to="lamindb.user",
5428
- ),
5429
- ),
5430
- (
5431
- "feature",
5432
- lamindb.base.fields.ForeignKey(
5433
- blank=True,
5434
- default=None,
5435
- null=True,
5436
- on_delete=django.db.models.deletion.PROTECT,
5437
- related_name="links_artifactuser",
5438
- to="lamindb.feature",
5439
- ),
5440
- ),
5441
- (
5442
- "run",
5443
- lamindb.base.fields.ForeignKey(
5444
- blank=True,
5445
- default=lamindb.models.run.current_run,
5446
- null=True,
5447
- on_delete=django.db.models.deletion.PROTECT,
5448
- related_name="+",
5449
- to="lamindb.run",
5450
- ),
5451
- ),
5452
- (
5453
- "user",
5454
- lamindb.base.fields.ForeignKey(
5455
- blank=True,
5456
- on_delete=django.db.models.deletion.PROTECT,
5457
- related_name="links_artifact",
5458
- to="lamindb.user",
5459
- ),
5460
- ),
5461
- ],
5462
- options={
5463
- "unique_together": {("artifact", "user", "feature")},
5464
- },
5465
- bases=(lamindb.models.sqlrecord.IsLink, models.Model),
5466
- ),
5467
- migrations.AddField(
5468
- model_name="artifact",
5469
- name="users",
5470
- field=models.ManyToManyField(
5471
- related_name="+", through="lamindb.ArtifactUser", to="lamindb.user"
5472
- ),
5473
- ),
5474
5477
  migrations.RunPython(
5475
5478
  apply_postgres_constraint, reverse_code=revert_postgres_constraint
5476
5479
  ),
@@ -345,7 +345,7 @@ def describe_features(
345
345
 
346
346
  internal_feature_names: dict[str, str] = {}
347
347
  if isinstance(self, Artifact):
348
- feature_sets = self.feature_sets.filter(itype="Feature").all()
348
+ feature_sets = self.feature_sets.filter(itype="Feature")
349
349
  internal_feature_names = {}
350
350
  if len(feature_sets) > 0:
351
351
  for schema in feature_sets:
@@ -470,7 +470,7 @@ def describe_features(
470
470
  for child in int_features_tree_children:
471
471
  dataset_tree.add(child)
472
472
 
473
- # Linked features
473
+ # External features
474
474
  ext_features_tree_children = []
475
475
  if external_data:
476
476
  ext_features_tree_children.append(
@@ -481,7 +481,7 @@ def describe_features(
481
481
  )
482
482
  )
483
483
  # ext_features_tree = None
484
- ext_features_header = Text("Linked features", style="bold dark_orange")
484
+ ext_features_header = Text("External features", style="bold dark_orange")
485
485
  if ext_features_tree_children:
486
486
  ext_features_tree = tree.add(ext_features_header)
487
487
  for child in ext_features_tree_children:
@@ -584,7 +584,7 @@ def filter_base(
584
584
  f"Some keys in the filter expression are not registered as features: {np.array(keys_normalized)[~validated]}"
585
585
  )
586
586
  new_expression = {}
587
- features = model.using(db).filter(name__in=keys_normalized).all().distinct()
587
+ features = model.using(db).filter(name__in=keys_normalized).distinct()
588
588
  feature_param = "feature"
589
589
  for key, value in expression.items():
590
590
  split_key = key.split("__")
@@ -650,7 +650,7 @@ def filter_base(
650
650
  # we need the comparator here because users might query like so
651
651
  # ln.Artifact.filter(experiment__contains="Experi")
652
652
  expression = {f"{field_name}{comparator}": value}
653
- labels = result["registry"].using(db).filter(**expression).all()
653
+ labels = result["registry"].using(db).filter(**expression)
654
654
  if len(labels) == 0:
655
655
  raise DoesNotExist(
656
656
  f"Did not find a {label_registry.__name__} matching `{field_name}{comparator}={value}`"
@@ -702,8 +702,8 @@ def filter_with_features(
702
702
 
703
703
  if expressions:
704
704
  keys_normalized = [key.split("__")[0] for key in expressions]
705
- field_or_feature_or_param = keys_normalized[0].split("__")[0]
706
- if field_or_feature_or_param in registry.__get_available_fields__():
705
+ field_or_feature = keys_normalized[0]
706
+ if field_or_feature in registry.__get_available_fields__():
707
707
  qs = queryset.filter(*queries, **expressions, **filter_kwargs)
708
708
  elif all(
709
709
  features_validated := Feature.objects.using(queryset.db).validate(
@@ -896,7 +896,7 @@ class FeatureManager:
896
896
  "feature_id": None,
897
897
  f"{field_name}__in": [l.id for _, l in registry_features_labels],
898
898
  }
899
- ).all().delete()
899
+ ).delete()
900
900
 
901
901
  def add_values(
902
902
  self,
@@ -928,11 +928,9 @@ class FeatureManager:
928
928
  model_name = "Feature"
929
929
 
930
930
  if schema is not None:
931
- from lamindb.curators import DataFrameCurator
931
+ from lamindb.curators.core import ExperimentalDictCurator
932
932
 
933
- temp_df = pd.DataFrame([values])
934
- curator = DataFrameCurator(temp_df, schema)
935
- curator.validate()
933
+ ExperimentalDictCurator(values, schema).validate()
936
934
  records = schema.members.filter(name__in=keys)
937
935
  else:
938
936
  records = registry.from_values(keys, field=feature_field, mute=True)
@@ -990,13 +988,25 @@ class FeatureManager:
990
988
  raise TypeError(
991
989
  f"Value for feature '{feature.name}' with dtype '{feature.dtype}' must be a string or record, but is {value} with dtype {inferred_type}"
992
990
  )
993
- elif (feature.dtype == "str" and feature.dtype not in inferred_type) or (
994
- feature.dtype != "str" and feature.dtype != inferred_type
991
+ elif (
992
+ (feature.dtype == "str" and inferred_type != "cat ? str")
993
+ or (feature.dtype == "list[str]" and inferred_type != "list[cat ? str]")
994
+ or (
995
+ feature.dtype.startswith("list[cat")
996
+ and inferred_type != "list[cat ? str]"
997
+ )
998
+ or (
999
+ feature.dtype not in {"str", "list[str]"}
1000
+ and not feature.dtype.startswith("list[cat")
1001
+ and feature.dtype != inferred_type
1002
+ )
995
1003
  ):
996
1004
  raise ValidationError(
997
1005
  f"Expected dtype for '{feature.name}' is '{feature.dtype}', got '{inferred_type}'"
998
1006
  )
999
- if not feature.dtype.startswith("cat"):
1007
+ if not (
1008
+ feature.dtype.startswith("cat") or feature.dtype.startswith("list[cat")
1009
+ ):
1000
1010
  filter_kwargs = {model_name.lower(): feature, "value": converted_value}
1001
1011
  feature_value, _ = value_model.get_or_create(**filter_kwargs)
1002
1012
  _feature_values.append(feature_value)
@@ -1078,7 +1088,7 @@ class FeatureManager:
1078
1088
  f"{model_name.lower()}__in": dict_typed_features,
1079
1089
  }
1080
1090
  try:
1081
- value_model.filter(**kwargs).all().delete()
1091
+ value_model.filter(**kwargs).delete()
1082
1092
  except ProtectedError:
1083
1093
  pass
1084
1094
  # add new feature links
@@ -1140,7 +1150,7 @@ class FeatureManager:
1140
1150
  if obj.related_model.__get_name_with_module__()
1141
1151
  in link_models_on_models
1142
1152
  }.pop()
1143
- getattr(self._host, link_attribute).filter(**filter_kwargs).all().delete()
1153
+ getattr(self._host, link_attribute).filter(**filter_kwargs).delete()
1144
1154
  else:
1145
1155
  if value is not None:
1146
1156
  filter_kwargs["value"] = value
@@ -1215,7 +1225,7 @@ class FeatureManager:
1215
1225
  member_uids = list(members.values_list(field, flat=True))
1216
1226
  validated = registry.validate(member_uids, field=field, mute=True)
1217
1227
  new_members_uids = list(compress(member_uids, ~validated))
1218
- new_members = members.filter(**{f"{field}__in": new_members_uids}).all()
1228
+ new_members = members.filter(**{f"{field}__in": new_members_uids})
1219
1229
  n_new_members = len(new_members)
1220
1230
  if len(members) > settings.annotation.n_max_records:
1221
1231
  logger.warning(
@@ -1275,9 +1285,9 @@ class FeatureManager:
1275
1285
  """
1276
1286
  if not isinstance(feature, Feature):
1277
1287
  raise TypeError("feature must be a Feature record!")
1278
- feature_sets = Schema.filter(features=feature).all()
1288
+ feature_sets = Schema.filter(features=feature)
1279
1289
  for fs in feature_sets:
1280
- f = Feature.filter(uid=feature.uid).all()
1290
+ f = Feature.filter(uid=feature.uid)
1281
1291
  features_updated = fs.members.difference(f)
1282
1292
  if len(features_updated) > 0:
1283
1293
  # re-compute the hash of feature sets based on the updated members
@@ -231,7 +231,7 @@ class LabelManager:
231
231
  >>> artifact2 = ln.Artifact(pd.DataFrame(index=[2, 3])).save()
232
232
  >>> ulabels = ln.ULabel.from_values(["Label1", "Label2"], field="name")
233
233
  >>> ln.save(ulabels)
234
- >>> labels = ln.ULabel.filter(name__icontains = "label").all()
234
+ >>> labels = ln.ULabel.filter(name__icontains = "label")
235
235
  >>> artifact1.ulabels.set(labels)
236
236
  >>> artifact2.labels.add_from(artifact1)
237
237
  """
@@ -260,10 +260,8 @@ class LabelManager:
260
260
  key = None
261
261
  keys.append(key)
262
262
  else:
263
- links = (
264
- getattr(label, f"links_{data_name_lower}")
265
- .filter(**{f"{data_name_lower}_id": data.id})
266
- .all()
263
+ links = getattr(label, f"links_{data_name_lower}").filter(
264
+ **{f"{data_name_lower}_id": data.id}
267
265
  )
268
266
  for link in links:
269
267
  if link.feature is not None: