lamindb 0.58.0__py3-none-any.whl → 0.58.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/__init__.py CHANGED
@@ -53,7 +53,7 @@ Static classes & modules:
53
53
 
54
54
  """
55
55
 
56
- __version__ = "0.58.0" # denote a release candidate for 0.1.0 with 0.1rc1
56
+ __version__ = "0.58.1" # denote a release candidate for 0.1.0 with 0.1rc1
57
57
 
58
58
  import os as _os
59
59
 
lamindb/_dataset.py CHANGED
@@ -63,12 +63,15 @@ def __init__(
63
63
  kwargs.pop("initial_version_id") if "initial_version_id" in kwargs else None
64
64
  )
65
65
  version: Optional[str] = kwargs.pop("version") if "version" in kwargs else None
66
+ visibility: Optional[int] = (
67
+ kwargs.pop("visibility") if "visibility" in kwargs else 0
68
+ )
66
69
  feature_sets: Dict[str, FeatureSet] = (
67
70
  kwargs.pop("feature_sets") if "feature_sets" in kwargs else {}
68
71
  )
69
72
  if not len(kwargs) == 0:
70
73
  raise ValueError(
71
- f"Only data, name, run, description, reference, reference_type can be passed, you passed: {kwargs}" # noqa
74
+ f"Only data, name, run, description, reference, reference_type, visibility can be passed, you passed: {kwargs}" # noqa
72
75
  )
73
76
 
74
77
  if is_new_version_of is None:
@@ -145,6 +148,7 @@ def __init__(
145
148
  hash, feature_sets = from_files(files) # type: ignore
146
149
  else:
147
150
  raise ValueError("Only DataFrame, AnnData and iterable of File is allowed")
151
+ # we ignore datasets in trash containing the same hash
148
152
  existing_dataset = Dataset.filter(hash=hash).one_or_none()
149
153
  if existing_dataset is not None:
150
154
  logger.warning(f"returning existing dataset with same hash: {existing_dataset}")
@@ -169,6 +173,7 @@ def __init__(
169
173
  run=run,
170
174
  version=version,
171
175
  initial_version_id=initial_version_id,
176
+ visibility=visibility,
172
177
  **kwargs,
173
178
  )
174
179
  dataset._files = files
@@ -344,12 +349,12 @@ def load(
344
349
  # because we're tracking data flow on the dataset-level, here, we don't
345
350
  # want to track it on the file-level
346
351
  objects = [file.load(is_run_input=False) for file in all_files]
347
- file_ids = [file.id for file in all_files]
352
+ file_uids = [file.uid for file in all_files]
348
353
  if isinstance(objects[0], pd.DataFrame):
349
354
  concat_object = pd.concat(objects, join=join)
350
355
  elif isinstance(objects[0], ad.AnnData):
351
356
  concat_object = ad.concat(
352
- objects, join=join, label="file_id", keys=file_ids
357
+ objects, join=join, label="file_uid", keys=file_uids
353
358
  )
354
359
  # only call it here because there might be errors during concat
355
360
  _track_run_input(self, is_run_input)
@@ -357,10 +362,32 @@ def load(
357
362
 
358
363
 
359
364
  # docstring handled through attach_func_to_class_method
360
- def delete(self, storage: Optional[bool] = None) -> None:
361
- super(Dataset, self).delete()
365
+ def delete(
366
+ self, permanent: Optional[bool] = None, storage: Optional[bool] = None
367
+ ) -> None:
368
+ # change visibility to 2 (trash)
369
+ if self.visibility < 2 and permanent is not True:
370
+ self.visibility = 2
371
+ self.save()
372
+ if self.file is not None:
373
+ self.file.visibility = 2
374
+ self.file.save()
375
+ return
376
+
377
+ # permanent delete
378
+ if permanent is None:
379
+ response = input(
380
+ "File record is already in trash! Are you sure to delete it from your"
381
+ " database? (y/n) You can't undo this action."
382
+ )
383
+ delete_record = response == "y"
384
+ else:
385
+ delete_record = permanent
386
+
387
+ if delete_record:
388
+ super(Dataset, self).delete()
362
389
  if self.file is not None:
363
- self.file.delete(storage=storage)
390
+ self.file.delete(permanent=permanent, storage=storage)
364
391
 
365
392
 
366
393
  # docstring handled through attach_func_to_class_method
@@ -384,6 +411,15 @@ def path(self) -> Union[Path, UPath]:
384
411
  return self.storage.path
385
412
 
386
413
 
414
+ # docstring handled through attach_func_to_class_method
415
+ def restore(self) -> None:
416
+ self.visibility = 0
417
+ self.save()
418
+ if self.file is not None:
419
+ self.file.visibility = 0
420
+ self.file.save()
421
+
422
+
387
423
  METHOD_NAMES = [
388
424
  "__init__",
389
425
  "from_anndata",
@@ -392,6 +428,7 @@ METHOD_NAMES = [
392
428
  "load",
393
429
  "delete",
394
430
  "save",
431
+ "restore",
395
432
  ]
396
433
 
397
434
  if _TESTING:
lamindb/_file.py CHANGED
@@ -179,6 +179,7 @@ def get_hash(
179
179
  hash, hash_type = hash_file(filepath)
180
180
  if not check_hash:
181
181
  return hash, hash_type
182
+ # we ignore datasets in trash containing the same hash
182
183
  result = File.filter(hash=hash).list()
183
184
  if len(result) > 0:
184
185
  if settings.upon_file_create_if_hash_exists == "error":
@@ -454,6 +455,9 @@ def __init__(file: File, *args, **kwargs):
454
455
  kwargs.pop("initial_version_id") if "initial_version_id" in kwargs else None
455
456
  )
456
457
  version: Optional[str] = kwargs.pop("version") if "version" in kwargs else None
458
+ visibility: Optional[int] = (
459
+ kwargs.pop("visibility") if "visibility" in kwargs else 0
460
+ )
457
461
  format = kwargs.pop("format") if "format" in kwargs else None
458
462
  log_hint = kwargs.pop("log_hint") if "log_hint" in kwargs else True
459
463
  skip_check_exists = (
@@ -462,8 +466,8 @@ def __init__(file: File, *args, **kwargs):
462
466
 
463
467
  if not len(kwargs) == 0:
464
468
  raise ValueError(
465
- "Only data, key, run, description, version, is_new_version_of can be"
466
- f" passed, you passed: {kwargs}"
469
+ "Only data, key, run, description, version, is_new_version_of, visibility"
470
+ f" can be passed, you passed: {kwargs}"
467
471
  )
468
472
 
469
473
  if is_new_version_of is None:
@@ -523,6 +527,7 @@ def __init__(file: File, *args, **kwargs):
523
527
  kwargs["initial_version_id"] = initial_version_id
524
528
  kwargs["version"] = version
525
529
  kwargs["description"] = description
530
+ kwargs["visibility"] = visibility
526
531
  # this check needs to come down here because key might be populated from an
527
532
  # existing file path during get_file_kwargs_from_data()
528
533
  if (
@@ -820,23 +825,49 @@ def stage(self, is_run_input: Optional[bool] = None) -> Path:
820
825
 
821
826
 
822
827
  # docstring handled through attach_func_to_class_method
823
- def delete(self, storage: Optional[bool] = None) -> None:
824
- if storage is None:
825
- response = input(f"Are you sure you want to delete {self} from storage? (y/n)")
826
- delete_in_storage = response == "y"
828
+ def delete(
829
+ self, permanent: Optional[bool] = None, storage: Optional[bool] = None
830
+ ) -> None:
831
+ # change visibility to 2 (trash)
832
+ if self.visibility < 2 and permanent is not True:
833
+ self.visibility = 2
834
+ self.save()
835
+ return
836
+
837
+ # if the file is already in the trash
838
+ # permanent delete skips the trash
839
+ if permanent is None:
840
+ response = input(
841
+ "File record is already in trash! Are you sure to delete it from your"
842
+ " database? (y/n) You can't undo this action."
843
+ )
844
+ delete_record = response == "y"
827
845
  else:
828
- delete_in_storage = storage
846
+ delete_record = permanent
829
847
 
830
848
  # need to grab file path before deletion
831
849
  filepath = self.path
850
+
832
851
  # only delete in storage if DB delete is successful
833
852
  # DB delete might error because of a foreign key constraint violated etc.
834
- self._delete_skip_storage()
835
- # we don't yet have any way to bring back the deleted metadata record
836
- # in case the storage deletion fails - this is important for ACID down the road
837
- if delete_in_storage:
838
- delete_storage(filepath)
839
- logger.success(f"deleted stored object {colors.yellow(f'{filepath}')}")
853
+ if delete_record:
854
+ self._delete_skip_storage()
855
+ if self.key is None:
856
+ delete_in_storage = True
857
+ else:
858
+ if storage is None:
859
+ response = input(
860
+ f"Are you sure to delete {filepath}? (y/n) You can't undo this"
861
+ " action."
862
+ )
863
+ delete_in_storage = response == "y"
864
+ else:
865
+ delete_in_storage = storage
866
+ # we don't yet have any way to bring back the deleted metadata record
867
+ # in case storage deletion fails - this is important for ACID down the road
868
+ if delete_in_storage:
869
+ delete_storage(filepath)
870
+ logger.success(f"deleted {colors.yellow(f'{filepath}')}")
840
871
 
841
872
 
842
873
  def _delete_skip_storage(file, *args, **kwargs) -> None:
@@ -961,6 +992,12 @@ def view_tree(
961
992
  )
962
993
 
963
994
 
995
+ # docstring handled through attach_func_to_class_method
996
+ def restore(self) -> None:
997
+ self.visibility = 0
998
+ self.save()
999
+
1000
+
964
1001
  METHOD_NAMES = [
965
1002
  "__init__",
966
1003
  "from_anndata",
@@ -973,6 +1010,7 @@ METHOD_NAMES = [
973
1010
  "replace",
974
1011
  "from_dir",
975
1012
  "view_tree",
1013
+ "restore",
976
1014
  ]
977
1015
 
978
1016
  if _TESTING:
lamindb/_filter.py CHANGED
@@ -42,6 +42,18 @@ def filter(Registry: Type[Registry], using: str = None, **expressions) -> QueryS
42
42
  id=UUID(instance_result["id"]),
43
43
  )
44
44
  add_db_connection(isettings, using)
45
+
46
+ if Registry.__name__ in {"File", "Dataset"}:
47
+ # visibility is set to <2 by default
48
+ if not any([e.startswith("visibility") for e in expressions]):
49
+ expressions["visibility__lt"] = 2
50
+ # if visibility is None, will not apply any filter for visibility
51
+ elif "visibility" in expressions:
52
+ if expressions["visibility"] is None:
53
+ expressions.pop("visibility")
54
+ elif expressions["visibility"] == "default":
55
+ expressions.pop("visibility")
56
+ expressions["visibility__lt"] = 2
45
57
  qs = QuerySet(model=Registry, using=using)
46
58
  if len(expressions) > 0:
47
59
  return qs.filter(**expressions)
lamindb/_query_set.py CHANGED
@@ -210,13 +210,11 @@ class QuerySet(models.QuerySet):
210
210
  return _search(cls=self, string=string, **kwargs)
211
211
 
212
212
  @doc_args(Registry.lookup.__doc__)
213
- def lookup(
214
- self, field: Optional[StrField] = None, return_field: Optional[StrField] = None
215
- ) -> NamedTuple:
213
+ def lookup(self, field: Optional[StrField] = None, **kwargs) -> NamedTuple:
216
214
  """{}"""
217
215
  from ._registry import _lookup
218
216
 
219
- return _lookup(cls=self, field=field, return_field=return_field)
217
+ return _lookup(cls=self, field=field, **kwargs)
220
218
 
221
219
  @doc_args(CanValidate.validate.__doc__)
222
220
  def validate(
lamindb/_registry.py CHANGED
@@ -147,8 +147,9 @@ def _search(
147
147
  return_queryset: bool = False,
148
148
  case_sensitive: bool = False,
149
149
  synonyms_field: Optional[StrField] = "synonyms",
150
+ **expressions,
150
151
  ) -> Union["pd.DataFrame", "QuerySet"]:
151
- queryset = _queryset(cls)
152
+ queryset = _queryset(cls, **expressions)
152
153
  orm = queryset.model
153
154
 
154
155
  def _search_single_field(
@@ -229,6 +230,7 @@ def search(
229
230
  return_queryset: bool = False,
230
231
  case_sensitive: bool = False,
231
232
  synonyms_field: Optional[StrField] = "synonyms",
233
+ **expressions,
232
234
  ) -> Union["pd.DataFrame", "QuerySet"]:
233
235
  """{}"""
234
236
  return _search(
@@ -239,14 +241,18 @@ def search(
239
241
  limit=limit,
240
242
  case_sensitive=case_sensitive,
241
243
  synonyms_field=synonyms_field,
244
+ **expressions,
242
245
  )
243
246
 
244
247
 
245
248
  def _lookup(
246
- cls, field: Optional[StrField] = None, return_field: Optional[StrField] = None
249
+ cls,
250
+ field: Optional[StrField] = None,
251
+ return_field: Optional[StrField] = None,
252
+ **expressions,
247
253
  ) -> NamedTuple:
248
254
  """{}"""
249
- queryset = _queryset(cls)
255
+ queryset = _queryset(cls, **expressions)
250
256
  field = get_default_str_field(orm=queryset.model, field=field)
251
257
 
252
258
  return Lookup(
@@ -264,10 +270,13 @@ def _lookup(
264
270
  @classmethod # type: ignore
265
271
  @doc_args(Registry.lookup.__doc__)
266
272
  def lookup(
267
- cls, field: Optional[StrField] = None, return_field: Optional[StrField] = None
273
+ cls,
274
+ field: Optional[StrField] = None,
275
+ return_field: Optional[StrField] = None,
276
+ **expressions,
268
277
  ) -> NamedTuple:
269
278
  """{}"""
270
- return _lookup(cls=cls, field=field, return_field=return_field)
279
+ return _lookup(cls=cls, field=field, return_field=return_field, **expressions)
271
280
 
272
281
 
273
282
  def get_default_str_field(
@@ -316,8 +325,12 @@ def get_default_str_field(
316
325
  return field
317
326
 
318
327
 
319
- def _queryset(cls: Union[Registry, QuerySet, Manager]) -> QuerySet:
320
- queryset = cls.all() if isinstance(cls, QuerySet) else cls.objects.all()
328
+ def _queryset(cls: Union[Registry, QuerySet, Manager], **expressions) -> QuerySet:
329
+ queryset = (
330
+ cls.filter(**expressions).all()
331
+ if isinstance(cls, QuerySet)
332
+ else cls.filter(**expressions).all()
333
+ )
321
334
  return queryset
322
335
 
323
336
 
lamindb/_save.py CHANGED
@@ -252,18 +252,20 @@ def upload_data_object(file) -> None:
252
252
  """Store and add file and its linked entries."""
253
253
  # do NOT hand-craft the storage key!
254
254
  file_storage_key = auto_storage_key_from_file(file)
255
- msg = f"storing file '{file.id}' at '{file_storage_key}'"
255
+ storage_path = lamindb_setup.settings.instance.storage.key_to_filepath(
256
+ file_storage_key
257
+ )
258
+ msg = f"storing file '{file.uid}' at '{storage_path}'"
256
259
  if (
257
260
  file.suffix in {".zarr", ".zrad"}
258
261
  and hasattr(file, "_memory_rep")
259
262
  and file._memory_rep is not None
260
263
  ):
261
264
  logger.save(msg)
262
- storagepath = lamindb_setup.settings.storage.key_to_filepath(file_storage_key)
263
265
  print_progress = partial(
264
266
  print_hook, filepath=file_storage_key, action="uploading"
265
267
  )
266
- write_adata_zarr(file._memory_rep, storagepath, callback=print_progress)
268
+ write_adata_zarr(file._memory_rep, storage_path, callback=print_progress)
267
269
  elif hasattr(file, "_to_store") and file._to_store:
268
270
  logger.save(msg)
269
271
  store_object(file._local_filepath, file_storage_key)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lamindb
3
- Version: 0.58.0
3
+ Version: 0.58.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
@@ -8,8 +8,8 @@ Description-Content-Type: text/markdown
8
8
  Classifier: Programming Language :: Python :: 3.8
9
9
  Classifier: Programming Language :: Python :: 3.9
10
10
  Classifier: Programming Language :: Python :: 3.10
11
- Requires-Dist: lnschema_core==0.52.1
12
- Requires-Dist: lamindb_setup==0.55.6
11
+ Requires-Dist: lnschema_core==0.53.0
12
+ Requires-Dist: lamindb_setup==0.56.3
13
13
  Requires-Dist: lamin_utils==0.11.5
14
14
  Requires-Dist: rapidfuzz
15
15
  Requires-Dist: pyarrow
@@ -1,17 +1,17 @@
1
- lamindb/__init__.py,sha256=BWW5E_HjLH9kJ3Fo6sUfWV2efj0pytTAYmPxnJ-iheA,2870
2
- lamindb/_dataset.py,sha256=6CJHTiwe1lWLUfPHQzJpq-hj8zRjNTCWLkPmrtxpP7Q,14712
1
+ lamindb/__init__.py,sha256=0YGJThA1KvrX4UlxWsOrnuNKxxjEe1FVhKvdZ_8KWTg,2870
2
+ lamindb/_dataset.py,sha256=GLGtwbZLlSjy2HtJsjHgRDTOO0u0PwuarRE5qp-rGUA,15810
3
3
  lamindb/_delete.py,sha256=wiYmYnvIEHrDdmw1NiXyfCY9mBt-FI5XNFi5jyR_mkA,1968
4
4
  lamindb/_feature.py,sha256=5gsa7zsMVVtm1DID4dF3Vwo5llWyY1dH3Hg5hjaIrQk,5554
5
5
  lamindb/_feature_set.py,sha256=G63pwauDQ7jg4ydFCQLhu-lgO6tm56iQwUdRuNHeKHY,9233
6
- lamindb/_file.py,sha256=V85tryLD0HsZiPQV4KNmcs3Gdi0qf8dZu27rl2G9D7E,36218
7
- lamindb/_filter.py,sha256=fNvPbLeOxYzvNKPcFYiFz3P7bkD5_84Xh8HHAoLNdas,1716
6
+ lamindb/_file.py,sha256=9McSL-DuhGDihfusIX1UKZ195HwhXohlWhJHV9Ki0c4,37358
7
+ lamindb/_filter.py,sha256=JrE4tdExNkOmNf0_tnO3vo-W3tecsH6ZB74gLO_fvKE,2293
8
8
  lamindb/_from_values.py,sha256=GitpmKOqV6YHJggaCnJgGsRIHI_bnuLRVE2oo9W-SgE,11613
9
9
  lamindb/_parents.py,sha256=VT_gtomf1Erd_AKLVd1uLwigeDqMHtcaAbma3_AbQAw,13408
10
10
  lamindb/_query_manager.py,sha256=MXueabWHqft7GWNkzmWbhfTqdk-0mKU7nWrhXG6wpYQ,3693
11
- lamindb/_query_set.py,sha256=Lf7vLvOsEfUWRQ3iImSj4eQPmUK1KCgeoKS_m66Lp7o,10279
12
- lamindb/_registry.py,sha256=_pdlEvAtemiQCzpK2s14MsTKkLqE6ORDjhDs7ABs4i4,14893
11
+ lamindb/_query_set.py,sha256=1vjTLkCCrs1GiS2KTyqmSgVRSx966UsMhApXbW7GgI0,10217
12
+ lamindb/_registry.py,sha256=lUnHCeDDOw4mlak0_Q_EbQU1_qDrsE23l7IEbeoaV8w,15138
13
13
  lamindb/_run.py,sha256=659lqY32GW7F41rFUUo37OftUa38-p8yaV9Z0oF32CE,1120
14
- lamindb/_save.py,sha256=m6l5mMsxlrmlkdWhfjbwOtZ3haGEYyg63QcPG8twTMQ,10136
14
+ lamindb/_save.py,sha256=hL34zgm-L3MFfi6P9O0AzeptFHtEnHdKheJqdOlGDM4,10154
15
15
  lamindb/_storage.py,sha256=HUdXGj4839C606gvxWXo0tDITbtbuyJKOgUPhagYPTI,415
16
16
  lamindb/_transform.py,sha256=87yUTz0RndJ_C98tBt4t2SPw8fksRgqJKwCQG_H40Kk,2515
17
17
  lamindb/_ulabel.py,sha256=lEAENh_dluNkBi8xKUH_CjJNMXldOm2liy6Rg3IH1pE,1900
@@ -41,8 +41,8 @@ lamindb/dev/storage/file.py,sha256=xfeU8X1ty80-PhnHOpupBJfibZKhp6MPLA2IjYdTBoY,7
41
41
  lamindb/dev/storage/object.py,sha256=KGuOwwYuN2yCJxTXn9v0LanC0fjKwy_62P-WksHcf40,1140
42
42
  lamindb/setup/__init__.py,sha256=8-0F2C4Glx23-b8-D_1CBGgRBM5PppVhazhoXZYOLsg,275
43
43
  lamindb/setup/dev/__init__.py,sha256=tBty426VGF2PGqqt2XuNU-WgvOrbOp1aZBDowjLuzgA,242
44
- lamindb-0.58.0.dist-info/entry_points.txt,sha256=MioM8vSpKwXxY3geNBwjo1wnwy1l15WjJYlI3lpKuZI,53
45
- lamindb-0.58.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
46
- lamindb-0.58.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
47
- lamindb-0.58.0.dist-info/METADATA,sha256=thZUko5v-kPa9zVxlYdnMZZrh2CXnZrsrC8o0z_Dsts,3030
48
- lamindb-0.58.0.dist-info/RECORD,,
44
+ lamindb-0.58.1.dist-info/entry_points.txt,sha256=MioM8vSpKwXxY3geNBwjo1wnwy1l15WjJYlI3lpKuZI,53
45
+ lamindb-0.58.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
46
+ lamindb-0.58.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
47
+ lamindb-0.58.1.dist-info/METADATA,sha256=vsEe2aNzGIKTdXiRH07Cr6wZuFn5COOO9U1DuZRkBRM,3030
48
+ lamindb-0.58.1.dist-info/RECORD,,