lamindb 1.10.1__tar.gz → 1.10.2__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 (288) hide show
  1. {lamindb-1.10.1 → lamindb-1.10.2}/PKG-INFO +3 -2
  2. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/__init__.py +1 -1
  3. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_settings.py +45 -2
  4. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_anndata_accessor.py +90 -18
  5. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_backed_access.py +10 -7
  6. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_spatialdata_accessor.py +15 -4
  7. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_zarr.py +3 -0
  8. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/curators/core.py +7 -5
  9. lamindb-1.10.2/lamindb/migrations/0118_alter_recordproject_value_projectrecord.py +99 -0
  10. lamindb-1.10.2/lamindb/migrations/0119_rename_records_project_linked_in_records.py +26 -0
  11. lamindb-1.10.1/lamindb/migrations/0117_squashed.py → lamindb-1.10.2/lamindb/migrations/0119_squashed.py +87 -3
  12. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/artifact.py +31 -20
  13. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/project.py +24 -2
  14. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_curators_general.py +19 -0
  15. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/conftest.py +3 -4
  16. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_artifact_storage.py +6 -9
  17. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_storage_lifecycle.py +1 -0
  18. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_streaming.py +45 -1
  19. {lamindb-1.10.1 → lamindb-1.10.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  20. {lamindb-1.10.1 → lamindb-1.10.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  21. {lamindb-1.10.1 → lamindb-1.10.2}/.github/ISSUE_TEMPLATE/enhancement.yml +0 -0
  22. {lamindb-1.10.1 → lamindb-1.10.2}/.github/ISSUE_TEMPLATE/usage_question.yml +0 -0
  23. {lamindb-1.10.1 → lamindb-1.10.2}/.github/workflows/build.yml +0 -0
  24. {lamindb-1.10.1 → lamindb-1.10.2}/.github/workflows/doc-changes.yml +0 -0
  25. {lamindb-1.10.1 → lamindb-1.10.2}/.gitignore +0 -0
  26. {lamindb-1.10.1 → lamindb-1.10.2}/.gitmodules +0 -0
  27. {lamindb-1.10.1 → lamindb-1.10.2}/.pre-commit-config.yaml +0 -0
  28. {lamindb-1.10.1 → lamindb-1.10.2}/CONTRIBUTING.md +0 -0
  29. {lamindb-1.10.1 → lamindb-1.10.2}/LICENSE +0 -0
  30. {lamindb-1.10.1 → lamindb-1.10.2}/README.md +0 -0
  31. {lamindb-1.10.1 → lamindb-1.10.2}/docs/api.md +0 -0
  32. {lamindb-1.10.1 → lamindb-1.10.2}/docs/arrays.ipynb +0 -0
  33. {lamindb-1.10.1 → lamindb-1.10.2}/docs/bio-registries.ipynb +0 -0
  34. {lamindb-1.10.1 → lamindb-1.10.2}/docs/bionty.md +0 -0
  35. {lamindb-1.10.1 → lamindb-1.10.2}/docs/changelog.md +0 -0
  36. {lamindb-1.10.1 → lamindb-1.10.2}/docs/clinicore.md +0 -0
  37. {lamindb-1.10.1 → lamindb-1.10.2}/docs/curate.ipynb +0 -0
  38. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/acid.ipynb +0 -0
  39. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/curate-any.ipynb +0 -0
  40. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/delete.ipynb +0 -0
  41. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/idempotency.ipynb +0 -0
  42. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/import-modules.ipynb +0 -0
  43. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/keep-artifacts-local.ipynb +0 -0
  44. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/pydantic-pandera.ipynb +0 -0
  45. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/reference-field.ipynb +0 -0
  46. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/search.ipynb +0 -0
  47. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/setup.ipynb +0 -0
  48. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/symbol-mapping.ipynb +0 -0
  49. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/test_notebooks.py +0 -0
  50. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/track-run-inputs.ipynb +0 -0
  51. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/validate-fields.ipynb +0 -0
  52. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq/visibility.ipynb +0 -0
  53. {lamindb-1.10.1 → lamindb-1.10.2}/docs/faq.md +0 -0
  54. {lamindb-1.10.1 → lamindb-1.10.2}/docs/guide.md +0 -0
  55. {lamindb-1.10.1 → lamindb-1.10.2}/docs/includes/installation.md +0 -0
  56. {lamindb-1.10.1 → lamindb-1.10.2}/docs/index.md +0 -0
  57. {lamindb-1.10.1 → lamindb-1.10.2}/docs/lamindb.md +0 -0
  58. {lamindb-1.10.1 → lamindb-1.10.2}/docs/query-search.md +0 -0
  59. {lamindb-1.10.1 → lamindb-1.10.2}/docs/registries.ipynb +0 -0
  60. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_anndata_flexible.py +0 -0
  61. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_dataframe_flexible.py +0 -0
  62. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_dataframe_minimal_errors.py +0 -0
  63. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_mudata.py +0 -0
  64. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_soma_experiment.py +0 -0
  65. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/curate_spatialdata.py +0 -0
  66. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/define_mini_immuno_features_labels.py +0 -0
  67. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/define_mini_immuno_schema_flexible.py +0 -0
  68. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/define_schema_anndata_ensembl_gene_ids_and_valid_features_in_obs.py +0 -0
  69. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/define_schema_spatialdata.py +0 -0
  70. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/define_valid_features.py +0 -0
  71. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/run_track_and_finish.py +0 -0
  72. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/run_track_with_params.py +0 -0
  73. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/run_workflow.py +0 -0
  74. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/save_mini_immuno_datasets.py +0 -0
  75. {lamindb-1.10.1 → lamindb-1.10.2}/docs/scripts/synced_with_git.py +0 -0
  76. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/add-replace-cache.ipynb +0 -0
  77. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/anndata-accessor.ipynb +0 -0
  78. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/prepare-transfer-local-to-cloud.ipynb +0 -0
  79. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/test-files/iris.csv +0 -0
  80. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/test-files/iris.data +0 -0
  81. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/test-files/new_iris.csv +0 -0
  82. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/test_notebooks.py +0 -0
  83. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/transfer-local-to-cloud.ipynb +0 -0
  84. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/upload.ipynb +0 -0
  85. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage/vitessce.ipynb +0 -0
  86. {lamindb-1.10.1 → lamindb-1.10.2}/docs/storage.md +0 -0
  87. {lamindb-1.10.1 → lamindb-1.10.2}/docs/test_notebooks.py +0 -0
  88. {lamindb-1.10.1 → lamindb-1.10.2}/docs/track.ipynb +0 -0
  89. {lamindb-1.10.1 → lamindb-1.10.2}/docs/transfer.ipynb +0 -0
  90. {lamindb-1.10.1 → lamindb-1.10.2}/docs/wetlab.md +0 -0
  91. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/_finish.py +0 -0
  92. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/_tracked.py +0 -0
  93. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/_view.py +0 -0
  94. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/__init__.py +0 -0
  95. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/fields.py +0 -0
  96. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/ids.py +0 -0
  97. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/types.py +0 -0
  98. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/uids.py +0 -0
  99. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/base/users.py +0 -0
  100. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/__init__.py +0 -0
  101. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_compat.py +0 -0
  102. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_context.py +0 -0
  103. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_mapped_collection.py +0 -0
  104. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_sync_git.py +0 -0
  105. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/_track_environment.py +0 -0
  106. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/exceptions.py +0 -0
  107. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/loaders.py +0 -0
  108. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/__init__.py +0 -0
  109. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_polars_lazy_df.py +0 -0
  110. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_pyarrow_dataset.py +0 -0
  111. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_tiledbsoma.py +0 -0
  112. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/_valid_suffixes.py +0 -0
  113. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/objects.py +0 -0
  114. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/storage/paths.py +0 -0
  115. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/subsettings/__init__.py +0 -0
  116. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/subsettings/_annotation_settings.py +0 -0
  117. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/subsettings/_creation_settings.py +0 -0
  118. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/core/types.py +0 -0
  119. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/curators/__init__.py +0 -0
  120. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/curators/_legacy.py +0 -0
  121. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/errors.py +0 -0
  122. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/__init__.py +0 -0
  123. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/cellxgene/__init__.py +0 -0
  124. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/cellxgene/_cellxgene.py +0 -0
  125. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/cellxgene/cxg_schema_versions.csv +0 -0
  126. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/croissant/__init__.py +0 -0
  127. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/croissant/mini_immuno.anndata.zarr_metadata.json +0 -0
  128. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/datasets/__init__.py +0 -0
  129. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/datasets/_core.py +0 -0
  130. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/datasets/_fake.py +0 -0
  131. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/datasets/_small.py +0 -0
  132. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/datasets/mini_immuno.py +0 -0
  133. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/fixtures/__init__.py +0 -0
  134. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/fixtures/sheets.py +0 -0
  135. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/schemas/__init__.py +0 -0
  136. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/schemas/_anndata.py +0 -0
  137. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/examples/schemas/_simple.py +0 -0
  138. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/integrations/__init__.py +0 -0
  139. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/integrations/_croissant.py +0 -0
  140. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/integrations/_vitessce.py +0 -0
  141. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0069_squashed.py +0 -0
  142. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0070_lamindbv1_migrate_data.py +0 -0
  143. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0071_lamindbv1_migrate_schema.py +0 -0
  144. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0072_remove_user__branch_code_remove_user_aux_and_more.py +0 -0
  145. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0073_merge_ourprojects.py +0 -0
  146. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0074_lamindbv1_part4.py +0 -0
  147. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0075_lamindbv1_part5.py +0 -0
  148. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0076_lamindbv1_part6.py +0 -0
  149. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0077_lamindbv1_part6b.py +0 -0
  150. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0078_lamindbv1_part6c.py +0 -0
  151. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0079_alter_rundata_value_json_and_more.py +0 -0
  152. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0080_polish_lamindbv1.py +0 -0
  153. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0081_revert_textfield_collection.py +0 -0
  154. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0082_alter_feature_dtype.py +0 -0
  155. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0083_alter_feature_is_type_alter_flextable_is_type_and_more.py +0 -0
  156. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0084_alter_schemafeature_feature_and_more.py +0 -0
  157. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0085_alter_feature_is_type_alter_flextable_is_type_and_more.py +0 -0
  158. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0086_various.py +0 -0
  159. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0087_rename__schemas_m2m_artifact_feature_sets_and_more.py +0 -0
  160. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0088_schema_components.py +0 -0
  161. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0089_subsequent_runs.py +0 -0
  162. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0090_runproject_project_runs.py +0 -0
  163. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0091_alter_featurevalue_options_alter_space_options_and_more.py +0 -0
  164. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0092_alter_artifactfeaturevalue_artifact_and_more.py +0 -0
  165. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0093_alter_schemacomponent_unique_together.py +0 -0
  166. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0094_writeloglock_writelogmigrationstate_and_more.py +0 -0
  167. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0095_remove_rundata_flextable.py +0 -0
  168. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0096_remove_artifact__param_values_and_more.py +0 -0
  169. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0097_remove_schemaparam_param_remove_paramvalue_param_and_more.py +0 -0
  170. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0098_alter_feature_type_alter_project_type_and_more.py +0 -0
  171. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0099_alter_writelog_seqno.py +0 -0
  172. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0100_branch_alter_artifact__branch_code_and_more.py +0 -0
  173. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0101_alter_artifact_hash_alter_feature_name_and_more.py +0 -0
  174. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0102_remove_writelog_branch_code_and_more.py +0 -0
  175. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0103_remove_writelog_migration_state_and_more.py +0 -0
  176. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0104_alter_branch_uid.py +0 -0
  177. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0105_record_unique_name.py +0 -0
  178. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0106_transfer_data_migration.py +0 -0
  179. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0107_add_schema_to_record.py +0 -0
  180. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0108_remove_record_sheet_remove_sheetproject_sheet_and_more.py +0 -0
  181. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0109_record_input_of_runs_alter_record_run_and_more.py +0 -0
  182. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0110_rename_values_artifacts_record_linked_artifacts.py +0 -0
  183. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0111_remove_record__sort_order.py +0 -0
  184. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0112_alter_recordartifact_feature_and_more.py +0 -0
  185. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0113_lower_case_branch_and_space_names.py +0 -0
  186. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0114_alter_run__status_code.py +0 -0
  187. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0115_alter_space_uid.py +0 -0
  188. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0116_remove_artifact_unique_artifact_storage_key_hash_and_more.py +0 -0
  189. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/0117_fix_artifact_storage_hash_unique_constraints.py +0 -0
  190. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/migrations/__init__.py +0 -0
  191. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/__init__.py +0 -0
  192. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_describe.py +0 -0
  193. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_django.py +0 -0
  194. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_feature_manager.py +0 -0
  195. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_from_values.py +0 -0
  196. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_is_versioned.py +0 -0
  197. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_label_manager.py +0 -0
  198. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/_relations.py +0 -0
  199. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/artifact_set.py +0 -0
  200. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/can_curate.py +0 -0
  201. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/collection.py +0 -0
  202. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/feature.py +0 -0
  203. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/has_parents.py +0 -0
  204. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/query_manager.py +0 -0
  205. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/query_set.py +0 -0
  206. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/record.py +0 -0
  207. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/run.py +0 -0
  208. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/save.py +0 -0
  209. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/schema.py +0 -0
  210. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/sqlrecord.py +0 -0
  211. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/storage.py +0 -0
  212. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/transform.py +0 -0
  213. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/models/ulabel.py +0 -0
  214. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/py.typed +0 -0
  215. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/setup/__init__.py +0 -0
  216. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/setup/_switch.py +0 -0
  217. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/setup/core/__init__.py +0 -0
  218. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/setup/errors/__init__.py +0 -0
  219. {lamindb-1.10.1 → lamindb-1.10.2}/lamindb/setup/types/__init__.py +0 -0
  220. {lamindb-1.10.1 → lamindb-1.10.2}/noxfile.py +0 -0
  221. {lamindb-1.10.1 → lamindb-1.10.2}/pyproject.toml +0 -0
  222. {lamindb-1.10.1 → lamindb-1.10.2}/tests/conftest.py +0 -0
  223. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/_dataset_fixtures.py +0 -0
  224. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/conftest.py +0 -0
  225. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/basic-r-notebook.Rmd.cleaned.html +0 -0
  226. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/basic-r-notebook.Rmd.html +0 -0
  227. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/duplicate/with-title-initialized-consecutive-finish.ipynb +0 -0
  228. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/no-title.ipynb +0 -0
  229. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/with-title-initialized-consecutive-finish-not-last-cell.ipynb +0 -0
  230. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/notebooks/with-title-initialized-consecutive-finish.ipynb +0 -0
  231. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/duplicate1/script-to-test-versioning.py +0 -0
  232. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/duplicate2/script-to-test-versioning.py +0 -0
  233. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/duplicate3/script-to-test-versioning.py +0 -0
  234. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/duplicate4/script-to-test-versioning.py +0 -0
  235. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/duplicate5/script-to-test-versioning.py +0 -0
  236. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/script-to-test-filename-change.py +0 -0
  237. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/scripts/script-to-test-versioning.py +0 -0
  238. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_artifact.py +0 -0
  239. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_artifact_folders.py +0 -0
  240. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_can_curate.py +0 -0
  241. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_collection.py +0 -0
  242. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_data.py +0 -0
  243. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_db.py +0 -0
  244. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_delete.py +0 -0
  245. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_describe_and_df_calls.py +0 -0
  246. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_dtype.py +0 -0
  247. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_feature.py +0 -0
  248. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_feature_label_manager.py +0 -0
  249. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_from_values.py +0 -0
  250. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_has_parents.py +0 -0
  251. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_integrity.py +0 -0
  252. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_load.py +0 -0
  253. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_manager.py +0 -0
  254. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_models.py +0 -0
  255. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_notebooks.py +0 -0
  256. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_queryset.py +0 -0
  257. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_record.py +0 -0
  258. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_run.py +0 -0
  259. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_save.py +0 -0
  260. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_schema.py +0 -0
  261. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_search.py +0 -0
  262. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_settings.py +0 -0
  263. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_storage.py +0 -0
  264. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_track.py +0 -0
  265. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_tracked.py +0 -0
  266. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_transform.py +0 -0
  267. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_ulabel.py +0 -0
  268. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_versioning.py +0 -0
  269. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_view.py +0 -0
  270. {lamindb-1.10.1 → lamindb-1.10.2}/tests/core/test_visibility.py +0 -0
  271. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/conftest.py +0 -0
  272. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_cat_managers.py +0 -0
  273. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_curate_from_croissant.py +0 -0
  274. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_curators_examples.py +0 -0
  275. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_curators_multivalue.py +0 -0
  276. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_cxg_curator.py +0 -0
  277. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_dataframe_curators_accounting_example.py +0 -0
  278. {lamindb-1.10.1 → lamindb-1.10.2}/tests/curators/test_records.py +0 -0
  279. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/conftest.py +0 -0
  280. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/jwt_utils.py +0 -0
  281. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/scripts/check_lamin_dev.py +0 -0
  282. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/scripts/clean_lamin_dev.py +0 -0
  283. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/scripts/setup_access.py +0 -0
  284. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/scripts/setup_instance.py +0 -0
  285. {lamindb-1.10.1 → lamindb-1.10.2}/tests/permissions/test_permissions.py +0 -0
  286. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_artifact_zarr.py +0 -0
  287. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_cache.py +0 -0
  288. {lamindb-1.10.1 → lamindb-1.10.2}/tests/storage/test_transfer.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: lamindb
3
- Version: 1.10.1
3
+ Version: 1.10.2
4
4
  Summary: A data framework for biology.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.10,<3.14
@@ -9,6 +9,7 @@ Classifier: Programming Language :: Python :: 3.10
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
+ License-File: LICENSE
12
13
  Requires-Dist: lamin_utils==0.15.0
13
14
  Requires-Dist: lamin_cli==1.6.1
14
15
  Requires-Dist: lamindb_setup[aws]==1.9.1
@@ -108,7 +108,7 @@ Backwards compatibility.
108
108
 
109
109
  # ruff: noqa: I001
110
110
  # denote a release candidate for 0.1.0 with 0.1rc1, 0.1a1, 0.1b1, etc.
111
- __version__ = "1.10.1"
111
+ __version__ = "1.10.2"
112
112
 
113
113
  import warnings
114
114
 
@@ -10,6 +10,7 @@ from lamindb_setup import settings as setup_settings
10
10
  from lamindb_setup._set_managed_storage import set_managed_storage
11
11
  from lamindb_setup.core import deprecated
12
12
  from lamindb_setup.core._settings_instance import sanitize_git_repo_url
13
+ from lamindb_setup.core._settings_storage import StorageSettings
13
14
 
14
15
  from .subsettings._annotation_settings import AnnotationSettings, annotation_settings
15
16
  from .subsettings._creation_settings import CreationSettings, creation_settings
@@ -18,7 +19,6 @@ if TYPE_CHECKING:
18
19
  from collections.abc import Mapping
19
20
  from pathlib import Path
20
21
 
21
- from lamindb_setup.core._settings_storage import StorageSettings
22
22
  from upath import UPath
23
23
 
24
24
 
@@ -193,13 +193,39 @@ class Settings:
193
193
 
194
194
  @storage.setter
195
195
  def storage(self, path_kwargs: str | Path | UPath | tuple[str | UPath, Mapping]):
196
+ import lamindb as ln
197
+
196
198
  if isinstance(path_kwargs, tuple):
197
199
  path, kwargs = path_kwargs
200
+ # we should ultimately deprecate passing host here, I think
198
201
  if isinstance(kwargs, str):
199
202
  kwargs = {"host": kwargs}
200
203
  else:
201
204
  path, kwargs = path_kwargs, {}
202
- set_managed_storage(path, **kwargs)
205
+ ssettings = StorageSettings(root=path) # there is no need to pass kwargs here!
206
+ exists = ln.Storage.filter(root=ssettings.root_as_str).one_or_none()
207
+ if exists is None:
208
+ response = input(
209
+ f"Storage location {ssettings.root_as_str} does not yet exist. Do you want to continue with creating it? (y/n)"
210
+ )
211
+ # logger.warning(f"deprecated call because storage location does **not yet** exist; going forward, please create through ln.Storage(root={path}).save() going forward")
212
+ if response != "y":
213
+ return None
214
+ set_managed_storage(path, **kwargs)
215
+ else:
216
+ if exists.instance_uid != ln_setup.settings.instance.uid:
217
+ raise ValueError(
218
+ f"Storage {ssettings.root_as_str} exists in another instance ({exists.instance_uid}), cannot write to it from here."
219
+ )
220
+ ssettings = StorageSettings(
221
+ root=exists.root,
222
+ region=exists.region,
223
+ uid=exists.uid,
224
+ instance_id=ln_setup.settings.instance._id,
225
+ )
226
+ ln_setup.settings.instance._storage = ssettings
227
+ kwargs.pop("host", None) # host is not needed for existing storage
228
+ settings.storage._set_fs_kwargs(**kwargs)
203
229
 
204
230
  @property
205
231
  def instance_uid(self) -> str:
@@ -223,6 +249,23 @@ class Settings:
223
249
 
224
250
  @local_storage.setter
225
251
  def local_storage(self, local_root: Path):
252
+ import lamindb as ln
253
+
254
+ # note duplication with storage setter!
255
+ ssettings = StorageSettings(root=local_root)
256
+ exists = ln.Storage.filter(root=ssettings.root_as_str).one_or_none()
257
+ if exists is None:
258
+ response = input(
259
+ f"Storage location {ssettings.root_as_str} does not yet exist. Do you want to continue with creating it? (y/n)"
260
+ )
261
+ # logger.warning(f"deprecated call because storage location does **not yet** exist; going forward, please create through ln.Storage(root={path}).save() going forward")
262
+ if response != "y":
263
+ return None
264
+ else:
265
+ if exists.instance_uid != ln_setup.settings.instance.uid:
266
+ raise ValueError(
267
+ f"Storage {ssettings.root_as_str} exists in another instance ({exists.instance_uid}), cannot write to it from here."
268
+ )
226
269
  ln_setup.settings.instance.local_storage = local_root
227
270
 
228
271
  @property
@@ -13,12 +13,17 @@ from anndata import __version__ as anndata_version
13
13
  from anndata._core.index import _normalize_indices
14
14
  from anndata._core.views import _resolve_idx
15
15
  from anndata._io.h5ad import read_dataframe_legacy as read_dataframe_legacy_h5
16
- from anndata._io.specs.registry import get_spec, read_elem, read_elem_partial
16
+ from anndata._io.specs.registry import (
17
+ get_spec,
18
+ read_elem,
19
+ read_elem_partial,
20
+ write_elem,
21
+ )
17
22
  from anndata.compat import _read_attr
18
23
  from fsspec.implementations.local import LocalFileSystem
19
24
  from fsspec.utils import infer_compression
20
25
  from lamin_utils import logger
21
- from lamindb_setup.core.upath import infer_filesystem
26
+ from lamindb_setup.core.upath import S3FSMap, infer_filesystem
22
27
  from packaging import version
23
28
  from upath import UPath
24
29
 
@@ -28,6 +33,8 @@ if TYPE_CHECKING:
28
33
  from fsspec.core import OpenFile
29
34
  from lamindb_setup.types import UPathStr
30
35
 
36
+ from lamindb import Artifact
37
+
31
38
 
32
39
  anndata_version_parse = version.parse(anndata_version)
33
40
 
@@ -300,6 +307,11 @@ if ZARR_INSTALLED:
300
307
 
301
308
  store = get_zarr_store(filepath)
302
309
  storage = zarr.open(store, mode=mode)
310
+ # zarr v2 re-initializes the mapper
311
+ # we need to put back the correct one
312
+ # S3FSMap is returned from get_zarr_store only for zarr v2
313
+ if isinstance(store, S3FSMap):
314
+ storage.store.map = store
303
315
  conn = None
304
316
  return conn, storage
305
317
 
@@ -438,9 +450,15 @@ def _try_backed_full(elem):
438
450
  return read_elem(elem)
439
451
 
440
452
 
453
+ def _to_index(elem: np.ndarray):
454
+ if elem.dtype in (np.float64, np.int64):
455
+ elem = elem.astype(str)
456
+ return pd.Index(elem)
457
+
458
+
441
459
  def _safer_read_index(elem):
442
460
  if isinstance(elem, GroupTypes):
443
- return pd.Index(read_elem(elem[_read_attr(elem.attrs, "_index")]))
461
+ return _to_index(read_elem(elem[_read_attr(elem.attrs, "_index")]))
444
462
  elif isinstance(elem, ArrayTypes):
445
463
  indices = None
446
464
  for index_name in ("index", "_index"):
@@ -450,7 +468,7 @@ def _safer_read_index(elem):
450
468
  if indices is not None and len(indices) > 0:
451
469
  if isinstance(indices[0], bytes):
452
470
  indices = np.frompyfunc(lambda x: x.decode("utf-8"), 1, 1)(indices)
453
- return pd.Index(indices)
471
+ return _to_index(indices)
454
472
  else:
455
473
  raise ValueError("Indices not found.")
456
474
  else:
@@ -479,33 +497,40 @@ class _MapAccessor:
479
497
  return descr
480
498
 
481
499
 
500
+ def _safer_read_df(elem, indices=None):
501
+ if indices is not None:
502
+ obj = registry.safer_read_partial(elem, indices=indices)
503
+ df = _records_to_df(obj)
504
+ else:
505
+ df = registry.read_dataframe(elem)
506
+ if df.index.dtype in (np.float64, np.int64):
507
+ df.index = df.index.astype(str)
508
+ return df
509
+
510
+
482
511
  class _AnnDataAttrsMixin:
483
512
  storage: StorageType
484
513
  _attrs_keys: Mapping[str, list]
485
514
 
486
515
  @cached_property
487
- def obs(self) -> pd.DataFrame:
516
+ def obs(self) -> pd.DataFrame | None:
488
517
  if "obs" not in self._attrs_keys:
489
518
  return None
490
519
  indices = getattr(self, "indices", None)
491
- if indices is not None:
492
- indices = (indices[0], slice(None))
493
- obj = registry.safer_read_partial(self.storage["obs"], indices=indices) # type: ignore
494
- return _records_to_df(obj)
495
- else:
496
- return registry.read_dataframe(self.storage["obs"]) # type: ignore
520
+ return _safer_read_df(
521
+ self.storage["obs"], # type: ignore
522
+ indices=(indices[0], slice(None)) if indices is not None else None,
523
+ )
497
524
 
498
525
  @cached_property
499
- def var(self) -> pd.DataFrame:
526
+ def var(self) -> pd.DataFrame | None:
500
527
  if "var" not in self._attrs_keys:
501
528
  return None
502
529
  indices = getattr(self, "indices", None)
503
- if indices is not None:
504
- indices = (indices[1], slice(None))
505
- obj = registry.safer_read_partial(self.storage["var"], indices=indices) # type: ignore
506
- return _records_to_df(obj)
507
- else:
508
- return registry.read_dataframe(self.storage["var"]) # type: ignore
530
+ return _safer_read_df(
531
+ self.storage["var"], # type: ignore
532
+ indices=(indices[1], slice(None)) if indices is not None else None,
533
+ )
509
534
 
510
535
  @cached_property
511
536
  def uns(self):
@@ -702,6 +727,7 @@ class AnnDataAccessor(_AnnDataAttrsMixin):
702
727
  connection: OpenFile | None,
703
728
  storage: StorageType,
704
729
  filename: str,
730
+ artifact: Artifact | None = None,
705
731
  ):
706
732
  self._conn = connection
707
733
  self.storage = storage
@@ -713,6 +739,11 @@ class AnnDataAccessor(_AnnDataAttrsMixin):
713
739
  self._obs_names = _safer_read_index(self.storage["obs"]) # type: ignore
714
740
  self._var_names = _safer_read_index(self.storage["var"]) # type: ignore
715
741
 
742
+ self._artifact = artifact # save artifact to update in write mode
743
+
744
+ self._updated = False # track updates in r+ mode for zarr
745
+
746
+ self._entered = False # check that the context manager is used
716
747
  self._closed = False
717
748
 
718
749
  def close(self):
@@ -723,11 +754,23 @@ class AnnDataAccessor(_AnnDataAttrsMixin):
723
754
  self._conn.close()
724
755
  self._closed = True
725
756
 
757
+ if self._updated and (artifact := self._artifact) is not None:
758
+ from lamindb.models.artifact import Artifact
759
+ from lamindb.models.sqlrecord import init_self_from_db
760
+
761
+ new_version = Artifact(
762
+ artifact.path, revises=artifact, _is_internal_call=True
763
+ ).save()
764
+ # note: sets _state.db = "default"
765
+ init_self_from_db(artifact, new_version)
766
+
726
767
  @property
727
768
  def closed(self):
728
769
  return self._closed
729
770
 
730
771
  def __enter__(self):
772
+ self._entered = True
773
+
731
774
  return self
732
775
 
733
776
  def __exit__(self, exc_type, exc_val, exc_tb):
@@ -763,6 +806,35 @@ class AnnDataAccessor(_AnnDataAttrsMixin):
763
806
  self.storage["raw"], None, None, self._obs_names, None, self.shape[0]
764
807
  )
