lamindb 1.4.0__py3-none-any.whl → 1.5.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 (55) hide show
  1. lamindb/__init__.py +52 -36
  2. lamindb/_finish.py +17 -10
  3. lamindb/_tracked.py +1 -1
  4. lamindb/base/__init__.py +3 -1
  5. lamindb/base/fields.py +40 -22
  6. lamindb/base/ids.py +1 -94
  7. lamindb/base/types.py +2 -0
  8. lamindb/base/uids.py +117 -0
  9. lamindb/core/_context.py +177 -89
  10. lamindb/core/_settings.py +38 -25
  11. lamindb/core/datasets/__init__.py +11 -4
  12. lamindb/core/datasets/_core.py +5 -5
  13. lamindb/core/datasets/_small.py +0 -93
  14. lamindb/core/datasets/mini_immuno.py +172 -0
  15. lamindb/core/loaders.py +1 -1
  16. lamindb/core/storage/_backed_access.py +100 -6
  17. lamindb/core/storage/_polars_lazy_df.py +51 -0
  18. lamindb/core/storage/_pyarrow_dataset.py +15 -30
  19. lamindb/core/storage/objects.py +6 -0
  20. lamindb/core/subsettings/__init__.py +2 -0
  21. lamindb/core/subsettings/_annotation_settings.py +11 -0
  22. lamindb/curators/__init__.py +7 -3349
  23. lamindb/curators/_legacy.py +2056 -0
  24. lamindb/curators/core.py +1546 -0
  25. lamindb/errors.py +11 -0
  26. lamindb/examples/__init__.py +27 -0
  27. lamindb/examples/schemas/__init__.py +12 -0
  28. lamindb/examples/schemas/_anndata.py +25 -0
  29. lamindb/examples/schemas/_simple.py +19 -0
  30. lamindb/integrations/_vitessce.py +8 -5
  31. lamindb/migrations/0091_alter_featurevalue_options_alter_space_options_and_more.py +24 -0
  32. lamindb/migrations/0092_alter_artifactfeaturevalue_artifact_and_more.py +75 -0
  33. lamindb/models/__init__.py +4 -1
  34. lamindb/models/_describe.py +21 -4
  35. lamindb/models/_feature_manager.py +365 -286
  36. lamindb/models/_label_manager.py +8 -2
  37. lamindb/models/artifact.py +173 -95
  38. lamindb/models/artifact_set.py +122 -0
  39. lamindb/models/collection.py +73 -52
  40. lamindb/models/core.py +1 -1
  41. lamindb/models/feature.py +51 -17
  42. lamindb/models/has_parents.py +2 -2
  43. lamindb/models/project.py +1 -1
  44. lamindb/models/query_manager.py +221 -22
  45. lamindb/models/query_set.py +245 -171
  46. lamindb/models/record.py +62 -243
  47. lamindb/models/run.py +4 -4
  48. lamindb/models/save.py +8 -2
  49. lamindb/models/schema.py +458 -181
  50. lamindb/models/transform.py +2 -2
  51. lamindb/models/ulabel.py +8 -5
  52. {lamindb-1.4.0.dist-info → lamindb-1.5.0.dist-info}/METADATA +6 -6
  53. {lamindb-1.4.0.dist-info → lamindb-1.5.0.dist-info}/RECORD +55 -42
  54. {lamindb-1.4.0.dist-info → lamindb-1.5.0.dist-info}/LICENSE +0 -0
  55. {lamindb-1.4.0.dist-info → lamindb-1.5.0.dist-info}/WHEEL +0 -0
lamindb/core/_context.py CHANGED
@@ -54,19 +54,21 @@ def get_uid_ext(version: str) -> str:
54
54
  return encodebytes(hashlib.md5(version.encode()).digest())[:4] # noqa: S324
55
55
 
56
56
 
57
- def get_notebook_path() -> Path:
57
+ def get_notebook_path() -> tuple[Path, str]:
58
58
  from nbproject.dev._jupyter_communicate import (
59
59
  notebook_path as get_notebook_path,
60
60
  )
61
61
 
62
62
  path = None
63
63
  try:
64
- path = get_notebook_path()
65
- except Exception:
66
- raise RuntimeError(msg_path_failed) from None
64
+ path, env = get_notebook_path(return_env=True)
65
+ except ValueError as ve:
66
+ raise ve
67
+ except Exception as error:
68
+ raise RuntimeError(msg_path_failed) from error
67
69
  if path is None:
