lamindb 1.3.1__py3-none-any.whl → 1.4.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.
- lamindb/__init__.py +3 -3
- lamindb/core/_context.py +64 -69
- lamindb/core/datasets/_small.py +2 -2
- lamindb/curators/__init__.py +683 -893
- lamindb/models/__init__.py +8 -1
- lamindb/models/_feature_manager.py +23 -19
- lamindb/models/_from_values.py +1 -1
- lamindb/models/_is_versioned.py +5 -15
- lamindb/models/artifact.py +210 -111
- lamindb/models/can_curate.py +4 -1
- lamindb/models/collection.py +6 -4
- lamindb/models/feature.py +27 -30
- lamindb/models/has_parents.py +22 -7
- lamindb/models/project.py +2 -2
- lamindb/models/query_set.py +6 -35
- lamindb/models/record.py +167 -117
- lamindb/models/run.py +56 -2
- lamindb/models/save.py +1 -3
- lamindb/models/schema.py +277 -77
- lamindb/models/transform.py +4 -13
- {lamindb-1.3.1.dist-info → lamindb-1.4.0.dist-info}/METADATA +6 -5
- {lamindb-1.3.1.dist-info → lamindb-1.4.0.dist-info}/RECORD +24 -24
- {lamindb-1.3.1.dist-info → lamindb-1.4.0.dist-info}/LICENSE +0 -0
- {lamindb-1.3.1.dist-info → lamindb-1.4.0.dist-info}/WHEEL +0 -0
lamindb/__init__.py
CHANGED
@@ -72,7 +72,7 @@ Backward compatibility.
|
|
72
72
|
|
73
73
|
# ruff: noqa: I001
|
74
74
|
# denote a release candidate for 0.1.0 with 0.1rc1, 0.1a1, 0.1b1, etc.
|
75
|
-
__version__ = "1.
|
75
|
+
__version__ = "1.4.0"
|
76
76
|
|
77
77
|
import warnings
|
78
78
|
|
@@ -123,8 +123,8 @@ if _check_instance_setup(from_module="lamindb"):
|
|
123
123
|
from . import integrations
|
124
124
|
from . import curators
|
125
125
|
|
126
|
-
track = context.track
|
127
|
-
finish = context.finish
|
126
|
+
track = context.track
|
127
|
+
finish = context.finish
|
128
128
|
settings.__doc__ = """Global settings (:class:`~lamindb.core.Settings`)."""
|
129
129
|
context.__doc__ = """Global run context (:class:`~lamindb.core.Context`).
|
130
130
|
|
lamindb/core/_context.py
CHANGED
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import builtins
|
4
4
|
import hashlib
|
5
|
+
import os
|
5
6
|
import signal
|
6
7
|
import sys
|
7
8
|
import threading
|
@@ -22,7 +23,6 @@ from lamindb.models import Run, Transform, format_field_value
|
|
22
23
|
|
23
24
|
from ..core._settings import settings
|
24
25
|
from ..errors import (
|
25
|
-
InconsistentKey,
|
26
26
|
InvalidArgument,
|
27
27
|
TrackNotCalled,
|
28
28
|
UpdateContext,
|
@@ -30,7 +30,6 @@ from ..errors import (
|
|
30
30
|
from ..models._is_versioned import bump_version as bump_version_function
|
31
31
|
from ..models._is_versioned import (
|
32
32
|
increment_base62,
|
33
|
-
message_update_key_in_version_family,
|
34
33
|
)
|
35
34
|
from ._sync_git import get_transform_reference_from_git_repo
|
36
35
|
from ._track_environment import track_environment
|
@@ -39,7 +38,7 @@ if TYPE_CHECKING:
|
|
39
38
|
from lamindb_setup.core.types import UPathStr
|
40
39
|
|
41
40
|
from lamindb.base.types import TransformType
|
42
|
-
from lamindb.models import Project
|
41
|
+
from lamindb.models import Project, Space
|
43
42
|
|
44
43
|
is_run_from_ipython = getattr(builtins, "__IPYTHON__", False)
|
45
44
|
|
@@ -204,6 +203,7 @@ class Context:
|
|
204
203
|
self._path: Path | None = None
|
205
204
|
"""A local path to the script that's running."""
|
206
205
|
self._project: Project | None = None
|
206
|
+
self._space: Space | None = None
|
207
207
|
self._logging_message_track: str = ""
|
208
208
|
self._logging_message_imports: str = ""
|
209
209
|
self._stream_tracker: LogStreamTracker = LogStreamTracker()
|
@@ -255,6 +255,11 @@ class Context:
|
|
255
255
|
"""Project to label entities created during the run."""
|
256
256
|
return self._project
|
257
257
|
|
258
|
+
@property
|
259
|
+
def space(self) -> Space | None:
|
260
|
+
"""The space in which entities are created during the run."""
|
261
|
+
return self._space
|
262
|
+
|
258
263
|
@property
|
259
264
|
def run(self) -> Run | None:
|
260
265
|
"""Managed run of context."""
|
@@ -265,23 +270,26 @@ class Context:
|
|
265
270
|
transform: str | Transform | None = None,
|
266
271
|
*,
|
267
272
|
project: str | None = None,
|
273
|
+
space: str | None = None,
|
268
274
|
params: dict | None = None,
|
269
275
|
new_run: bool | None = None,
|
270
276
|
path: str | None = None,
|
271
277
|
) -> None:
|
272
|
-
"""Track a global run
|
278
|
+
"""Track a global run in your compute session.
|
273
279
|
|
274
280
|
- sets :attr:`~lamindb.core.Context.transform` &
|
275
281
|
:attr:`~lamindb.core.Context.run` by creating or loading `Transform` &
|
276
282
|
`Run` records
|
277
|
-
-
|
283
|
+
- writes compute environment to prepare populating: `run.environment`
|
284
|
+
- if :attr:`~lamindb.core.Settings.sync_git_repo` is set, checks whether a script-like
|
285
|
+
transform exists in a git repository and links it
|
278
286
|
|
279
|
-
|
280
|
-
script-like transform exists in a git repository and links it.
|
287
|
+
Guide: :doc:`/track`
|
281
288
|
|
282
289
|
Args:
|
283
|
-
transform: A transform `uid` or record. If `None`,
|
290
|
+
transform: A transform `uid` or record. If `None`, manages the `transform` based on the script or notebook that calls `ln.track()`.
|
284
291
|
project: A project `name` or `uid` for labeling entities created during the run.
|
292
|
+
space: A space `name` or `uid` for creating entities during the run. This doesn't affect bionty entities given they should typically be commonly accessible.
|
285
293
|
params: A dictionary of parameters to track for the run.
|
286
294
|
new_run: If `False`, loads the latest run of transform
|
287
295
|
(default notebook), if `True`, creates new run (default non-notebook).
|
@@ -290,16 +298,16 @@ class Context:
|
|
290
298
|
|
291
299
|
Examples:
|
292
300
|
|
293
|
-
To track the run of a notebook or script, call
|
301
|
+
To track the run of a notebook or script, call::
|
294
302
|
|
295
|
-
|
303
|
+
ln.track()
|
296
304
|
|
297
|
-
If you want to ensure a single version history across renames of the notebook or script, pass the auto-generated `uid` that you'll find in the logs
|
305
|
+
If you want to ensure a single version history across renames of the notebook or script, pass the auto-generated `uid` that you'll find in the logs::
|
298
306
|
|
299
|
-
|
307
|
+
ln.track("Onv04I53OgtT0000") # example uid, the last four characters encode the version of the transform
|
300
308
|
|
301
309
|
"""
|
302
|
-
from lamindb.models import Project
|
310
|
+
from lamindb.models import Project, Space
|
303
311
|
|
304
312
|
instance_settings = ln_setup.settings.instance
|
305
313
|
# similar logic here: https://github.com/laminlabs/lamindb/pull/2527
|
@@ -307,6 +315,8 @@ class Context:
|
|
307
315
|
if instance_settings.dialect == "postgresql" and "read" in instance_settings.db:
|
308
316
|
logger.warning("skipping track(), connected in read-only mode")
|
309
317
|
return None
|
318
|
+
if project is None:
|
319
|
+
project = os.environ.get("LAMIN_CURRENT_PROJECT")
|
310
320
|
if project is not None:
|
311
321
|
project_record = Project.filter(
|
312
322
|
Q(name=project) | Q(uid=project)
|
@@ -316,6 +326,13 @@ class Context:
|
|
316
326
|
f"Project '{project}' not found, either create it with `ln.Project(name='...').save()` or fix typos."
|
317
327
|
)
|
318
328
|
self._project = project_record
|
329
|
+
if space is not None:
|
330
|
+
space_record = Space.filter(Q(name=space) | Q(uid=space)).one_or_none()
|
331
|
+
if space_record is None:
|
332
|
+
raise InvalidArgument(
|
333
|
+
f"Space '{space}', please check on the hub UI whether you have the correct `uid` or `name`."
|
334
|
+
)
|
335
|
+
self._space = space_record
|
319
336
|
self._logging_message_track = ""
|
320
337
|
self._logging_message_imports = ""
|
321
338
|
if transform is not None and isinstance(transform, str):
|
@@ -495,19 +512,6 @@ class Context:
|
|
495
512
|
transform_ref_type: str | None = None,
|
496
513
|
transform_type: TransformType = None,
|
497
514
|
):
|
498
|
-
def get_key_clashing_message(transform: Transform, key: str) -> str:
|
499
|
-
update_key_note = message_update_key_in_version_family(
|
500
|
-
suid=transform.stem_uid,
|
501
|
-
existing_key=transform.key,
|
502
|
-
new_key=key,
|
503
|
-
registry="Transform",
|
504
|
-
)
|
505
|
-
return (
|
506
|
-
f'Filepath "{key}" clashes with the existing key "{transform.key}" for uid "{transform.uid[:-4]}...."\n\nEither init a new transform with a new uid:\n\n'
|
507
|
-
f'ln.track("{ids.base62_12()}0000")\n\n{update_key_note}'
|
508
|
-
)
|
509
|
-
|
510
|
-
revises = None
|
511
515
|
# the user did not pass the uid
|
512
516
|
if self.uid is None:
|
513
517
|
|
@@ -531,27 +535,28 @@ class Context:
|
|
531
535
|
for aux_transform in transforms:
|
532
536
|
if aux_transform.key in self._path.as_posix():
|
533
537
|
key = aux_transform.key
|
538
|
+
# first part of the if condition: no version bump, second part: version bump
|
534
539
|
if (
|
535
|
-
#
|
536
|
-
|
537
|
-
and (
|
538
|
-
# if the transform source code wasn't yet saved
|
540
|
+
# if a user hasn't yet saved the transform source code, needs to be same user
|
541
|
+
(
|
539
542
|
aux_transform.source_code is None
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
543
|
+
and aux_transform.created_by_id
|
544
|
+
== ln_setup.settings.user.id
|
545
|
+
)
|
546
|
+
# if the transform source code is unchanged
|
547
|
+
# if aux_transform.type == "notebook", we anticipate the user makes changes to the notebook source code
|
548
|
+
# in an interactive session, hence we *pro-actively bump* the version number by setting `revises`
|
549
|
+
# in the second part of the if condition even though the source code is unchanged at point of running track()
|
550
|
+
or (
|
551
|
+
aux_transform.hash == hash
|
552
|
+
and aux_transform.type != "notebook"
|
548
553
|
)
|
549
554
|
):
|
550
555
|
uid = aux_transform.uid
|
551
556
|
target_transform = aux_transform
|
552
557
|
else:
|
553
558
|
uid = f"{aux_transform.uid[:-4]}{increment_base62(aux_transform.uid[-4:])}"
|
554
|
-
message = f"there already is a {aux_transform.type} with
|
559
|
+
message = f"there already is a {aux_transform.type} with key '{aux_transform.key}'"
|
555
560
|
if (
|
556
561
|
aux_transform.hash == hash
|
557
562
|
and aux_transform.type == "notebook"
|
@@ -564,7 +569,6 @@ class Context:
|
|
564
569
|
):
|
565
570
|
message += f" -- {aux_transform.created_by.handle} already works on this draft"
|
566
571
|
message += f", creating new version '{uid}'"
|
567
|
-
revises = aux_transform
|
568
572
|
found_key = True
|
569
573
|
break
|
570
574
|
if not found_key:
|
@@ -585,15 +589,14 @@ class Context:
|
|
585
589
|
if transform is not None:
|
586
590
|
if transform.key not in self._path.as_posix():
|
587
591
|
n_parts = len(Path(transform.key).parts)
|
588
|
-
|
592
|
+
(
|
589
593
|
Path(*self._path.parts[-n_parts:]).as_posix()
|
590
594
|
if n_parts > 0
|
591
595
|
else ""
|
592
596
|
)
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
key = transform.key # type: ignore
|
597
|
+
key = self._path.name
|
598
|
+
else:
|
599
|
+
key = transform.key # type: ignore
|
597
600
|
else:
|
598
601
|
key = self._path.name
|
599
602
|
if self.version is not None:
|
@@ -619,27 +622,15 @@ class Context:
|
|
619
622
|
# make a new transform record
|
620
623
|
if transform is None:
|
621
624
|
assert key is not None # noqa: S101
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
type=transform_type,
|
632
|
-
).save()
|
633
|
-
except InconsistentKey:
|
634
|
-
raise_update_context = True
|
635
|
-
if raise_update_context:
|
636
|
-
if revises is None:
|
637
|
-
revises = (
|
638
|
-
Transform.filter(uid__startswith=self.uid[:-4], is_latest=True)
|
639
|
-
.order_by("-created_at")
|
640
|
-
.first()
|
641
|
-
)
|
642
|
-
raise UpdateContext(get_key_clashing_message(revises, key))
|
625
|
+
transform = Transform( # type: ignore
|
626
|
+
uid=self.uid,
|
627
|
+
version=self.version,
|
628
|
+
description=description,
|
629
|
+
key=key,
|
630
|
+
reference=transform_ref,
|
631
|
+
reference_type=transform_ref_type,
|
632
|
+
type=transform_type,
|
633
|
+
).save()
|
643
634
|
self._logging_message_track += f"created Transform('{transform.uid}')"
|
644
635
|
else:
|
645
636
|
uid = transform.uid
|
@@ -647,7 +638,11 @@ class Context:
|
|
647
638
|
transform_was_saved = transform.source_code is not None
|
648
639
|
# check whether the transform.key is consistent
|
649
640
|
if transform.key != key:
|
650
|
-
|
641
|
+
self._logging_message_track += (
|
642
|
+
f"renaming transform {transform.key} to {key}"
|
643
|
+
)
|
644
|
+
transform.key = key
|
645
|
+
transform.save()
|
651
646
|
elif transform.description != description and description is not None:
|
652
647
|
transform.description = description
|
653
648
|
transform.save()
|
@@ -659,7 +654,7 @@ class Context:
|
|
659
654
|
and not transform_was_saved
|
660
655
|
):
|
661
656
|
raise UpdateContext(
|
662
|
-
f'{transform.created_by.name} ({transform.created_by.handle}) already works on this draft {transform.type}.\n\nPlease create a revision via `ln.track("{uid[:-4]}{increment_base62(uid[-4:])}")` or a new transform with a *different*
|
657
|
+
f'{transform.created_by.name} ({transform.created_by.handle}) already works on this draft {transform.type}.\n\nPlease create a revision via `ln.track("{uid[:-4]}{increment_base62(uid[-4:])}")` or a new transform with a *different* key and `ln.track("{ids.base62_12()}0000")`.'
|
663
658
|
)
|
664
659
|
# check whether transform source code was already saved
|
665
660
|
if transform_was_saved:
|
@@ -752,4 +747,4 @@ class Context:
|
|
752
747
|
self._description = None
|
753
748
|
|
754
749
|
|
755
|
-
context = Context()
|
750
|
+
context: Context = Context()
|
lamindb/core/datasets/_small.py
CHANGED
@@ -44,7 +44,7 @@ def small_dataset1(
|
|
44
44
|
# define the dataset-level metadata
|
45
45
|
metadata = {
|
46
46
|
"temperature": 21.6,
|
47
|
-
"
|
47
|
+
"experiment": "Experiment 1",
|
48
48
|
"date_of_study": "2024-12-01",
|
49
49
|
"study_note": "We had a great time performing this study and the results look compelling.",
|
50
50
|
}
|
@@ -78,7 +78,7 @@ def small_dataset2(
|
|
78
78
|
}
|
79
79
|
metadata = {
|
80
80
|
"temperature": 22.6,
|
81
|
-
"
|
81
|
+
"experiment": "Experiment 2",
|
82
82
|
"date_of_study": "2025-02-13",
|
83
83
|
}
|
84
84
|
dataset_df = pd.DataFrame(
|