lamindb 0.76.9__py3-none-any.whl → 0.76.10__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/_record.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import builtins
4
- from typing import TYPE_CHECKING, List, NamedTuple
4
+ from typing import TYPE_CHECKING, NamedTuple
5
5
 
6
6
  import dj_database_url
7
7
  import lamindb_setup as ln_setup
@@ -9,16 +9,19 @@ from django.db import connections, transaction
9
9
  from django.db.models import IntegerField, Manager, Q, QuerySet, Value
10
10
  from lamin_utils import logger
11
11
  from lamin_utils._lookup import Lookup
12
- from lamindb_setup._connect_instance import get_owner_name_from_identifier
12
+ from lamindb_setup._connect_instance import (
13
+ get_owner_name_from_identifier,
14
+ load_instance_settings,
15
+ update_db_using_local,
16
+ )
13
17
  from lamindb_setup.core._docs import doc_args
14
18
  from lamindb_setup.core._hub_core import connect_instance
15
- from lnschema_core.models import IsVersioned, Record
19
+ from lamindb_setup.core._settings_store import instance_settings_file
20
+ from lnschema_core.models import IsVersioned, Record, Run, Transform
16
21
 
17
22
  from lamindb._utils import attach_func_to_class_method
18
23
  from lamindb.core._settings import settings
19
24
 
20
- from ._from_values import get_or_create_records
21
-
22
25
  if TYPE_CHECKING:
23
26
  import pandas as pd
24
27
  from lnschema_core.types import ListLike, StrField
