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
lamindb/_run.py DELETED
@@ -1,60 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from lamindb.models import ParamManager, Run, Transform
4
-
5
-
6
- def __init__(run: Run, *args, **kwargs):
7
- run.params = ParamManager(run) # type: ignore
8
- if len(args) == len(run._meta.concrete_fields):
9
- super(Run, run).__init__(*args, **kwargs)
10
- return None
11
- # now we proceed with the user-facing constructor
12
- if len(args) > 1:
13
- raise ValueError("Only one non-keyword arg allowed: transform")
14
- transform: Transform = None
15
- if "transform" in kwargs or len(args) == 1:
16
- transform = kwargs.pop("transform") if len(args) == 0 else args[0]
17
- reference: str | None = kwargs.pop("reference") if "reference" in kwargs else None
18
- reference_type: str | None = (
19
- kwargs.pop("reference_type") if "reference_type" in kwargs else None
20
- )
21
- initiated_by_run: Run | None = kwargs.pop("initiated_by_run", None)
22
- if transform is None:
23
- raise TypeError("Pass transform parameter")
24
- if transform._state.adding:
25
- raise ValueError("Please save transform record before creating a run")
26
-
27
- super(Run, run).__init__( # type: ignore
28
- transform=transform,
29
- reference=reference,
30
- initiated_by_run=initiated_by_run,
31
- reference_type=reference_type,
32
- )
33
-
34
-
35
- def delete_run_artifacts(run: Run) -> None:
36
- environment = None
37
- if run.environment is not None:
38
- environment = run.environment
39
- run.environment = None
40
- report = None
41
- if run.report is not None:
42
- report = run.report
43
- run.report = None
44
- if environment is not None or report is not None:
45
- run.save()
46
- if environment is not None:
47
- # only delete if there are no other runs attached to this environment
48
- if environment._environment_of.count() == 0:
49
- environment.delete(permanent=True)
50
- if report is not None:
51
- report.delete(permanent=True)
52
-
53
-
54
- def delete(self) -> None:
55
- delete_run_artifacts(self)
56
- super(Run, self).delete()
57
-
58
-
59
- Run.__init__ = __init__ # type: ignore
60
- Run.delete = delete # type: ignore
lamindb/_schema.py DELETED
@@ -1,347 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING
4
-
5
- import lamindb_setup as ln_setup
6
- import numpy as np
7
- from lamin_utils import logger
8
- from lamindb_setup.core._docs import doc_args
9
- from lamindb_setup.core.hashing import hash_set
10
-
11
- from lamindb.base import ids
12
- from lamindb.base.types import FieldAttr, ListLike
13
- from lamindb.errors import InvalidArgument
14
- from lamindb.models import Feature, Record, Schema
15
-
16
- from ._feature import convert_pandas_dtype_to_lamin_dtype, get_dtype_str_from_dtype
17
- from ._record import init_self_from_db, update_attributes
18
- from ._utils import attach_func_to_class_method
19
- from .core.relations import (
20
- dict_related_model_to_related_name,
21
- get_related_name,
22
- )
23
- from .errors import ValidationError
24
-
25
- if TYPE_CHECKING:
26
- from collections.abc import Iterable
27
-
28
- import pandas as pd
29
- from django.db.models.query_utils import DeferredAttribute
30
-
31
- from ._query_set import QuerySet
32
-
33
- NUMBER_TYPE = "num"
34
- DICT_KEYS_TYPE = type({}.keys()) # type: ignore
35
-
36
-
37
- def validate_features(features: list[Record]) -> Record:
38
- """Validate and return feature type."""
39
- try:
40
- if len(features) == 0:
41
- raise ValueError("Provide list of features with at least one element")
42
- except TypeError:
43
- raise ValueError(
44
- "Please pass a ListLike of features, not a single feature"
45
- ) from None
46
- if not hasattr(features, "__getitem__"):
47
- raise TypeError("features has to be list-like")
48
- if not isinstance(features[0], Record):
49
- raise TypeError(
50
- "features has to store feature records! use .from_values() otherwise"
51
- )
52
- feature_types = {feature.__class__ for feature in features}
53
- if len(feature_types) > 1:
54
- raise TypeError("schema can only contain a single type")
55
- for feature in features:
56
- if feature._state.adding:
57
- raise ValueError("Can only construct feature sets from validated features")
58
- return next(iter(feature_types)) # return value in set of cardinality 1
59
-
60
-
61
- def __init__(self, *args, **kwargs):
62
- if len(args) == len(self._meta.concrete_fields):
63
- super(Schema, self).__init__(*args, **kwargs)
64
- return None
65
- if len(args) > 1:
66
- raise ValueError("Only one non-keyword arg allowed: features")
67
-
68
- features: Iterable[Record] | None = args[0] if args else kwargs.pop("features", [])
69
- # typing here anticipates transitioning to a ManyToMany
70
- # between composites and components similar to feature_sets
71
- # in lamindb v2
72
- components: dict[str, Schema] = kwargs.pop("components", {})
73
- name: str | None = kwargs.pop("name", None)
74
- description: str | None = kwargs.pop("description", None)
75
- dtype: str | None = kwargs.pop("dtype", None)
76
- itype: str | Record | DeferredAttribute | None = kwargs.pop("itype", None)
77
- type: Feature | None = kwargs.pop("type", None)
78
- is_type: bool = kwargs.pop("is_type", False)
79
- otype: str | None = kwargs.pop("otype", None)
80
- minimal_set: bool = kwargs.pop("minimal_set", True)
81
- ordered_set: bool = kwargs.pop("ordered_set", False)
82
- maximal_set: bool = kwargs.pop("maximal_set", False)
83
- slot: str | None = kwargs.pop("slot", None)
84
- coerce_dtype: bool | None = kwargs.pop("coerce_dtype", None)
85
-
86
- if kwargs:
87
- raise ValueError(
88
- f"Unexpected keyword arguments: {', '.join(kwargs.keys())}\n"
89
- "Valid arguments are: features, description, dtype, itype, type, "
90
- "is_type, otype, minimal_set, ordered_set, maximal_set, "
91
- "slot, validated_by, coerce_dtype"
92
- )
93
-
94
- if features:
95
- features_registry = validate_features(features)
96
- itype_compare = features_registry.__get_name_with_module__()
97
- if itype is not None:
98
- assert itype == itype_compare, str(itype_compare) # noqa: S101
99
- else:
100
- itype = itype_compare
101
- n_features = len(features)
102
- else:
103
- n_features = -1
104
- if dtype is None:
105
- dtype = None if itype is not None and itype == "Feature" else NUMBER_TYPE
106
- else:
107
- dtype = get_type_str(dtype)
108
- components: dict[str, Schema]
109
- if components:
110
- itype = "Composite"
111
- if otype is None:
112
- raise InvalidArgument("Please pass otype != None for composite schemas")
113
- if itype is not None and not isinstance(itype, str):
114
- itype_str = get_dtype_str_from_dtype(itype, is_itype=True)
115
- else:
116
- itype_str = itype
117
- validated_kwargs = {
118
- "name": name,
119
- "description": description,
120
- "type": type,
121
- "dtype": dtype,
122
- "is_type": is_type,
123
- "otype": otype,
124
- "n": n_features,
125
- "itype": itype_str,
126
- "minimal_set": minimal_set,
127
- "ordered_set": ordered_set,
128
- "maximal_set": maximal_set,
129
- }
130
- if coerce_dtype:
131
- validated_kwargs["_aux"] = {"af": {"0": coerce_dtype}}
132
- if features:
133
- hash = hash_set({feature.uid for feature in features})
134
- elif components:
135
- hash = hash_set({component.hash for component in components.values()})
136
- else:
137
- hash = hash_set({str(value) for value in validated_kwargs.values()})
138
- validated_kwargs["hash"] = hash
139
- validated_kwargs["slot"] = slot
140
- schema = Schema.filter(hash=hash).one_or_none()
141
- if schema is not None:
142
- logger.important(f"returning existing schema with same hash: {schema}")
143
- init_self_from_db(self, schema)
144
- update_attributes(self, validated_kwargs)
145
- return None
146
- if features:
147
- self._features = (get_related_name(features_registry), features)
148
- elif components:
149
- for slot, component in components.items():
150
- if component._state.adding:
151
- raise InvalidArgument(
152
- f"component {slot} {component} must be saved before use"
153
- )
154
- self._components = components
155
- validated_kwargs["uid"] = ids.base62_20()
156
- super(Schema, self).__init__(**validated_kwargs)
157
-
158
-
159
- @doc_args(Schema.save.__doc__)
160
- def save(self, *args, **kwargs) -> Schema:
161
- """{}""" # noqa: D415
162
- from lamindb._save import bulk_create
163
-
164
- super(Schema, self).save(*args, **kwargs)
165
- if hasattr(self, "_components"):
166
- # analogous to save_schema_links in core._data.py
167
- # which is called to save feature sets in artifact.save()
168
- links = []
169
- for slot, component in self._components.items():
170
- kwargs = {
171
- "composite_id": self.id,
172
- "component_id": component.id,
173
- "slot": slot,
174
- }
175
- links.append(Schema.components.through(**kwargs))
176
- bulk_create(links, ignore_conflicts=True)
177
- if hasattr(self, "_features"):
178
- assert self.n > 0 # noqa: S101
179
- related_name, records = self._features
180
- # only the following method preserves the order
181
- # .set() does not preserve the order but orders by
182
- # the feature primary key
183
- through_model = getattr(self, related_name).through
184
- related_model_split = self.itype.split(".")
185
- if len(related_model_split) == 1:
186
- related_field = related_model_split[0].lower()
187
- else:
188
- related_field = related_model_split[1].lower()
189
- related_field_id = f"{related_field}_id"
190
- links = [
191
- through_model(**{"schema_id": self.id, related_field_id: record.id})
192
- for record in records
193
- ]
194
- through_model.objects.bulk_create(links, ignore_conflicts=True)
195
- return self
196
-
197
-
198
- def get_type_str(dtype: str | None) -> str | None:
199
- if dtype is not None:
200
- type_str = dtype.__name__ if not isinstance(dtype, str) else dtype # type: ignore
201
- else:
202
- type_str = None
203
- return type_str
204
-
205
-
206
- @classmethod # type:ignore
207
- @doc_args(Schema.from_values.__doc__)
208
- def from_values(
209
- cls,
210
- values: ListLike,
211
- field: FieldAttr = Feature.name,
212
- type: str | None = None,
213
- name: str | None = None,
214
- mute: bool = False,
215
- organism: Record | str | None = None,
216
- source: Record | None = None,
217
- raise_validation_error: bool = True,
218
- ) -> Schema:
219
- """{}""" # noqa: D415
220
- if not isinstance(field, FieldAttr):
221
- raise TypeError("Argument `field` must be a Record field, e.g., `Feature.name`")
222
- if len(values) == 0:
223
- raise ValueError("Provide a list of at least one value")
224
- if isinstance(values, DICT_KEYS_TYPE):
225
- values = list(values)
226
- registry = field.field.model
227
- if registry != Feature and type is None:
228
- type = NUMBER_TYPE
229
- logger.debug("setting feature set to 'number'")
230
- validated = registry.validate(values, field=field, mute=mute, organism=organism)
231
- values_array = np.array(values)
232
- validated_values = values_array[validated]
233
- if validated.sum() != len(values):
234
- not_validated_values = values_array[~validated]
235
- msg = (
236
- f"These values could not be validated: {not_validated_values.tolist()}\n"
237
- f"If there are no typos, add them to their registry: {registry.__name__}"
238
- )
239
- if raise_validation_error:
240
- raise ValidationError(msg)
241
- elif len(validated_values) == 0:
242
- return None # temporarily return None here
243
- validated_features = registry.from_values(
244
- validated_values,
245
- field=field,
246
- organism=organism,
247
- source=source,
248
- )
249
- schema = Schema(
250
- features=validated_features,
251
- name=name,
252
- dtype=get_type_str(type),
253
- )
254
- return schema
255
-
256
-
257
- @classmethod # type:ignore
258
- @doc_args(Schema.from_df.__doc__)
259
- def from_df(
260
- cls,
261
- df: pd.DataFrame,
262
- field: FieldAttr = Feature.name,
263
- name: str | None = None,
264
- mute: bool = False,
265
- organism: Record | str | None = None,
266
- source: Record | None = None,
267
- ) -> Schema | None:
268
- """{}""" # noqa: D415
269
- registry = field.field.model
270
- validated = registry.validate(df.columns, field=field, mute=mute, organism=organism)
271
- if validated.sum() == 0:
272
- if mute is True:
273
- logger.warning("no validated features, skip creating feature set")
274
- return None
275
- if registry == Feature:
276
- validated_features = Feature.from_values( # type: ignore
277
- df.columns, field=field, organism=organism
278
- )
279
- schema = Schema(validated_features, name=name, dtype=None, otype="DataFrame")
280
- else:
281
- dtypes = [col.dtype for (_, col) in df.loc[:, validated].items()]
282
- if len(set(dtypes)) != 1:
283
- raise ValueError(f"data types are heterogeneous: {set(dtypes)}")
284
- dtype = convert_pandas_dtype_to_lamin_dtype(dtypes[0])
285
- validated_features = registry.from_values(
286
- df.columns[validated],
287
- field=field,
288
- organism=organism,
289
- source=source,
290
- )
291
- schema = Schema(
292
- features=validated_features,
293
- name=name,
294
- dtype=get_type_str(dtype),
295
- otype="DataFrame",
296
- )
297
- return schema
298
-
299
-
300
- @property # type: ignore
301
- @doc_args(Schema.members.__doc__)
302
- def members(self) -> QuerySet:
303
- """{}""" # noqa: D415
304
- if self._state.adding:
305
- # this should return a queryset and not a list...
306
- # need to fix this
307
- return self._features[1]
308
- related_name = self._get_related_name()
309
- if related_name is None:
310
- related_name = "features"
311
- return self.__getattribute__(related_name).order_by("links_schema__id")
312
-
313
-
314
- def _get_related_name(self: Schema) -> str:
315
- related_models = dict_related_model_to_related_name(self, instance=self._state.db)
316
- related_name = related_models.get(self.itype)
317
- return related_name
318
-
319
-
320
- METHOD_NAMES = [
321
- "__init__",
322
- "from_values",
323
- "from_df",
324
- "save",
325
- ]
326
-
327
- if ln_setup._TESTING:
328
- from inspect import signature
329
-
330
- SIGS = {
331
- name: signature(getattr(Schema, name))
332
- for name in METHOD_NAMES
333
- if name != "__init__"
334
- }
335
-
336
- for name in METHOD_NAMES:
337
- attach_func_to_class_method(name, Schema, globals())
338
-
339
- Schema.members = members # type: ignore
340
- Schema._get_related_name = _get_related_name
341
- # excluded on docs via
342
- # https://github.com/laminlabs/lndocs/blob/8c1963de65445107ea69b3fd59354c3828e067d1/lndocs/lamin_sphinx/__init__.py#L584-L588
343
- delattr(Schema, "validated_by") # we don't want to expose these
344
- delattr(Schema, "validated_by_id") # we don't want to expose these
345
- delattr(Schema, "validated_schemas") # we don't want to expose these
346
- delattr(Schema, "composite") # we don't want to expose these
347
- delattr(Schema, "composite_id") # we don't want to expose these
lamindb/_storage.py DELETED
@@ -1,15 +0,0 @@
1
- from lamindb_setup.core._docs import doc_args
2
- from lamindb_setup.core.upath import UPath, create_path
3
-
4
- from lamindb.models import Storage
5
-
6
-
7
- @property # type: ignore
8
- @doc_args(Storage.path.__doc__)
9
- def path(self) -> UPath:
10
- """{}""" # noqa: D415
11
- access_token = self._access_token if hasattr(self, "_access_token") else None
12
- return create_path(self.root, access_token=access_token)
13
-
14
-
15
- Storage.path = path # type: ignore
lamindb/_transform.py DELETED
@@ -1,170 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import warnings
4
- from typing import TYPE_CHECKING
5
-
6
- from lamin_utils import logger
7
- from lamindb_setup.core._docs import doc_args
8
- from lamindb_setup.core.hashing import hash_string
9
-
10
- from lamindb.models import Run, Transform
11
-
12
- from ._parents import _view_parents
13
- from ._record import init_self_from_db, update_attributes
14
- from ._run import delete_run_artifacts
15
- from .core._settings import settings
16
- from .core.versioning import message_update_key_in_version_family, process_revises
17
- from .errors import InconsistentKey
18
-
19
- if TYPE_CHECKING:
20
- from lamindb.base.types import TransformType
21
-
22
-
23
- def __init__(transform: Transform, *args, **kwargs):
24
- if len(args) == len(transform._meta.concrete_fields):
25
- super(Transform, transform).__init__(*args, **kwargs)
26
- return None
27
- key: str | None = kwargs.pop("key") if "key" in kwargs else None
28
- description: str | None = (
29
- kwargs.pop("description") if "description" in kwargs else None
30
- )
31
- revises: Transform | None = kwargs.pop("revises") if "revises" in kwargs else None
32
- version: str | None = kwargs.pop("version") if "version" in kwargs else None
33
- type: TransformType | None = kwargs.pop("type") if "type" in kwargs else "pipeline"
34
- reference: str | None = kwargs.pop("reference") if "reference" in kwargs else None
35
- reference_type: str | None = (
36
- kwargs.pop("reference_type") if "reference_type" in kwargs else None
37
- )
38
- using_key = (
39
- kwargs.pop("using_key") if "using_key" in kwargs else settings._using_key
40
- )
41
- if "name" in kwargs:
42
- if key is None:
43
- key = kwargs.pop("name")
44
- warnings.warn(
45
- f"`name` will be removed soon, please pass '{key}' to `key` instead",
46
- FutureWarning,
47
- stacklevel=2,
48
- )
49
- else:
50
- # description wasn't exist, so no check necessary
51
- description = kwargs.pop("name")
52
- warnings.warn(
53
- f"`name` will be removed soon, please pass '{description}' to `description` instead",
54
- FutureWarning,
55
- stacklevel=2,
56
- )
57
- # below is internal use that we'll hopefully be able to eliminate
58
- uid: str | None = kwargs.pop("uid") if "uid" in kwargs else None
59
- source_code: str | None = (
60
- kwargs.pop("source_code") if "source_code" in kwargs else None
61
- )
62
- if not len(kwargs) == 0:
63
- raise ValueError(
64
- "Only key, description, version, type, revises, reference, "
65
- f"reference_type can be passed, but you passed: {kwargs}"
66
- )
67
- if revises is None:
68
- # need to check uid before checking key
69
- if uid is not None:
70
- revises = (
71
- Transform.objects.using(using_key)
72
- .filter(uid__startswith=uid[:-4], is_latest=True)
73
- .order_by("-created_at")
74
- .first()
75
- )
76
- elif key is not None:
77
- candidate_for_revises = (
78
- Transform.objects.using(using_key)
79
- .filter(key=key, is_latest=True)
80
- .order_by("-created_at")
81
- .first()
82
- )
83
- if candidate_for_revises is not None:
84
- revises = candidate_for_revises
85
- if candidate_for_revises.source_code is None:
86
- # no source code was yet saved, return the same transform
87
- logger.important(
88
- "no source code was yet saved, returning existing transform with same key"
89
- )
90
- uid = revises.uid
91
- if revises is not None and uid is not None and uid == revises.uid:
92
- if revises.key != key:
93
- logger.warning("ignoring inconsistent key")
94
- init_self_from_db(transform, revises)
95
- update_attributes(transform, {"description": description})
96
- return None
97
- if revises is not None and key is not None and revises.key != key:
98
- note = message_update_key_in_version_family(
99
- suid=revises.stem_uid,
100
- existing_key=revises.key,
101
- new_key=key,
102
- registry="Transform",
103
- )
104
- raise InconsistentKey(
105
- f"`key` is '{key}', but `revises.key` is '{revises.key}'\n\nEither do *not* pass `key`.\n\n{note}"
106
- )
107
- new_uid, version, key, description, revises = process_revises(
108
- revises, version, key, description, Transform
109
- )
110
- # this is only because the user-facing constructor allows passing a uid
111
- # most others don't
112
- if uid is None:
113
- has_consciously_provided_uid = False
114
- uid = new_uid
115
- else:
116
- has_consciously_provided_uid = True
117
- hash = None
118
- if source_code is not None:
119
- hash = hash_string(source_code)
120
- transform_candidate = Transform.filter(hash=hash, is_latest=True).one_or_none()
121
- if transform_candidate is not None:
122
- init_self_from_db(transform, transform_candidate)
123
- update_attributes(transform, {"key": key, "description": description})
124
- return None
125
- super(Transform, transform).__init__( # type: ignore
126
- uid=uid,
127
- description=description,
128
- key=key,
129
- type=type,
130
- version=version,
131
- reference=reference,
132
- reference_type=reference_type,
133
- source_code=source_code,
134
- hash=hash,
135
- _has_consciously_provided_uid=has_consciously_provided_uid,
136
- revises=revises,
137
- )
138
-
139
-
140
- def delete(self) -> None:
141
- # query all runs and delete their artifacts
142
- runs = Run.filter(transform=self)
143
- for run in runs:
144
- delete_run_artifacts(run)
145
- # at this point, all artifacts have been taken care of
146
- # we can now leverage CASCADE delete
147
- super(Transform, self).delete()
148
-
149
-
150
- @property # type: ignore
151
- @doc_args(Transform.latest_run.__doc__)
152
- def latest_run(self) -> Run:
153
- """{}""" # noqa: D415
154
- return self.runs.order_by("-started_at").first()
155
-
156
-
157
- def view_lineage(self, with_successors: bool = False, distance: int = 5):
158
- return _view_parents(
159
- record=self,
160
- field="key",
161
- with_children=with_successors,
162
- distance=distance,
163
- attr_name="predecessors",
164
- )
165
-
166
-
167
- Transform.__init__ = __init__ # type: ignore
168
- Transform.delete = delete # type: ignore
169
- Transform.latest_run = latest_run # type: ignore
170
- Transform.view_lineage = view_lineage # type: ignore
lamindb/_ulabel.py DELETED
@@ -1,56 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import lamindb_setup as ln_setup
4
-
5
- from lamindb._record import _get_record_kwargs
6
- from lamindb.errors import FieldValidationError
7
- from lamindb.models import ULabel
8
-
9
- from ._utils import attach_func_to_class_method
10
-
11
-
12
- def __init__(self, *args, **kwargs):
13
- if len(args) == len(self._meta.concrete_fields):
14
- super(ULabel, self).__init__(*args, **kwargs)
15
- return None
16
- # now we proceed with the user-facing constructor
17
- if len(args) > 0:
18
- raise ValueError("Only one non-keyword arg allowed")
19
- name: str = kwargs.pop("name") if "name" in kwargs else None
20
- type: str | None = kwargs.pop("type") if "type" in kwargs else None
21
- is_type: bool = kwargs.pop("is_type") if "is_type" in kwargs else False
22
- description: str | None = (
23
- kwargs.pop("description") if "description" in kwargs else None
24
- )
25
- reference: str | None = kwargs.pop("reference") if "reference" in kwargs else None
26
- reference_type: str | None = (
27
- kwargs.pop("reference_type") if "reference_type" in kwargs else None
28
- )
29
- if len(kwargs) > 0:
30
- valid_keywords = ", ".join([val[0] for val in _get_record_kwargs(ULabel)])
31
- raise FieldValidationError(f"Only {valid_keywords} are valid keyword arguments")
32
- super(ULabel, self).__init__(
33
- name=name,
34
- type=type,
35
- is_type=is_type,
36
- description=description,
37
- reference=reference,
38
- reference_type=reference_type,
39
- )
40
-
41
-
42
- METHOD_NAMES = [
43
- "__init__",
44
- ]
45
-
46
- if ln_setup._TESTING:
47
- from inspect import signature
48
-
49
- SIGS = {
50
- name: signature(getattr(ULabel, name))
51
- for name in METHOD_NAMES
52
- if name != "__init__"
53
- }
54
-
55
- for name in METHOD_NAMES:
56
- attach_func_to_class_method(name, ULabel, globals())
lamindb/_utils.py DELETED
@@ -1,9 +0,0 @@
1
- def attach_func_to_class_method(func_name, cls, globals):
2
- implementation = globals[func_name]
3
- target = getattr(cls, func_name)
4
- # assigning the original class definition docstring
5
- # to the implementation only has an effect for regular methods
6
- # not for class methods
7
- # this is why we need @doc_args for class methods
8
- implementation.__doc__ = target.__doc__
9
- setattr(cls, func_name, implementation)