lamindb_setup 1.5.2__tar.gz → 1.6.0__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 (103) hide show
  1. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/.github/workflows/build.yml +0 -8
  2. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/PKG-INFO +1 -1
  3. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/08-test-multi-session.ipynb +45 -4
  4. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/__init__.py +1 -1
  5. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_connect_instance.py +19 -14
  6. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_init_instance.py +20 -2
  7. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_schema_metadata.py +6 -6
  8. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_hub_core.py +23 -0
  9. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings.py +11 -1
  10. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_store.py +6 -1
  11. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/django.py +44 -2
  12. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_connect_instance.py +7 -16
  13. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-local/test_all.py +16 -6
  14. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/.github/workflows/doc-changes.yml +0 -0
  15. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/.gitignore +0 -0
  16. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/.pre-commit-config.yaml +0 -0
  17. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/LICENSE +0 -0
  18. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/README.md +0 -0
  19. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/changelog.md +0 -0
  20. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  21. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
  22. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  23. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  24. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  25. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  26. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  27. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-cloud/test_notebooks.py +0 -0
  28. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-cache-management.ipynb +0 -0
  29. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
  30. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  31. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-empty-init.ipynb +0 -0
  32. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-import-schema.ipynb +0 -0
  33. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  34. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
  35. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  36. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  37. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/hub-prod/test_notebooks2.py +0 -0
  38. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/index.md +0 -0
  39. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/notebooks.md +0 -0
  40. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/docs/reference.md +0 -0
  41. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_cache.py +0 -0
  42. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_check.py +0 -0
  43. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_check_setup.py +0 -0
  44. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_delete.py +0 -0
  45. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_disconnect.py +0 -0
  46. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_django.py +0 -0
  47. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_entry_points.py +0 -0
  48. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_exportdb.py +0 -0
  49. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_importdb.py +0 -0
  50. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_migrate.py +0 -0
  51. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_register_instance.py +0 -0
  52. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_schema.py +0 -0
  53. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_set_managed_storage.py +0 -0
  54. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_setup_user.py +0 -0
  55. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/_silence_loggers.py +0 -0
  56. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/__init__.py +0 -0
  57. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_aws_options.py +0 -0
  58. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_aws_storage.py +0 -0
  59. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_deprecated.py +0 -0
  60. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_docs.py +0 -0
  61. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_hub_client.py +0 -0
  62. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_hub_crud.py +0 -0
  63. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_hub_utils.py +0 -0
  64. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_private_django_api.py +0 -0
  65. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_instance.py +0 -0
  66. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_load.py +0 -0
  67. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_save.py +0 -0
  68. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_storage.py +0 -0
  69. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_settings_user.py +0 -0
  70. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  71. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  72. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/exceptions.py +0 -0
  73. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/hashing.py +0 -0
  74. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/types.py +0 -0
  75. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/lamindb_setup/core/upath.py +0 -0
  76. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/noxfile.py +0 -0
  77. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/pyproject.toml +0 -0
  78. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  79. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  80. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_delete_instance.py +0 -0
  81. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_edge_request.py +0 -0
  82. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  83. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_init_instance.py +0 -0
  84. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  85. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_login.py +0 -0
  86. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_migrate.py +0 -0
  87. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-cloud/test_set_storage.py +0 -0
  88. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-local/conftest.py +0 -0
  89. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-local/scripts/script-connect-fine-grained-access.py +0 -0
  90. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-local/test_update_schema_in_hub.py +0 -0
  91. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/conftest.py +0 -0
  92. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/test_aws_options_manager.py +0 -0
  93. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/test_django.py +0 -0
  94. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/test_global_settings.py +0 -0
  95. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
  96. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/hub-prod/test_upath.py +0 -0
  97. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_entry_point.py +0 -0
  98. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_hashing.py +0 -0
  99. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_storage_access.py +0 -0
  100. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_storage_basis.py +0 -0
  101. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_storage_settings.py +0 -0
  102. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_storage_stats.py +0 -0
  103. {lamindb_setup-1.5.2 → lamindb_setup-1.6.0}/tests/storage/test_to_url.py +0 -0
@@ -231,14 +231,6 @@ jobs:
231
231
  needs: [hub-local, hub-cloud]
232
232
  runs-on: ubuntu-latest
233
233
  steps:
