lamindb_setup 0.81.2__py3-none-any.whl → 0.81.4__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_setup/__init__.py CHANGED
@@ -33,7 +33,7 @@ Modules & settings:
33
33
 
34
34
  """
35
35
 
36
- __version__ = "0.81.2" # denote a release candidate for 0.1.0 with 0.1rc1
36
+ __version__ = "0.81.4" # denote a release candidate for 0.1.0 with 0.1rc1
37
37
 
38
38
  import os as _os
39
39
  import sys as _sys
@@ -239,6 +239,10 @@ def _delete_instance(
239
239
  )
240
240
  root_string = storage_record["root"]
241
241
  # gate storage and instance deletion on empty storage location for
242
+ # normally auth.get_session() doesn't have access_token
243
+ # so this block is useless i think (Sergei)
244
+ # the token is received from user settings inside create_path
245
+ # might be needed in the hub though
242
246
  if client.auth.get_session() is not None:
243
247
  access_token = client.auth.get_session().access_token
244
248
  else:
@@ -251,7 +255,6 @@ def _delete_instance(
251
255
  check_storage_is_empty(
252
256
  root_path, account_for_sqlite_file=account_for_sqlite_file
253
257
  )
254
- _update_instance_record(instance_with_storage["id"], {"storage_id": None}, client)
255
258
  # first delete the storage records because we will turn instance_id on
256
259
  # storage into a FK soon
257
260
  for storage_record in storage_records:
@@ -12,25 +12,23 @@ def select_instance_by_owner_name(
12
12
  name: str,
13
13
  client: Client,
14
14
  ) -> dict | None:
15
- try:
16
- data = (
17
- client.table("instance")
18
- .select(
19
- "*, account!inner!instance_account_id_28936e8f_fk_account_id(*),"
20
- " storage!inner!storage_instance_id_359fca71_fk_instance_id(*)"
21
- )
22
- .eq("name", name)
23
- .eq("account.handle", owner)
24
- .eq("storage.is_default", True)
25
- .execute()
26
- .data
15
+ # this won't find an instance without the default storage
16
+ data = (
17
+ client.table("instance")
18
+ .select(
19
+ "*, account!inner!instance_account_id_28936e8f_fk_account_id(*),"
20
+ " storage!inner!storage_instance_id_359fca71_fk_instance_id(*)"
27
21
  )
28
- except Exception:
29
- return None
22
+ .eq("name", name)
23
+ .eq("account.handle", owner)
24
+ .eq("storage.is_default", True)
25
+ .execute()
26
+ .data
27
+ )
30
28
  if len(data) == 0:
31
29
  return None
32
30
  result = data[0]
33
- # this is now a list
31
+ # this is a list
34
32
  # assume only one default storage
35
33
  result["storage"] = result["storage"][0]
36
34
  return result
@@ -89,15 +87,22 @@ def select_instance_by_id_with_storage(
89
87
  instance_id: str,
90
88
  client: Client,
91
89
  ):
92
- response = (
90
+ # this won't find an instance without the default storage
91
+ data = (
93
92
  client.table("instance")
94
- .select("*, storage!instance_storage_id_87963cc8_fk_storage_id(*)")
93
+ .select("*, storage!inner!storage_instance_id_359fca71_fk_instance_id(*)")
95
94
  .eq("id", instance_id)
95
+ .eq("storage.is_default", True)
96
96
  .execute()
97
+ .data
97
98
  )
98
- if len(response.data) == 0:
99
+ if len(data) == 0:
99
100
  return None
100
- return response.data[0]
101
+ result = data[0]
102
+ # this is a list
103
+ # assume only one default storage
104
+ result["storage"] = result["storage"][0]
105
+ return result
101
106
 
102
107
 
103
108
  def update_instance(instance_id: str, instance_fields: dict, client: Client):
@@ -195,27 +195,35 @@ class SetupPaths:
195
195
  filepath: UPathStr, cache_key: str | None = None
196
196
  ) -> UPath:
197
197
  """Local (or local cache) filepath from filepath without synchronization."""
198
- # cache_key is ignored if filepath is a string or a local path
199
- # ignores a mere string even if it represents a cloud path
200
- if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
198
+ if not isinstance(filepath, UPath):
199
+ filepath = UPath(filepath)
200
+ # cache_key is ignored if filepath is a local path
201
+ if not isinstance(filepath, LocalPathClasses):
201
202
  # settings is defined further in this file
202
- local_filepath = settings.cache_dir / (
203
- filepath.path if cache_key is None else cache_key
204
- )
203
+ if cache_key is None:
204
+ local_key = filepath.path # type: ignore
205
+ protocol = filepath.protocol # type: ignore
206
+ if protocol in {"http", "https"}:
207
+ local_key = local_key.removeprefix(protocol + "://")
208
+ else:
209
+ local_key = cache_key
210
+ local_filepath = settings.cache_dir / local_key
205
211
  else:
206
212
  local_filepath = filepath
207
- return UPath(local_filepath)
213
+ return local_filepath
208
214
 
209
215
  @staticmethod
210
216
  def cloud_to_local(
211
217
  filepath: UPathStr, cache_key: str | None = None, **kwargs
212
218
  ) -> UPath:
213
219
  """Local (or local cache) filepath from filepath."""
214
- # cache_key is ignored in cloud_to_local_no_update if filepath is local or a string
220
+ if not isinstance(filepath, UPath):
221
+ filepath = UPath(filepath)
222
+ # cache_key is ignored in cloud_to_local_no_update if filepath is local
215
223
  local_filepath = SetupPaths.cloud_to_local_no_update(filepath, cache_key)
216
- if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
224
+ if not isinstance(filepath, LocalPathClasses):
217
225
  local_filepath.parent.mkdir(parents=True, exist_ok=True)
218
- filepath.synchronize(local_filepath, **kwargs)
226
+ filepath.synchronize(local_filepath, **kwargs) # type: ignore
219
227
  return local_filepath
220
228
 
221
229
 
@@ -7,6 +7,7 @@ import string
7
7
  from pathlib import Path
8
8
  from typing import TYPE_CHECKING, Any, Literal
9
9
 
10
+ import fsspec
10
11
  from lamin_utils import logger
11
12
 
12
13
  from ._aws_credentials import HOSTED_REGIONS, get_aws_credentials_manager
@@ -24,6 +25,10 @@ if TYPE_CHECKING:
24
25
 
25
26
  IS_INITIALIZED_KEY = ".lamindb/_is_initialized"
26
27
 
28
+ # a list of supported fsspec protocols
29
+ # rename file to local before showing to a user
30
+ VALID_PROTOCOLS = ("file", "gs", "s3", "hf", "http", "https")
31
+
27
32
 
28
33
  def base62(n_char: int) -> str:
29
34
  """Like nanoid without hyphen and underscore."""
@@ -114,16 +119,11 @@ def init_storage(
114
119
  root_str = f"s3://lamin-{region}/{uid}"
115
120
  else:
116
121
  root_str = f"s3://lamin-hosted-test/{uid}"
117
- elif root_str.startswith(("gs://", "s3://", "hf://")):
118
- pass
119
- else: # local path
120
- try:
121
- _ = Path(root_str)
122
- except Exception as e:
123
- logger.error(
124
- "`storage` is not a valid local, GCP storage, AWS S3 path or Hugging Face path"
125
- )
126
- raise e
122
+ elif (input_protocol := fsspec.utils.get_protocol(root_str)) not in VALID_PROTOCOLS:
123
+ valid_protocols = ("local",) + VALID_PROTOCOLS[1:] # show local instead of file
124
+ raise ValueError(
125
+ f"Protocol {input_protocol} is not supported, valid protocols are {', '.join(valid_protocols)}"
126
+ )
127
127
  ssettings = StorageSettings(
128
128
  uid=uid,
129
129
  root=root_str,
@@ -227,7 +227,7 @@ class StorageSettings:
227
227
 
228
228
  @property
229
229
  def record(self) -> Any:
230
- """Storage record in current instance."""
230
+ """Storage record in the current instance."""
231
231
  if self._record is None:
232
232
  # dynamic import because of import order
233
233
  from lnschema_core.models import Storage
@@ -299,14 +299,15 @@ class StorageSettings:
299
299
  return self._region
300
300
 
301
301
  @property
302
- def type(self) -> Literal["local", "s3", "gs"]:
302
+ def type(self) -> Literal["local", "s3", "gs", "hf", "http", "https"]:
303
303
  """AWS S3 vs. Google Cloud vs. local.