765
808
 
809
+ def add_column(
810
+ self,
811
+ where: Literal["obs", "var"],
812
+ col_name: str,
813
+ col: np.ndarray | pd.Categorical,
814
+ ):
815
+ """Add a new column to .obs or .var of the underlying AnnData object."""
816
+ df_store = self.storage[where] # type: ignore
817
+ if getattr(df_store, "read_only", True):
818
+ raise ValueError(
819
+ "You can use .add_column(...) only with zarr in a writable mode."
820
+ )
821
+ write_elem(df_store, col_name, col)
822
+ df_store.attrs["column-order"] = df_store.attrs["column-order"] + [col_name]
823
+ # remind only once if this wasn't updated before and not in the context manager
824
+ if not self._updated and not self._entered and self._artifact is not None:
825
+ logger.important(
826
+ "Do not forget to call .close() after you finish "
827
+ f"working with this accessor for {self._name} "
828
+ "to automatically update the corresponding artifact."
829
+ )
830
+
831
+ self._updated = True
832
+ # reset the cached property
833
+ # todo: maybe just append the column if the df was already loaded
834
+ self.__dict__.pop(where, None)
835
+ # update the cached columns
836
+ self._attrs_keys[where].append(col_name)
837
+
766
838
 
767
839
  # get the number of observations in an anndata object or file fast and safely