234
- - uses: voxmedia/github-action-slack-notify-build@v1
235
- if: ${{ needs.hub-local.result == 'success' && needs.hub-cloud.result == 'success' && github.event_name == 'repository_dispatch' }}
236
- env:
237
- SLACK_BOT_TOKEN: ${{ secrets.SLACK_GITHUB_ACTION }}
238
- with:
239
- channel_id: C05S2C02JHM
240
- status: SUCCESS
241
- color: good
242
234
  - uses: voxmedia/github-action-slack-notify-build@v1
243
235
  if: ${{ ( needs.hub-local.result == 'failure' || needs.hub-cloud.result == 'failure' ) && github.event_name == 'repository_dispatch' }}
244
236
  env:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lamindb_setup
3
- Version: 1.5.2
3
+ Version: 1.6.0
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.10
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "cells": [
3
3
  {
4
- "attachments": {},
5
4
  "cell_type": "markdown",
6
5
  "metadata": {},
7
6
  "source": [
@@ -28,6 +27,15 @@
28
27
  "import pytest"
29
28
  ]
30
29
  },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": null,
33
+ "metadata": {},
34
+ "outputs": [],
35
+ "source": [
36
+ "ln_setup.core.django.reset_django() # django is not configured, so no effect"
37
+ ]
38
+ },
31
39
  {
32
40
  "cell_type": "code",
33
41
  "execution_count": null,
@@ -100,7 +108,6 @@
100
108
  ]
101
109
  },
102
110
  {
103
- "attachments": {},
104
111
  "cell_type": "markdown",
105
112
  "metadata": {},
106
113
  "source": [
@@ -127,6 +134,40 @@
127
134
  "assert ln_setup.settings.instance.slug == \"testuser1/testsetup\""
128
135
  ]
129
136
  },
137
+ {
138
+ "cell_type": "markdown",
139
+ "metadata": {},
140
+ "source": [
141
+ "Reset `django` and connect to another instance:"
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "code",
146
+ "execution_count": null,
147
+ "metadata": {},
148
+ "outputs": [],
149
+ "source": [
150
+ "ln_setup.core.django.reset_django()"
151
+ ]
152
+ },
153
+ {
154
+ "cell_type": "code",
155
+ "execution_count": null,
156
+ "metadata": {},
157
+ "outputs": [],
158
+ "source": [
159
+ "ln_setup.connect(\"testuser1/testsetup-prepare\")"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": null,
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": [
168
+ "assert ln_setup.settings.instance.slug == \"testuser1/testsetup-prepare\""
169
+ ]
170
+ },
130
171
  {
131
172
  "cell_type": "code",
132
173
  "execution_count": null,
@@ -141,7 +182,7 @@
141
182
  ],
142
183
  "metadata": {
143
184
  "kernelspec": {
144
- "display_name": "py39",
185
+ "display_name": "Python 3 (ipykernel)",
145
186
  "language": "python",
146
187
  "name": "python3"
147
188
  },
@@ -155,7 +196,7 @@
155
196
  "name": "python",
156
197
  "nbconvert_exporter": "python",
157
198
  "pygments_lexer": "ipython3",
158
- "version": "3.10.13"
199
+ "version": "3.10.16"
159
200
  }
160
201
  },
161
202
  "nbformat": 4,
@@ -33,7 +33,7 @@ Modules & settings:
33
33
 
