lamindb 1.1.0__py3-none-any.whl → 1.2.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.
Files changed (74) hide show
  1. lamindb/__init__.py +33 -26
  2. lamindb/_finish.py +9 -1
  3. lamindb/_tracked.py +26 -3
  4. lamindb/_view.py +2 -3
  5. lamindb/base/__init__.py +1 -1
  6. lamindb/base/ids.py +1 -10
  7. lamindb/base/users.py +1 -4
  8. lamindb/core/__init__.py +7 -65
  9. lamindb/core/_compat.py +60 -0
  10. lamindb/core/_context.py +50 -22
  11. lamindb/core/_mapped_collection.py +4 -2
  12. lamindb/core/_settings.py +6 -6
  13. lamindb/core/_sync_git.py +1 -1
  14. lamindb/core/_track_environment.py +2 -1
  15. lamindb/core/datasets/_small.py +3 -3
  16. lamindb/core/loaders.py +43 -20
  17. lamindb/core/storage/_anndata_accessor.py +8 -3
  18. lamindb/core/storage/_backed_access.py +14 -7
  19. lamindb/core/storage/_pyarrow_dataset.py +24 -9
  20. lamindb/core/storage/_tiledbsoma.py +8 -6
  21. lamindb/core/storage/_zarr.py +104 -25
  22. lamindb/core/storage/objects.py +63 -28
  23. lamindb/core/storage/paths.py +16 -13
  24. lamindb/core/types.py +10 -0
  25. lamindb/curators/__init__.py +176 -149
  26. lamindb/errors.py +1 -1
  27. lamindb/integrations/_vitessce.py +4 -4
  28. lamindb/migrations/0089_subsequent_runs.py +159 -0
  29. lamindb/migrations/0090_runproject_project_runs.py +73 -0
  30. lamindb/migrations/{0088_squashed.py → 0090_squashed.py} +245 -177
  31. lamindb/models/__init__.py +79 -0
  32. lamindb/{core → models}/_describe.py +3 -3
  33. lamindb/{core → models}/_django.py +8 -5
  34. lamindb/{core → models}/_feature_manager.py +103 -87
  35. lamindb/{_from_values.py → models/_from_values.py} +5 -2
  36. lamindb/{core/versioning.py → models/_is_versioned.py} +94 -6
  37. lamindb/{core → models}/_label_manager.py +10 -17
  38. lamindb/{core/relations.py → models/_relations.py} +8 -1
  39. lamindb/models/artifact.py +2602 -0
  40. lamindb/{_can_curate.py → models/can_curate.py} +349 -180
  41. lamindb/models/collection.py +683 -0
  42. lamindb/models/core.py +135 -0
  43. lamindb/models/feature.py +643 -0
  44. lamindb/models/flextable.py +163 -0
  45. lamindb/{_parents.py → models/has_parents.py} +55 -49
  46. lamindb/models/project.py +384 -0
  47. lamindb/{_query_manager.py → models/query_manager.py} +10 -8
  48. lamindb/{_query_set.py → models/query_set.py} +64 -32
  49. lamindb/models/record.py +1762 -0
  50. lamindb/models/run.py +563 -0
  51. lamindb/{_save.py → models/save.py} +18 -8
  52. lamindb/models/schema.py +732 -0
  53. lamindb/models/transform.py +360 -0
  54. lamindb/models/ulabel.py +249 -0
  55. {lamindb-1.1.0.dist-info → lamindb-1.2.0.dist-info}/METADATA +6 -6
  56. lamindb-1.2.0.dist-info/RECORD +95 -0
  57. lamindb/_artifact.py +0 -1361
  58. lamindb/_collection.py +0 -440
  59. lamindb/_feature.py +0 -316
  60. lamindb/_is_versioned.py +0 -40
  61. lamindb/_record.py +0 -1065
  62. lamindb/_run.py +0 -60
  63. lamindb/_schema.py +0 -347
  64. lamindb/_storage.py +0 -15
  65. lamindb/_transform.py +0 -170
  66. lamindb/_ulabel.py +0 -56
  67. lamindb/_utils.py +0 -9
  68. lamindb/base/validation.py +0 -63
  69. lamindb/core/_data.py +0 -491
  70. lamindb/core/fields.py +0 -12
  71. lamindb/models.py +0 -4435
  72. lamindb-1.1.0.dist-info/RECORD +0 -95
  73. {lamindb-1.1.0.dist-info → lamindb-1.2.0.dist-info}/LICENSE +0 -0
  74. {lamindb-1.1.0.dist-info → lamindb-1.2.0.dist-info}/WHEEL +0 -0