768
840
  def _anndata_n_observations(object: UPathStr | AnnData) -> int | None:
@@ -4,6 +4,7 @@ from dataclasses import dataclass
4
4
  from pathlib import Path
5
5
  from typing import TYPE_CHECKING, Any, Callable, Literal
6
6
 
7
+ import h5py
7
8
  from anndata._io.specs.registry import get_spec
8
9
 
9
10
  from ._anndata_accessor import AnnDataAccessor, StorageType, registry
@@ -92,10 +93,10 @@ def backed_access(
92
93
  from lamindb.models import Artifact
93
94
 
94
95
  if isinstance(artifact_or_filepath, Artifact):
95
- objectpath, _ = filepath_from_artifact(
96
- artifact_or_filepath, using_key=using_key
97
- )
96
+ artifact = artifact_or_filepath
97
+ objectpath, _ = filepath_from_artifact(artifact, using_key=using_key)
98
98
  else:
99
+ artifact = None
99
100
  objectpath = artifact_or_filepath
100
101
  name = objectpath.name
101
102
  # ignore .gz, only check the real suffix
@@ -111,9 +112,11 @@ def backed_access(
111
112
  elif suffix in {".h5", ".hdf5", ".h5ad"}:
112
113
  conn, storage = registry.open("h5py", objectpath, mode=mode, **kwargs)
113
114
  elif suffix == ".zarr":
115
+ if mode not in {"r", "r+"}:
116
+ raise ValueError("`mode` should be either 'r' or 'r+' for zarr.")
114
117
  conn, storage = registry.open("zarr", objectpath, mode=mode, **kwargs)
115
118
  if "spatialdata_attrs" in storage.attrs:
116
- return SpatialDataAccessor(storage, name)
119
+ return SpatialDataAccessor(storage, name, artifact)
117
120
  elif len(df_suffixes := _flat_suffixes(objectpath)) == 1 and (
118
121
  df_suffix := df_suffixes.pop()
119
122
  ) in set(PYARROW_SUFFIXES).union(POLARS_SUFFIXES):
@@ -127,9 +130,9 @@ def backed_access(
127
130
 
128
131
  is_anndata = suffix == ".h5ad" or get_spec(storage).encoding_type == "anndata"
129
132
  if is_anndata:
130
- if mode != "r":
131
- raise ValueError("Can only access `AnnData` with mode='r'.")
132
- return AnnDataAccessor(conn, storage, name)
133
+ if mode != "r" and isinstance(storage, h5py.Group):
134
+ raise ValueError("Can only access `hdf5` `AnnData` with mode='r'.")
135
+ return AnnDataAccessor(conn, storage, name, artifact)
133
136
  else:
134
137
  return BackedAccessor(conn, storage)
135
138
 
@@ -8,13 +8,22 @@ from ._anndata_accessor import AnnDataAccessor
8
8
  if TYPE_CHECKING:
9
9
  from zarr import Group
10
10
 
11
+ from lamindb import Artifact
12
+
11
13
 
12
14
  class _TablesAccessor:
13
- def __init__(self, tables: Group):
15
+ def __init__(self, tables: Group, artifact: Artifact | None = None):
14
16
  self._tables = tables
15
17
 
18
+ self._artifact = artifact
19
+
16
20
  def __getitem__(self, key: str) -> AnnDataAccessor:
17
- return AnnDataAccessor(connection=None, storage=self._tables[key], filename=key)
21
+ return AnnDataAccessor(
22
+ connection=None,
23
+ storage=self._tables[key],
24
+ filename=key,
25
+ artifact=self._artifact,
26
+ )
18
27
 
19
28
  def keys(self) -> list[str]:
20
29
  return list(self._tables.keys())
@@ -33,14 +42,16 @@ class SpatialDataAccessor:
33
42
  For now only allows to access `tables`.
34
43
  """
35
44
 
36
- def __init__(self, storage: Group, name: str):
45
+ def __init__(self, storage: Group, name: str, artifact: Artifact | None = None):
37
46
  self.storage = storage
38
47
  self._name = name
39
48
 
49
+ self._artifact = artifact
50
+
40
51
  @cached_property
41
52
  def tables(self) -> _TablesAccessor:
42
53
  """tables of the underlying SpatialData object."""
43
- return _TablesAccessor(self.storage["tables"])
54
+ return _TablesAccessor(self.storage["tables"], self._artifact)
44
55
 
45
56
  def __repr__(self):
46
57
  """Description of the SpatialDataAccessor object."""
@@ -37,6 +37,9 @@ def get_zarr_store(
37
37
  if isinstance(storepath, LocalPathClasses):
38
38
  store = storepath_str
39
39
  elif IS_ZARR_V3:
40
+ # todo: also check how to treat non-asynchronous filesystems
41
+ # zarr has something for this, using fsspec async wrapper
42
+ # check FsspecStore code
40
43
  store = zarr.storage.FsspecStore.from_upath(UPath(storepath, asynchronous=True))
41
44
  else:
42
45
  store = create_mapper(storepath.fs, storepath_str, check=check, create=create)
@@ -364,12 +364,13 @@ class SlotsCurator(Curator):
364
364
  )
365
365
  break
366
366
 
367
- self._artifact.schema = self._schema
368
- self._artifact.save()
369
367
  cat_vectors = {}
370
368
  for curator in self._slots.values():
371
369
  for key, cat_vector in curator.cat._cat_vectors.items():
372
370
  cat_vectors[key] = cat_vector
371
+
372
+ self._artifact.schema = self._schema
373
+ self._artifact.save()
373
374
  return annotate_artifact( # type: ignore
374
375
  self._artifact,
375
376
  curator=self,
@@ -685,10 +686,11 @@ class DataFrameCurator(Curator):
685
686
  description=description,
686
687
  revises=revises,
687
688
  run=run,
688
- format=".csv" if key.endswith(".csv") else None,
689
+ format=".csv" if key is not None and key.endswith(".csv") else None,
689
690
  )
690
- self._artifact.schema = self._schema
691
- self._artifact.save()
691
+
692
+ self._artifact.schema = self._schema
693
+ self._artifact.save()
692
694
  return annotate_artifact( # type: ignore
693
695
  self._artifact,
694
696
  cat_vectors=self.cat._cat_vectors,
@@ -0,0 +1,99 @@
1
+ # Generated by Django 5.2 on 2025-08-07 15:28
2
+
3
+ import django.db.models.deletion
4
+ import django.db.models.functions.datetime
5
+ from django.db import migrations, models
6
+
7
+ import lamindb.base.fields
8
+ import lamindb.base.users
9
+ import lamindb.models.run
10
+ import lamindb.models.sqlrecord
11
+
12
+
13
+ class Migration(migrations.Migration):
14
+ dependencies = [
15
+ ("lamindb", "0117_fix_artifact_storage_hash_unique_constraints"),
16
+ ]
17
+
18
+ operations = [
19
+ migrations.AlterField(
20
+ model_name="recordproject",
21
+ name="value",
22
+ field=lamindb.base.fields.ForeignKey(
23
+ blank=True,
24
+ on_delete=django.db.models.deletion.PROTECT,
25
+ related_name="links_in_record",
26
+ to="lamindb.project",
27
+ ),
28
+ ),
29
+ migrations.CreateModel(
30
+ name="ProjectRecord",
31
+ fields=[
32
+ (
33
+ "created_at",
34
+ lamindb.base.fields.DateTimeField(
35
+ blank=True,
36
+ db_default=django.db.models.functions.datetime.Now(),
37
+ db_index=True,
38
+ editable=False,
39
+ ),
40
+ ),
41
+ ("id", models.BigAutoField(primary_key=True, serialize=False)),
42
+ (
43
+ "created_by",
44
+ lamindb.base.fields.ForeignKey(
45
+ blank=True,
46
+ default=lamindb.base.users.current_user_id,
47
+ editable=False,
48
+ on_delete=django.db.models.deletion.PROTECT,
49
+ related_name="+",
50
+ to="lamindb.user",
51
+ ),
52
+ ),
53
+ (
54
+ "feature",
55
+ lamindb.base.fields.ForeignKey(
56
+ blank=True,
57
+ default=None,
58
+ null=True,
59
+ on_delete=django.db.models.deletion.PROTECT,
60
+ related_name="links_projectrecord",
61
+ to="lamindb.feature",
62
+ ),
63
+ ),
64
+ (
65
+ "project",
66
+ lamindb.base.fields.ForeignKey(
67
+ blank=True,
68
+ on_delete=django.db.models.deletion.PROTECT,
69
+ related_name="links_record",
70
+ to="lamindb.project",
71
+ ),
72
+ ),
73
+ (
74
+ "record",
75
+ lamindb.base.fields.ForeignKey(
76
+ blank=True,
77
+ on_delete=django.db.models.deletion.CASCADE,
78
+ related_name="links_project",
79
+ to="lamindb.record",
80
+ ),
81
+ ),
82
+ (
83
+ "run",
84
+ lamindb.base.fields.ForeignKey(
85
+ blank=True,
86
+ default=lamindb.models.run.current_run,
87
+ null=True,
88
+ on_delete=django.db.models.deletion.PROTECT,
89
+ related_name="+",
90
+ to="lamindb.run",
91
+ ),
92
+ ),
93
+ ],
94
+ options={
95
+ "unique_together": {("record", "project", "feature")},
96
+ },
97
+ bases=(lamindb.models.sqlrecord.IsLink, models.Model),
98
+ ),
99
+ ]
@@ -0,0 +1,26 @@
1
+ # Generated by Django 5.2 on 2025-08-09 13:31
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("lamindb", "0118_alter_recordproject_value_projectrecord"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.RenameField(
13
+ model_name="project",
14
+ old_name="records",
15
+ new_name="linked_in_records",
16
+ ),
17
+ migrations.AddField(
18
+ model_name="project",
19
+ name="records",
20
+ field=models.ManyToManyField(
21
+ related_name="projects",
22
+ through="lamindb.ProjectRecord",
23
+ to="lamindb.record",
24
+ ),
25
+ ),
26
+ ]