lamindb 0.75.0__py3-none-any.whl → 0.75.1__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/_from_values.py CHANGED
@@ -335,9 +335,9 @@ def _bulk_create_dicts_from_df(
335
335
  return df.reset_index().to_dict(orient="records"), multi_msg
336
336
 
337
337
 
338
- def _has_organism_field(orm: type[Record]) -> bool:
338
+ def _has_organism_field(registry: type[Record]) -> bool:
339
339
  try:
340
- orm._meta.get_field("organism")
340
+ registry._meta.get_field("organism")
341
341
  return True
342
342
  except FieldDoesNotExist:
343
343
  return False
@@ -346,17 +346,17 @@ def _has_organism_field(orm: type[Record]) -> bool:
346
346
  def _get_organism_record(
347
347
  field: StrField, organism: str | Record, force: bool = False
348
348
  ) -> Record:
349
- model = field.field.model
349
+ registry = field.field.model
350
350
  check = True
351
- if not force and hasattr(model, "_ontology_id_field"):
352
- check = field.field.name != model._ontology_id_field
351
+ if not force and hasattr(registry, "_ontology_id_field"):
352
+ check = field.field.name != registry._ontology_id_field
353
353
  # e.g. bionty.CellMarker has "name" as _ontology_id_field
354
- if not model._ontology_id_field.endswith("id"):
354
+ if not registry._ontology_id_field.endswith("id"):
355
355
  check = True
356
356
 
357
- if _has_organism_field(model) and check:
357
+ if _has_organism_field(registry) and check:
358
358
  from bionty._bionty import create_or_get_organism_record
359
359
 
360
- organism_record = create_or_get_organism_record(organism=organism, orm=model)
360
+ organism_record = create_or_get_organism_record(organism=organism, orm=registry)
361
361
  if organism_record is not None:
362
362
  return organism_record
lamindb/_record.py CHANGED
@@ -36,9 +36,9 @@ def init_self_from_db(self: Record, existing_record: Record):
36
36
  self._state.db = "default"
37
37
 
38
38
 
39
- def validate_required_fields(orm: Record, kwargs):
39
+ def validate_required_fields(record: Record, kwargs):
40
40
  required_fields = {
41
- k.name for k in orm._meta.fields if not k.null and k.default is None
41
+ k.name for k in record._meta.fields if not k.null and k.default is None
42
42
  }
43
43
  required_fields_not_passed = {k: None for k in required_fields if k not in kwargs}
44
44
  kwargs.update(required_fields_not_passed)
@@ -77,9 +77,9 @@ def suggest_records_with_similar_names(record: Record, kwargs) -> bool:
77
77
  return False
78
78
 
79
79
 
80
- def __init__(orm: Record, *args, **kwargs):
80
+ def __init__(record: Record, *args, **kwargs):
81
81
  if not args:
82
- validate_required_fields(orm, kwargs)
82
+ validate_required_fields(record, kwargs)
83
83
 
84
84
  # do not search for names if an id is passed; this is important
85
85
  # e.g. when synching ids from the notebook store to lamindb
@@ -87,29 +87,29 @@ def __init__(orm: Record, *args, **kwargs):
87
87
  if "_has_consciously_provided_uid" in kwargs:
88
88
  has_consciously_provided_uid = kwargs.pop("_has_consciously_provided_uid")
89
89
  if settings.creation.search_names and not has_consciously_provided_uid:
90
- match = suggest_records_with_similar_names(orm, kwargs)
90
+ match = suggest_records_with_similar_names(record, kwargs)
91
91
  if match:
92
92
  if "version" in kwargs:
93
93
  version_comment = " and version"
94
- existing_record = orm.__class__.filter(
94
+ existing_record = record.__class__.filter(
95
95
  name=kwargs["name"], version=kwargs["version"]
96
96
  ).one_or_none()
97
97
  else:
98
98
  version_comment = ""
99
- existing_record = orm.__class__.filter(name=kwargs["name"]).one()
99
+ existing_record = record.__class__.filter(name=kwargs["name"]).one()
100
100
  if existing_record is not None:
101
101
  logger.important(
102
- f"returning existing {orm.__class__.__name__} record with same"
102
+ f"returning existing {record.__class__.__name__} record with same"
103
103
  f" name{version_comment}: '{kwargs['name']}'"
104
104
  )
105
- init_self_from_db(orm, existing_record)
105
+ init_self_from_db(record, existing_record)
106
106
  return None
107
- super(Record, orm).__init__(**kwargs)
108
- elif len(args) != len(orm._meta.concrete_fields):
107
+ super(Record, record).__init__(**kwargs)
108
+ elif len(args) != len(record._meta.concrete_fields):
109
109
  raise ValueError("please provide keyword arguments, not plain arguments")
110
110
  else:
111
111
  # object is loaded from DB (**kwargs could be omitted below, I believe)
112
- super(Record, orm).__init__(*args, **kwargs)
112
+ super(Record, record).__init__(*args, **kwargs)
113
113
 
114
114
 
115
115
  @classmethod # type:ignore
@@ -191,11 +191,11 @@ def _search(
191
191
  truncate_words: bool = False,
192
192
  ) -> QuerySet:
193
193
  input_queryset = _queryset(cls, using_key=using_key)
194
- orm = input_queryset.model
194
+ registry = input_queryset.model
195
195
  if field is None:
196
196
  fields = [
197
197
  field.name
198
- for field in orm._meta.fields
198
+ for field in registry._meta.fields
199
199
  if field.get_internal_type() in {"CharField", "TextField"}
200
200
  ]
201
201
  else:
@@ -287,7 +287,7 @@ def _lookup(
287
287
  ) -> NamedTuple:
288
288
  """{}""" # noqa: D415
289
289
  queryset = _queryset(cls, using_key=using_key)
290
- field = get_name_field(orm=queryset.model, field=field)
290
+ field = get_name_field(registry=queryset.model, field=field)
291
291
 
292
292
  return Lookup(
293
293
  records=queryset,
@@ -296,7 +296,7 @@ def _lookup(
296
296
  prefix="ln",
297
297
  ).lookup(
298
298
  return_field=(
299
- get_name_field(orm=queryset.model, field=return_field)
299
+ get_name_field(registry=queryset.model, field=return_field)
300
300
  if return_field is not None
301
301
  else None
302
302
  )
@@ -315,24 +315,24 @@ def lookup(
315
315
 
316
316
 
317
317
  def get_name_field(
318
- orm: Record | QuerySet | Manager,
318
+ registry: type[Record] | QuerySet | Manager,
319
319
  *,
320
320
  field: str | StrField | None = None,
321
321
  ) -> str:
322
- """Get the 1st char or text field from the orm."""
323
- if isinstance(orm, (QuerySet, Manager)):
324
- orm = orm.model
325
- model_field_names = [i.name for i in orm._meta.fields]
322
+ """Get the 1st char or text field from the registry."""
323
+ if isinstance(registry, (QuerySet, Manager)):
324
+ registry = registry.model
325
+ model_field_names = [i.name for i in registry._meta.fields]
326
326
 
327
327
  # set to default name field
328
328
  if field is None:
329
- if hasattr(orm, "_name_field"):
330
- field = orm._meta.get_field(orm._name_field)
329
+ if hasattr(registry, "_name_field"):
330
+ field = registry._meta.get_field(registry._name_field)
331
331
  elif "name" in model_field_names:
332
- field = orm._meta.get_field("name")
332
+ field = registry._meta.get_field("name")
333
333
  else:
334
334
  # first char or text field that doesn't contain "id"
335
- for i in orm._meta.fields:
335
+ for i in registry._meta.fields:
336
336
  if "id" in i.name:
337
337
  continue
338
338
  if i.get_internal_type() in {"CharField", "TextField"}:
@@ -360,7 +360,7 @@ def get_name_field(
360
360
  def _queryset(cls: Record | QuerySet | Manager, using_key: str) -> QuerySet:
361
361
  if isinstance(cls, (QuerySet, Manager)):
362
362
  return cls.all()
363
- elif using_key is None:
363
+ elif using_key is None or using_key == "default":
364
364
  return cls.objects.all()
365
365
  else:
366
366
  # using must be called on cls, otherwise the connection isn't found
lamindb/_save.py CHANGED
@@ -108,21 +108,21 @@ def bulk_create(records: Iterable[Record], ignore_conflicts: bool | None = False
108
108
  records_by_orm = defaultdict(list)
109
109
  for record in records:
110
110
  records_by_orm[record.__class__].append(record)
111
- for orm, records in records_by_orm.items():
112
- orm.objects.bulk_create(records, ignore_conflicts=ignore_conflicts)
111
+ for registry, records in records_by_orm.items():
112
+ registry.objects.bulk_create(records, ignore_conflicts=ignore_conflicts)
113
113
 
114
114
 
115
115
  def bulk_update(records: Iterable[Record], ignore_conflicts: bool | None = False):
116
116
  records_by_orm = defaultdict(list)
117
117
  for record in records:
118
118
  records_by_orm[record.__class__].append(record)
119
- for orm, records in records_by_orm.items():
119
+ for registry, records in records_by_orm.items():
120
120
  field_names = [
121
121
  field.name
122
- for field in orm._meta.fields
122
+ for field in registry._meta.fields
123
123
  if (field.name != "created_at" and field.name != "id")
124
124
  ]
125
- orm.objects.bulk_update(records, field_names)
125
+ registry.objects.bulk_update(records, field_names)
126
126
 
127
127
 
128
128
  # This is also used within Artifact.save()
lamindb/_view.py CHANGED
@@ -41,15 +41,17 @@ def view(
41
41
  schema_module = importlib.import_module(get_schema_module_name(schema_name))
42
42
 
43
43
  all_registries = {
44
- orm
45
- for orm in schema_module.__dict__.values()
46
- if inspect.isclass(orm)
47
- and issubclass(orm, Record)
48
- and orm.__name__ != "Record"
44
+ registry
45
+ for registry in schema_module.__dict__.values()
46
+ if inspect.isclass(registry)
47
+ and issubclass(registry, Record)
48
+ and registry is not Record
49
49
  }
50
50
  if registries is not None:
51
51
  filtered_registries = {
52
- orm for orm in all_registries if orm.__name__ in registries
52
+ registry
53
+ for registry in all_registries
54
+ if registry.__name__ in registries
53
55
  }
54
56
  else:
55
57
  filtered_registries = all_registries
@@ -59,12 +61,12 @@ def view(
59
61
  logger.print("*" * len(section_no_color))
60
62
  logger.print(section)
61
63
  logger.print("*" * len(section_no_color))
62
- for orm in sorted(filtered_registries, key=lambda x: x.__name__):
63
- if hasattr(orm, "updated_at"):
64
- df = orm.filter().order_by("-updated_at")[:n].df()
64
+ for registry in sorted(filtered_registries, key=lambda x: x.__name__):
65
+ if hasattr(registry, "updated_at"):
66
+ df = registry.filter().order_by("-updated_at")[:n].df()
65
67
  else:
66
68
  # need to adjust in the future
67
- df = orm.df().iloc[-n:]
69
+ df = registry.df().iloc[-n:]
68
70
  if df.shape[0] > 0:
69
- logger.print(colors.blue(colors.bold(orm.__name__)))
71
+ logger.print(colors.blue(colors.bold(registry.__name__)))
70
72
  show(df)
lamindb/core/__init__.py CHANGED
@@ -6,6 +6,7 @@ Registries:
6
6
  :toctree: .
7
7
 
8
8
  Record
9
+ Registry
9
10
  QuerySet
10
11
  QueryManager
11
12
  RecordsList
@@ -66,6 +67,7 @@ from lnschema_core.models import (
66
67
  IsVersioned,
67
68
  ParamValue,
68
69
  Record,
70
+ Registry,
69
71
  TracksRun,
70
72
  TracksUpdates,
71
73
  )
lamindb/core/_data.py CHANGED
@@ -14,8 +14,8 @@ from lnschema_core.models import (
14
14
  Record,
15
15
  Run,
16
16
  ULabel,
17
- __repr__,
18
17
  format_field_value,
18
+ record_repr,
19
19
  )
20
20
 
21
21
  from lamindb._parents import view_lineage
@@ -108,7 +108,7 @@ def describe(self: HasFeatures, print_types: bool = False):
108
108
  # )
109
109
 
110
110
  model_name = self.__class__.__name__
111
- msg = f"{colors.green(model_name)}{__repr__(self, include_foreign_keys=False).lstrip(model_name)}\n"
111
+ msg = f"{colors.green(model_name)}{record_repr(self, include_foreign_keys=False).lstrip(model_name)}\n"
112
112
  prov_msg = ""
113
113
 
114
114
  fields = self._meta.fields
@@ -251,8 +251,8 @@ def add_labels(
251
251
  if feature.dtype.startswith("cat["):
252
252
  orm_dict = dict_schema_name_to_model_name(Artifact)
253
253
  for reg in feature.dtype.replace("cat[", "").rstrip("]").split("|"):
254
- orm = orm_dict.get(reg)
255
- records_validated += orm.from_values(records, field=field)
254
+ registry = orm_dict.get(reg)
255
+ records_validated += registry.from_values(records, field=field)
256
256
 
257
257
  # feature doesn't have registries and therefore can't create records from values
258
258
  # ask users to pass records
@@ -118,9 +118,7 @@ def get_feature_set_links(host: Artifact | Collection) -> QuerySet:
118
118
 
119
119
  def get_link_attr(link: LinkORM | type[LinkORM], data: HasFeatures) -> str:
120
120
  link_model_name = link.__class__.__name__
121
- if (
122
- link_model_name == "ModelBase" or link_model_name == "RecordMeta"
123
- ): # we passed the type of the link
121
+ if link_model_name in {"Registry", "ModelBase"}: # we passed the type of the link
124
122
  link_model_name = link.__name__
125
123
  return link_model_name.replace(data.__class__.__name__, "").lower()
126
124
 
@@ -746,9 +744,9 @@ def _add_set_from_mudata(
746
744
  # parse and register features
747
745
  mdata = self._host.load()
748
746
  feature_sets = {}
749
- obs_features = features = Feature.from_values(mdata.obs.columns)
747
+ obs_features = Feature.from_values(mdata.obs.columns)
750
748
  if len(obs_features) > 0:
751
- feature_sets["obs"] = FeatureSet(features=features)
749
+ feature_sets["obs"] = FeatureSet(features=obs_features)
752
750
  for modality, field in var_fields.items():
753
751
  modality_fs = parse_feature_sets_from_anndata(
754
752
  mdata[modality],
@@ -760,8 +758,20 @@ def _add_set_from_mudata(
760
758
  for k, v in modality_fs.items():
761
759
  feature_sets[f"['{modality}'].{k}"] = v
762
760
 
761
+ def unify_feature_sets_by_hash(feature_sets):
762
+ unique_values = {}
763
+
764
+ for key, value in feature_sets.items():
765
+ value_hash = value.hash # Assuming each value has a .hash attribute
766
+ if value_hash in unique_values:
767
+ feature_sets[key] = unique_values[value_hash]
768
+ else:
769
+ unique_values[value_hash] = value
770
+
771
+ return feature_sets
772
+
763
773
  # link feature sets
764
- self._host._feature_sets = feature_sets
774
+ self._host._feature_sets = unify_feature_sets_by_hash(feature_sets)
765
775
  self._host.save()
766
776
 
767
777
 
lamindb/core/schema.py CHANGED
@@ -4,16 +4,16 @@ from django.db.models import ManyToManyField
4
4
  from lnschema_core.models import Feature, FeatureSet, LinkORM, Record
5
5
 
6
6
 
7
- def dict_schema_name_to_model_name(orm: type[Record]) -> dict[str, Record]:
7
+ def dict_schema_name_to_model_name(registry: type[Record]) -> dict[str, Record]:
8
8
  d: dict = {
9
9
  i.related_model.__get_name_with_schema__(): i.related_model
10
- for i in orm._meta.related_objects
10
+ for i in registry._meta.related_objects
11
11
  if i.related_name is not None
12
12
  }
13
13
  d.update(
14
14
  {
15
15
  i.related_model.__get_name_with_schema__(): i.related_model
16
- for i in orm._meta.many_to_many
16
+ for i in registry._meta.many_to_many
17
17
  if i.name is not None
18
18
  }
19
19
  )
@@ -21,12 +21,12 @@ def dict_schema_name_to_model_name(orm: type[Record]) -> dict[str, Record]:
21
21
 
22
22
 
23
23
  def dict_related_model_to_related_name(
24
- orm: type[Record], links: bool = False
24
+ registry: type[Record], links: bool = False
25
25
  ) -> dict[str, str]:
26
26
  def include(model: Record):
27
27
  return not links != issubclass(model, LinkORM)
28
28
 
29
- related_objects = orm._meta.related_objects + orm._meta.many_to_many
29
+ related_objects = registry._meta.related_objects + registry._meta.many_to_many
30
30
  d: dict = {
31
31
  record.related_model.__get_name_with_schema__(): (
32
32
  record.related_name
@@ -1,4 +1,13 @@
1
- """Storage tools.
1
+ """Storage API.
2
+
3
+ Valid suffixes.
4
+
5
+ .. autosummary::
6
+ :toctree: .
7
+
8
+ VALID_SUFFIXES
9
+
10
+ Array accessors.
2
11
 
3
12
  .. autosummary::
4
13
  :toctree: .
@@ -11,6 +20,6 @@ from lamindb_setup.core.upath import LocalPathClasses, UPath, infer_filesystem
11
20
 
12
21
  from ._anndata_sizes import size_adata
13
22
  from ._backed_access import AnnDataAccessor, BackedAccessor
14
- from ._valid_suffixes import VALID_COMPOSITE_SUFFIXES, VALID_SUFFIXES
23
+ from ._valid_suffixes import VALID_SUFFIXES
15
24
  from .objects import infer_suffix, write_to_disk
16
25
  from .paths import delete_storage, load_to_memory
@@ -1,5 +1,19 @@
1
- from lamindb_setup.core.upath import VALID_COMPOSITE_SUFFIXES, VALID_SUFFIXES
1
+ from lamindb_setup.core.upath import VALID_COMPOSITE_SUFFIXES, VALID_SIMPLE_SUFFIXES
2
2
 
3
3
  # add new composite suffixes like so
4
- VALID_COMPOSITE_SUFFIXES.update({".vitessce.json"})
4
+ VALID_COMPOSITE_SUFFIXES.update(
5
+ {
6
+ ".vitessce.json",
7
+ ".ome.zarr",
8
+ }
9
+ )
5
10
  # can do the same for simple valid suffixes
11
+
12
+
13
+ class VALID_SUFFIXES:
14
+ """Valid suffixes."""
15
+
16
+ SIMPLE: set[str] = VALID_SIMPLE_SUFFIXES
17
+ """Simple suffixes."""
18
+ COMPOSITE: set[str] = VALID_COMPOSITE_SUFFIXES
19
+ """Composite suffixes."""
@@ -15,61 +15,98 @@ if TYPE_CHECKING:
15
15
  from vitessce import VitessceConfig
16
16
 
17
17
 
18
- # tested & context in https://github.com/laminlabs/lamin-spatial
19
- def save_vitessce_config(vitessce_config: VitessceConfig, description: str) -> Artifact:
18
+ # "unit test": https://github.com/laminlabs/lamindb/blob/main/docs/storage/vitessce.ipynb
19
+ # integration test & context: https://github.com/laminlabs/lamin-spatial/blob/main/docs/vitessce.ipynb
20
+ def save_vitessce_config(
21
+ vitessce_config: VitessceConfig, description: str | None = None
22
+ ) -> Artifact:
20
23
  """Validates and saves a ``VitessceConfig`` object.
21
24
 
22
- Example: :doc:`docs:vitessce`.
25
+ Guide: :doc:`docs:vitessce`.
23
26
 
24
27
  Args:
25
- vitessce_config (``VitessceConfig``): A VitessceConfig object.
26
- description: A description for the artifact.
28
+ vitessce_config (``VitessceConfig``): A `VitessceConfig` object.
29
+ description: A description for the `VitessceConfig` artifact.
27
30
 
31
+ .. versionchanged:: 0.75.1
32
+ Now displays the "Vitessce button" on the hub next to the dataset. It additionally keeps displaying it next to the configuration file.
28
33
  .. versionchanged:: 0.70.2
29
- This function no longer saves the dataset. It only saves the VitessceConfig object.
34
+ No longer saves the dataset. It only saves the `VitessceConfig` object.
30
35
  """
36
+ # can only import here because vitessce is not a dependency
37
+ from vitessce import VitessceConfig
38
+
39
+ from lamindb.core.storage import VALID_SUFFIXES
40
+
41
+ assert isinstance(vitessce_config, VitessceConfig) # noqa: S101
31
42
  vc_dict = vitessce_config.to_dict()
43
+ valid_composite_zarr_suffixes = [
44
+ suffix for suffix in VALID_SUFFIXES.COMPOSITE if suffix.endswith(".zarr")
45
+ ]
32
46
  # validate
33
- datasets = vc_dict["datasets"]
34
- input_artifacts = []
35
- for dataset in datasets:
36
- if "files" not in dataset:
37
- raise ValueError("Each dataset must have a 'files' key.")
38
- for file in dataset["files"]:
47
+ dataset_artifacts = []
48
+ assert vc_dict["datasets"] # noqa: S101
49
+ for vitessce_dataset in vc_dict["datasets"]:
50
+ # didn't find good ways to violate the below, hence using plain asserts
51
+ # without user feedback
52
+ assert "files" in vitessce_dataset # noqa: S101
53
+ assert vitessce_dataset["files"] # noqa: S101
54
+ for file in vitessce_dataset["files"]:
39
55
  if "url" not in file:
40
56
  raise ValueError("Each file must have a 'url' key.")
41
- filename = file["url"].split("/")[-1]
42
- if not filename.endswith((".anndata.zarr", ".zarr", ".ome.zarr")):
43
- logger.warning(
44
- "filename should end with '.anndata.zarr', '.zarr', or '.ome.zarr'."
57
+ s3_path = file["url"]
58
+ s3_path_last_element = s3_path.split("/")[-1]
59
+ # note 1: the following parses the stem uid of the artifact from the S3 path
60
+ # there might be a better way of doing this in case the vitessce config
61
+ # gets updated in the future; but given these paths are set in stone
62
+ # this should be more robust than it looks
63
+ #
64
+ # note 2: what's not great is the fact that people might use composite suffixes we don't recognize
65
+ # I don't know what to do about it other than documenting it clearly
66
+ # https://github.com/laminlabs/lamindb/blob/main/lamindb/core/storage/_valid_suffixes.py
67
+ # https://docs.lamin.ai/lamindb.core.storage.valid_suffixes
68
+ #
69
+ # now start with attempting to strip the composite suffix candidates
70
+ for suffix in valid_composite_zarr_suffixes:
71
+ s3_path_last_element = s3_path_last_element.replace(suffix, "")
72
+ # in case there was no hit, strip plain ".zarr"
73
+ artifact_stem_uid = s3_path_last_element.replace(".zarr", "")
74
+ # if there is still a "." in string, we
75
+ if "." in artifact_stem_uid:
76
+ raise ValueError(
77
+ f"Suffix should be '.zarr' or one of {valid_composite_zarr_suffixes}. Inspect your path {s3_path}"
45
78
  )
46
- filestem = (
47
- filename.replace(".anndata.zarr", "")
48
- .replace(".spatialdata.zarr", "")
49
- .replace(".ome.zarr", "")
50
- )
51
- artifact = Artifact.filter(uid__startswith=filestem).one_or_none()
79
+ artifact = Artifact.filter(uid__startswith=artifact_stem_uid).one_or_none()
52
80
  if artifact is None:
53
- logger.warning(
54
- f"could not find dataset '{filestem}' in lamindb: {dataset}"
81
+ raise ValueError(
82
+ f"Could not find dataset with stem uid '{artifact_stem_uid}' in lamindb: {vitessce_dataset}. Did you follow https://docs.lamin.ai/vitessce? It appears the AWS S3 path doesn't encode a lamindb uid."
55
83
  )
56
84
  else:
57
- input_artifacts.append(artifact)
85
+ dataset_artifacts.append(artifact)
58
86
  # link inputs
59
87
  with logger.mute():
60
- transform = Transform(name="save_vitessce_config", type="function", version="1")
88
+ transform = Transform(name="save_vitessce_config", type="function", version="2")
61
89
  transform.save()
62
90
  run = Run(transform=transform)
63
91
  run.save()
64
- run.input_artifacts.set(input_artifacts)
92
+ if len(dataset_artifacts) > 1:
93
+ # if we have more datasets, we should create a collection
94
+ # and attach an action to the collection
95
+ raise NotImplementedError
96
+ run.input_artifacts.set(dataset_artifacts)
65
97
  # create a JSON export
66
98
  config_file_local_path = (
67
99
  ln_setup.settings.storage.cache_dir / "config.vitessce.json"
68
100
  )
69
101
  with open(config_file_local_path, "w") as file:
70
102
  json.dump(vc_dict, file)
71
- artifact = Artifact(config_file_local_path, description=description, run=run)
72
- artifact.save()
103
+ vitessce_config_artifact = Artifact(
104
+ config_file_local_path, description=description, run=run
105
+ ).save()
106
+ # we have one and only one dataset artifact, hence the following line is OK
107
+ dataset_artifacts[0]._actions.add(vitessce_config_artifact)
73
108
  slug = ln_setup.settings.instance.slug
74
- logger.important(f"go to: https://lamin.ai/{slug}/artifact/{artifact.uid}")
75
- return artifact
109
+ logger.important(
110
+ f"go to: https://lamin.ai/{slug}/artifact/{vitessce_config_artifact.uid}"
111
+ )
112
+ return vitessce_config_artifact
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lamindb
3
- Version: 0.75.0
3
+ Version: 0.75.1
4
4
  Summary: A data framework for biology.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.8
@@ -9,8 +9,8 @@ Classifier: Programming Language :: Python :: 3.8
9
9
  Classifier: Programming Language :: Python :: 3.9
10
10
  Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
- Requires-Dist: lnschema_core==0.72.1
13
- Requires-Dist: lamindb_setup==0.76.2
12
+ Requires-Dist: lnschema_core==0.72.2
13
+ Requires-Dist: lamindb_setup==0.76.3
14
14
  Requires-Dist: lamin_utils==0.13.2
15
15
  Requires-Dist: lamin_cli==0.16.0
16
16
  Requires-Dist: rapidfuzz
@@ -24,7 +24,7 @@ Requires-Dist: pandas
24
24
  Requires-Dist: graphviz
25
25
  Requires-Dist: psycopg2-binary
26
26
  Requires-Dist: lamindb_setup[aws] ; extra == "aws"
27
- Requires-Dist: bionty==0.47.1 ; extra == "bionty"
27
+ Requires-Dist: bionty==0.48.0 ; extra == "bionty"
28
28
  Requires-Dist: pre-commit ; extra == "dev"
29
29
  Requires-Dist: nox ; extra == "dev"
30
30
  Requires-Dist: laminci>=0.3 ; extra == "dev"
@@ -1,28 +1,28 @@
1
- lamindb/__init__.py,sha256=Ua3SskTHYGZID5uI9cI_KyVS3h0f93U6rUqxHVAjUm8,2249
1
+ lamindb/__init__.py,sha256=i66mHHYh9TUGKmCb5WgvaZx0flzOQyKKLcCpz01_8X0,2249
2
2
  lamindb/_artifact.py,sha256=pu0d2CuRkT4xP5hLpkrKlSPU3Maihmdk1B7VKu-cjj4,42575
3
- lamindb/_can_validate.py,sha256=R5xZtCiH_rHl_PyAD1cHDYzd-n53KDtWiEkis6VGKsE,16347
3
+ lamindb/_can_validate.py,sha256=jPZHhtPaZJos5bL8Mk8ZNh34mItwYRwOynhofKUxwfY,17472
4
4
  lamindb/_collection.py,sha256=vrvuww4MLPx6qHqRWy6tLvRHOmXdvEjYN7dnG9DIIPU,14894
5
- lamindb/_curate.py,sha256=lN7O7cmV703VDDOemvSENc7Jr35pqfI916ctYG7Psdc,45455
5
+ lamindb/_curate.py,sha256=M42XxtcSKSo2Vh3xCio1UogVS0kCG1Dtx5cwEwnWOEM,55508
6
6
  lamindb/_feature.py,sha256=hLj5KDhIVkZrIa7IbiHGyUBGZS7PeDNxKsNK1EadBAc,7377
7
7
  lamindb/_feature_set.py,sha256=DmAy96V_RyV0yiyvWOCHgustXPsCaMwn4TrWwh2qDd8,8104
8
8
  lamindb/_filter.py,sha256=I1tSIF-izmD48YpB8NtKmau59Vpt6C54RPHWc3oinGM,1412
9
9
  lamindb/_finish.py,sha256=3rGY4RxhcKR1BIdgf6GPB03AArhlV5vYtD6wxtTHYxE,10817
10
- lamindb/_from_values.py,sha256=HwnH3Q0d80DswF2Bs0q6S02RVjbcQb-fCRzwBDwb3Q8,12831
10
+ lamindb/_from_values.py,sha256=tE6LKWbixZWbt-ILsy2EeRh7i9eKvvAeJv081VQRGAk,12859
11
11
  lamindb/_is_versioned.py,sha256=Z_zVsHTnNaWvD15dEz18VocsSgka53jUzs_wDr78ltI,1344
12
12
  lamindb/_parents.py,sha256=eMavdd6IO6STOVJSlR2TzdRtx6sKYDKsMOtlR3DZlgQ,15599
13
13
  lamindb/_query_manager.py,sha256=6_ZqQr0SKTYsbMtJd5BmaMoyASIVel5v2F1KMgacWlU,4252
14
14
  lamindb/_query_set.py,sha256=LM5NsAWy5FCZZSxX1VbE20RdA5nsyk4_5p4abuMjdcI,11662
15
- lamindb/_record.py,sha256=Kv6dZhdbSo8NbOUXVwJ_ZY2P53NmBjdmUEMM7Xmvaoo,19076
15
+ lamindb/_record.py,sha256=XtNKZol9X5u3h1fecDhOn5Z1OpTXunQ9UrZwcHSiajM,19219
16
16
  lamindb/_run.py,sha256=xj3ER4F_yWvuNw1mr0XU-QuIPi5hBO7Ue0ygBgJQ6mc,1887
17
- lamindb/_save.py,sha256=1A6NBy6Yp76fyc_5Sn7PMQZzwDHWu5KMVzqXrIt1hfg,11002
17
+ lamindb/_save.py,sha256=9lpFpQLgmPOp6JcoM9XetNkbYu2JgY70sGQZVz3Gzmw,11027
18
18
  lamindb/_storage.py,sha256=GBVChv-DHVMNEBJL5l_JT6B4RDhZ6NnwgzmUICphYKk,413
19
19
  lamindb/_transform.py,sha256=OtZmCSS-mxWPJU0S6p79sYauY-cKIxqBYfeMX0-SQ_A,3435
20
20
  lamindb/_ulabel.py,sha256=XDSdZBXX_ki5s1vOths3MjF2x5DPggBR_PV_KF4SGyg,1611
21
21
  lamindb/_utils.py,sha256=LGdiW4k3GClLz65vKAVRkL6Tw-Gkx9DWAdez1jyA5bE,428
22
- lamindb/_view.py,sha256=_sAcSD06bC92R_LLDcHMhO0IfOqybBXXRYxOXvm1Juc,2322
23
- lamindb/core/__init__.py,sha256=HtyYVFrS6RclSk7gZty9vgXU0k2_oZiqFPfqghBc4Fc,1415
24
- lamindb/core/_data.py,sha256=LPfbWqTw3w5o_HygSg5V67hK7XFvbvv0QVzkk6kcFzY,16237
25
- lamindb/core/_feature_manager.py,sha256=NMC2SuIJJUJzgQjgupU2a0Gu6m0mjgnjbBf-hRXrO10,31585
22
+ lamindb/_view.py,sha256=4Ln2ItTb3857PAI-70O8eJYqoTJ_NNFc7E_wds6OGns,2412
23
+ lamindb/core/__init__.py,sha256=uCtTkcUwJ7w40cWdaVBymiyxMuKlkdX-QZMtOr3k0us,1441
24
+ lamindb/core/_data.py,sha256=AbBVvutI7iFZW5PVEYzx0HIlaLhR9KpnoPyba9OI86k,16253
25
+ lamindb/core/_feature_manager.py,sha256=gJowe9-tifi0Z5IcChV-jtGYVdl6XgR2m_oswwhU17Y,31980
26
26
  lamindb/core/_label_manager.py,sha256=5jbBF9aYPeeqpDZDimAXskGw76YUaQTQak6XAt6madU,9183
27
27
  lamindb/core/_mapped_collection.py,sha256=wN2qEXnKrx0kqblVml_Kx9hYomUAZLYKckGXX4-9dr0,19621
28
28
  lamindb/core/_run_context.py,sha256=bk4hwbGbx1no-JKVpLioIOU5dyO8YCdg-srbQV7UuGE,18371
@@ -31,17 +31,17 @@ lamindb/core/_sync_git.py,sha256=qc0yfPyKeG4uuNT_3qsv-mkIMqhLFqfXNeNVO49vV00,454
31
31
  lamindb/core/_track_environment.py,sha256=STzEVUzOeUEWdX7WDJUkKH4u08k7eupRX6AXQwoVt14,828
32
32
  lamindb/core/exceptions.py,sha256=rNLDlqDJt6stM_HGeE9qR0oWRNn4Kq0WDdIi85P53VA,1057
33
33
  lamindb/core/fields.py,sha256=47Jmh3efUr5ZscgimR_yckY-I3cNf8ScLutbwKCK3j4,162
34
- lamindb/core/schema.py,sha256=sNrzWnqeyh9Ppr7yImlSAXiHb_VJdB5DWuO-QERxQGY,1847
34
+ lamindb/core/schema.py,sha256=KiYQn_8fokSMztTNDe6qUocZzKXWxU32H-YChNJv51A,1877
35
35
  lamindb/core/types.py,sha256=uVBqSVLoQaTkqP9nqsJhwU6yYnx8H5e6-ZxrB6vpOOw,265
36
36
  lamindb/core/versioning.py,sha256=8fapNQmpxlUaiJG9oo9Y4I7xtFXzD077dMwzkeWTMMY,4965
37
37
  lamindb/core/datasets/__init__.py,sha256=zRP98oqUAaXhqWyKMiH0s_ImVIuNeziQQ2kQ_t0f-DI,1353
38
38
  lamindb/core/datasets/_core.py,sha256=04mqj3IVIpY97HlD7VVL6-E6tf7za9suzE3lH07Z698,19564
39
39
  lamindb/core/datasets/_fake.py,sha256=BZF9R_1iF0HDnvtZNqL2FtsjSMuqDIfuFxnw_LJYIh4,953
40
- lamindb/core/storage/__init__.py,sha256=pLF8maqC18zrzIwbQZmBCmJJm34io7ECPDjdrb0-FMw,442
40
+ lamindb/core/storage/__init__.py,sha256=MjKg2-p8EbOYTVsgufnK93ut7HG7_MzLDAqtzXt0U2Q,501
41
41
  lamindb/core/storage/_anndata_accessor.py,sha256=jmEZeeZlt8-qBXRkU0tTA-t6dVEb_dH86wc1ok0jSRY,24030
42
42
  lamindb/core/storage/_anndata_sizes.py,sha256=aXO3OB--tF5MChenSsigW6Q-RuE8YJJOUTVukkLrv9A,1029
43
43
  lamindb/core/storage/_backed_access.py,sha256=MsSgiIccHVhqOcur2lZ4mj5LSIL5OL8nX4eqK6mloU0,4732
44
- lamindb/core/storage/_valid_suffixes.py,sha256=J08aglC9oo35pzahj0SQXW9IHib8Asp4dc11co-2uys,212
44
+ lamindb/core/storage/_valid_suffixes.py,sha256=9erE4FX1ArRoTkvn5pzUmlKQSYnKeQBymOnOPCv2TD4,465
45
45
  lamindb/core/storage/_zarr.py,sha256=5ceEz6YIvgvUnVVNWhK5Z4W0WfrvyvY82Yna5jSX1_E,3661
46
46
  lamindb/core/storage/objects.py,sha256=OzvBCS-Urz5mr-O95qYt6RGBDDX5HmjfRRKWPPDn1ZE,1797
47
47
  lamindb/core/storage/paths.py,sha256=I0UjQfXfh3MptfLgpA0iSyR-X9pLOvgtXNr4B_Lwk4g,7810
@@ -49,10 +49,10 @@ lamindb/core/subsettings/__init__.py,sha256=KFHPzIE7f7Bj4RgMjGQF4CjTdHVG_VNFBrCn
49
49
  lamindb/core/subsettings/_creation_settings.py,sha256=54mfMH_osC753hpxcl7Dq1rwBD2LHnWveXtQpkLBITE,1194
50
50
  lamindb/core/subsettings/_transform_settings.py,sha256=4YbCuZtJo6zdytl6UQR4GvdDkTtT6SRBqVzofGzNOt8,583
51
51
  lamindb/integrations/__init__.py,sha256=MoLRD_qqX5WHFUAqHL6zoY_cDkWH0zimaGT_1CyXKnk,124
52
- lamindb/integrations/_vitessce.py,sha256=VC80NKuzZ0FdjFxaLvyD78kAUNwvwBnGblSPDqT_mGQ,2737
52
+ lamindb/integrations/_vitessce.py,sha256=jcpLUNFq1BsDpZDkG5L3nzWNXDLuy3Eep_GfY0XqhhA,5077
53
53
  lamindb/setup/__init__.py,sha256=OwZpZzPDv5lPPGXZP7-zK6UdO4FHvvuBh439yZvIp3A,410
54
54
  lamindb/setup/core/__init__.py,sha256=SevlVrc2AZWL3uALbE5sopxBnIZPWZ1IB0NBDudiAL8,167
55
- lamindb-0.75.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
56
- lamindb-0.75.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
57
- lamindb-0.75.0.dist-info/METADATA,sha256=JH3tndNGYawedC8nYQRkX60azWmKmUKmRuxNn0sZOkg,2669
58
- lamindb-0.75.0.dist-info/RECORD,,
55
+ lamindb-0.75.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
56
+ lamindb-0.75.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
57
+ lamindb-0.75.1.dist-info/METADATA,sha256=dhUZDNTu6BtE-J67uGuRwJMQEJk8V_NpEurzVc90GoM,2669
58
+ lamindb-0.75.1.dist-info/RECORD,,