lamindb 0.76.11__py3-none-any.whl → 0.76.13__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 +1 -1
- lamindb/_artifact.py +8 -11
- lamindb/_collection.py +6 -5
- lamindb/_feature_set.py +3 -1
- lamindb/_filter.py +0 -2
- lamindb/_finish.py +3 -3
- lamindb/_parents.py +18 -3
- lamindb/_query_manager.py +0 -15
- lamindb/_query_set.py +3 -3
- lamindb/_record.py +10 -12
- lamindb/_save.py +1 -1
- lamindb/core/_context.py +17 -11
- lamindb/core/_django.py +19 -5
- lamindb/core/_feature_manager.py +43 -10
- lamindb/core/_label_manager.py +70 -93
- lamindb/core/_settings.py +5 -0
- lamindb/core/_sync_git.py +2 -2
- lamindb/core/_track_environment.py +1 -1
- lamindb/core/exceptions.py +1 -1
- lamindb/core/schema.py +42 -3
- lamindb/integrations/_vitessce.py +45 -51
- {lamindb-0.76.11.dist-info → lamindb-0.76.13.dist-info}/METADATA +5 -5
- {lamindb-0.76.11.dist-info → lamindb-0.76.13.dist-info}/RECORD +25 -25
- {lamindb-0.76.11.dist-info → lamindb-0.76.13.dist-info}/LICENSE +0 -0
- {lamindb-0.76.11.dist-info → lamindb-0.76.13.dist-info}/WHEEL +0 -0
lamindb/__init__.py
CHANGED
lamindb/_artifact.py
CHANGED
@@ -10,7 +10,7 @@ import fsspec
|
|
10
10
|
import lamindb_setup as ln_setup
|
11
11
|
import pandas as pd
|
12
12
|
from anndata import AnnData
|
13
|
-
from django.db.models import Q
|
13
|
+
from django.db.models import Q
|
14
14
|
from lamin_utils import colors, logger
|
15
15
|
from lamindb_setup import settings as setup_settings
|
16
16
|
from lamindb_setup._init_instance import register_storage_in_instance
|
@@ -180,7 +180,7 @@ def process_data(
|
|
180
180
|
f" be '{suffix}'."
|
181
181
|
)
|
182
182
|
cache_name = f"{provisional_uid}{suffix}"
|
183
|
-
path = settings.
|
183
|
+
path = settings.cache_dir / cache_name
|
184
184
|
# Alex: I don't understand the line below
|
185
185
|
if path.suffixes == []:
|
186
186
|
path = path.with_suffix(suffix)
|
@@ -331,9 +331,10 @@ def get_artifact_kwargs_from_data(
|
|
331
331
|
artifact = stat_or_artifact
|
332
332
|
# update the run of the existing artifact
|
333
333
|
if run is not None:
|
334
|
-
# save the information that this artifact was previously
|
335
|
-
#
|
336
|
-
|
334
|
+
# save the information that this artifact was previously produced by
|
335
|
+
# another run
|
336
|
+
# note: same logic exists for _output_collections_with_later_updates
|
337
|
+
if artifact.run is not None and artifact.run != run:
|
337
338
|
artifact.run._output_artifacts_with_later_updates.add(artifact)
|
338
339
|
# update the run of the artifact with the latest run
|
339
340
|
stat_or_artifact.run = run
|
@@ -344,7 +345,7 @@ def get_artifact_kwargs_from_data(
|
|
344
345
|
|
345
346
|
if revises is not None: # update provisional_uid
|
346
347
|
provisional_uid, revises = create_uid(revises=revises, version=version)
|
347
|
-
if settings.
|
348
|
+
if settings.cache_dir in path.parents:
|
348
349
|
path = path.rename(path.with_name(f"{provisional_uid}{suffix}"))
|
349
350
|
|
350
351
|
check_path_in_storage = False
|
@@ -749,10 +750,6 @@ def from_dir(
|
|
749
750
|
run: Run | None = None,
|
750
751
|
) -> list[Artifact]:
|
751
752
|
"""{}""" # noqa: D415
|
752
|
-
logger.warning(
|
753
|
-
"this creates one artifact per file in the directory - consider"
|
754
|
-
" ln.Artifact(dir_path) to get one artifact for the entire directory"
|
755
|
-
)
|
756
753
|
folderpath: UPath = create_path(path) # returns Path for local
|
757
754
|
default_storage = settings.storage.record
|
758
755
|
using_key = settings._using_key
|
@@ -1125,7 +1122,7 @@ def save(self, upload: bool | None = None, **kwargs) -> Artifact:
|
|
1125
1122
|
raise RuntimeError(exception)
|
1126
1123
|
if local_path is not None and not state_was_adding:
|
1127
1124
|
# only move the local artifact to cache if it was not newly created
|
1128
|
-
local_path_cache = ln_setup.settings.
|
1125
|
+
local_path_cache = ln_setup.settings.cache_dir / local_path.name
|
1129
1126
|
# don't use Path.rename here because of cross-device link error
|
1130
1127
|
# https://laminlabs.slack.com/archives/C04A0RMA0SC/p1710259102686969
|
1131
1128
|
shutil.move(
|
lamindb/_collection.py
CHANGED
@@ -145,15 +145,16 @@ def __init__(
|
|
145
145
|
logger.warning(
|
146
146
|
f"returning existing collection with same hash: {existing_collection}"
|
147
147
|
)
|
148
|
-
# update the run of the existing
|
148
|
+
# update the run of the existing collection
|
149
149
|
if run is not None:
|
150
|
-
# save the information that this
|
151
|
-
#
|
152
|
-
|
150
|
+
# save the information that this collection was previously produced
|
151
|
+
# by another run
|
152
|
+
# note: same logic exists for _output_artifacts_with_later_updates
|
153
|
+
if existing_collection.run is not None and existing_collection.run != run:
|
153
154
|
existing_collection.run._output_collections_with_later_updates.add(
|
154
155
|
existing_collection
|
155
156
|
)
|
156
|
-
# update the run of the
|
157
|
+
# update the run of the collection with the latest run
|
157
158
|
existing_collection.run = run
|
158
159
|
existing_collection.transform = run.transform
|
159
160
|
init_self_from_db(collection, existing_collection)
|
lamindb/_feature_set.py
CHANGED
@@ -216,7 +216,9 @@ def members(self) -> QuerySet:
|
|
216
216
|
|
217
217
|
|
218
218
|
def _get_related_name(self: FeatureSet) -> str:
|
219
|
-
feature_sets_related_models = dict_related_model_to_related_name(
|
219
|
+
feature_sets_related_models = dict_related_model_to_related_name(
|
220
|
+
self, instance=self._state.db
|
221
|
+
)
|
220
222
|
related_name = feature_sets_related_models.get(self.registry)
|
221
223
|
return related_name
|
222
224
|
|
lamindb/_filter.py
CHANGED
lamindb/_finish.py
CHANGED
@@ -130,12 +130,12 @@ def save_context_core(
|
|
130
130
|
if response != "y":
|
131
131
|
return "aborted-non-consecutive"
|
132
132
|
# write the report
|
133
|
-
report_path = ln_setup.settings.
|
133
|
+
report_path = ln_setup.settings.cache_dir / filepath.name.replace(
|
134
134
|
".ipynb", ".html"
|
135
135
|
)
|
136
136
|
notebook_to_report(filepath, report_path)
|
137
137
|
# write the source code
|
138
|
-
source_code_path = ln_setup.settings.
|
138
|
+
source_code_path = ln_setup.settings.cache_dir / filepath.name.replace(
|
139
139
|
".ipynb", ".py"
|
140
140
|
)
|
141
141
|
notebook_to_script(transform, filepath, source_code_path)
|
@@ -171,7 +171,7 @@ def save_context_core(
|
|
171
171
|
transform.hash = hash
|
172
172
|
|
173
173
|
# track environment
|
174
|
-
env_path = ln_setup.settings.
|
174
|
+
env_path = ln_setup.settings.cache_dir / f"run_env_pip_{run.uid}.txt"
|
175
175
|
if env_path.exists():
|
176
176
|
overwrite_env = True
|
177
177
|
if run.environment_id is not None and from_cli:
|
lamindb/_parents.py
CHANGED
@@ -310,7 +310,12 @@ def _record_label(record: Record, field: str | None = None):
|
|
310
310
|
rf' FACE="Monospace">uid={record.uid}<BR/>version={record.version}</FONT>>'
|
311
311
|
)
|
312
312
|
elif isinstance(record, Run):
|
313
|
-
|
313
|
+
if record.transform.name:
|
314
|
+
name = f'{record.transform.name.replace("&", "&")}'
|
315
|
+
elif record.transform.key:
|
316
|
+
name = f'{record.transform.key.replace("&", "&")}'
|
317
|
+
else:
|
318
|
+
name = f"{record.transform.uid}"
|
314
319
|
user_display = (
|
315
320
|
record.created_by.handle
|
316
321
|
if record.created_by.name is None
|
@@ -365,7 +370,6 @@ def _get_all_parent_runs(data: Artifact | Collection) -> list:
|
|
365
370
|
inputs_run += (
|
366
371
|
r.input_collections.all().filter(visibility__in=[0, 1]).list()
|
367
372
|
)
|
368
|
-
run_inputs_outputs += [(inputs_run, r)]
|
369
373
|
outputs_run = (
|
370
374
|
r.__getattribute__(f"output_{name}s")
|
371
375
|
.all()
|
@@ -376,7 +380,18 @@ def _get_all_parent_runs(data: Artifact | Collection) -> list:
|
|
376
380
|
outputs_run += (
|
377
381
|
r.output_collections.all().filter(visibility__in=[0, 1]).list()
|
378
382
|
)
|
379
|
-
|
383
|
+
# if inputs are outputs artifacts are the same, will result infinite loop
|
384
|
+
# so only show as outputs
|
385
|
+
overlap = set(inputs_run).intersection(outputs_run)
|
386
|
+
if overlap:
|
387
|
+
logger.warning(
|
388
|
+
f"The following artifacts are both inputs and outputs of Run(uid={r.uid}): {overlap}\n → Only showing as outputs."
|
389
|
+
)
|
390
|
+
inputs_run = list(set(inputs_run) - overlap)
|
391
|
+
if len(inputs_run) > 0:
|
392
|
+
run_inputs_outputs += [(inputs_run, r)]
|
393
|
+
if len(outputs_run) > 0:
|
394
|
+
run_inputs_outputs += [(r, outputs_run)]
|
380
395
|
inputs += inputs_run
|
381
396
|
runs = [f.run for f in inputs if f.run is not None]
|
382
397
|
return run_inputs_outputs
|
lamindb/_query_manager.py
CHANGED
@@ -98,26 +98,11 @@ class QueryManager(models.Manager):
|
|
98
98
|
|
99
99
|
return _lookup(cls=self.all(), field=field, **kwargs)
|
100
100
|
|
101
|
-
def __getitem__(self, item: str):
|
102
|
-
try:
|
103
|
-
source_field_name = self.source_field_name
|
104
|
-
target_field_name = self.target_field_name
|
105
|
-
|
106
|
-
if (
|
107
|
-
source_field_name in {"artifact", "collection"}
|
108
|
-
and target_field_name == "feature_set"
|
109
|
-
):
|
110
|
-
return get_feature_set_by_slot_(host=self.instance).get(item)
|
111
|
-
|
112
|
-
except Exception: # pragma: no cover
|
113
|
-
return
|
114
|
-
|
115
101
|
|
116
102
|
models.Manager.list = QueryManager.list
|
117
103
|
models.Manager.df = QueryManager.df
|
118
104
|
models.Manager.search = QueryManager.search
|
119
105
|
models.Manager.lookup = QueryManager.lookup
|
120
|
-
models.Manager.__getitem__ = QueryManager.__getitem__
|
121
106
|
models.Manager._track_run_input_manager = QueryManager._track_run_input_manager
|
122
107
|
# the two lines below would be easy if we could actually inherit; like this,
|
123
108
|
# they're suboptimal
|
lamindb/_query_set.py
CHANGED
@@ -115,7 +115,7 @@ def get(
|
|
115
115
|
else:
|
116
116
|
assert idlike is None # noqa: S101
|
117
117
|
expressions = process_expressions(registry, expressions)
|
118
|
-
return registry.objects.get(**expressions)
|
118
|
+
return registry.objects.using(qs.db).get(**expressions)
|
119
119
|
|
120
120
|
|
121
121
|
class RecordsList(UserList):
|
@@ -139,7 +139,7 @@ class QuerySet(models.QuerySet):
|
|
139
139
|
|
140
140
|
See Also:
|
141
141
|
|
142
|
-
`django QuerySet <https://docs.djangoproject.com/en/4.2/ref/models/querysets/>`__
|
142
|
+
`django QuerySet <https://docs.djangoproject.com/en/4.2/ref/models/querysets/>`__
|
143
143
|
|
144
144
|
Examples:
|
145
145
|
|
@@ -154,7 +154,7 @@ class QuerySet(models.QuerySet):
|
|
154
154
|
) -> pd.DataFrame:
|
155
155
|
"""{}""" # noqa: D415
|
156
156
|
# re-order the columns
|
157
|
-
exclude_field_names = ["
|
157
|
+
exclude_field_names = ["updated_at"]
|
158
158
|
field_names = [
|
159
159
|
field.name
|
160
160
|
for field in self.model._meta.fields
|
lamindb/_record.py
CHANGED
@@ -15,7 +15,7 @@ from lamindb_setup._connect_instance import (
|
|
15
15
|
update_db_using_local,
|
16
16
|
)
|
17
17
|
from lamindb_setup.core._docs import doc_args
|
18
|
-
from lamindb_setup.core._hub_core import
|
18
|
+
from lamindb_setup.core._hub_core import connect_instance_hub
|
19
19
|
from lamindb_setup.core._settings_store import instance_settings_file
|
20
20
|
from lnschema_core.models import IsVersioned, Record, Run, Transform
|
21
21
|
|
@@ -376,15 +376,15 @@ def using(
|
|
376
376
|
instance: str | None,
|
377
377
|
) -> QuerySet:
|
378
378
|
"""{}""" # noqa: D415
|
379
|
+
from ._query_set import QuerySet
|
380
|
+
|
379
381
|
if instance is None:
|
380
382
|
return QuerySet(model=cls, using=None)
|
381
383
|
owner, name = get_owner_name_from_identifier(instance)
|
382
384
|
settings_file = instance_settings_file(name, owner)
|
383
|
-
cache_filepath =
|
384
|
-
ln_setup.settings.storage.cache_dir / f"instance--{owner}--{name}--uid.txt"
|
385
|
-
)
|
385
|
+
cache_filepath = ln_setup.settings.cache_dir / f"instance--{owner}--{name}--uid.txt"
|
386
386
|
if not settings_file.exists():
|
387
|
-
result =
|
387
|
+
result = connect_instance_hub(owner=owner, name=name)
|
388
388
|
if isinstance(result, str):
|
389
389
|
raise RuntimeError(
|
390
390
|
f"Failed to load instance {instance}, please check your permissions!"
|
@@ -397,15 +397,15 @@ def using(
|
|
397
397
|
if not source_schema.issubset(target_schema):
|
398
398
|
missing_members = source_schema - target_schema
|
399
399
|
logger.warning(
|
400
|
-
f"source schema has additional modules: {missing_members}\nconsider mounting these schema modules to
|
400
|
+
f"source schema has additional modules: {missing_members}\nconsider mounting these schema modules to transfer all metadata"
|
401
401
|
)
|
402
|
-
cache_filepath.write_text(iresult[
|
402
|
+
cache_filepath.write_text(f"{iresult['lnid']}\n{iresult['schema_str']}") # type: ignore
|
403
403
|
settings_file = instance_settings_file(name, owner)
|
404
404
|
db = update_db_using_local(iresult, settings_file)
|
405
405
|
else:
|
406
406
|
isettings = load_instance_settings(settings_file)
|
407
407
|
db = isettings.db
|
408
|
-
cache_filepath.write_text(isettings.uid)
|
408
|
+
cache_filepath.write_text(f"{isettings.uid}\n{','.join(isettings.schema)}") # type: ignore
|
409
409
|
add_db_connection(db, instance)
|
410
410
|
return QuerySet(model=cls, using=instance)
|
411
411
|
|
@@ -469,12 +469,10 @@ def get_transfer_run(record) -> Run:
|
|
469
469
|
|
470
470
|
slug = record._state.db
|
471
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
|
-
)
|
472
|
+
cache_filepath = ln_setup.settings.cache_dir / f"instance--{owner}--{name}--uid.txt"
|
475
473
|
if not cache_filepath.exists():
|
476
474
|
raise SystemExit("Need to call .using() before")
|
477
|
-
instance_uid = cache_filepath.read_text()
|
475
|
+
instance_uid = cache_filepath.read_text().split("\n")[0]
|
478
476
|
key = f"transfers/{instance_uid}"
|
479
477
|
uid = instance_uid + "0000"
|
480
478
|
transform = Transform.filter(uid=uid).one_or_none()
|
lamindb/_save.py
CHANGED
@@ -168,7 +168,7 @@ def copy_or_move_to_cache(
|
|
168
168
|
|
169
169
|
local_path = local_path.resolve()
|
170
170
|
is_dir = local_path.is_dir()
|
171
|
-
cache_dir = settings.
|
171
|
+
cache_dir = settings.cache_dir
|
172
172
|
|
173
173
|
# just delete from the cache dir if storage_path is local
|
174
174
|
if cache_path is None:
|
lamindb/core/_context.py
CHANGED
@@ -438,7 +438,7 @@ class Context:
|
|
438
438
|
)
|
439
439
|
return (
|
440
440
|
f'Filename "{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'
|
441
|
-
f'ln.track("{ids.base62_12()}0000)
|
441
|
+
f'ln.track("{ids.base62_12()}0000")\n\n{update_key_note}'
|
442
442
|
)
|
443
443
|
|
444
444
|
# make a new transform record
|
@@ -562,26 +562,32 @@ class Context:
|
|
562
562
|
|
563
563
|
return "CMD + s" if platform.system() == "Darwin" else "CTRL + s"
|
564
564
|
|
565
|
-
if
|
565
|
+
if self.run is None:
|
566
566
|
raise TrackNotCalled("Please run `ln.track()` before `ln.finish()`")
|
567
|
-
if
|
568
|
-
if
|
567
|
+
if self._path is None:
|
568
|
+
if self.run.transform.type in {"script", "notebook"}:
|
569
569
|
raise ValueError(
|
570
|
-
|
570
|
+
"Transform type is not allowed to be 'script' or 'notebook' because `context._path` is `None`."
|
571
571
|
)
|
572
|
-
|
573
|
-
|
572
|
+
self.run.finished_at = datetime.now(timezone.utc)
|
573
|
+
self.run.save()
|
574
574
|
# nothing else to do
|
575
575
|
return None
|
576
576
|
if is_run_from_ipython: # notebooks
|
577
|
-
|
577
|
+
import nbproject
|
578
|
+
|
579
|
+
# it might be that the user modifies the title just before ln.finish()
|
580
|
+
if (nbproject_title := nbproject.meta.live.title) != self.transform.name:
|
581
|
+
self.transform.name = nbproject_title
|
582
|
+
self.transform.save()
|
583
|
+
if get_seconds_since_modified(self._path) > 2 and not ln_setup._TESTING:
|
578
584
|
raise NotebookNotSaved(
|
579
585
|
f"Please save the notebook in your editor (shortcut `{get_shortcut()}`) right before calling `ln.finish()`"
|
580
586
|
)
|
581
587
|
save_context_core(
|
582
|
-
run=
|
583
|
-
transform=
|
584
|
-
filepath=
|
588
|
+
run=self.run,
|
589
|
+
transform=self.run.transform,
|
590
|
+
filepath=self._path,
|
585
591
|
finished_at=True,
|
586
592
|
ignore_non_consecutive=ignore_non_consecutive,
|
587
593
|
)
|
lamindb/core/_django.py
CHANGED
@@ -6,7 +6,7 @@ from django.db.models.fields.reverse_related import ManyToManyRel, ManyToOneRel
|
|
6
6
|
from django.db.models.functions import JSONObject
|
7
7
|
from lnschema_core.models import Artifact, FeatureSet, Record
|
8
8
|
|
9
|
-
from .schema import dict_related_model_to_related_name
|
9
|
+
from .schema import dict_related_model_to_related_name, get_schemas_modules
|
10
10
|
|
11
11
|
|
12
12
|
def get_related_model(model, field_name):
|
@@ -35,22 +35,36 @@ def get_artifact_with_related(
|
|
35
35
|
"""Fetch an artifact with its related data."""
|
36
36
|
from lamindb._can_validate import get_name_field
|
37
37
|
|
38
|
+
from ._label_manager import LABELS_EXCLUDE_SET
|
39
|
+
|
38
40
|
model = artifact.__class__
|
39
|
-
|
41
|
+
schema_modules = get_schemas_modules(artifact._state.db)
|
42
|
+
|
43
|
+
foreign_key_fields = [
|
44
|
+
f.name
|
45
|
+
for f in model._meta.fields
|
46
|
+
if f.is_relation and f.related_model.__get_schema_name__() in schema_modules
|
47
|
+
]
|
40
48
|
|
41
49
|
m2m_relations = (
|
42
50
|
[]
|
43
51
|
if not include_m2m
|
44
52
|
else [
|
45
53
|
v
|
46
|
-
for v in dict_related_model_to_related_name(
|
47
|
-
|
54
|
+
for v in dict_related_model_to_related_name(
|
55
|
+
model, instance=artifact._state.db
|
56
|
+
).values()
|
57
|
+
if not v.startswith("_") and v not in LABELS_EXCLUDE_SET
|
48
58
|
]
|
49
59
|
)
|
50
60
|
link_tables = (
|
51
61
|
[]
|
52
62
|
if not include_feature_link
|
53
|
-
else list(
|
63
|
+
else list(
|
64
|
+
dict_related_model_to_related_name(
|
65
|
+
model, links=True, instance=artifact._state.db
|
66
|
+
).values()
|
67
|
+
)
|
54
68
|
)
|
55
69
|
|
56
70
|
# Clear previous queries
|
lamindb/core/_feature_manager.py
CHANGED
@@ -40,7 +40,7 @@ from lamindb._record import (
|
|
40
40
|
transfer_to_default_db,
|
41
41
|
)
|
42
42
|
from lamindb._save import save
|
43
|
-
from lamindb.core.exceptions import ValidationError
|
43
|
+
from lamindb.core.exceptions import DoesNotExist, ValidationError
|
44
44
|
from lamindb.core.storage import LocalPathClasses
|
45
45
|
|
46
46
|
from ._django import get_artifact_with_related
|
@@ -216,9 +216,11 @@ def _print_categoricals(
|
|
216
216
|
if not print_params:
|
217
217
|
labels_msg = ""
|
218
218
|
labels_by_feature = defaultdict(list)
|
219
|
-
for _, (_, links) in get_labels_as_dict(
|
219
|
+
for _, (_, links) in get_labels_as_dict(
|
220
|
+
self, links=True, instance=self._state.db
|
221
|
+
).items():
|
220
222
|
for link in links:
|
221
|
-
if link.feature_id is not None:
|
223
|
+
if hasattr(link, "feature_id") and link.feature_id is not None:
|
222
224
|
link_attr = get_link_attr(link, self)
|
223
225
|
labels_by_feature[link.feature_id].append(
|
224
226
|
getattr(link, link_attr).name
|
@@ -509,18 +511,49 @@ def filter_base(cls, **expression):
|
|
509
511
|
expression = {feature_param: feature, f"value{comparator}": value}
|
510
512
|
feature_value = value_model.filter(**expression)
|
511
513
|
new_expression[f"_{feature_param}_values__in"] = feature_value
|
512
|
-
|
514
|
+
elif isinstance(value, (str, Record)):
|
515
|
+
# because SQL is sensitive to whether querying with __in or not
|
516
|
+
# and might return multiple equivalent records for the latter
|
517
|
+
# we distinguish cases in which we have multiple label matches vs. one
|
518
|
+
label = None
|
519
|
+
labels = None
|
513
520
|
if isinstance(value, str):
|
521
|
+
# we need the comparator here because users might query like so
|
522
|
+
# ln.Artifact.features.filter(experiment__contains="Experi")
|
514
523
|
expression = {f"name{comparator}": value}
|
515
|
-
|
516
|
-
|
524
|
+
labels = ULabel.filter(**expression).all()
|
525
|
+
if len(labels) == 0:
|
526
|
+
raise DoesNotExist(
|
527
|
+
f"Did not find a ULabel matching `name{comparator}={value}`"
|
528
|
+
)
|
529
|
+
elif len(labels) == 1:
|
530
|
+
label = labels[0]
|
531
|
+
elif isinstance(value, Record):
|
532
|
+
label = value
|
533
|
+
label_registry = (
|
534
|
+
label.__class__ if label is not None else labels[0].__class__
|
535
|
+
)
|
536
|
+
accessor_name = (
|
537
|
+
label_registry.artifacts.through.artifact.field._related_name
|
538
|
+
)
|
539
|
+
new_expression[f"{accessor_name}__feature"] = feature
|
540
|
+
if label is not None:
|
541
|
+
# simplified query if we have exactly one label
|
542
|
+
new_expression[
|
543
|
+
f"{accessor_name}__{label_registry.__name__.lower()}"
|
544
|
+
] = label
|
517
545
|
else:
|
518
|
-
|
546
|
+
new_expression[
|
547
|
+
f"{accessor_name}__{label_registry.__name__.lower()}__in"
|
548
|
+
] = labels
|
549
|
+
else:
|
550
|
+
# if passing a list of records, we want to
|
551
|
+
# find artifacts that are annotated by all of them at the same
|
552
|
+
# time; hence, we don't want the __in construct that we use to match strings
|
553
|
+
# https://laminlabs.slack.com/archives/C04FPE8V01W/p1688328084810609
|
554
|
+
raise NotImplementedError
|
519
555
|
if cls == FeatureManager or cls == ParamManagerArtifact:
|
520
556
|
return Artifact.filter(**new_expression)
|
521
|
-
# might renable something similar in the future
|
522
|
-
# elif cls == FeatureManagerCollection:
|
523
|
-
# return Collection.filter(**new_expression)
|
524
557
|
elif cls == ParamManagerRun:
|
525
558
|
return Run.filter(**new_expression)
|
526
559
|
|
lamindb/core/_label_manager.py
CHANGED
@@ -26,32 +26,19 @@ if TYPE_CHECKING:
|
|
26
26
|
|
27
27
|
from lamindb._query_set import QuerySet
|
28
28
|
|
29
|
+
LABELS_EXCLUDE_SET = {"feature_sets"}
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
"input_of_runs",
|
35
|
-
"collections",
|
36
|
-
"_source_code_artifact_of",
|
37
|
-
"_report_of",
|
38
|
-
"_environment_of",
|
39
|
-
"links_collection",
|
40
|
-
"links_artifact",
|
41
|
-
"links_feature_set",
|
42
|
-
"previous_runs",
|
43
|
-
"_feature_values",
|
44
|
-
"_action_targets",
|
45
|
-
"_lnschema_core_collection__actions_+", # something seems off with this one
|
46
|
-
"_actions",
|
47
|
-
}
|
31
|
+
|
32
|
+
def get_labels_as_dict(
|
33
|
+
self: Artifact | Collection, links: bool = False, instance: str | None = None
|
34
|
+
) -> dict:
|
48
35
|
labels = {} # type: ignore
|
49
36
|
if self.id is None:
|
50
37
|
return labels
|
51
38
|
for related_model_name, related_name in dict_related_model_to_related_name(
|
52
|
-
self.__class__, links=links
|
39
|
+
self.__class__, links=links, instance=instance
|
53
40
|
).items():
|
54
|
-
if related_name not in
|
41
|
+
if related_name not in LABELS_EXCLUDE_SET and not related_name.startswith("_"):
|
55
42
|
labels[related_name] = (
|
56
43
|
related_model_name,
|
57
44
|
getattr(self, related_name).all(),
|
@@ -86,18 +73,15 @@ def print_labels(
|
|
86
73
|
labels_msg = _print_labels_postgres(self, m2m_data, print_types)
|
87
74
|
else:
|
88
75
|
labels_msg = ""
|
89
|
-
for related_name, (related_model, labels) in get_labels_as_dict(
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
labels_msg += f" .{related_name}{type_str} = {print_values}\n"
|
99
|
-
except Exception: # noqa: S112
|
100
|
-
continue
|
76
|
+
for related_name, (related_model, labels) in get_labels_as_dict(
|
77
|
+
self, instance=self._state.db
|
78
|
+
).items():
|
79
|
+
field = get_name_field(labels)
|
80
|
+
labels_list = list(labels.values_list(field, flat=True))
|
81
|
+
if len(labels_list) > 0:
|
82
|
+
print_values = _print_values(labels_list, n=10)
|
83
|
+
type_str = f": {related_model}" if print_types else ""
|
84
|
+
labels_msg += f" .{related_name}{type_str} = {print_values}\n"
|
101
85
|
|
102
86
|
msg = ""
|
103
87
|
if labels_msg:
|
@@ -214,75 +198,68 @@ class LabelManager:
|
|
214
198
|
>>> artifact1.ulabels.set(labels)
|
215
199
|
>>> artifact2.labels.add_from(artifact1)
|
216
200
|
"""
|
217
|
-
from django.db.utils import ProgrammingError
|
218
|
-
|
219
201
|
if transfer_logs is None:
|
220
202
|
transfer_logs = {"mapped": [], "transferred": [], "run": None}
|
221
203
|
using_key = settings._using_key
|
222
|
-
for related_name, (_, labels) in get_labels_as_dict(
|
204
|
+
for related_name, (_, labels) in get_labels_as_dict(
|
205
|
+
data, instance=self._host._state.db
|
206
|
+
).items():
|
223
207
|
labels = labels.all()
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
208
|
+
if not labels.exists():
|
209
|
+
continue
|
210
|
+
# look for features
|
211
|
+
data_name_lower = data.__class__.__name__.lower()
|
212
|
+
labels_by_features = defaultdict(list)
|
213
|
+
features = set()
|
214
|
+
_, new_labels = validate_labels(labels)
|
215
|
+
if len(new_labels) > 0:
|
216
|
+
transfer_fk_to_default_db_bulk(
|
217
|
+
new_labels, using_key, transfer_logs=transfer_logs
|
218
|
+
)
|
219
|
+
for label in labels:
|
220
|
+
# if the link table doesn't follow this convention, we'll ignore it
|
221
|
+
if not hasattr(label, f"links_{data_name_lower}"):
|
222
|
+
key = None
|
223
|
+
else:
|
224
|
+
link = getattr(label, f"links_{data_name_lower}").get(
|
225
|
+
**{f"{data_name_lower}_id": data.id}
|
235
226
|
)
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
key = None
|
227
|
+
if link.feature is not None:
|
228
|
+
features.add(link.feature)
|
229
|
+
key = link.feature.name
|
240
230
|
else:
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
231
|
+
key = None
|
232
|
+
label_returned = transfer_to_default_db(
|
233
|
+
label,
|
234
|
+
using_key,
|
235
|
+
transfer_logs=transfer_logs,
|
236
|
+
transfer_fk=False,
|
237
|
+
save=True,
|
238
|
+
)
|
239
|
+
# TODO: refactor return value of transfer to default db
|
240
|
+
if label_returned is not None:
|
241
|
+
label = label_returned
|
242
|
+
labels_by_features[key].append(label)
|
243
|
+
# treat features
|
244
|
+
_, new_features = validate_labels(list(features))
|
245
|
+
if len(new_features) > 0:
|
246
|
+
transfer_fk_to_default_db_bulk(
|
247
|
+
new_features, using_key, transfer_logs=transfer_logs
|
248
|
+
)
|
249
|
+
for feature in new_features:
|
250
|
+
transfer_to_default_db(
|
251
|
+
feature,
|
251
252
|
using_key,
|
252
253
|
transfer_logs=transfer_logs,
|
253
254
|
transfer_fk=False,
|
254
|
-
save=True,
|
255
255
|
)
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
256
|
+
save(new_features)
|
257
|
+
if hasattr(self._host, related_name):
|
258
|
+
for feature_name, labels in labels_by_features.items():
|
259
|
+
if feature_name is not None:
|
260
|
+
feature_id = Feature.get(name=feature_name).id
|
261
|
+
else:
|
262
|
+
feature_id = None
|
263
|
+
getattr(self._host, related_name).add(
|
264
|
+
*labels, through_defaults={"feature_id": feature_id}
|
265
265
|
)
|
266
|
-
for feature in new_features:
|
267
|
-
transfer_to_default_db(
|
268
|
-
feature,
|
269
|
-
using_key,
|
270
|
-
transfer_logs=transfer_logs,
|
271
|
-
transfer_fk=False,
|
272
|
-
)
|
273
|
-
save(new_features)
|
274
|
-
if hasattr(self._host, related_name):
|
275
|
-
for feature_name, labels in labels_by_features.items():
|
276
|
-
if feature_name is not None:
|
277
|
-
feature_id = Feature.get(name=feature_name).id
|
278
|
-
else:
|
279
|
-
feature_id = None
|
280
|
-
getattr(self._host, related_name).add(
|
281
|
-
*labels, through_defaults={"feature_id": feature_id}
|
282
|
-
)
|
283
|
-
# ProgrammingError is raised when schemas don't match between source and target instances
|
284
|
-
except ProgrammingError:
|
285
|
-
logger.warning(
|
286
|
-
f"{related_name} labels cannot be transferred because schema module does not exist in target instance: {labels}"
|
287
|
-
)
|
288
|
-
continue
|
lamindb/core/_settings.py
CHANGED
@@ -143,6 +143,11 @@ class Settings:
|
|
143
143
|
path, kwargs = path_kwargs, {}
|
144
144
|
set_managed_storage(path, **kwargs)
|
145
145
|
|
146
|
+
@property
|
147
|
+
def cache_dir(self) -> UPath:
|
148
|
+
"""Cache root, a local directory to cache cloud files."""
|
149
|
+
return ln_setup.settings.cache_dir
|
150
|
+
|
146
151
|
@property
|
147
152
|
def storage_local(self) -> StorageSettings:
|
148
153
|
"""An additional local default storage (a path to its root).
|
lamindb/core/_sync_git.py
CHANGED
@@ -16,7 +16,7 @@ class BlobHashNotFound(SystemExit):
|
|
16
16
|
|
17
17
|
def get_git_repo_from_remote() -> Path:
|
18
18
|
repo_url = settings.sync_git_repo
|
19
|
-
repo_dir = setup_settings.
|
19
|
+
repo_dir = setup_settings.cache_dir / repo_url.split("/")[-1]
|
20
20
|
if repo_dir.exists():
|
21
21
|
logger.warning(f"git repo {repo_dir} already exists locally")
|
22
22
|
return repo_dir
|
@@ -26,7 +26,7 @@ def get_git_repo_from_remote() -> Path:
|
|
26
26
|
result = subprocess.run(
|
27
27
|
["git", "clone", "--depth", "10", f"{repo_url}.git"],
|
28
28
|
capture_output=True,
|
29
|
-
cwd=setup_settings.
|
29
|
+
cwd=setup_settings.cache_dir,
|
30
30
|
)
|
31
31
|
if result.returncode != 0 or not repo_dir.exists():
|
32
32
|
raise RuntimeError(result.stderr.decode())
|
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
|
|
11
11
|
|
12
12
|
|
13
13
|
def track_environment(run: Run) -> None:
|
14
|
-
filepath = ln_setup.settings.
|
14
|
+
filepath = ln_setup.settings.cache_dir / f"run_env_pip_{run.uid}.txt"
|
15
15
|
# create a requirements.txt
|
16
16
|
# we don't create a conda environment.yml mostly for its slowness
|
17
17
|
try:
|
lamindb/core/exceptions.py
CHANGED
lamindb/core/schema.py
CHANGED
@@ -1,31 +1,66 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import lamindb_setup as ln_setup
|
3
4
|
from django.db.models import ManyToManyField
|
5
|
+
from lamindb_setup._connect_instance import (
|
6
|
+
get_owner_name_from_identifier,
|
7
|
+
load_instance_settings,
|
8
|
+
)
|
9
|
+
from lamindb_setup.core._settings_store import instance_settings_file
|
4
10
|
from lnschema_core.models import Feature, FeatureSet, LinkORM, Record
|
5
11
|
|
6
12
|
|
7
|
-
def
|
13
|
+
def get_schemas_modules(instance: str | None) -> set[str]:
|
14
|
+
if instance is None or instance == "default":
|
15
|
+
schema_modules = set(ln_setup.settings.instance.schema)
|
16
|
+
schema_modules.add("core")
|
17
|
+
return schema_modules
|
18
|
+
owner, name = get_owner_name_from_identifier(instance)
|
19
|
+
settings_file = instance_settings_file(name, owner)
|
20
|
+
if settings_file.exists():
|
21
|
+
schema = set(load_instance_settings(settings_file).schema)
|
22
|
+
else:
|
23
|
+
cache_filepath = (
|
24
|
+
ln_setup.settings.cache_dir / f"instance--{owner}--{name}--uid.txt"
|
25
|
+
)
|
26
|
+
if cache_filepath.exists():
|
27
|
+
schema = set(cache_filepath.read_text().split("\n")[1].split(","))
|
28
|
+
else:
|
29
|
+
raise ValueError(f"Instance {instance} not found")
|
30
|
+
shared_schema_modules = set(ln_setup.settings.instance.schema).intersection(schema)
|
31
|
+
shared_schema_modules.add("core")
|
32
|
+
return shared_schema_modules
|
33
|
+
|
34
|
+
|
35
|
+
def dict_schema_name_to_model_name(
|
36
|
+
registry: type[Record], instance: str | None = None
|
37
|
+
) -> dict[str, Record]:
|
38
|
+
schema_modules = get_schemas_modules(instance)
|
8
39
|
d: dict = {
|
9
40
|
i.related_model.__get_name_with_schema__(): i.related_model
|
10
41
|
for i in registry._meta.related_objects
|
11
42
|
if i.related_name is not None
|
43
|
+
and i.related_model.__get_schema_name__() in schema_modules
|
12
44
|
}
|
13
45
|
d.update(
|
14
46
|
{
|
15
47
|
i.related_model.__get_name_with_schema__(): i.related_model
|
16
48
|
for i in registry._meta.many_to_many
|
17
49
|
if i.name is not None
|
50
|
+
and i.related_model.__get_schema_name__() in schema_modules
|
18
51
|
}
|
19
52
|
)
|
20
53
|
return d
|
21
54
|
|
22
55
|
|
23
56
|
def dict_related_model_to_related_name(
|
24
|
-
registry: type[Record], links: bool = False
|
57
|
+
registry: type[Record], links: bool = False, instance: str | None = None
|
25
58
|
) -> dict[str, str]:
|
26
59
|
def include(model: Record):
|
27
60
|
return not links != issubclass(model, LinkORM)
|
28
61
|
|
62
|
+
schema_modules = get_schemas_modules(instance)
|
63
|
+
|
29
64
|
related_objects = registry._meta.related_objects + registry._meta.many_to_many
|
30
65
|
d: dict = {
|
31
66
|
record.related_model.__get_name_with_schema__(): (
|
@@ -34,7 +69,11 @@ def dict_related_model_to_related_name(
|
|
34
69
|
else record.name
|
35
70
|
)
|
36
71
|
for record in related_objects
|
37
|
-
if (
|
72
|
+
if (
|
73
|
+
record.name is not None
|
74
|
+
and include(record.related_model)
|
75
|
+
and record.related_model.__get_schema_name__() in schema_modules
|
76
|
+
)
|
38
77
|
}
|
39
78
|
return d
|
40
79
|
|
@@ -8,6 +8,7 @@ import lamindb_setup as ln_setup
|
|
8
8
|
from lamin_utils import logger
|
9
9
|
|
10
10
|
from lamindb._artifact import Artifact
|
11
|
+
from lamindb._collection import Collection
|
11
12
|
from lamindb._run import Run
|
12
13
|
from lamindb._transform import Transform
|
13
14
|
|
@@ -20,14 +21,21 @@ if TYPE_CHECKING:
|
|
20
21
|
def save_vitessce_config(
|
21
22
|
vitessce_config: VitessceConfig, description: str | None = None
|
22
23
|
) -> Artifact:
|
23
|
-
"""Validates and saves a
|
24
|
+
"""Validates and saves a `VitessceConfig` object.
|
25
|
+
|
26
|
+
If the `VitessceConfig` object references multiple artifacts, automatically
|
27
|
+
creates a `Collection` and displays the "Vitessce button" next to it.
|
24
28
|
|
25
29
|
Guide: :doc:`docs:vitessce`.
|
26
30
|
|
27
31
|
Args:
|
28
|
-
vitessce_config
|
29
|
-
description: A description for the `VitessceConfig`
|
32
|
+
vitessce_config: A `VitessceConfig` object.
|
33
|
+
description: A description for the `VitessceConfig` object. Is used as
|
34
|
+
`name` for a `Collection` in case the `VitessceConfig` object
|
35
|
+
references multiple artifacts.
|
30
36
|
|
37
|
+
.. versionchanged:: 0.76.12
|
38
|
+
Now assumes `vitessce-python >= 3.4.0`, which allows passing artifacts within `VitessceConfig`.
|
31
39
|
.. versionchanged:: 0.75.1
|
32
40
|
Now displays the "Vitessce button" on the hub next to the dataset. It additionally keeps displaying it next to the configuration file.
|
33
41
|
.. versionchanged:: 0.70.2
|
@@ -40,68 +48,54 @@ def save_vitessce_config(
|
|
40
48
|
|
41
49
|
assert isinstance(vitessce_config, VitessceConfig) # noqa: S101
|
42
50
|
vc_dict = vitessce_config.to_dict()
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
for file in vitessce_dataset["files"]:
|
55
|
-
if "url" not in file:
|
56
|
-
raise ValueError("Each file must have a 'url' key.")
|
57
|
-
s3_path = file["url"]
|
58
|
-
s3_path_last_element = s3_path.split("/")[-1]
|
59
|
-
# now start with attempting to strip the composite suffix candidates
|
60
|
-
for suffix in valid_composite_zarr_suffixes:
|
61
|
-
s3_path_last_element = s3_path_last_element.replace(suffix, "")
|
62
|
-
# in case there was no hit, strip plain ".zarr"
|
63
|
-
artifact_stem_uid = s3_path_last_element.replace(".zarr", "")
|
64
|
-
# if there is still a "." in string, raise an error
|
65
|
-
if "." in artifact_stem_uid:
|
66
|
-
raise ValueError(
|
67
|
-
f"Suffix should be '.zarr' or one of {valid_composite_zarr_suffixes}. Inspect your path {s3_path}"
|
68
|
-
)
|
69
|
-
artifact = Artifact.filter(uid__startswith=artifact_stem_uid).one_or_none()
|
70
|
-
if artifact is None:
|
71
|
-
raise ValueError(
|
72
|
-
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."
|
73
|
-
)
|
74
|
-
else:
|
75
|
-
dataset_artifacts.append(artifact)
|
51
|
+
try:
|
52
|
+
url_to_artifact_dict = vitessce_config.get_artifacts()
|
53
|
+
except AttributeError as e:
|
54
|
+
raise SystemExit(
|
55
|
+
"save_vitessce_config() requires vitessce>=3.4.0: pip install vitessce>=3.4.0"
|
56
|
+
) from e
|
57
|
+
dataset_artifacts = list(url_to_artifact_dict.values())
|
58
|
+
message = "\n".join([artifact.__repr__() for artifact in dataset_artifacts])
|
59
|
+
logger.important(f"VitessceConfig references these artifacts:\n{message}")
|
60
|
+
assert len(dataset_artifacts) > 0 # noqa: S101
|
61
|
+
|
76
62
|
# the below will be replaced with a `ln.tracked()` decorator soon
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
).save()
|
63
|
+
transform = Transform(
|
64
|
+
uid="kup03MJBsIVa0002",
|
65
|
+
name="save_vitessce_config",
|
66
|
+
type="function",
|
67
|
+
version="3",
|
68
|
+
).save()
|
84
69
|
run = Run(transform=transform).save()
|
70
|
+
run.input_artifacts.set(dataset_artifacts)
|
71
|
+
collection = None
|
85
72
|
if len(dataset_artifacts) > 1:
|
86
73
|
# if we have more datasets, we should create a collection
|
87
74
|
# and attach an action to the collection
|
88
|
-
|
89
|
-
|
75
|
+
collection = Collection(dataset_artifacts, name=description).save()
|
76
|
+
|
90
77
|
# create a JSON export
|
91
|
-
config_file_local_path =
|
92
|
-
ln_setup.settings.storage.cache_dir / "config.vitessce.json"
|
93
|
-
)
|
78
|
+
config_file_local_path = ln_setup.settings.cache_dir / "config.vitessce.json"
|
94
79
|
with open(config_file_local_path, "w") as file:
|
95
80
|
json.dump(vc_dict, file)
|
96
81
|
vitessce_config_artifact = Artifact(
|
97
82
|
config_file_local_path, description=description, run=run
|
98
83
|
).save()
|
99
|
-
# we have one and only one dataset artifact, hence the following line is OK
|
100
|
-
dataset_artifacts[0]._actions.add(vitessce_config_artifact)
|
101
84
|
slug = ln_setup.settings.instance.slug
|
102
85
|
logger.important(
|
103
|
-
f"
|
86
|
+
f"VitessceConfig: https://lamin.ai/{slug}/artifact/{vitessce_config_artifact.uid}"
|
104
87
|
)
|
88
|
+
if collection is None:
|
89
|
+
# we have one and only one dataset artifact, hence the following line is OK
|
90
|
+
dataset_artifacts[0]._actions.add(vitessce_config_artifact)
|
91
|
+
logger.important(
|
92
|
+
f"Dataset: https://lamin.ai/{slug}/artifact/{dataset_artifacts[0].uid}"
|
93
|
+
)
|
94
|
+
else:
|
95
|
+
collection._actions.add(vitessce_config_artifact)
|
96
|
+
logger.important(
|
97
|
+
f"Collection: https://lamin.ai/{slug}/collection/{collection.uid}"
|
98
|
+
)
|
105
99
|
run.finished_at = datetime.now(timezone.utc)
|
106
100
|
run.save()
|
107
101
|
return vitessce_config_artifact
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lamindb
|
3
|
-
Version: 0.76.
|
3
|
+
Version: 0.76.13
|
4
4
|
Summary: A data framework for biology.
|
5
5
|
Author-email: Lamin Labs <open-source@lamin.ai>
|
6
6
|
Requires-Python: >=3.9
|
@@ -8,10 +8,10 @@ Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: Programming Language :: Python :: 3.9
|
9
9
|
Classifier: Programming Language :: Python :: 3.10
|
10
10
|
Classifier: Programming Language :: Python :: 3.11
|
11
|
-
Requires-Dist: lnschema_core==0.
|
12
|
-
Requires-Dist: lamindb_setup==0.77.7
|
11
|
+
Requires-Dist: lnschema_core==0.75.0
|
13
12
|
Requires-Dist: lamin_utils==0.13.6
|
14
|
-
Requires-Dist: lamin_cli==0.
|
13
|
+
Requires-Dist: lamin_cli==0.19.0
|
14
|
+
Requires-Dist: lamindb_setup
|
15
15
|
Requires-Dist: rapidfuzz
|
16
16
|
Requires-Dist: pyarrow
|
17
17
|
Requires-Dist: typing_extensions!=4.6.0
|
@@ -23,7 +23,7 @@ Requires-Dist: pandas
|
|
23
23
|
Requires-Dist: graphviz
|
24
24
|
Requires-Dist: psycopg2-binary
|
25
25
|
Requires-Dist: lamindb_setup[aws] ; extra == "aws"
|
26
|
-
Requires-Dist: bionty==0.51.
|
26
|
+
Requires-Dist: bionty==0.51.2 ; extra == "bionty"
|
27
27
|
Requires-Dist: line_profiler ; extra == "dev"
|
28
28
|
Requires-Dist: pre-commit ; extra == "dev"
|
29
29
|
Requires-Dist: nox ; extra == "dev"
|
@@ -1,39 +1,39 @@
|
|
1
|
-
lamindb/__init__.py,sha256
|
2
|
-
lamindb/_artifact.py,sha256=
|
1
|
+
lamindb/__init__.py,sha256=h_byT1s_9NrqH157lvPgG336CTBqGzIIJNlNU7MKWQE,2278
|
2
|
+
lamindb/_artifact.py,sha256=AHyKZ_D_qMGtywAklo42je8BZB-VBqQ-ZTaI9AB_Yxs,44905
|
3
3
|
lamindb/_can_validate.py,sha256=1pUavLwZ_yPAtbVYKOGYUHaPxlJGZ246qZ0e-4ZUDSc,19552
|
4
|
-
lamindb/_collection.py,sha256=
|
4
|
+
lamindb/_collection.py,sha256=ZQ45_9n0komz1y8l_vzZ-cRn-ghinE0Izl889Mk-TSA,14249
|
5
5
|
lamindb/_curate.py,sha256=KpEP0-9nQUmiRn6z7fiGs6BmQdpqSg3QPlAgV-S9_wA,58839
|
6
6
|
lamindb/_feature.py,sha256=nZhtrH0ssoNls-hV-dkwfK9sKypg2El59R9qfarxfUE,5340
|
7
|
-
lamindb/_feature_set.py,sha256=
|
8
|
-
lamindb/_filter.py,sha256=
|
9
|
-
lamindb/_finish.py,sha256=
|
7
|
+
lamindb/_feature_set.py,sha256=JQSP-YLam1KW-rDzly5Dm4IYVL2A6ec7ufIf6iCc2W8,8169
|
8
|
+
lamindb/_filter.py,sha256=Pf9NHV4gm7NOC0Frtvx4W7nvwt2EowOP74DwppyXAZs,635
|
9
|
+
lamindb/_finish.py,sha256=VMAmxCUFmTKIMSCx7LEh4QAnWDeue6MeUAAzkMVEYMU,9546
|
10
10
|
lamindb/_from_values.py,sha256=iqcR8T8i9VZpnn09W441zW-djhWMh8EZj7XFJq9jP2k,14211
|
11
11
|
lamindb/_is_versioned.py,sha256=5lAnhTboltFkZCKVRV1uxkm0OCjJz_HKi3yQq_vEuMs,1306
|
12
|
-
lamindb/_parents.py,sha256=
|
13
|
-
lamindb/_query_manager.py,sha256=
|
14
|
-
lamindb/_query_set.py,sha256=
|
15
|
-
lamindb/_record.py,sha256=
|
12
|
+
lamindb/_parents.py,sha256=KMBUfCLNqjmFzOdZIXaUFqDPeEpWP28MCkHHPq887h8,16341
|
13
|
+
lamindb/_query_manager.py,sha256=pmPhJQ85-7XeAU9TFv6LPGi9F7dBgztZgZcXz33HYJM,3710
|
14
|
+
lamindb/_query_set.py,sha256=81y1c-Y-PGCmqc64WizlCiGTbsrApsajtX2xj7xrsMo,12730
|
15
|
+
lamindb/_record.py,sha256=LkKtDxqlTq43UlAiJ96_nbpjj9eQbU4N3HpfBVfImJo,23395
|
16
16
|
lamindb/_run.py,sha256=K_5drpLn3D7y3XtZ3vtAw35rG5RCSvB4bXQZx4ESSI0,1964
|
17
|
-
lamindb/_save.py,sha256=
|
17
|
+
lamindb/_save.py,sha256=BCaSFnANYPxTqL5gw7Hrh_9kz7SDyOxrJV2KW6rXqts,11366
|
18
18
|
lamindb/_storage.py,sha256=GBVChv-DHVMNEBJL5l_JT6B4RDhZ6NnwgzmUICphYKk,413
|
19
19
|
lamindb/_transform.py,sha256=wZDkY8lp4d_OsO5a7rLs1RamkDzBXZSLaWJU34zRnmA,4728
|
20
20
|
lamindb/_ulabel.py,sha256=XDSdZBXX_ki5s1vOths3MjF2x5DPggBR_PV_KF4SGyg,1611
|
21
21
|
lamindb/_utils.py,sha256=LGdiW4k3GClLz65vKAVRkL6Tw-Gkx9DWAdez1jyA5bE,428
|
22
22
|
lamindb/_view.py,sha256=4Ln2ItTb3857PAI-70O8eJYqoTJ_NNFc7E_wds6OGns,2412
|
23
23
|
lamindb/core/__init__.py,sha256=57AXQ286eOX2_o5HUeqIFJrfqN-OZ_E7FVHd3Xm5oOk,1483
|
24
|
-
lamindb/core/_context.py,sha256=
|
24
|
+
lamindb/core/_context.py,sha256=dI3z7fCMRPC3IMb7-EIaQYhacSZBA4HfLVFyoJtVL7I,22900
|
25
25
|
lamindb/core/_data.py,sha256=P0vlbw_UQp9UDNMmUo9YFxqurcWtMaKPWCT12IRA2C0,19672
|
26
|
-
lamindb/core/_django.py,sha256=
|
27
|
-
lamindb/core/_feature_manager.py,sha256=
|
28
|
-
lamindb/core/_label_manager.py,sha256=
|
26
|
+
lamindb/core/_django.py,sha256=yeMPp1n9WrFmEjVRdavfpVqAolPLd24RseTQlvsK67w,7157
|
27
|
+
lamindb/core/_feature_manager.py,sha256=yH6HGF_c0NwIE6hxcMUZdZrokx75Kz-54d_3JhyCglQ,39020
|
28
|
+
lamindb/core/_label_manager.py,sha256=bhD6krOrf0SWXfCzhFsEA-7h0ZLJ59YSPxWnlq7DZiE,9991
|
29
29
|
lamindb/core/_mapped_collection.py,sha256=M50haewVAFONeF71QQbzD09L8lVZCL1hyf0W87jKE5U,24575
|
30
|
-
lamindb/core/_settings.py,sha256=
|
31
|
-
lamindb/core/_sync_git.py,sha256=
|
32
|
-
lamindb/core/_track_environment.py,sha256=
|
33
|
-
lamindb/core/exceptions.py,sha256=
|
30
|
+
lamindb/core/_settings.py,sha256=6jNadlQdimxCsKR2ZyUD0YJYzOdubTnKktki-MqEWqQ,6137
|
31
|
+
lamindb/core/_sync_git.py,sha256=lIgl6YfpH4rCFT1WILAp7zlemZfxog1d0zp3cX0KIZw,4531
|
32
|
+
lamindb/core/_track_environment.py,sha256=Ywzg_sJ7guI1dcsN7h5orce9VdYl8VGVE3OLITlHBXQ,820
|
33
|
+
lamindb/core/exceptions.py,sha256=0B36kOVo-Dik5KbSKvy5GPuMjUfhVb99dJwXl_J0ldo,1636
|
34
34
|
lamindb/core/fields.py,sha256=47Jmh3efUr5ZscgimR_yckY-I3cNf8ScLutbwKCK3j4,162
|
35
35
|
lamindb/core/loaders.py,sha256=KMTkDa73jkRVvI9uc5Fgr0t6mq22cAxBwhSlUZKUaBg,4016
|
36
|
-
lamindb/core/schema.py,sha256=
|
36
|
+
lamindb/core/schema.py,sha256=Y1tGn93B236PtnYZkpgTNFgTXBcVujPM1qh1kNG6Nbs,3441
|
37
37
|
lamindb/core/types.py,sha256=uVBqSVLoQaTkqP9nqsJhwU6yYnx8H5e6-ZxrB6vpOOw,265
|
38
38
|
lamindb/core/versioning.py,sha256=KUpd94F5QpbDxx9eKgZwrpPyQpm9YeHVedrTEO2a_mI,4839
|
39
39
|
lamindb/core/datasets/__init__.py,sha256=zRP98oqUAaXhqWyKMiH0s_ImVIuNeziQQ2kQ_t0f-DI,1353
|
@@ -52,10 +52,10 @@ lamindb/core/subsettings/__init__.py,sha256=KFHPzIE7f7Bj4RgMjGQF4CjTdHVG_VNFBrCn
|
|
52
52
|
lamindb/core/subsettings/_creation_settings.py,sha256=54mfMH_osC753hpxcl7Dq1rwBD2LHnWveXtQpkLBITE,1194
|
53
53
|
lamindb/core/subsettings/_transform_settings.py,sha256=4YbCuZtJo6zdytl6UQR4GvdDkTtT6SRBqVzofGzNOt8,583
|
54
54
|
lamindb/integrations/__init__.py,sha256=RWGMYYIzr8zvmNPyVB4m-p4gMDhxdRbjES2Ed23OItw,215
|
55
|
-
lamindb/integrations/_vitessce.py,sha256=
|
55
|
+
lamindb/integrations/_vitessce.py,sha256=uPl45_w4Uu9_BhpBDDVonC1nDOuAnB7DAnzi5w5bZAE,4032
|
56
56
|
lamindb/setup/__init__.py,sha256=OwZpZzPDv5lPPGXZP7-zK6UdO4FHvvuBh439yZvIp3A,410
|
57
57
|
lamindb/setup/core/__init__.py,sha256=SevlVrc2AZWL3uALbE5sopxBnIZPWZ1IB0NBDudiAL8,167
|
58
|
-
lamindb-0.76.
|
59
|
-
lamindb-0.76.
|
60
|
-
lamindb-0.76.
|
61
|
-
lamindb-0.76.
|
58
|
+
lamindb-0.76.13.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
59
|
+
lamindb-0.76.13.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
60
|
+
lamindb-0.76.13.dist-info/METADATA,sha256=Vo8AcZfSEBwW3NJsFndztG1b2fAVSquWw2OENmncc8M,2361
|
61
|
+
lamindb-0.76.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|