@@ -8,8 +8,6 @@ from lamindb_setup.core._docs import doc_args
8
8
 
9
9
  from lamindb.models import Record
10
10
 
11
- from .core._settings import settings
12
-
13
11
  if TYPE_CHECKING:
14
12
  from lamindb.base.types import StrField
15
13
 
@@ -19,7 +17,7 @@ class QueryManager(models.Manager):
19
17
 
20
18
  See Also:
21
19
 
22
- :class:`lamindb.core.QuerySet`
20
+ :class:`lamindb.models.QuerySet`
23
21
  `django Manager <https://docs.djangoproject.com/en/4.2/topics/db/managers/>`__
24
22
 
25
23
  Examples:
@@ -39,8 +37,12 @@ class QueryManager(models.Manager):
39
37
  self.source_field_name == "collection"
40
38
  and self.target_field_name == "artifact"
41
39
  ):
40
+ from lamindb import settings
42
41
  from lamindb.core._context import context
43
- from lamindb.core._data import WARNING_RUN_TRANSFORM, _track_run_input
42
+ from lamindb.models.artifact import (
43
+ WARNING_RUN_TRANSFORM,
44
+ _track_run_input,
45
+ )
44
46
 
45
47
  if (
46
48
  context.run is None
@@ -71,14 +73,14 @@ class QueryManager(models.Manager):
71
73
  def df(self, **kwargs):
72
74
  """Convert to DataFrame.
73
75
 
74
- For `**kwargs`, see :meth:`lamindb.core.QuerySet.df`.
76
+ For `**kwargs`, see :meth:`lamindb.models.QuerySet.df`.
75
77
  """
76
78
  return self.all().df(**kwargs)
77
79
 
78
80
  def all(self):
79
81
  """Return QuerySet of all.
80
82
 
81
- For `**kwargs`, see :meth:`lamindb.core.QuerySet.df`.
83
+ For `**kwargs`, see :meth:`lamindb.models.QuerySet.df`.
82
84
  """
83
85
  self._track_run_input_manager()
84
86
  return self._all_base_class()
@@ -86,14 +88,14 @@ class QueryManager(models.Manager):
86
88
  @doc_args(Record.search.__doc__)
87
89
  def search(self, string: str, **kwargs):
88
90
  """{}""" # noqa: D415
89
- from ._record import _search
91
+ from .record import _search
90
92
 
91
93
  return _search(cls=self.all(), string=string, **kwargs)
92
94
 
93
95
  @doc_args(Record.lookup.__doc__)
94
96
  def lookup(self, field: StrField | None = None, **kwargs) -> NamedTuple:
95
97
  """{}""" # noqa: D415
96
- from ._record import _lookup
98
+ from .record import _lookup
97
99
 
98
100
  return _lookup(cls=self.all(), field=field, **kwargs)
99
101
 
@@ -5,37 +5,27 @@ import warnings
5
5
  from collections import UserList
6
6
  from collections.abc import Iterable
7
7
  from collections.abc import Iterable as IterableType
8
- from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypeVar
8
+ from typing import TYPE_CHECKING, Any, Generic, NamedTuple, TypeVar, Union
9
9
 
10
10
  import pandas as pd
11
11
  from django.core.exceptions import FieldError
12
12
  from django.db import models
13
- from django.db.models import F, ForeignKey, ManyToManyField
13
+ from django.db.models import F, ForeignKey, ManyToManyField, Subquery
14
14
  from django.db.models.fields.related import ForeignObjectRel
15
15
  from lamin_utils import logger
16
16
  from lamindb_setup.core._docs import doc_args
17
17
 
18
- from lamindb.models import (
19
- Artifact,
20
- CanCurate,
21
- Collection,
22
- Feature,
23
- IsVersioned,
24
- Record,
25
- Run,
26
- Schema,
27
- Transform,
28
- )
18
+ from lamindb.models._is_versioned import IsVersioned
19
+ from lamindb.models.record import Record
29
20
 
30
- from .errors import DoesNotExist
31
-
32
- T = TypeVar("T")
21
+ from ..errors import DoesNotExist
22
+ from .can_curate import CanCurate
33
23
 
34
24
  if TYPE_CHECKING:
35
- from collections.abc import Iterable
36
-
37
25
  from lamindb.base.types import ListLike, StrField
38
26
 
27
+ T = TypeVar("T")
28
+
39
29
 
40
30
  class MultipleResultsFound(Exception):
41
31
  pass
@@ -82,6 +72,13 @@ def one_helper(self):
82
72
 
83
73
 
84
74
  def get_backward_compat_filter_kwargs(queryset, expressions):
75
+ from lamindb.models import (
76
+ Artifact,
77
+ Collection,
78
+ Schema,
79
+ Transform,
80
+ )
81
+
85
82
  if queryset.model in {Collection, Transform}:
86
83
  name_mappings = {
87
84
  "name": "key",
@@ -190,7 +187,7 @@ def process_expressions(queryset: QuerySet, expressions: dict) -> dict:
190
187
 
191
188
 
192
189
  def get(
193
- registry_or_queryset: type[Record] | QuerySet,
190
+ registry_or_queryset: Union[type[Record], QuerySet],
194
191
  idlike: int | str | None = None,
195
192
  **expressions,
196
193
  ) -> Record:
@@ -214,10 +211,27 @@ def get(
214
211
  else:
215
212
  assert idlike is None # noqa: S101
216
213
  expressions = process_expressions(qs, expressions)
214
+ # don't want _branch_code here in .get(), only in .filter()
215
+ expressions.pop("_branch_code", None)
217
216
  # inject is_latest for consistency with idlike
218
- if issubclass(registry, IsVersioned) and "is_latest" not in expressions:
217
+ is_latest_was_not_in_expressions = "is_latest" not in expressions
218
+ if issubclass(registry, IsVersioned) and is_latest_was_not_in_expressions:
219
219
  expressions["is_latest"] = True
220
- return registry.objects.using(qs.db).get(**expressions)
220
+ try:
221
+ return registry.objects.using(qs.db).get(**expressions)
222
+ except registry.DoesNotExist:
223
+ # handle the case in which the is_latest injection led to a missed query
224
+ if "is_latest" in expressions and is_latest_was_not_in_expressions:
225
+ expressions.pop("is_latest")
226
+ result = (
227
+ registry.objects.using(qs.db)
228
+ .filter(**expressions)
229
+ .order_by("-created_at")
230
+ .first()
231
+ )
232
+ if result is not None:
233
+ return result
234
+ raise registry.DoesNotExist from registry.DoesNotExist
221
235
 
222
236
 
223
237
  class RecordList(UserList, Generic[T]):
@@ -240,7 +254,7 @@ class RecordList(UserList, Generic[T]):
240
254
 
241
255
  def save(self) -> RecordList[T]:
242
256
  """Save all records to the database."""
243
- from lamindb._save import save
257
+ from lamindb.models.save import save
244
258
 
245
259
  save(self)
246
260
  return self
@@ -288,6 +302,11 @@ def get_basic_field_names(
288
302
 
289
303
 
290
304
  def get_feature_annotate_kwargs(show_features: bool | list[str]) -> dict[str, Any]:
305
+ from lamindb.models import (
306
+ Artifact,
307
+ Feature,
308
+ )
309
+
291
310
  features = Feature.filter()
292
311
  if isinstance(show_features, list):
293
312
  features.filter(name__in=show_features)
@@ -548,7 +567,17 @@ class QuerySet(models.QuerySet):
548
567
  include_kwargs = {s: F(s) for s in include if s not in field_names}
549
568
  annotate_kwargs.update(include_kwargs)
550
569
  if annotate_kwargs:
551
- queryset = self.annotate(**annotate_kwargs)
570
+ id_subquery = self.values("id")
571
+ # for annotate, we want the queryset without filters so that joins don't affect the annotations
572
+ query_set_without_filters = self.model.objects.filter(
573
+ id__in=Subquery(id_subquery)
574
+ )
575
+ if self.query.order_by:
576
+ # Apply the same ordering to the new queryset
577
+ query_set_without_filters = query_set_without_filters.order_by(
578
+ *self.query.order_by
579
+ )
580
+ queryset = query_set_without_filters.annotate(**annotate_kwargs)
552
581
  else:
553
582
  queryset = self
554
583
 
@@ -566,6 +595,8 @@ class QuerySet(models.QuerySet):
566
595
 
567
596
  def delete(self, *args, **kwargs):
568
597
  """Delete all records in the query set."""
598
+ from lamindb.models import Artifact, Collection, Run, Transform
599
+
569
600
  # both Transform & Run might reference artifacts
570
601
  if self.model in {Artifact, Collection, Transform, Run}:
571
602
  for record in self:
@@ -641,11 +672,12 @@ class QuerySet(models.QuerySet):
641
672
  and value.strip("-").isalpha()
642
673
  and "__" not in field
643
674
  and hasattr(self.model, field)
644
- and getattr(self.model, field).field.related_model
645
675
  ):
646
- raise FieldError(
647
- f"Invalid lookup '{value}' for {field}. Did you mean {field}__name?"
648
- )
676
+ field_attr = getattr(self.model, field)
677
+ if hasattr(field_attr, "field") and field_attr.field.related_model:
678
+ raise FieldError(
679
+ f"Invalid lookup '{value}' for {field}. Did you mean {field}__name?"
680
+ )
649
681
 
650
682
  expressions = process_expressions(self, expressions)
651
683
  if len(expressions) > 0:
@@ -689,7 +721,7 @@ class QuerySet(models.QuerySet):
689
721
  @doc_args(Record.search.__doc__)
690
722
  def search(self, string: str, **kwargs):
691
723
  """{}""" # noqa: D415
692
- from ._record import _search
724
+ from .record import _search
693
725
 
694
726
  return _search(cls=self, string=string, **kwargs)
695
727
 
@@ -697,7 +729,7 @@ def search(self, string: str, **kwargs):
697
729
  @doc_args(Record.lookup.__doc__)
698
730
  def lookup(self, field: StrField | None = None, **kwargs) -> NamedTuple:
699
731
  """{}""" # noqa: D415
700
- from ._record import _lookup
732
+ from .record import _lookup
701
733
 
702
734
  return _lookup(cls=self, field=field, **kwargs)
703
735
 
@@ -705,7 +737,7 @@ def lookup(self, field: StrField | None = None, **kwargs) -> NamedTuple:
705
737
  @doc_args(CanCurate.validate.__doc__)
706
738
  def validate(self, values: ListLike, field: str | StrField | None = None, **kwargs):
707
739
  """{}""" # noqa: D415
708
- from ._can_curate import _validate
740
+ from .can_curate import _validate
709
741
 
710
742
  return _validate(cls=self, values=values, field=field, **kwargs)
711
743
 
@@ -713,7 +745,7 @@ def validate(self, values: ListLike, field: str | StrField | None = None, **kwar
713
745
  @doc_args(CanCurate.inspect.__doc__)
714
746
  def inspect(self, values: ListLike, field: str | StrField | None = None, **kwargs):
715
747
  """{}""" # noqa: D415
716
- from ._can_curate import _inspect
748
+ from .can_curate import _inspect
717
749
 
718
750
  return _inspect(cls=self, values=values, field=field, **kwargs)
719
751
 
@@ -721,7 +753,7 @@ def inspect(self, values: ListLike, field: str | StrField | None = None, **kwarg
721
753
  @doc_args(CanCurate.standardize.__doc__)
722
754
  def standardize(self, values: Iterable, field: str | StrField | None = None, **kwargs):
723
755
  """{}""" # noqa: D415
724
- from ._can_curate import _standardize
756
+ from .can_curate import _standardize
725
757
 
726
758
  return _standardize(cls=self, values=values, field=field, **kwargs)
727
759