lamindb_setup 0.81.4__tar.gz → 0.81.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/PKG-INFO +2 -2
  2. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-cloud-sync.ipynb +0 -82
  3. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/__init__.py +1 -1
  4. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_check_setup.py +44 -0
  5. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_migrate.py +5 -8
  6. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_hub_core.py +1 -4
  7. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_hub_crud.py +19 -24
  8. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings.py +3 -8
  9. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_storage.py +14 -15
  10. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/upath.py +26 -36
  11. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-local/test_all.py +0 -6
  12. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/.github/workflows/build.yml +0 -0
  13. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/.github/workflows/doc-changes.yml +0 -0
  14. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/.gitignore +0 -0
  15. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/.pre-commit-config.yaml +0 -0
  16. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/LICENSE +0 -0
  17. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/README.md +0 -0
  18. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/changelog.md +0 -0
  19. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  20. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
  21. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  22. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  23. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  24. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  25. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  26. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
  27. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-cloud/test_notebooks.py +0 -0
  28. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-cache-management.ipynb +0 -0
  29. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  30. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-empty-init.ipynb +0 -0
  31. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-import-schema.ipynb +0 -0
  32. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  33. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
  34. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  35. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  36. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/hub-prod/test_notebooks2.py +0 -0
  37. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/index.md +0 -0
  38. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/notebooks.md +0 -0
  39. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/docs/reference.md +0 -0
  40. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_cache.py +0 -0
  41. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_check.py +0 -0
  42. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_close.py +0 -0
  43. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_connect_instance.py +0 -0
  44. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_delete.py +0 -0
  45. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_django.py +0 -0
  46. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_entry_points.py +0 -0
  47. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_exportdb.py +0 -0
  48. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_importdb.py +0 -0
  49. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_init_instance.py +0 -0
  50. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_register_instance.py +0 -0
  51. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_schema.py +0 -0
  52. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_schema_metadata.py +0 -0
  53. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_set_managed_storage.py +0 -0
  54. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_setup_user.py +0 -0
  55. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/_silence_loggers.py +0 -0
  56. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/__init__.py +0 -0
  57. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_aws_credentials.py +0 -0
  58. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_aws_storage.py +0 -0
  59. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_deprecated.py +0 -0
  60. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_docs.py +0 -0
  61. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_hub_client.py +0 -0
  62. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_hub_utils.py +0 -0
  63. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_private_django_api.py +0 -0
  64. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_instance.py +0 -0
  65. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_load.py +0 -0
  66. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_save.py +0 -0
  67. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_store.py +0 -0
  68. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_settings_user.py +0 -0
  69. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  70. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  71. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/django.py +0 -0
  72. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/exceptions.py +0 -0
  73. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/hashing.py +0 -0
  74. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/lamindb_setup/core/types.py +0 -0
  75. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/noxfile.py +0 -0
  76. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/pyproject.toml +0 -0
  77. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  78. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  79. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_connect_instance.py +0 -0
  80. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_delete_instance.py +0 -0
  81. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_edge_request.py +0 -0
  82. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  83. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_init_instance.py +0 -0
  84. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  85. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_login.py +0 -0
  86. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_migrate.py +0 -0
  87. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-cloud/test_set_storage.py +0 -0
  88. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-local/conftest.py +0 -0
  89. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-local/test_update_schema_in_hub.py +0 -0
  90. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/conftest.py +0 -0
  91. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/test_aws_credentials_manager.py +0 -0
  92. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/test_django.py +0 -0
  93. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/test_global_settings.py +0 -0
  94. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
  95. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/hub-prod/test_upath.py +0 -0
  96. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_entry_point.py +0 -0
  97. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_hashing.py +0 -0
  98. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_storage_access.py +0 -0
  99. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_storage_basis.py +0 -0
  100. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_storage_stats.py +0 -0
  101. {lamindb_setup-0.81.4 → lamindb_setup-0.81.5}/tests/storage/test_to_url.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: lamindb_setup
3
- Version: 0.81.4
3
+ Version: 0.81.5
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.9
@@ -149,37 +149,6 @@
149
149
  "assert settings.paths.cloud_to_local_no_update(dir_sync.as_posix(), cache_key=\"dir_cache/key\") == settings.cache_dir / \"dir_cache/key\""
150
150
  ]
151
151
  },