68
70
  raise RuntimeError(msg_path_failed) from None
69
- return Path(path)
71
+ return Path(path), env
70
72
 
71
73
 
72
74
  # from https://stackoverflow.com/questions/61901628
@@ -178,20 +180,7 @@ class LogStreamTracker:
178
180
  class Context:
179
181
  """Run context.
180
182
 
181
- Enables convenient data lineage tracking by managing a transform & run
182
- upon :meth:`~lamindb.core.Context.track` & :meth:`~lamindb.core.Context.finish`.
183
-
184
- Guide: :doc:`/track`
185
-
186
- Examples:
187
-
188
- Is typically used via the global :class:`~lamindb.context` object via `ln.track()` and `ln.finish()`:
189
-
190
- >>> import lamindb as ln
191
- >>> ln.track()
192
- >>> # do things
193
- >>> ln.finish()
194
-
183
+ Is the book keeper for :meth:`~lamindb.core.Context.track`.
195
184
  """
196
185
 
197
186
  def __init__(self):
@@ -201,13 +190,14 @@ class Context:
201
190
  self._transform: Transform | None = None
202
191
  self._run: Run | None = None
203
192
  self._path: Path | None = None
204
- """A local path to the script that's running."""
193
+ """A local path to the script or notebook that's running."""
205
194
  self._project: Project | None = None
206
195
  self._space: Space | None = None
207
196
  self._logging_message_track: str = ""
208
197
  self._logging_message_imports: str = ""
209
198
  self._stream_tracker: LogStreamTracker = LogStreamTracker()
210
199
  self._is_finish_retry: bool = False
200
+ self._notebook_runner: str | None = None
211
201
 
212
202
  @property
213
203
  def transform(self) -> Transform | None:
@@ -265,7 +255,7 @@ class Context:
265
255
  """Managed run of context."""
266
256
  return self._run
267
257
 