@@ -169,33 +172,6 @@ def df(
169
172
  return query_set[:limit].df(include=include, join=join)
170
173
 
171
174
 
172
- # from_values doesn't apply for QuerySet or Manager
173
- @classmethod # type:ignore
174
- @doc_args(Record.from_values.__doc__)
175
- def from_values(
176
- cls,
177
- values: ListLike,
178
- field: StrField | None = None,
179
- create: bool = False,
180
- organism: Record | str | None = None,
181
- source: Record | None = None,
182
- mute: bool = False,
183
- ) -> list[Record]:
184
- """{}""" # noqa: D415
185
- from_source = True if cls.__module__.startswith("bionty.") else False
186
-
187
- field_str = get_name_field(cls, field=field)
188
- return get_or_create_records(
189
- iterable=values,
190
- field=getattr(cls, field_str),
191
- create=create,
192
- from_source=from_source,
193
- organism=organism,
194
- source=source,
195
- mute=mute,
196
- )
197
-
198
-
199
175
  def _search(
200
176
  cls,
201
177
  string: str,
@@ -402,26 +378,34 @@ def using(
402
378
  """{}""" # noqa: D415
403
379
  if instance is None:
404
380
  return QuerySet(model=cls, using=None)
405
- from lamindb_setup._connect_instance import (
406
- load_instance_settings,
407
- update_db_using_local,
408
- )
409
- from lamindb_setup.core._settings_store import instance_settings_file
410
-
411
381
  owner, name = get_owner_name_from_identifier(instance)
412
382
  settings_file = instance_settings_file(name, owner)
383
+ cache_filepath = (
384
+ ln_setup.settings.storage.cache_dir / f"instance--{owner}--{name}--uid.txt"
385
+ )
413
386
  if not settings_file.exists():
414
- load_result = connect_instance(owner=owner, name=name)
415
- if isinstance(load_result, str):
387
+ result = connect_instance(owner=owner, name=name)
388
+ if isinstance(result, str):
416
389
  raise RuntimeError(
417
- f"Failed to load instance {instance}, please check your permission!"
390
+ f"Failed to load instance {instance}, please check your permissions!"
391
+ )
392
+ iresult, _ = result
393
+ source_schema = {
394
+ schema for schema in iresult["schema_str"].split(",") if schema != ""
395
+ } # type: ignore
396
+ target_schema = ln_setup.settings.instance.schema
397
+ if not source_schema.issubset(target_schema):
398
+ missing_members = source_schema - target_schema
399
+ logger.warning(
400
+ f"source schema has additional modules: {missing_members}\nconsider mounting these schema modules to not encounter errors"
418
401
  )
419
- instance_result, _ = load_result
402
+ cache_filepath.write_text(iresult["lnid"]) # type: ignore
420
403
  settings_file = instance_settings_file(name, owner)
421
- db = update_db_using_local(instance_result, settings_file)
404
+ db = update_db_using_local(iresult, settings_file)
422
405
  else:
423
406
  isettings = load_instance_settings(settings_file)
424
407
  db = isettings.db
408
+ cache_filepath.write_text(isettings.uid)
425
409
  add_db_connection(db, instance)
426
410
  return QuerySet(model=cls, using=instance)
427
411
 
@@ -439,7 +423,7 @@ def update_fk_to_default_db(
439
423
  using_key: str | None,
440
424
  transfer_logs: dict,
441
425
  ):
442
- record = records[0] if isinstance(records, (List, QuerySet)) else records
426
+ record = records[0] if isinstance(records, (list, QuerySet)) else records
443
427
  if hasattr(record, f"{fk}_id") and getattr(record, f"{fk}_id") is not None:
444
428
  fk_record = getattr(record, fk)
445
429
  field = REGISTRY_UNIQUE_FIELD.get(fk, "uid")
@@ -453,7 +437,7 @@ def update_fk_to_default_db(
453
437
  transfer_to_default_db(
454
438
  fk_record_default, using_key, save=True, transfer_logs=transfer_logs
455
439
  )
456
- if isinstance(records, (List, QuerySet)):
440
+ if isinstance(records, (list, QuerySet)):
457
441
  for r in records:
458
442
  setattr(r, f"{fk}", None)
459
443
  setattr(r, f"{fk}_id", fk_record_default.id)
@@ -477,6 +461,45 @@ def transfer_fk_to_default_db_bulk(
477
461
  update_fk_to_default_db(records, fk, using_key, transfer_logs=transfer_logs)
478
462
 
479
463
 
464
+ def get_transfer_run(record) -> Run:
465
+ from lamindb_setup import settings as setup_settings
466
+
467
+ from lamindb.core._context import context
468
+ from lamindb.core._data import WARNING_RUN_TRANSFORM
469
+
470
+ slug = record._state.db
471
+ owner, name = get_owner_name_from_identifier(slug)
472
+ cache_filepath = (
473
+ ln_setup.settings.storage.cache_dir / f"instance--{owner}--{name}--uid.txt"
474
+ )
475
+ if not cache_filepath.exists():
476
+ raise SystemExit("Need to call .using() before")
477
+ instance_uid = cache_filepath.read_text()
478
+ key = f"transfers/{instance_uid}"
479
+ uid = instance_uid + "0000"
480
+ transform = Transform.filter(uid=uid).one_or_none()
481
+ if transform is None:
482
+ search_names = settings.creation.search_names
483
+ settings.creation.search_names = False
484
+ transform = Transform(
485
+ uid=uid, name=f"Transfer from `{slug}`", key=key, type="function"
486
+ ).save()
487
+ settings.creation.search_names = search_names
488
+ # use the global run context to get the parent run id
489
+ if context.run is not None:
490
+ parent = context.run
491
+ else:
492
+ if not settings.creation.artifact_silence_missing_run_warning:
493
+ logger.warning(WARNING_RUN_TRANSFORM)
494
+ parent = None
495
+ # it doesn't seem to make sense to create new runs for every transfer
496
+ run = Run.filter(transform=transform, parent=parent).one_or_none()
497
+ if run is None:
498
+ run = Run(transform=transform, parent=parent).save()
499
+ run.parent = parent # so that it's available in memory
500
+ return run
501
+
502
+
480
503
  def transfer_to_default_db(
481
504
  record: Record,
482
505
  using_key: str | None,
@@ -485,12 +508,13 @@ def transfer_to_default_db(
485
508
  save: bool = False,
486
509
  transfer_fk: bool = True,
487
510
  ) -> Record | None:
488
- from lamindb.core._context import context
489
- from lamindb.core._data import WARNING_RUN_TRANSFORM
490
-
511
+ if record._state.db is None or record._state.db == "default":
512
+ return None
491
513
  registry = record.__class__
492
514
  record_on_default = registry.objects.filter(uid=record.uid).one_or_none()
493
515
  record_str = f"{record.__class__.__name__}(uid='{record.uid}')"
516
+ if transfer_logs["run"] is None:
517
+ transfer_logs["run"] = get_transfer_run(record)
494
518
  if record_on_default is not None:
495
519
  transfer_logs["mapped"].append(record_str)
496
520
  return record_on_default
@@ -500,20 +524,15 @@ def transfer_to_default_db(
500
524
  if hasattr(record, "created_by_id"):
501
525
  record.created_by = None
502
526
  record.created_by_id = ln_setup.settings.user.id
527
+ # run & transform
528
+ run = transfer_logs["run"]
503
529
  if hasattr(record, "run_id"):
504
530
  record.run = None
505
- if context.run is not None:
506
- record.run_id = context.run.id
507
- else:
508
- if not settings.creation.artifact_silence_missing_run_warning:
509
- logger.warning(WARNING_RUN_TRANSFORM)
510
- record.run_id = None
511
- if hasattr(record, "transform_id") and record._meta.model_name != "run":
531
+ record.run_id = run.id
532
+ # deal with denormalized transform FK on artifact and collection
533
+ if hasattr(record, "transform_id"):
512
534
  record.transform = None
513
- if context.run is not None:
514
- record.transform_id = context.run.transform_id
515
- else:
516
- record.transform_id = None
535
+ record.transform_id = run.transform_id
517
536
  # transfer other foreign key fields
518
537
  fk_fields = [
519
538
  i.name
@@ -546,7 +565,7 @@ def save(self, *args, **kwargs) -> Record:
546
565
  artifacts = self.ordered_artifacts.list()
547
566
  pre_existing_record = None
548
567
  # consider records that are being transferred from other databases
549
- transfer_logs: dict[str, list[str]] = {"mapped": [], "transferred": []}
568
+ transfer_logs: dict[str, list[str]] = {"mapped": [], "transferred": [], "run": None}
550
569
  if db is not None and db != "default" and using_key is None:
551
570
  if isinstance(self, IsVersioned):
552
571
  if not self.is_latest:
@@ -594,7 +613,8 @@ def save(self, *args, **kwargs) -> Record:
594
613
  self.features._add_from(self_on_db, transfer_logs=transfer_logs)
595
614
  self.labels.add_from(self_on_db, transfer_logs=transfer_logs)
596
615
  for k, v in transfer_logs.items():
597
- logger.important(f"{k} records: {', '.join(v)}")
616
+ if k != "run":
617
+ logger.important(f"{k} records: {', '.join(v)}")
598
618
  return self
599
619
 
600
620
 
@@ -632,7 +652,6 @@ METHOD_NAMES = [
632
652
  "lookup",
633
653
  "save",
634
654
  "delete",
635
- "from_values",
636
655
  "using",
637
656
  ]
638
657
 
lamindb/_run.py CHANGED
@@ -18,13 +18,16 @@ def __init__(run: Run, *args, **kwargs):
18
18
  reference_type: str | None = (
19
19
  kwargs.pop("reference_type") if "reference_type" in kwargs else None
20
20
  )
21
+ parent: Run | None = kwargs.pop("parent", None)
21
22
  if transform is None:
22
23
  raise TypeError("Pass transform parameter")
23
24
  if transform._state.adding:
24
25
  raise ValueError("Please save transform record before creating a run")
26
+
25
27
  super(Run, run).__init__(
26
28
  transform=transform,
27
29
  reference=reference,
30
+ parent=parent,
28
31
  reference_type=reference_type,
29
32
  )
30
33
 
lamindb/_save.py CHANGED
@@ -6,7 +6,7 @@ import traceback
6
6
  from collections import defaultdict
7
7
  from datetime import datetime
8
8
  from functools import partial
9
- from typing import TYPE_CHECKING, Iterable, overload
9
+ from typing import TYPE_CHECKING, overload
10
10
 
11
11
  import lamindb_setup
12
12
  from django.db import IntegrityError, transaction
@@ -25,6 +25,8 @@ from lamindb.core.storage.paths import (
25
25
  )
26
26
 
27
27
  if TYPE_CHECKING:
28
+ from collections.abc import Iterable
29
+
28
30
  from lamindb_setup.core.upath import UPath
29
31
 
30
32
 
lamindb/_transform.py CHANGED
@@ -6,6 +6,8 @@ from lamin_utils import logger
6
6
  from lamindb_setup.core._docs import doc_args
7
7
  from lnschema_core.models import Run, Transform
8
8
 
9
+ from lamindb.core.exceptions import InconsistentKey
10
+
9
11
  from ._parents import _view_parents
10
12
  from ._run import delete_run_artifacts
11
13
  from .core.versioning import message_update_key_in_version_family, process_revises
@@ -38,15 +40,16 @@ def __init__(transform: Transform, *args, **kwargs):
38
40
  f"reference_type can be passed, but you passed: {kwargs}"
39
41
  )
40
42
  if revises is None:
41
- if key is not None:
43
+ # need to check uid before checking key
44
+ if uid is not None:
42
45
  revises = (
43
- Transform.filter(key=key, is_latest=True)
46
+ Transform.filter(uid__startswith=uid[:-4], is_latest=True)
44
47
  .order_by("-created_at")
45
48
  .first()
46
49
  )
47
- elif uid is not None:
50
+ elif key is not None:
48
51
  revises = (
49
- Transform.filter(uid__startswith=uid[:-4], is_latest=True)
52
+ Transform.filter(key=key, is_latest=True)
50
53
  .order_by("-created_at")
51
54
  .first()
52
55
  )
@@ -63,8 +66,8 @@ def __init__(transform: Transform, *args, **kwargs):
63
66
  new_key=key,
64
67
  registry="Artifact",
65
68
  )
66
- raise ValueError(
67
- f"`key` is {key}, but `revises.key` is '{revises.key}'\n\n Either do *not* pass `key`.\n\n{note}"
69
+ raise InconsistentKey(
70
+ f"`key` is {key}, but `revises.key` is '{revises.key}'\n\nEither do *not* pass `key`.\n\n{note}"
68
71
  )
69
72
  new_uid, version, name, revises = process_revises(revises, version, name, Transform)
70
73
  # this is only because the user-facing constructor allows passing a uid