152
- {
153
- "cell_type": "code",
154
- "execution_count": null,
155
- "id": "eda84820",
156
- "metadata": {},
157
- "outputs": [],
158
- "source": [
159
- "# for http urls\n",
160
- "http_path = UPath(\"https://raw.githubusercontent.com/laminlabs/lamindb-setup/refs/heads/main/README.md\")\n",
161
- "assert http_path.protocol == \"https\"\n",
162
- "\n",
163
- "http_stat = http_path.stat()\n",
164
- "assert http_stat.st_size != 0\n",
165
- "assert http_stat.st_mtime == 0\n",
166
- "assert http_stat.as_info()[\"type\"] == \"file\""
167
- ]
168
- },
169
- {
170
- "cell_type": "code",
171
- "execution_count": null,
172
- "id": "df6a9be4",
173
- "metadata": {},
174
- "outputs": [],
175
- "source": [
176
- "http_key = \"raw.githubusercontent.com/laminlabs/lamindb-setup/refs/heads/main/README.md\"\n",
177
- "\n",
178
- "assert settings.paths.cloud_to_local_no_update(http_path) == settings.cache_dir / http_key\n",
179
- "assert settings.paths.cloud_to_local_no_update(str(http_path)) == settings.cache_dir / http_key\n",
180
- "assert settings.paths.cloud_to_local_no_update(http_path, cache_key=\"check/README.md\") == settings.cache_dir / \"check/README.md\""
181
- ]
182
- },
183
152
  {
184
153
  "cell_type": "markdown",
185
154
  "id": "0b79f2f7",
@@ -222,57 +191,6 @@
222
191
  "dir_sync_local.rmdir()"
223
192
  ]
224
193
  },