268
- def track(
258
+ def _track(
269
259
  self,
270
260
  transform: str | Transform | None = None,
271
261
  *,
@@ -275,21 +265,18 @@ class Context:
275
265
  new_run: bool | None = None,
276
266
  path: str | None = None,
277
267
  ) -> None:
278
- """Track a global run in your compute session.
268
+ """Track a run of your notebook or script.
279
269
 
280
- - sets :attr:`~lamindb.core.Context.transform` &
281
- :attr:`~lamindb.core.Context.run` by creating or loading `Transform` &
282
- `Run` records
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
270
+ Populates the global run :class:`~lamindb.context` by managing `Transform` & `Run` records and caching the compute environment.
286
271
 
287
- Guide: :doc:`/track`
272
+ If :attr:`~lamindb.core.Settings.sync_git_repo` is set, checks whether a script-like transform exists in a git repository and links it.
288
273
 
289
274
  Args:
290
- transform: A transform `uid` or record. If `None`, manages the `transform` based on the script or notebook that calls `ln.track()`.
275
+ transform: A transform (stem) `uid` (or record). If `None`, auto-creates a `transform` with its `uid`.
291
276
  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.
277
+ space: A space `name` or `uid` to identify where potentially sensitive entities are created during the run.
278
+ This doesn't affect `Storage`, `ULabel`, `Feature`, `Schema`, `Param` and bionty entities as these provide mere structure that should typically be commonly accessible.
279
+ If you want to manually move entities to a different space, set the `.space` field (:doc:`docs:access`).
293
280
  params: A dictionary of parameters to track for the run.
294
281
  new_run: If `False`, loads the latest run of transform
295
282
  (default notebook), if `True`, creates new run (default non-notebook).
@@ -301,11 +288,15 @@ class Context:
301
288
  To track the run of a notebook or script, call::
302
289
 
303
290
  ln.track()
291
+ #> → created Transform('Onv04I53OgtT0000'), started new Run('dpSfd7Ds...') at 2025-04-25 11:00:03 UTC
292
+ #> • recommendation: to identify the notebook across renames, pass the uid: ln.track("Onv04I53OgtT")
304
293
 
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::
294
+ Ensure one version history across file renames::
306
295
 
307
- ln.track("Onv04I53OgtT0000") # example uid, the last four characters encode the version of the transform
296
+ ln.track("Onv04I53OgtT")
297
+ #> → created Transform('Onv04I53OgtT0000'), started new Run('dpSfd7Ds...') at 2025-04-25 11:00:03 UTC
308
298
 
299
+ More examples: :doc:`/track`
309
300
  """
310
301
  from lamindb.models import Project, Space
311
302
 
@@ -338,6 +329,9 @@ class Context:
338
329
  if transform is not None and isinstance(transform, str):
339
330
  self.uid = transform
340
331
  transform = None
332
+ uid_was_none = False
333
+ else:
334
+ uid_was_none = True
341
335
  self._path = None
342
336
  if transform is None:
343
337
  description = None
@@ -384,7 +378,14 @@ class Context:
384
378
  self._transform = transform_exists
385
379
 
386
380
  if new_run is None: # for notebooks, default to loading latest runs
387
- new_run = False if self._transform.type == "notebook" else True # type: ignore
381
+ new_run = (
382
+ False
383
+ if (
384
+ self._transform.type == "notebook"
385
+ and self._notebook_runner != "nbconvert"
386
+ )
387
+ else True
388
+ ) # type: ignore
388
389
 
389
390
  run = None
390
391
  if not new_run: # try loading latest run by same user
@@ -431,6 +432,22 @@ class Context:
431
432
  logger.important(self._logging_message_track)
432
433
  if self._logging_message_imports:
433
434
  logger.important(self._logging_message_imports)
435
+ if uid_was_none:
436
+ notebook_or_script = (
437
+ "notebook" if self._transform.type == "notebook" else "script"
438
+ )
439
+ r_or_python = "."
440
+ if self._path is not None:
441
+ r_or_python = "." if self._path.suffix in {".py", ".ipynb"} else "$"
442
+ project_str = f', project="{project}"' if project is not None else ""
443
+ space_str = f', space="{space}"' if space is not None else ""
444
+ params_str = (
445
+ ", params={...}" if params is not None else ""
446
+ ) # do not put the values because typically parameterized by user
447
+ kwargs_str = f"{project_str}{space_str}{params_str}"
448
+ logger.important_hint(
449
+ f'recommendation: to identify the {notebook_or_script} across renames, pass the uid: ln{r_or_python}track("{self.transform.uid[:-4]}"{kwargs_str})'
450
+ )
434
451
 
435
452
  def _track_source_code(
436
453
  self,
@@ -472,7 +489,7 @@ class Context:
472
489
  path_str: str | None,
473
490
  ) -> tuple[Path, str | None]:
474
491
  if path_str is None:
475
- path = get_notebook_path()
492
+ path, self._notebook_runner = get_notebook_path()
476
493
  else:
477
494
  path = Path(path_str)
478
495
  description = None
@@ -504,6 +521,53 @@ class Context:
504
521
  pass
505
522
  return path, description
506
523
 
524
+ def _process_aux_transform(
525
+ self,
526
+ aux_transform: Transform,
527
+ transform_hash: str,
528
+ ) -> tuple[str, Transform | None, str]:
529
+ # first part of the if condition: no version bump, second part: version bump
530
+ message = ""
531
+ if (
532
+ # if a user hasn't yet saved the transform source code AND is the same user
533
+ (
534
+ aux_transform.source_code is None
535
+ and aux_transform.created_by_id == ln_setup.settings.user.id
536
+ )
537
+ # if the transform source code is unchanged
538
+ # if aux_transform.type == "notebook", we anticipate the user makes changes to the notebook source code
539
+ # in an interactive session, hence we *pro-actively bump* the version number by setting `revises` / 'nbconvert' execution is NOT interactive
540
+ # in the second part of the if condition even though the source code is unchanged at point of running track()
541
+ or (
542
+ aux_transform.hash == transform_hash
543
+ and (
544
+ aux_transform.type != "notebook"
545
+ or self._notebook_runner == "nbconvert"
546
+ )
547
+ )
548
+ ):
549
+ uid = aux_transform.uid
550
+ return uid, aux_transform, message
551
+ else:
552
+ uid = f"{aux_transform.uid[:-4]}{increment_base62(aux_transform.uid[-4:])}"
553
+ message = (
554
+ f"found {aux_transform.type} {aux_transform.key}, making new version"
555
+ )
556
+ if (
557
+ aux_transform.hash == transform_hash
558
+ and aux_transform.type == "notebook"
559
+ ):
560
+ message += " -- anticipating changes"
561
+ elif aux_transform.hash != transform_hash:
562
+ message += (
563
+ "" # could log "source code changed", but this seems too much
564
+ )
565
+ elif aux_transform.created_by_id != ln_setup.settings.user.id:
566
+ message += (
567
+ f" -- {aux_transform.created_by.handle} already works on this draft"
568
+ )
569
+ return uid, None, message
570
+
507
571
  def _create_or_load_transform(
508
572
  self,
509
573
  *,
@@ -512,8 +576,22 @@ class Context:
512
576
  transform_ref_type: str | None = None,
513
577
  transform_type: TransformType = None,
514
578
  ):
515
- # the user did not pass the uid
516
- if self.uid is None:
579
+ from .._finish import notebook_to_script
580
+
581
+ if not self._path.suffix == ".ipynb":
582
+ transform_hash, _ = hash_file(self._path)
583
+ else:
584
+ # need to convert to stripped py:percent format for hashing
585
+ source_code_path = ln_setup.settings.cache_dir / self._path.name.replace(
586
+ ".ipynb", ".py"
587
+ )
588
+ notebook_to_script(description, self._path, source_code_path)
589
+ transform_hash, _ = hash_file(source_code_path)
590
+ # see whether we find a transform with the exact same hash
591
+ aux_transform = Transform.filter(hash=transform_hash).one_or_none()
592
+ # if the user did not pass a uid and there is no matching aux_transform
593
+ # need to search for the transform based on the filename
594
+ if self.uid is None and aux_transform is None:
517
595
 
518
596
  class SlashCount(Func):
519
597
  template = "LENGTH(%(expressions)s) - LENGTH(REPLACE(%(expressions)s, '/', ''))"
@@ -528,47 +606,15 @@ class Context:
528
606
  uid = f"{base62_12()}0000"
529
607
  key = self._path.name
530
608
  target_transform = None
531
- hash, _ = hash_file(self._path)
532
609
  if len(transforms) != 0:
533
610
  message = ""
534
611
  found_key = False
535
612
  for aux_transform in transforms:
536
613
  if aux_transform.key in self._path.as_posix():
537
614
  key = aux_transform.key
538
- # first part of the if condition: no version bump, second part: version bump
539
- if (
540
- # if a user hasn't yet saved the transform source code, needs to be same user
541
- (
542
- aux_transform.source_code is None
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"
553
- )
554
- ):
555
- uid = aux_transform.uid
556
- target_transform = aux_transform
557
- else:
558
- uid = f"{aux_transform.uid[:-4]}{increment_base62(aux_transform.uid[-4:])}"
559
- message = f"there already is a {aux_transform.type} with key '{aux_transform.key}'"
560
- if (
561
- aux_transform.hash == hash
562
- and aux_transform.type == "notebook"
563
- ):
564
- message += " -- anticipating changes"
565
- elif aux_transform.hash != hash:
566
- message += "" # could log "source code changed", but this seems too much
567
- elif (
568
- aux_transform.created_by_id != ln_setup.settings.user.id
569
- ):
570
- message += f" -- {aux_transform.created_by.handle} already works on this draft"
571
- message += f", creating new version '{uid}'"
615
+ uid, target_transform, message = self._process_aux_transform(
616
+ aux_transform, transform_hash
617
+ )
572
618
  found_key = True
573
619
  break
574
620
  if not found_key:
@@ -584,7 +630,7 @@ class Context:
584
630
  logger.important(message)
585
631
  self.uid, transform = uid, target_transform
586
632
  # the user did pass the uid
587
- else:
633
+ elif self.uid is not None and len(self.uid) == 16:
588
634
  transform = Transform.filter(uid=self.uid).one_or_none()
589
635
  if transform is not None:
590
636
  if transform.key not in self._path.as_posix():
@@ -599,6 +645,33 @@ class Context:
599
645
  key = transform.key # type: ignore
600
646
  else:
601
647
  key = self._path.name
648
+ else:
649
+ if self.uid is not None:
650
+ assert len(self.uid) == 12, ( # noqa: S101
651
+ "uid must be 12 (stem) or 16 (full) characters long"
652
+ )
653
+ aux_transform = (
654
+ Transform.filter(uid__startswith=self.uid)
655
+ .order_by("-created_at")
656
+ .first()
657
+ )
658
+ if aux_transform is not None:
659
+ if aux_transform.key.endswith(self._path.name):
660
+ key = aux_transform.key
661
+ else:
662
+ key = "/".join(
663
+ aux_transform.key.split("/")[:-1] + [self._path.name]
664
+ )
665
+ uid, target_transform, message = self._process_aux_transform(
666
+ aux_transform, transform_hash
667
+ )
668
+ if message != "":
669
+ logger.important(message)
670
+ else:
671
+ uid = f"{self.uid}0000" if self.uid is not None else None
672
+ target_transform = None
673
+ key = self._path.name
674
+ self.uid, transform = uid, target_transform
602
675
  if self.version is not None:
603
676
  # test inconsistent version passed
604
677
  if (
@@ -610,15 +683,16 @@ class Context:
610
683
  f"✗ please pass consistent version: ln.context.version = '{transform.version}'" # type: ignore
611
684
  )
612
685
  # test whether version was already used for another member of the family
613
- suid, vuid = (self.uid[:-4], self.uid[-4:])
614
- transform = Transform.filter(
615
- uid__startswith=suid, version=self.version
616
- ).one_or_none()
617
- if transform is not None and vuid != transform.uid[-4:]:
618
- better_version = bump_version_function(self.version)
619
- raise SystemExit(
620
- f"✗ version '{self.version}' is already taken by Transform('{transform.uid}'); please set another version, e.g., ln.context.version = '{better_version}'"
621
- )
686
+ if self.uid is not None and len(self.uid) == 16:
687
+ suid, vuid = (self.uid[:-4], self.uid[-4:])
688
+ transform = Transform.filter(
689
+ uid__startswith=suid, version=self.version
690
+ ).one_or_none()
691
+ if transform is not None and vuid != transform.uid[-4:]:
692
+ better_version = bump_version_function(self.version)
693
+ raise SystemExit(
694
+ f"✗ version '{self.version}' is already taken by Transform('{transform.uid}'); please set another version, e.g., ln.context.version = '{better_version}'"
695
+ )
622
696
  # make a new transform record
623
697
  if transform is None:
624
698
  assert key is not None # noqa: S101
@@ -659,13 +733,15 @@ class Context:
659
733
  # check whether transform source code was already saved
660
734
  if transform_was_saved:
661
735
  bump_revision = False
662
- if transform.type == "notebook":
736
+ if (
737
+ transform.type == "notebook"
738
+ and self._notebook_runner != "nbconvert"
739
+ ):
663
740
  # we anticipate the user makes changes to the notebook source code
664
741
  # in an interactive session, hence we pro-actively bump the version number
665
742
  bump_revision = True
666
743
  else:
667
- hash, _ = hash_file(self._path) # ignore hash_type for now
668
- if hash != transform.hash:
744
+ if transform_hash != transform.hash:
669
745
  bump_revision = True
670
746
  else:
671
747
  self._logging_message_track += (
@@ -674,7 +750,10 @@ class Context:
674
750
  if bump_revision:
675
751
  change_type = (
676
752
  "re-running notebook with already-saved source code"
677
- if transform.type == "notebook"
753
+ if (
754
+ transform.type == "notebook"
755
+ and self._notebook_runner != "nbconvert"
756
+ )
678
757
  else "source code changed"
679
758
  )
680
759
  raise UpdateContext(
@@ -684,11 +763,11 @@ class Context:
684
763
  self._logging_message_track += f"loaded Transform('{transform.uid}')"
685
764
  self._transform = transform
686
765
 
687
- def finish(self, ignore_non_consecutive: None | bool = None) -> None:
688
- """Finish a tracked run.
766
+ def _finish(self, ignore_non_consecutive: None | bool = None) -> None:
767
+ """Finish the run and write a run report.
689
768
 
690
769
  - writes a timestamp: `run.finished_at`
691
- - saves the source code: `transform.source_code`
770
+ - saves the source code if it is not yet saved: `transform.source_code`
692
771
  - saves a run report: `run.report`
693
772
 
694
773
  When called in the last cell of a notebook:
@@ -732,6 +811,7 @@ class Context:
732
811
  finished_at=True,
733
812
  ignore_non_consecutive=ignore_non_consecutive,
734
813
  is_retry=self._is_finish_retry,
814
+ notebook_runner=self._notebook_runner,
735
815
  )
736
816
  if return_code == "retry":
737
817
  self._is_finish_retry = True
@@ -746,5 +826,13 @@ class Context:
746
826
  self._version = None
747
827
  self._description = None
748
828
 
829
+ @deprecated("ln.track()")
830
+ def track(self, *args, **kwargs):
831
+ return self._track(*args, **kwargs)
832
+
833
+ @deprecated("ln.finish()")
834
+ def finish(self, *args, **kwargs):
835
+ return self._finish(*args, **kwargs)
836
+
749
837
 
750
838
  context: Context = Context()
lamindb/core/_settings.py CHANGED
@@ -9,6 +9,7 @@ from lamindb_setup._set_managed_storage import set_managed_storage
9
9
  from lamindb_setup.core._settings import settings as setup_settings
10
10
  from lamindb_setup.core._settings_instance import sanitize_git_repo_url
11
11
 
12
+ from .subsettings._annotation_settings import AnnotationSettings, annotation_settings
12
13
  from .subsettings._creation_settings import CreationSettings, creation_settings
13
14
 
14
15
  if TYPE_CHECKING:
@@ -34,13 +35,13 @@ VERBOSITY_TO_STR: dict[int, str] = dict(
34
35
  class Settings:
35
36
  """Settings.
36
37
 
37
- Use ``lamindb.settings`` instead of instantiating this class yourself.
38
+ Use `lamindb.settings` instead of instantiating this class yourself.
38
39
  """
39
40
 
40
- def __init__(self, git_repo: str | None):
41
+ def __init__(self):
41
42
  self._verbosity_int: int = 1 # warning-level logging
42
43
  logger.set_verbosity(self._verbosity_int)
43
- self._sync_git_repo: str | None = git_repo
44
+ self._sync_git_repo: str | None = None
44
45
 
45
46
  @property
46
47
  def creation(self) -> CreationSettings:
@@ -51,6 +52,15 @@ class Settings:
51
52
  """
52
53
  return creation_settings
53
54
 
55
+ @property
56
+ def annotation(self) -> AnnotationSettings:
57
+ """Artifact annotation settings.
58
+
59
+ For example, `ln.settings.creation.search_names = False` will disable
60
+ searching for records with similar names during creation.
61
+ """
62
+ return annotation_settings
63
+
54
64
  track_run_inputs: bool = True
55
65
  """Track files as input upon `.load()`, `.cache()` and `.open()`.
56
66
 
@@ -85,13 +95,18 @@ class Settings:
85
95
 
86
96
  Provide the full git repo URL.
87
97
  """
88
- return self._sync_git_repo
98
+ if self._sync_git_repo is not None:
99
+ return self._sync_git_repo
100
+ elif os.environ.get("LAMINDB_MULTI_INSTANCE") == "true":
101
+ return None
102
+ else:
103
+ return setup_settings.instance.git_repo
89
104
 
90
105
  @sync_git_repo.setter
91
106
  def sync_git_repo(self, value) -> None:
92
107
  """Sync transforms with scripts in git repository.
93
108
 
94
- For example: `ln.sync_git_repo = https://github.com/laminlabs/redun-lamin`
109
+ For example: `ln.settings.sync_git_repo = https://github.com/laminlabs/redun-lamin`
95
110
  """
96
111
  self._sync_git_repo = sanitize_git_repo_url(value)
97
112
  if not self._sync_git_repo.startswith("https://"): # pragma: nocover
@@ -99,28 +114,31 @@ class Settings:
99
114
 
100
115
  @property
101
116
  def storage(self) -> StorageSettings:
102
- """Default storage location.
117
+ """Current default storage location for writes.
103
118
 
104
119
  Examples:
105
120
 
106
- >>> ln.settings.storage
107
- StorageSettings(root='s3://my-bucket', uid='j7MaPxtLxPeE')
121
+ Retrieve the storage settings::
122
+
123
+ ln.settings.storage
124
+ #> StorageSettings(root='s3://my-bucket')
108
125
 
109
- >>> ln.settings.storage.root
110
- UPath('s3://my-bucket')
126
+ Retrieve the storage root::
111
127
 
112
- You can switch the default storage location to another managed storage
113
- location by passing a string:
128
+ ln.settings.storage.root
129
+ #> UPath('s3://my-bucket')
114
130
 
115
- >>> ln.settings.storage = "s3://some-bucket"
131
+ You can write artifacts to other storage locations by switching the current default storage location::
116
132
 
117
- You can also pass additional fsspec kwargs via:
133
+ ln.settings.storage = "s3://some-bucket"
118
134
 
119
- >>> kwargs = dict(
120
- >>> profile="some_profile", # fsspec arg
121
- >>> cache_regions=True # fsspec arg for s3
122
- >>> )
123
- >>> ln.settings.storage = "s3://some-bucket", kwargs
135
+ You can also pass additional fsspec kwargs via::
136
+
137
+ kwargs = dict(
138
+ profile="some_profile", # fsspec arg
139
+ cache_regions=True # fsspec arg for s3
140
+ )
141
+ ln.settings.storage = "s3://some-bucket", kwargs
124
142
  """
125
143
  return self._storage_settings
126
144
 
@@ -174,9 +192,4 @@ class Settings:
174
192
  logger.set_verbosity(verbosity_int)
175
193
 
176
194
 
177
- if os.environ.get("LAMINDB_MULTI_INSTANCE") == "true":
178
- git_repo = None
179
- else:
180
- git_repo = setup_settings.instance.git_repo
181
-
182
- settings = Settings(git_repo=git_repo)
195
+ settings = Settings()
@@ -1,12 +1,17 @@
1
1
  """Test datasets.
2
2
 
3
+ The mini immuno dataset.
4
+
5
+ .. autosummary::
6
+ :toctree: .
7
+
8
+ mini_immuno
9
+
3
10
  Small in-memory datasets.
4
11
 
5
12
  .. autosummary::
6
13
  :toctree: .
7
14
 
8
- small_dataset1
9
- small_dataset2
10
15
  anndata_with_obs
11
16
 
12
17
  Files.
@@ -59,6 +64,7 @@ Other.
59
64
  fake_bio_notebook_titles
60
65
  """
61
66
 
67
+ from . import mini_immuno
62
68
  from ._core import (
63
69
  anndata_file_pbmc68k_test,
64
70
  anndata_human_immune_cells,
@@ -88,7 +94,8 @@ from ._core import (
88
94
  from ._fake import fake_bio_notebook_titles
89
95
  from ._small import (
90
96
  anndata_with_obs,
91
- small_dataset1,
92
- small_dataset2,
93
97
  small_dataset3_cellxgene,
94
98
  )
99
+
100
+ small_dataset1 = mini_immuno.get_dataset1 # backward compat
101
+ small_dataset2 = mini_immuno.get_dataset2 # backward compat
@@ -322,8 +322,6 @@ def anndata_human_immune_cells(
322
322
 
323
323
  import lamindb as ln
324
324
 
325
- verbosity = ln.settings.verbosity
326
- ln.settings.verbosity = "error"
327
325
  ln.save(
328
326
  bt.Gene.from_values(
329
327
  adata.var.index, field="ensembl_gene_id", organism="human"
@@ -339,7 +337,6 @@ def anndata_human_immune_cells(
339
337
  ln.Feature(name="donor", dtype=[ln.ULabel]).save() # type: ignore
340
338
  bt.ExperimentalFactor.from_source(ontology_id="EFO:0008913").save()
341
339
  ln.save([ln.ULabel(name=name) for name in adata.obs.donor.unique()])
342
- ln.settings.verbosity = verbosity
343
340
  return adata
344
341
 
345
342
 
@@ -560,11 +557,14 @@ def spatialdata_blobs() -> SpatialData:
560
557
  from spatialdata.datasets import blobs
561
558
 
562
559
  sdata = blobs()
563
- sdata.attrs["sample"] = {
564
- "assay": "Visium Spatial Gene Expression",
560
+ sdata.attrs["bio"] = {
565
561
  "disease": "Alzheimer disease",
566
562
  "developmental_stage": "adult stage",
567
563
  }
564
+ sdata.attrs["tech"] = {
565
+ "assay": "Visium Spatial Gene Expression",
566
+ }
567
+ sdata.attrs["random_int"] = 20
568
568
  sdata.tables["table"].var.index = [
569
569
  "ENSG00000139618", # BRCA2
570
570
  "ENSG00000157764", # BRAF