34
34
  """
35
35
 
36
- __version__ = "1.5.2" # denote a release candidate for 0.1.0 with 0.1rc1
36
+ __version__ = "1.6.0" # denote a release candidate for 0.1.0 with 0.1rc1
37
37
 
38
38
  import os
39
39
 
@@ -67,10 +67,12 @@ def update_db_using_local(
67
67
  db_updated = None
68
68
  # check if postgres
69
69
  if hub_instance_result["db_scheme"] == "postgresql":
70
- db_dsn_hub = LaminDsnModel(db=hub_instance_result["db"])
71
70
  if db is not None:
72
- db_dsn_local = LaminDsnModel(db=db)
71
+ # use only the provided db if it is set
72
+ db_dsn_hub = LaminDsnModel(db=db)
73
+ db_dsn_local = db_dsn_hub
73
74
  else:
75
+ db_dsn_hub = LaminDsnModel(db=hub_instance_result["db"])
74
76
  # read directly from the environment
75
77
  if os.getenv("LAMINDB_INSTANCE_DB") is not None:
76
78
  logger.important("loading db URL from env variable LAMINDB_INSTANCE_DB")
@@ -78,16 +80,15 @@ def update_db_using_local(
78
80
  # read from a cached settings file in case the hub result is only
79
81
  # read level or inexistent
80
82
  elif settings_file.exists() and (
81
- db_dsn_hub.db.user is None
82
- or (db_dsn_hub.db.user is not None and "read" in db_dsn_hub.db.user)
83
+ db_dsn_hub.db.user in {None, "none"} or "read" in db_dsn_hub.db.user # type:ignore
83
84
  ):
84
85
  isettings = load_instance_settings(settings_file)
85
86
  db_dsn_local = LaminDsnModel(db=isettings.db)
86
87
  else:
87
88
  # just take the default hub result and ensure there is actually a user
88
89
  if (
89
- db_dsn_hub.db.user == "none"
90
- and db_dsn_hub.db.password == "none"
90
+ db_dsn_hub.db.user in {None, "none"}
91
+ and db_dsn_hub.db.password in {None, "none"}
91
92
  and raise_permission_error
92
93
  ):
93
94
  raise PermissionError(
@@ -95,13 +96,13 @@ def update_db_using_local(
95
96
  " a DB URL and pass it via --db <db_url>"
96
97
  )
97
98
  db_dsn_local = db_dsn_hub
98
- if not check_db_dsn_equal_up_to_credentials(db_dsn_hub.db, db_dsn_local.db):
99
- raise ValueError(
100
- "The local differs from the hub database information:\n 1. did you"
101
- " pass a wrong db URL with --db?\n 2. did your database get updated by"
102
- " an admin?\nConsider deleting your cached database environment:\nrm"
103
- f" {settings_file.as_posix()}"
104
- )
99
+ if not check_db_dsn_equal_up_to_credentials(db_dsn_hub.db, db_dsn_local.db):
100
+ raise ValueError(
101
+ "The local differs from the hub database information:\n"
102
+ "did your database get updated by an admin?\n"
103
+ "Consider deleting your cached database environment:\nrm"
104
+ f" {settings_file.as_posix()}"
105
+ )
105
106
  db_updated = LaminDsn.build(
106
107
  scheme=db_dsn_hub.db.scheme,
107
108
  user=db_dsn_local.db.user,
@@ -345,11 +346,15 @@ def migrate_lnschema_core(
345
346
  if db_type == "sqlite":
346
347
  import sqlite3
347
348
 
349
+ # maybe also use LAMINDB_DJANGO_DATABASE_URL here?
348
350
  conn = sqlite3.connect(parsed_uri.path)
349
351
  elif db_type in ["postgresql", "postgres"]:
350
352
  import psycopg2
351
353
 
352
- conn = psycopg2.connect(isettings.db)
354
+ # do not ignore LAMINDB_DJANGO_DATABASE_URL if it is set
355
+ conn = psycopg2.connect(
356
+ os.environ.get("LAMINDB_DJANGO_DATABASE_URL", isettings.db)
357
+ )
353
358
  else:
354
359
  raise ValueError("Unsupported database type. Use 'sqlite' or 'postgresql' URI.")
355
360
 
@@ -98,13 +98,31 @@ def register_user(usettings):
98
98
  def register_initial_records(isettings: InstanceSettings, usettings):
99
99
  """Register space, user & storage in DB."""
100
100
  from django.db.utils import OperationalError
101
- from lamindb.models import Space
101
+ from lamindb.models import Branch, Space
102
102
 
103
103
  try:
104
104
  Space.objects.get_or_create(
105
+ uid="A",
105
106
  name="All",
106
107
  description="Every team & user with access to the instance has access.",
107
108
  )
109
+ Branch.objects.get_or_create(
110
+ id=-1,
111
+ uid="T",
112
+ name="Trash",
113
+ description="The trash.",
114
+ )
115
+ Branch.objects.get_or_create(
116
+ id=0,
117
+ uid="A",
118
+ name="Archive",
119
+ description="The archive.",
120
+ )
121
+ Branch.objects.get_or_create(
122
+ uid="M",
123
+ name="Main",
124
+ description="The main & default branch of the instance.",
125
+ )
108
126
  register_user(usettings)
109
127
  register_storage_in_instance(isettings.storage)
110
128
  except OperationalError as error:
@@ -206,7 +224,7 @@ class CannotSwitchDefaultInstance(SystemExit):
206
224
  MESSAGE_CANNOT_SWITCH_DEFAULT_INSTANCE = """