225
- {
226
- "cell_type": "markdown",
227
- "id": "d2246f90",
228
- "metadata": {},
229
- "source": [
230
- "Test `cloud_to_local` for http"
231
- ]
232
- },
233
- {
234
- "cell_type": "code",
235
- "execution_count": null,
236
- "id": "bc1b7736",
237
- "metadata": {},
238
- "outputs": [],
239
- "source": [
240
- "http_local = settings.paths.cloud_to_local(http_path)"
241
- ]
242
- },
243
- {
244
- "cell_type": "code",
245
- "execution_count": null,
246
- "id": "ca9f0ba8",
247
- "metadata": {},
248
- "outputs": [],
249
- "source": [
250
- "assert isinstance(http_local, LocalPathClasses)\n",
251
- "assert http_local.stat().st_size == http_path.stat().st_size"
252
- ]
253
- },
254
- {
255
- "cell_type": "code",
256
- "execution_count": null,
257
- "id": "6ae7cd2b",
258
- "metadata": {},
259
- "outputs": [],
260
- "source": [
261
- "http_local_mtime = http_local.stat().st_mtime\n",
262
- "# no changes here because the file exists already\n",
263
- "assert settings.paths.cloud_to_local(http_path).stat().st_mtime == http_local_mtime"
264
- ]
265
- },
266
- {
267
- "cell_type": "code",
268
- "execution_count": null,
269
- "id": "9da41c21",
270
- "metadata": {},
271
- "outputs": [],
272
- "source": [
273
- "http_local.unlink()"
274
- ]
275
- },
276
194
  {
277
195
  "cell_type": "markdown",
278
196
  "id": "574c3f95",
@@ -33,7 +33,7 @@ Modules & settings:
33
33
 
34
34
  """
35
35
 
36
- __version__ = "0.81.4" # denote a release candidate for 0.1.0 with 0.1rc1
36
+ __version__ = "0.81.5" # 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
@@ -52,6 +52,49 @@ def _get_current_instance_settings() -> InstanceSettings | None:
52
52
  return None
53
53
 
54
54
 
55
+ def check_connecting_to_v1_instance(isettings: InstanceSettings):
56
+ from urllib.parse import urlparse
57
+
58
+ # we need to do this because the sqlite file should be already synced
59
+ # has no effect if not cloud sqlite
60
+ # errors if the sqlite file is not in the cloud and doesn't exist locally
61
+ # isettings.db syncs but doesn't error in this case due to error_no_origin=False
62
+ isettings._update_local_sqlite_file()
63
+
64
+ parsed_uri = urlparse(isettings.db)
65
+ db_type = parsed_uri.scheme
66
+
67
+ if db_type == "sqlite":
68
+ import sqlite3
69
+
70
+ conn = sqlite3.connect(parsed_uri.path)
71
+ elif db_type in ["postgresql", "postgres"]:
72
+ import psycopg2
73
+
74
+ conn = psycopg2.connect(isettings.db)
75
+ else:
76
+ raise ValueError("Unsupported database type. Use 'sqlite' or 'postgresql' URI.")
77
+
78
+ cur = conn.cursor()
79
+
80
+ if db_type == "sqlite":
81
+ cur.execute(
82
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='lamindb_user'"
83
+ )
84
+ isv1 = cur.fetchone() is not None
85
+
86
+ else: # postgres
87
+ cur.execute(
88
+ "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'lamindb_user')"
89
+ )
90
+ isv1 = cur.fetchone()[0]
91
+
92
+ if isv1:
93
+ raise SystemExit(
94
+ "This instance runs lamindb v1. Please run: pip install 'lamindb[bionty,wetlab,clinicore,ourprojects]'>1.0.0"
95
+ )
96
+
97
+
55
98
  # we make this a private function because in all the places it's used,
56
99
  # users should not see it
57
100
  def _check_instance_setup(
@@ -77,6 +120,7 @@ def _check_instance_setup(
77
120
  if isettings is not None:
78
121
  if reload_module and settings.auto_connect:
79
122
  if not django.IS_SETUP:
123
+ check_connecting_to_v1_instance(isettings)
80
124
  django.setup_django(isettings)
81
125
  if from_module is not None:
82
126
  # this only reloads `from_module`
@@ -28,14 +28,11 @@ def check_whether_migrations_in_sync(db_version_str: str):
28
28
  or installed_version.minor < db_version.minor
29
29
  ):
30
30
  db_version_lower = f"{db_version.major}.{db_version.minor}"
31
- db_version_upper = f"{db_version.major}.{db_version.minor + 1}"
32
- logger.warning(
33
- f"the database ({db_version_str}) is ahead of your installed lamindb"
34
- f" package ({installed_version_str})"
35
- )
36
- logger.important(
37
- "please update lamindb: pip install"
38
- f' "lamindb>={db_version_lower},<{db_version_upper}"'
31
+ raise SystemExit(
32
+ f"The database ({db_version_str}) is ahead of your installed lamindb"
33
+ f" package ({installed_version_str})\n"
34
+ "Please update lamindb: pip install"
35
+ f" 'lamindb[bionty,wetlab,clinicore,ourprojects]'>={db_version_lower}"
39
36
  )
40
37
  elif (
41
38
  installed_version.major > db_version.major
@@ -239,10 +239,6 @@ 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
246
242
  if client.auth.get_session() is not None:
247
243
  access_token = client.auth.get_session().access_token
248
244
  else:
@@ -255,6 +251,7 @@ def _delete_instance(
255
251
  check_storage_is_empty(
256
252
  root_path, account_for_sqlite_file=account_for_sqlite_file
257
253
  )
254
+ _update_instance_record(instance_with_storage["id"], {"storage_id": None}, client)
258
255
  # first delete the storage records because we will turn instance_id on
259
256
  # storage into a FK soon
260
257
  for storage_record in storage_records:
@@ -12,23 +12,25 @@ def select_instance_by_owner_name(
12
12
  name: str,
13
13
  client: Client,
14
14
  ) -> dict | None:
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(*)"
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
21
27
  )
22
- .eq("name", name)
23
- .eq("account.handle", owner)
24
- .eq("storage.is_default", True)
25
- .execute()
26
- .data
27
- )
28
+ except Exception:
29
+ return None
28
30
  if len(data) == 0:
29
31
  return None
30
32
  result = data[0]
31
- # this is a list
33
+ # this is now a list
32
34
  # assume only one default storage
33
35
  result["storage"] = result["storage"][0]
34
36
  return result
@@ -87,22 +89,15 @@ def select_instance_by_id_with_storage(
87
89
  instance_id: str,
88
90
  client: Client,
89
91
  ):
90
- # this won't find an instance without the default storage
91
- data = (
92
+ response = (
92
93
  client.table("instance")
93
- .select("*, storage!inner!storage_instance_id_359fca71_fk_instance_id(*)")
94
+ .select("*, storage!instance_storage_id_87963cc8_fk_storage_id(*)")
94
95
  .eq("id", instance_id)
95
- .eq("storage.is_default", True)
96
96
  .execute()
97
- .data
98
97
  )
99
- if len(data) == 0:
98
+ if len(response.data) == 0:
100
99
  return None
101
- result = data[0]
102
- # this is a list
103
- # assume only one default storage
104
- result["storage"] = result["storage"][0]
105
- return result
100
+ return response.data[0]
106
101
 
107
102
 
108
103
  def update_instance(instance_id: str, instance_fields: dict, client: Client):
@@ -200,14 +200,9 @@ class SetupPaths:
200
200
  # cache_key is ignored if filepath is a local path
201
201
  if not isinstance(filepath, LocalPathClasses):
202
202
  # settings is defined further in this file
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
203
+ local_filepath = settings.cache_dir / (
204
+ filepath.path if cache_key is None else cache_key # type: ignore
205
+ )
211
206
  else:
212
207
  local_filepath = filepath
213
208
  return local_filepath
@@ -7,7 +7,6 @@ import string
7
7
  from pathlib import Path
8
8
  from typing import TYPE_CHECKING, Any, Literal
9
9
 
10
- import fsspec
11
10
  from lamin_utils import logger
12
11
 
13
12
  from ._aws_credentials import HOSTED_REGIONS, get_aws_credentials_manager
@@ -25,10 +24,6 @@ if TYPE_CHECKING:
25
24
 
26
25
  IS_INITIALIZED_KEY = ".lamindb/_is_initialized"
27
26
 
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
-
32
27
 
33
28
  def base62(n_char: int) -> str:
34
29
  """Like nanoid without hyphen and underscore."""
@@ -119,11 +114,16 @@ def init_storage(
119
114
  root_str = f"s3://lamin-{region}/{uid}"
120
115
  else:
121
116
  root_str = f"s3://lamin-hosted-test/{uid}"
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
- )
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
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 the current instance."""
230
+ """Storage record in 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,15 +299,14 @@ class StorageSettings:
299
299
  return self._region
300
300
 
301
301
  @property
302
- def type(self) -> Literal["local", "s3", "gs", "hf", "http", "https"]:
302
+ def type(self) -> Literal["local", "s3", "gs"]:
303
303
  """AWS S3 vs. Google Cloud vs. local.
304
304
 
305
- Returns the protocol as a string: "local", "s3", "gs", "http", "https".
305
+ Returns the protocol as a string: "local", "s3", "gs".
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
311
310
  protocol = fsspec.utils.get_protocol(self.root_as_str)
312
311
  return convert.get(protocol, protocol) # type: ignore
313
312
 
@@ -346,5 +345,5 @@ class StorageSettings:
346
345
  return self.root / filekey
347
346
 
348
347
  def local_filepath(self, filekey: UPathStr) -> UPath:
349
- """Local (cache) filepath from filekey."""
348
+ """Local (cache) filepath from filekey: `local(filepath(...))`."""
350
349
  return self.cloud_to_local(self.key_to_filepath(filekey))
@@ -190,13 +190,7 @@ class ProgressCallback(fsspec.callbacks.Callback):
190
190
  pass
191
191
 
192
192
  def update_relative_value(self, inc=1):
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
193
+ self.value += inc
200
194
  self.call()
201
195
 
202
196
  def branch(self, path_1, path_2, kwargs):
@@ -356,19 +350,27 @@ def synchronize(
356
350
  exists = True
357
351
  cloud_mts = timestamp
358
352
  else:
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
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
372
374
 
373
375
  if not exists:
374
376
  warn_or_error = f"The original path {self} does not exist anymore."
@@ -384,7 +386,6 @@ def synchronize(
384
386
  return None
385
387
 
386
388
  # synchronization logic for directories
387
- # to synchronize directories, it should be possible to get modification times
388
389
  if is_dir:
389
390
  files = self.fs.find(str(self), detail=True)
390
391
  if protocol == "s3":
@@ -450,16 +451,8 @@ def synchronize(
450
451
  callback, print_progress, objectpath.name, "synchronizing"
451
452
  )
452
453
  if objectpath.exists():
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
454
+ local_mts_obj = objectpath.stat().st_mtime # type: ignore
455
+ need_synchronize = cloud_mts > local_mts_obj
463
456
  else:
464
457
  objectpath.parent.mkdir(parents=True, exist_ok=True)
465
458
  need_synchronize = True
@@ -471,8 +464,7 @@ def synchronize(
471
464
  self.download_to(
472
465
  objectpath, recursive=False, print_progress=False, callback=callback
473
466
  )
474
- if cloud_mts != 0:
475
- os.utime(objectpath, times=(cloud_mts, cloud_mts))
467
+ os.utime(objectpath, times=(cloud_mts, cloud_mts))
476
468
  else:
477
469
  # nothing happens if parent_update is not defined
478
470
  # because of Callback.no_op
@@ -747,9 +739,7 @@ def get_stat_file_cloud(stat: dict) -> tuple[int, str | None, str | None]:
747
739
  hash = b16_to_b64(stat["blob_id"])
748
740
  hash_type = "sha1"
749
741
  # s3
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:
742
+ elif "ETag" in stat:
753
743
  etag = stat["ETag"]
754
744
  # small files
755
745
  if "-" not in etag:
@@ -360,9 +360,3 @@ def test_init_storage_with_non_existing_bucket(create_testadmin1_session):
360
360
  )[0]
361
361
  )
362
362
  assert error.exconly().endswith("Not Found")
363
-
364
-
365
- def test_init_storage_incorrect_protocol():
366
- with pytest.raises(ValueError) as error:
367
- init_storage_base("incorrect-protocol://some-path/some-path-level")
368
- assert "Protocol incorrect-protocol is not supported" in error.exconly()
File without changes
File without changes