304
304
 
305
- Returns the protocol as a string: "local", "s3", "gs".
305
+ Returns the protocol as a string: "local", "s3", "gs", "http", "https".
306
306
  """
307
307
  import fsspec
308
308
 
309
309
  convert = {"file": "local"}
310
+ # init_storage checks that the root protocol belongs to VALID_PROTOCOLS
310
311
  protocol = fsspec.utils.get_protocol(self.root_as_str)
311
312
  return convert.get(protocol, protocol) # type: ignore
312
313
 
@@ -345,5 +346,5 @@ class StorageSettings:
345
346
  return self.root / filekey
346
347
 
347
348
  def local_filepath(self, filekey: UPathStr) -> UPath:
348
- """Local (cache) filepath from filekey: `local(filepath(...))`."""
349
+ """Local (cache) filepath from filekey."""
349
350
  return self.cloud_to_local(self.key_to_filepath(filekey))
@@ -190,7 +190,13 @@ class ProgressCallback(fsspec.callbacks.Callback):
190
190
  pass
191
191
 
192
192
  def update_relative_value(self, inc=1):
193
- self.value += inc
193
+ if inc != 0:
194
+ self.value += inc
195
+ # this is specific to http filesystem
196
+ # for some reason the last update is 0 always
197
+ # here 100% is forced manually in this case
198
+ elif self.value >= 0.999:
199
+ self.value = self.size
194
200
  self.call()
195
201
 
196
202
  def branch(self, path_1, path_2, kwargs):
@@ -350,27 +356,19 @@ def synchronize(
350
356
  exists = True
351
357
  cloud_mts = timestamp
352
358
  else:
353
- # hf requires special treatment
354
- if protocol == "hf":
355
- try:
356
- stat_hf = self.stat().as_info()
357
- is_dir = stat_hf["type"] == "directory"
358
- exists = True
359
- if not is_dir:
360
- cloud_mts = stat_hf["last_commit"].date.timestamp()
361
- except FileNotFoundError:
362
- exists = False
363
- else:
364
- # perform only one network request to check existence, type and timestamp
365
- try:
366
- cloud_mts = self.modified.timestamp()
367
- is_dir = False
368
- exists = True
369
- except FileNotFoundError:
370
- exists = False
371
- except IsADirectoryError:
372
- is_dir = True
373
- exists = True
359
+ try:
360
+ cloud_stat = self.stat()
361
+ cloud_info = cloud_stat.as_info()
362
+ exists = True
363
+ is_dir = cloud_info["type"] == "directory"
364
+ if not is_dir:
365
+ # hf requires special treatment
366
+ if protocol == "hf":
367
+ cloud_mts = cloud_info["last_commit"].date.timestamp()
368
+ else:
369
+ cloud_mts = cloud_stat.st_mtime
370
+ except FileNotFoundError:
371
+ exists = False
374
372
 
375
373
  if not exists:
376
374
  warn_or_error = f"The original path {self} does not exist anymore."
@@ -386,6 +384,7 @@ def synchronize(
386
384
  return None
387
385
 
388
386
  # synchronization logic for directories
387
+ # to synchronize directories, it should be possible to get modification times
389
388
  if is_dir:
390
389
  files = self.fs.find(str(self), detail=True)
391
390
  if protocol == "s3":
@@ -451,8 +450,16 @@ def synchronize(
451
450
  callback, print_progress, objectpath.name, "synchronizing"
452
451
  )
453
452
  if objectpath.exists():
454
- local_mts_obj = objectpath.stat().st_mtime # type: ignore
455
- need_synchronize = cloud_mts > local_mts_obj
453
+ if cloud_mts != 0:
454
+ local_mts_obj = objectpath.stat().st_mtime
455
+ need_synchronize = cloud_mts > local_mts_obj
456
+ else:
457
+ # this is true for http for example
458
+ # where size is present but st_mtime is not
459
+ # we assume that any change without the change in size is unlikely
460
+ cloud_size = cloud_stat.st_size
461
+ local_size_obj = objectpath.stat().st_size
462
+ need_synchronize = cloud_size != local_size_obj
456
463
  else:
457
464
  objectpath.parent.mkdir(parents=True, exist_ok=True)
458
465
  need_synchronize = True
@@ -464,7 +471,8 @@ def synchronize(
464
471
  self.download_to(
465
472
  objectpath, recursive=False, print_progress=False, callback=callback
466
473
  )
467
- os.utime(objectpath, times=(cloud_mts, cloud_mts))
474
+ if cloud_mts != 0:
475
+ os.utime(objectpath, times=(cloud_mts, cloud_mts))
468
476
  else:
469
477
  # nothing happens if parent_update is not defined
470
478
  # because of Callback.no_op
@@ -739,7 +747,9 @@ def get_stat_file_cloud(stat: dict) -> tuple[int, str | None, str | None]:
739
747
  hash = b16_to_b64(stat["blob_id"])
740
748
  hash_type = "sha1"
741
749
  # s3
742
- elif "ETag" in stat:
750
+ # StorageClass is checked to be sure that it is indeed s3
751
+ # because http also has ETag
752
+ elif "ETag" in stat and "StorageClass" in stat:
743
753
  etag = stat["ETag"]
744
754
  # small files
745
755
  if "-" not in etag:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lamindb_setup
3
- Version: 0.81.2
3
+ Version: 0.81.4
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.9
@@ -14,12 +14,11 @@ Requires-Dist: appdirs<2.0.0
14
14
  Requires-Dist: requests
15
15
  Requires-Dist: universal_pathlib==0.2.5
16
16
  Requires-Dist: botocore<2.0.0
17
- Requires-Dist: gotrue>=2.1.0,<2.9.0
18
- Requires-Dist: supabase==2.2.1
17
+ Requires-Dist: supabase>=2.8.1,<=2.10.0
19
18
  Requires-Dist: psutil
20
19
  Requires-Dist: urllib3<2 ; extra == "aws"
21
20
  Requires-Dist: aiobotocore[boto3]>=2.5.4,<3.0.0 ; extra == "aws"
22
- Requires-Dist: s3fs>=2023.12.2,<=2024.10.0 ; extra == "aws"
21
+ Requires-Dist: s3fs>=2023.12.2,<=2024.10.0,!=2024.10.0 ; extra == "aws"
23
22
  Requires-Dist: line_profiler ; extra == "dev"
24
23
  Requires-Dist: pyjwt<3.0.0 ; extra == "dev"
25
24
  Requires-Dist: psycopg2-binary ; extra == "dev"
@@ -1,4 +1,4 @@
1
- lamindb_setup/__init__.py,sha256=o71S_vO9_RpZ91eHTH0FZG57w9nwWR54OUufTiq-aN8,1714
1
+ lamindb_setup/__init__.py,sha256=41Rm-lDXIF3ir0Xsj-vpB0EZO5SmkipsRc5pmUzBEvk,1714
2
2
  lamindb_setup/_cache.py,sha256=1XnM-V_KprbjpgPY7Bg3FYn53Iz_2_fEgcMOaSdKKbg,1332
3
3
  lamindb_setup/_check.py,sha256=28PcG8Kp6OpjSLSi1r2boL2Ryeh6xkaCL87HFbjs6GA,129
4
4
  lamindb_setup/_check_setup.py,sha256=6cSfpmVOSgU7YiVHfJpBTGTQ7rrnwunt1pJT_jkgNM8,3196
@@ -23,15 +23,15 @@ lamindb_setup/core/_aws_storage.py,sha256=nEjeUv4xUVpoV0Lx-zjjmyb9w804bDyaeiM-Oq
23
23
  lamindb_setup/core/_deprecated.py,sha256=3qxUI1dnDlSeR0BYrv7ucjqRBEojbqotPgpShXs4KF8,2520
24
24
  lamindb_setup/core/_docs.py,sha256=3k-YY-oVaJd_9UIY-LfBg_u8raKOCNfkZQPA73KsUhs,276
25
25
  lamindb_setup/core/_hub_client.py,sha256=cN19XbZmvLCxL_GKdOcKbedNRL7kR47vmLmA--NMv-U,6306
26
- lamindb_setup/core/_hub_core.py,sha256=eUxRz9iJj6RA5-MWgQqqZYAU-di5LQDamRZn6t-VOiM,19838
27
- lamindb_setup/core/_hub_crud.py,sha256=eZErpq9t1Cp2ULBSi457ekrcqfesw4Y6IJgaqyrINMY,5276
26
+ lamindb_setup/core/_hub_core.py,sha256=cZfEDFq2z0cHjyPqwg7K5K3vw9C2nDHnbIHmmAjVbeM,19997
27
+ lamindb_setup/core/_hub_crud.py,sha256=ro4dvZ0EaHs7_QwJH6BoKl69Wc9HEPDkIc9BFyO_DMg,5460
28
28
  lamindb_setup/core/_hub_utils.py,sha256=08NwQsb53-tXa_pr-f0tPTN0FeeVf_i1p3dEbEWD0F4,3016
29
29
  lamindb_setup/core/_private_django_api.py,sha256=KIn43HOhiRjkbTbddyJqv-WNTTa1bAizbM1tWXoXPBg,2869
30
- lamindb_setup/core/_settings.py,sha256=mpGsSb98UsBedLsW2RuowZ17EP2tI2XRGPztqrJtrV4,7952
30
+ lamindb_setup/core/_settings.py,sha256=NYrSGtAw68mlQJgzBnV-DECi-RUBn32eeosyYCBtW3I,8232
31
31
  lamindb_setup/core/_settings_instance.py,sha256=ajcq9zRNE598tTqyMkMqaEOubVfFeE998DPtbgyzK3A,18801
32
32
  lamindb_setup/core/_settings_load.py,sha256=5OpghcbkrK9KBM_0Iu-61FTI76UbOpPkkJpUittXS-w,4098
33
33
  lamindb_setup/core/_settings_save.py,sha256=rxGxgaK5i9exKqSJERQQyY1WZio20meoQJoYXlVW-1w,3138
34
- lamindb_setup/core/_settings_storage.py,sha256=CYwGZm0fKYN7eLLsU-sOtOKG7HzswQVjTWb0ooHKcNg,11990
34
+ lamindb_setup/core/_settings_storage.py,sha256=FOTyi89BZ48a8elCQ91C4B6oMO4tBnva4J6Tk40N9Ww,12249
35
35
  lamindb_setup/core/_settings_store.py,sha256=WcsgOmgnu9gztcrhp-N4OONNZyxICHV8M0HdJllTaEo,2219
36
36
  lamindb_setup/core/_settings_user.py,sha256=iz0MqFLKXqm8LYx_CHmr02_oNvYWFLIxKkJLdpS5W08,1476
37
37
  lamindb_setup/core/_setup_bionty_sources.py,sha256=jZOPXpipW_5IjMO-bLMk-_wVwk7-5MLd72K2rnqqy7U,4001
@@ -40,8 +40,8 @@ lamindb_setup/core/django.py,sha256=E4U9nUlV2kHd-G5v6iSdFGAAWixlQDxOFwMwOMG9xfw,
40
40
  lamindb_setup/core/exceptions.py,sha256=4NpLUNUIfXYVTFX2FvLZF8RW34exk2Vn2X3G4YhnTRg,276
41
41
  lamindb_setup/core/hashing.py,sha256=26dtak7XgmrWa_D1zuDyxObRQcriMtnc1yEigkKASmM,3142
42
42
  lamindb_setup/core/types.py,sha256=zJii2le38BJUmsNVvzDrbzGYr0yaeb-9Rw9IKmsBr3k,523
43
- lamindb_setup/core/upath.py,sha256=a0yxP9dmujTn_hkDC0E2UuVsX-Img4i0kVNB-OV5K1s,29038
44
- lamindb_setup-0.81.2.dist-info/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
45
- lamindb_setup-0.81.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
46
- lamindb_setup-0.81.2.dist-info/METADATA,sha256=qUDiU989EwoOfdbfJ8jXPCiGLXc1TLwgXQQAN5H1Iu0,1745
47
- lamindb_setup-0.81.2.dist-info/RECORD,,
43
+ lamindb_setup/core/upath.py,sha256=X5Jjm50-br1Qq3eRlO6U4ykidptc2QzoQtq-ilaPt4Q,29602
44
+ lamindb_setup-0.81.4.dist-info/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
45
+ lamindb_setup-0.81.4.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
46
+ lamindb_setup-0.81.4.dist-info/METADATA,sha256=xstSHpwOECuKUylOziRLHFHtafUtglGT1CQA0lUGscs,1730
47
+ lamindb_setup-0.81.4.dist-info/RECORD,,