207
225
  You cannot write to different instances in the same Python session.
208
226
 
209
- Do you want to read from another instance via `Record.using()`? For example:
227
+ Do you want to read from another instance via `SQLRecord.using()`? For example:
210
228
 
211
229
  ln.Artifact.using("laminlabs/cellxgene").filter()
212
230
 
@@ -159,7 +159,7 @@ class FieldMetadata(BaseModel):
159
159
 
160
160
  class _ModelHandler:
161
161
  def __init__(self, model, module_name: str, included_modules: list[str]) -> None:
162
- from lamindb.models import LinkORM
162
+ from lamindb.models import IsLink
163
163
 
164
164
  self.model = model
165
165
  self.class_name = model.__name__
@@ -168,7 +168,7 @@ class _ModelHandler:
168
168
  self.table_name = model._meta.db_table
169
169
  self.included_modules = included_modules
170
170
  self.fields = self._get_fields_metadata(self.model)
171
- self.is_link_table = issubclass(model, LinkORM)
171
+ self.is_link_table = issubclass(model, IsLink)
172
172
  self.name_field = model._name_field if hasattr(model, "_name_field") else None
173
173
  self.ontology_id_field = (
174
174
  model._ontology_id_field if hasattr(model, "_ontology_id_field") else None
@@ -245,7 +245,7 @@ class _ModelHandler:
245
245
  return related_fields
246
246
 
247
247
  def _get_field_metadata(self, model, field: Field):
248
- from lamindb.models import LinkORM
248
+ from lamindb.models import IsLink
249
249
 
250
250
  internal_type = field.get_internal_type()
251
251
  model_name = field.model._meta.model_name
@@ -296,7 +296,7 @@ class _ModelHandler:
296
296
  model_name=model_name,
297
297
  field_name=field_name,
298
298
  type=internal_type,
299
- is_link_table=issubclass(field.model, LinkORM),
299
+ is_link_table=issubclass(field.model, IsLink),
300
300
  is_primary_key=is_primary_key,
301
301
  is_editable=is_editable,
302
302
  max_length=max_length,
@@ -400,7 +400,7 @@ class _SchemaHandler:
400
400
  return self.to_dict(include_django_objects=False)
401
401
 
402
402
  def _get_modules_metadata(self):
403
- from lamindb.models import Record, Registry
403
+ from lamindb.models import Registry, SQLRecord
404
404
 
405
405
  all_models = {
406
406
  module_name: {
@@ -411,7 +411,7 @@ class _SchemaHandler:
411
411
  module_name
412
412
  ).models.__dict__.values()
413
413
  if model.__class__ is Registry
414
- and model is not Record
414
+ and model is not SQLRecord
415
415
  and not model._meta.abstract
416
416
  and model.__get_module_name__() == module_name
417
417
  }
@@ -36,6 +36,7 @@ from ._hub_utils import (
36
36
  from ._settings import settings
37
37
  from ._settings_instance import InstanceSettings
38
38
  from ._settings_storage import StorageSettings, base62
39
+ from .hashing import hash_and_encode_as_b62
39
40
 
40
41
  if TYPE_CHECKING:
41
42
  from supabase import Client # type: ignore
@@ -116,6 +117,28 @@ def _select_storage(
116
117
  return True
117
118
 
118
119
 
120
+ def _select_storage_or_parent(path: str, client: Client) -> dict | None:
121
+ result = client.rpc("existing_root_or_child", {"_path": path}).execute().data
122
+ if result["root"] is None:
123
+ return None
124
+ result["uid"] = result.pop("lnid")
125
+ result["instance_uid"] = hash_and_encode_as_b62(
126
+ UUID(result.pop("instance_id")).hex
127
+ )[:12]
128
+ return result
129
+
130
+
131
+ def select_storage_or_parent(path: str, access_token: str | None = None) -> dict | None:
132
+ if settings.user.handle != "anonymous" or access_token is not None:
133
+ return call_with_fallback_auth(
134
+ _select_storage_or_parent,
135
+ path=path,
136
+ access_token=access_token,
137
+ )
138
+ else:
139
+ return call_with_fallback(_select_storage_or_parent, path=path)
140
+
141
+
119
142
  def init_storage_hub(
120
143
  ssettings: StorageSettings,
121
144
  auto_populate_instance: bool = True,
@@ -5,6 +5,7 @@ import sys
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  from appdirs import AppDirs
8
+ from lamin_utils import logger
8
9
 
9
10
  from ._settings_load import (
10
11
  load_instance_settings,
@@ -162,7 +163,16 @@ class SetupSettings:
162
163
  self._cache_dir = cache_dir
163
164
  else:
164
165
  cache_dir = self._cache_dir
165
- cache_dir.mkdir(parents=True, exist_ok=True)
166
+ try:
167
+ cache_dir.mkdir(parents=True, exist_ok=True)
168
+ # we don not want this to error
169
+ # beause no actual writing happens on just getting the cache dir
170
+ # in cloud_to_local_no_update for example
171
+ # so it should not fail on read-only systems
172
+ except Exception as e:
173
+ logger.warning(
174
+ f"Failed to create lamin cache directory at {cache_dir}: {e}"
175
+ )
166
176
  return cache_dir
167
177
 
168
178
  @property
@@ -2,6 +2,7 @@ import os
2
2
  from pathlib import Path
3
3
  from typing import Optional
4
4
 
5
+ from lamin_utils import logger
5
6
  from pydantic_settings import BaseSettings, SettingsConfigDict
6
7
 
7
8
  if "LAMIN_SETTINGS_DIR" in os.environ:
@@ -12,7 +13,11 @@ else:
12
13
  # hence, let's take home/.lamin
13
14
  settings_dir = Path.home() / ".lamin"
14
15
 
15
- settings_dir.mkdir(parents=True, exist_ok=True)
16
+
17
+ try:
18
+ settings_dir.mkdir(parents=True, exist_ok=True)
19
+ except Exception as e:
20
+ logger.warning(f"Failed to create lamin settings directory at {settings_dir}: {e}")
16
21
 
17
22
 
18
23
  def get_settings_file_name_prefix():
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  # flake8: noqa
4
4
  import builtins
5
5
  import os
6
+ import sys
7
+ import importlib as il
6
8
  import jwt
7
9
  import time
8
10
  from pathlib import Path
@@ -170,8 +172,7 @@ def setup_django(
170
172
 
171
173
  module_names = ["core"] + list(isettings.modules)
172
174
  raise_import_error = True if init else False
173
- installed_apps = ["django.contrib.contenttypes"]
174
- installed_apps += [
175
+ installed_apps = [
175
176
  package_name
176
177
  for name in module_names
177
178
  if (
@@ -242,3 +243,44 @@ def setup_django(
242
243
 
243
244
  if isettings.keep_artifacts_local:
244
245
  isettings._search_local_root()
246
+
247
+
248
+ # THIS IS NOT SAFE
249
+ # especially if lamindb is imported already
250
+ # django.setup fails if called for the second time
251
+ # reset_django() allows to call setup again,
252
+ # needed to connect to a different instance in the same process if connected already
253
+ # there could be problems if models are already imported from lamindb or other modules
254
+ # these 'old' models can have any number of problems
255
+ def reset_django():
256
+ from django.conf import settings
257
+ from django.apps import apps
258
+ from django.db import connections
259
+
260
+ if not settings.configured:
261
+ return
262
+
263
+ connections.close_all()
264
+
265
+ if getattr(settings, "_wrapped", None) is not None:
266
+ settings._wrapped = None
267
+
268
+ app_names = {"django"} | {app.name for app in apps.get_app_configs()}
269
+
270
+ apps.app_configs.clear()
271
+ apps.apps_ready = apps.models_ready = apps.ready = apps.loading = False
272
+ apps.clear_cache()
273
+
274
+ # i suspect it is enough to just drop django and all the apps from sys.modules
275
+ # the code above is just a precaution
276
+ for module_name in list(sys.modules):
277
+ if module_name.partition(".")[0] in app_names:
278
+ del sys.modules[module_name]
279
+
280
+ il.invalidate_caches()
281
+
282
+ global db_token_manager
283
+ db_token_manager = DBTokenManager()
284
+
285
+ global IS_SETUP
286
+ IS_SETUP = False
@@ -117,31 +117,22 @@ def test_connect_with_db_parameter():
117
117
  ln_setup.login("testuser1")
118
118
  # test load from hub
119
119
  ln_setup.connect("laminlabs/lamindata", _test=True)
120
- assert "root" in ln_setup.settings.instance.db
120
+ # this test assumes fine-grained access
121
+ assert ln_setup.settings.instance._db_permissions == "jwt"
122
+ assert "jwt" in ln_setup.settings.instance.db
121
123
  # test load from provided db argument
122
124
  db = "postgresql://testdbuser:testpwd@database2.cmyfs24wugc3.us-east-1.rds.amazonaws.com:5432/db1"
123
125
  ln_setup.connect("laminlabs/lamindata", _db=db, _test=True)
124
126
  assert "testdbuser" in ln_setup.settings.instance.db
125
- # test ignore loading from cache because hub result has >read access
127
+ # test ignore loading from cache because hub result has jwt access
126
128
  ln_setup.connect("laminlabs/lamindata", _test=True)
127
- assert "root" in ln_setup.settings.instance.db
129
+ assert "jwt" in ln_setup.settings.instance.db
128
130
 
129
131
  # now take a user that has no collaborator status
130
132
  ln_setup.login("testuser2")
131
- # the cached high priviledge connection string remains active
133
+ # receives public connection
132
134
  ln_setup.connect("laminlabs/lamindata", _test=True)
133
- assert "root" in ln_setup.settings.instance.db
135
+ assert "public" in ln_setup.settings.instance.db
134
136
  # now pass the connection string
135
137
  ln_setup.connect("laminlabs/lamindata", _db=db, _test=True)
136
138
  assert "testdbuser" in ln_setup.settings.instance.db
137
- # now the cache is used
138
- ln_setup.connect("laminlabs/lamindata", _test=True)
139
- assert "testdbuser" in ln_setup.settings.instance.db
140
-
141
- # test corrupted input
142
- db_corrupted = "postgresql://testuser:testpwd@wrongserver:5432/db1"
143
- with pytest.raises(ValueError) as error:
144
- ln_setup.connect("laminlabs/lamindata", _db=db_corrupted, _test=True)
145
- assert error.exconly().startswith(
146
- "ValueError: The local differs from the hub database information"
147
- )
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import subprocess
5
+ from unittest.mock import patch
5
6
  from uuid import UUID, uuid4
6
7
 
7
8
  import lamindb_setup as ln_setup
@@ -17,6 +18,7 @@ from lamindb_setup.core._hub_core import (
17
18
  connect_instance_hub,
18
19
  init_instance_hub,
19
20
  init_storage_hub,
21
+ select_storage_or_parent,
20
22
  sign_in_hub,
21
23
  sign_up_local_hub,
22
24
  )
@@ -39,7 +41,6 @@ from lamindb_setup.core._settings_storage import init_storage as init_storage_ba
39
41
  from lamindb_setup.core._settings_store import instance_settings_file
40
42
  from lamindb_setup.core._settings_user import UserSettings
41
43
  from laminhub_rest.core.legacy._instance_collaborator import InstanceCollaboratorHandler
42
- from laminhub_rest.core.organization import OrganizationHandler
43
44
  from laminhub_rest.test.instance.utils import (
44
45
  create_hosted_test_instance,
45
46
  delete_hosted_test_instance,
@@ -168,11 +169,6 @@ def create_myinstance(create_testadmin1_session): # -> Dict
168
169
 
169
170
  @pytest.fixture(scope="session")
170
171
  def create_instance_fine_grained_access(create_testadmin1_session):
171
- client, testadmin1 = create_testadmin1_session
172
-
173
- org_handler = OrganizationHandler(client)
174
- org_handler.create(testadmin1._uuid)
175
-
176
172
  instance = create_hosted_test_instance("instance_access_v2", access_v2=True)
177
173
  yield instance
178
174
  delete_hosted_test_instance(instance)
@@ -390,6 +386,20 @@ def test_init_storage_incorrect_protocol():
390
386
  assert "Protocol incorrect-protocol is not supported" in error.exconly()
391
387
 
392
388
 
389
+ def test_select_storage_or_parent(create_myinstance):
390
+ # check not exisitng
391
+ assert select_storage_or_parent("s3://does-not-exist") is None
392
+
393
+ root = "s3://lamindb-ci/myinstance"
394
+
395
+ result = select_storage_or_parent(root)
396
+ assert result["root"] == root
397
+ # check with a child path and anonymous user
398
+ with patch.object(ln_setup.settings.user, "handle", new="anonymous"):
399
+ result = select_storage_or_parent(root + "/subfolder")
400
+ assert result["root"] == root
401
+
402
+
393
403
  def test_fine_grained_access(
394
404
  create_testadmin1_session, create_instance_fine_grained_access
395
405
  ):
File without changes
File without changes
File without changes
File without changes