lamindb 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lamindb/__init__.py +1 -1
- lamindb/_finish.py +19 -13
- lamindb/core/_context.py +38 -8
- lamindb/curators/__init__.py +3 -7
- lamindb/migrations/0081_revert_textfield_collection.py +21 -0
- lamindb/models.py +4 -1
- {lamindb-1.0.2.dist-info → lamindb-1.0.4.dist-info}/METADATA +2 -2
- {lamindb-1.0.2.dist-info → lamindb-1.0.4.dist-info}/RECORD +10 -9
- {lamindb-1.0.2.dist-info → lamindb-1.0.4.dist-info}/LICENSE +0 -0
- {lamindb-1.0.2.dist-info → lamindb-1.0.4.dist-info}/WHEEL +0 -0
lamindb/__init__.py
CHANGED
@@ -53,7 +53,7 @@ Modules and settings.
|
|
53
53
|
"""
|
54
54
|
|
55
55
|
# denote a release candidate for 0.1.0 with 0.1rc1, 0.1a1, 0.1b1, etc.
|
56
|
-
__version__ = "1.0.
|
56
|
+
__version__ = "1.0.4"
|
57
57
|
|
58
58
|
from lamindb_setup._check_setup import InstanceNotSetupError as _InstanceNotSetupError
|
59
59
|
from lamindb_setup._check_setup import _check_instance_setup
|
lamindb/_finish.py
CHANGED
@@ -118,9 +118,8 @@ def notebook_to_script(
|
|
118
118
|
def clean_r_notebook_html(file_path: Path) -> tuple[str | None, Path]:
|
119
119
|
import re
|
120
120
|
|
121
|
-
cleaned_content = (
|
122
|
-
|
123
|
-
) # at this point cleaned_content is still raw
|
121
|
+
cleaned_content = file_path.read_text()
|
122
|
+
# remove title from content
|
124
123
|
pattern_title = r"<title>(.*?)</title>"
|
125
124
|
title_match = re.search(pattern_title, cleaned_content)
|
126
125
|
title_text = None
|
@@ -129,9 +128,18 @@ def clean_r_notebook_html(file_path: Path) -> tuple[str | None, Path]:
|
|
129
128
|
pattern_h1 = f"<h1[^>]*>{re.escape(title_text)}</h1>"
|
130
129
|
cleaned_content = re.sub(pattern_title, "", cleaned_content)
|
131
130
|
cleaned_content = re.sub(pattern_h1, "", cleaned_content)
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
# remove error message from content
|
132
|
+
if "NotebookNotSaved" in cleaned_content:
|
133
|
+
orig_error_message = f"NotebookNotSaved: {get_save_notebook_message()}"
|
134
|
+
# coming up with the regex for this is a bit tricky due to all the
|
135
|
+
# escape characters we'd need to insert into the message; hence,
|
136
|
+
# we do this with a replace() instead
|
137
|
+
cleaned_content = cleaned_content.replace(orig_error_message, "")
|
138
|
+
if "NotebookNotSaved" in cleaned_content:
|
139
|
+
orig_error_message = orig_error_message.replace(
|
140
|
+
" `finish()`", "\n`finish()`"
|
141
|
+
) # RStudio might insert a newline
|
142
|
+
cleaned_content = cleaned_content.replace(orig_error_message, "")
|
135
143
|
cleaned_path = file_path.parent / (f"{file_path.stem}.cleaned{file_path.suffix}")
|
136
144
|
cleaned_path.write_text(cleaned_content)
|
137
145
|
return title_text, cleaned_path
|
@@ -151,8 +159,6 @@ def save_context_core(
|
|
151
159
|
format_field_value, # needs to come after lamindb was imported because of CLI use
|
152
160
|
)
|
153
161
|
|
154
|
-
from .core._context import context
|
155
|
-
|
156
162
|
ln.settings.verbosity = "success"
|
157
163
|
|
158
164
|
# for scripts, things are easy
|
@@ -202,7 +208,7 @@ def save_context_core(
|
|
202
208
|
report_path = filepath.with_suffix(".html")
|
203
209
|
else:
|
204
210
|
logger.warning(
|
205
|
-
f"no
|
211
|
+
f"no html report found; to attach one, create an .html export for your {filepath.suffix} file and then run: lamin save {filepath}"
|
206
212
|
)
|
207
213
|
if report_path is not None and not from_cli:
|
208
214
|
if get_seconds_since_modified(report_path) > 2 and not ln_setup._TESTING:
|
@@ -292,10 +298,13 @@ def save_context_core(
|
|
292
298
|
)
|
293
299
|
report_file.save(upload=True, print_progress=False)
|
294
300
|
run.report = report_file
|
301
|
+
if is_r_notebook:
|
302
|
+
# this is the "cleaned" report
|
303
|
+
report_path.unlink()
|
295
304
|
logger.debug(
|
296
305
|
f"saved transform.latest_run.report: {transform.latest_run.report}"
|
297
306
|
)
|
298
|
-
run.
|
307
|
+
run._is_consecutive = is_consecutive
|
299
308
|
|
300
309
|
# save both run & transform records if we arrive here
|
301
310
|
run.save()
|
@@ -324,7 +333,4 @@ def save_context_core(
|
|
324
333
|
logger.important(
|
325
334
|
f"if you want to update your {thing} without re-running it, use `lamin save {filepath}`"
|
326
335
|
)
|
327
|
-
# because run & transform changed, update the global context
|
328
|
-
context._run = run
|
329
|
-
context._transform = transform
|
330
336
|
return None
|
lamindb/core/_context.py
CHANGED
@@ -395,6 +395,11 @@ class Context:
|
|
395
395
|
path = Path(module.__file__)
|
396
396
|
else:
|
397
397
|
path = Path(path)
|
398
|
+
# for Rmd and qmd, we could also extract the title
|
399
|
+
# we don't do this for now as we're setting the title upon `ln.finish()` or `lamin save`
|
400
|
+
# by extracting it from the html while cleaning it: see clean_r_notebook_html()
|
401
|
+
# also see the script_to_notebook() in the CLI _load.py where the title is extracted
|
402
|
+
# from the source code YAML and updated with the transform description
|
398
403
|
transform_type = "notebook" if path.suffix in {".Rmd", ".qmd"} else "script"
|
399
404
|
reference = None
|
400
405
|
reference_type = None
|
@@ -489,14 +494,30 @@ class Context:
|
|
489
494
|
if aux_transform.key in self._path.as_posix():
|
490
495
|
key = aux_transform.key
|
491
496
|
if (
|
497
|
+
# if the transform source code wasn't yet saved
|
492
498
|
aux_transform.source_code is None
|
493
|
-
|
499
|
+
# if the transform source code is unchanged
|
500
|
+
# if aux_transform.type == "notebook", we anticipate the user makes changes to the notebook source code
|
501
|
+
# in an interactive session, hence we *pro-actively bump* the version number by setting `revises`
|
502
|
+
# in the second part of the if condition even though the source code is unchanged at point of running track()
|
503
|
+
or (
|
504
|
+
aux_transform.hash == hash
|
505
|
+
and aux_transform.type != "notebook"
|
506
|
+
)
|
494
507
|
):
|
495
508
|
uid = aux_transform.uid
|
496
509
|
target_transform = aux_transform
|
497
510
|
else:
|
498
511
|
uid = f"{aux_transform.uid[:-4]}{increment_base62(aux_transform.uid[-4:])}"
|
499
|
-
message = f"there already is a
|
512
|
+
message = f"there already is a {aux_transform.type} with `key` '{aux_transform.key}'"
|
513
|
+
if (
|
514
|
+
aux_transform.hash == hash
|
515
|
+
and aux_transform.type == "notebook"
|
516
|
+
):
|
517
|
+
message += " -- notebook source code is unchanged, but anticipating changes during this run"
|
518
|
+
elif aux_transform.hash != hash:
|
519
|
+
message += " -- source code changed"
|
520
|
+
message += f", creating new version '{uid}'"
|
500
521
|
revises = aux_transform
|
501
522
|
found_key = True
|
502
523
|
break
|
@@ -508,7 +529,7 @@ class Context:
|
|
508
529
|
for transform in transforms
|
509
530
|
]
|
510
531
|
)
|
511
|
-
message = f"ignoring transform{plural_s} with same
|
532
|
+
message = f"ignoring transform{plural_s} with same filename:\n{transforms_str}"
|
512
533
|
if message != "":
|
513
534
|
logger.important(message)
|
514
535
|
self.uid, transform = uid, target_transform
|
@@ -581,7 +602,7 @@ class Context:
|
|
581
602
|
# check whether the transform.key is consistent
|
582
603
|
if transform.key != key:
|
583
604
|
raise UpdateContext(get_key_clashing_message(transform, key))
|
584
|
-
elif transform.description != description:
|
605
|
+
elif transform.description != description and description is not None:
|
585
606
|
transform.description = description
|
586
607
|
transform.save()
|
587
608
|
self._logging_message_track += (
|
@@ -597,7 +618,9 @@ class Context:
|
|
597
618
|
# check whether transform source code was already saved
|
598
619
|
if transform_was_saved:
|
599
620
|
bump_revision = False
|
600
|
-
if
|
621
|
+
if transform.type == "notebook":
|
622
|
+
# we anticipate the user makes changes to the notebook source code
|
623
|
+
# in an interactive session, hence we pro-actively bump the version number
|
601
624
|
bump_revision = True
|
602
625
|
else:
|
603
626
|
hash, _ = hash_file(self._path) # ignore hash_type for now
|
@@ -609,12 +632,12 @@ class Context:
|
|
609
632
|
)
|
610
633
|
if bump_revision:
|
611
634
|
change_type = (
|
612
|
-
"re-running saved
|
613
|
-
if
|
635
|
+
"re-running notebook with already-saved source code"
|
636
|
+
if transform.type == "notebook"
|
614
637
|
else "source code changed"
|
615
638
|
)
|
616
639
|
raise UpdateContext(
|
617
|
-
f'✗ {change_type},
|
640
|
+
f'✗ {change_type}, please update the `uid` argument in `track()` to "{uid[:-4]}{increment_base62(uid[-4:])}"'
|
618
641
|
)
|
619
642
|
else:
|
620
643
|
self._logging_message_track += f"loaded Transform('{transform.uid}')"
|
@@ -683,6 +706,13 @@ class Context:
|
|
683
706
|
)
|
684
707
|
if self.transform.type != "notebook":
|
685
708
|
self._stream_tracker.finish()
|
709
|
+
# reset the context attributes so that somebody who runs `track()` after finish
|
710
|
+
# starts fresh
|
711
|
+
self._uid = None
|
712
|
+
self._run = None
|
713
|
+
self._transform = None
|
714
|
+
self._version = None
|
715
|
+
self._description = None
|
686
716
|
|
687
717
|
|
688
718
|
context = Context()
|
lamindb/curators/__init__.py
CHANGED
@@ -13,6 +13,7 @@ from lamin_utils import colors, logger
|
|
13
13
|
from lamindb_setup.core._docs import doc_args
|
14
14
|
from lamindb_setup.core.upath import UPath
|
15
15
|
|
16
|
+
from lamindb.base.types import FieldAttr # noqa
|
16
17
|
from lamindb.models import (
|
17
18
|
Artifact,
|
18
19
|
Feature,
|
@@ -31,11 +32,6 @@ if TYPE_CHECKING:
|
|
31
32
|
|
32
33
|
from lamindb_setup.core.types import UPathStr
|
33
34
|
from mudata import MuData
|
34
|
-
from spatialdata import SpatialData
|
35
|
-
|
36
|
-
from lamindb.base.types import FieldAttr
|
37
|
-
|
38
|
-
from ._spatial import SpatialDataCurator
|
39
35
|
|
40
36
|
|
41
37
|
class CurateLookup:
|
@@ -1720,7 +1716,7 @@ class Curator(BaseCurator):
|
|
1720
1716
|
@classmethod
|
1721
1717
|
def from_spatialdata(
|
1722
1718
|
cls,
|
1723
|
-
sdata
|
1719
|
+
sdata,
|
1724
1720
|
var_index: dict[str, FieldAttr],
|
1725
1721
|
categoricals: dict[str, dict[str, FieldAttr]] | None = None,
|
1726
1722
|
using_key: str | None = None,
|
@@ -1730,7 +1726,7 @@ class Curator(BaseCurator):
|
|
1730
1726
|
verbosity: str = "hint",
|
1731
1727
|
*,
|
1732
1728
|
sample_metadata_key: str = "sample",
|
1733
|
-
)
|
1729
|
+
):
|
1734
1730
|
"""Curation flow for a ``Spatialdata`` object.
|
1735
1731
|
|
1736
1732
|
See also :class:`~lamindb.Curator`.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Generated by Django 5.2 on 2025-01-21 17:03
|
2
|
+
|
3
|
+
from django.db import migrations
|
4
|
+
|
5
|
+
import lamindb.base.fields
|
6
|
+
|
7
|
+
|
8
|
+
class Migration(migrations.Migration):
|
9
|
+
dependencies = [
|
10
|
+
("lamindb", "0080_polish_lamindbv1"),
|
11
|
+
]
|
12
|
+
|
13
|
+
operations = [
|
14
|
+
migrations.AlterField(
|
15
|
+
model_name="collection",
|
16
|
+
name="description",
|
17
|
+
field=lamindb.base.fields.TextField(
|
18
|
+
blank=True, db_index=True, default=None, null=True
|
19
|
+
),
|
20
|
+
),
|
21
|
+
]
|
lamindb/models.py
CHANGED
@@ -3008,7 +3008,10 @@ class Collection(Record, IsVersioned, TracksRun, TracksUpdates):
|
|
3008
3008
|
"""Universal id, valid across DB instances."""
|
3009
3009
|
key: str = CharField(db_index=True)
|
3010
3010
|
"""Name or path-like key."""
|
3011
|
-
|
3011
|
+
# these here is the only case in which we use a TextField
|
3012
|
+
# for description; we do so because users had descriptions exceeding 255 chars
|
3013
|
+
# in their instances
|
3014
|
+
description: str | None = TextField(null=True, db_index=True)
|
3012
3015
|
"""A description or title."""
|
3013
3016
|
hash: str | None = CharField(max_length=HASH_LENGTH, db_index=True, null=True)
|
3014
3017
|
"""Hash of collection content. 86 base64 chars allow to store 64 bytes, 512 bits."""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: lamindb
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.4
|
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.13
|
@@ -9,7 +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
|
Requires-Dist: lamin_utils==0.13.10
|
12
|
-
Requires-Dist: lamin_cli==1.0.
|
12
|
+
Requires-Dist: lamin_cli==1.0.4
|
13
13
|
Requires-Dist: lamindb_setup[aws]==1.0.3
|
14
14
|
Requires-Dist: pyarrow
|
15
15
|
Requires-Dist: typing_extensions!=4.6.0
|
@@ -1,9 +1,9 @@
|
|
1
|
-
lamindb/__init__.py,sha256=
|
1
|
+
lamindb/__init__.py,sha256=bKJLQZmbvlVCRAU9HxyoOpMUjG7p-gqbGRxCyCFXy_8,2255
|
2
2
|
lamindb/_artifact.py,sha256=22pKCA05PoIAgP2xszCHUovZ1VbMGIyrQqNLs5xDG_s,46580
|
3
3
|
lamindb/_can_curate.py,sha256=pIu9Ylgq5biUd_67rRbAHg9tkXSQrxMRM8TnVboL9YA,20341
|
4
4
|
lamindb/_collection.py,sha256=j2yTfR9v-NGUI85JfQk7vOQNExkWE5H_ulsSlBh1AOI,14456
|
5
5
|
lamindb/_feature.py,sha256=qpUZfmdxUpQFLq5GciTn6KgALL19tCs0raHhzJgm7Lo,6311
|
6
|
-
lamindb/_finish.py,sha256=
|
6
|
+
lamindb/_finish.py,sha256=aMN9CwTGAXeqWSDvCumVLztHtKttwI9z52mdz4ue5Uw,13362
|
7
7
|
lamindb/_from_values.py,sha256=uO3IfYzAI8VDDTqlbLzsZtawSYFS-Qzd_ZWwKGhH90o,14231
|
8
8
|
lamindb/_is_versioned.py,sha256=6_LBAKD_fng6BReqitJUIxTUaQok3AeIpNnE_D8kHnQ,1293
|
9
9
|
lamindb/_parents.py,sha256=PA--_ZH3PNqIVW0PpuYk9d4DAVlHUBPN-dN0rFUKUN0,17238
|
@@ -18,7 +18,7 @@ lamindb/_transform.py,sha256=LYFf8gScJrYLMZJECLYZ5nrW2vLPObdzRP47md-Tq-s,5731
|
|
18
18
|
lamindb/_ulabel.py,sha256=YTiUCYrcEqyUKD8nZO4iOqiyYnUP5bW_r7yry4KSeWA,2068
|
19
19
|
lamindb/_utils.py,sha256=LGdiW4k3GClLz65vKAVRkL6Tw-Gkx9DWAdez1jyA5bE,428
|
20
20
|
lamindb/_view.py,sha256=c4eN5hcBlg3TVnljKefbyWAq0eBncjMp2xQcb5OaGWg,4982
|
21
|
-
lamindb/models.py,sha256=
|
21
|
+
lamindb/models.py,sha256=9zjdIPuuBdjKi3V-LqdPK0uMrVocGhosOi29YViuwgo,147706
|
22
22
|
lamindb/base/__init__.py,sha256=J0UpYObi9hJBFyBpAXp4wB3DaJx48R2SaUeB4wjiFvc,267
|
23
23
|
lamindb/base/fields.py,sha256=RdwYHQmB7B-jopD_K2QNL5vjhOelu7DWGgqQItXr3pg,8024
|
24
24
|
lamindb/base/ids.py,sha256=WzHWiHZtlRUKqxz_p-76ks_JSW669ztvriE7Z3A0yHg,1736
|
@@ -26,7 +26,7 @@ lamindb/base/types.py,sha256=JfZk0xmhLsWusU0s4SNjhRnQ52mn-cSiG5Gf4SsACBs,1227
|
|
26
26
|
lamindb/base/users.py,sha256=g4ZLQb6eey66FO9eEumbfDpJi_FZZsiLVe2Frz9JuLI,978
|
27
27
|
lamindb/base/validation.py,sha256=Azz9y2-x0cPum4yULXMG3Yzra03mcVYzcKTiI23HgxE,2287
|
28
28
|
lamindb/core/__init__.py,sha256=4AGZqt5g8k3jFX53IXdezQR4Gf7JmMBLZRyTJJzS4sI,1628
|
29
|
-
lamindb/core/_context.py,sha256=
|
29
|
+
lamindb/core/_context.py,sha256=na1n_Lpj7Dvxjk1KB5Di4jEdhH-d6Jf757fvY1awEGM,28591
|
30
30
|
lamindb/core/_data.py,sha256=xg_St591OPCzLhaZAlGxVd8QkDxZsxDc_yEwY8Kop8w,19017
|
31
31
|
lamindb/core/_describe.py,sha256=3Z1xi9eKIBkYuPW9ctdWvFaGZym8mI9FcwyZF3a6YVo,4885
|
32
32
|
lamindb/core/_django.py,sha256=vPY4wJ4bf3a1uz5bhukCCF_mngR_9w2Ot3nvWZpa204,7629
|
@@ -58,7 +58,7 @@ lamindb/core/storage/objects.py,sha256=5vM2T_upuzrXt2b7fQeQ2FUO710-FRbubxTzKzV2E
|
|
58
58
|
lamindb/core/storage/paths.py,sha256=XXEy51qCw0z497y6ZEN_SULY3oXtA4XlanHo-TGK7jY,6302
|
59
59
|
lamindb/core/subsettings/__init__.py,sha256=j6G9WAJLK-x9FzPSFw-HJUmOseZKGTbK-oLTKI_X_zs,126
|
60
60
|
lamindb/core/subsettings/_creation_settings.py,sha256=54mfMH_osC753hpxcl7Dq1rwBD2LHnWveXtQpkLBITE,1194
|
61
|
-
lamindb/curators/__init__.py,sha256=
|
61
|
+
lamindb/curators/__init__.py,sha256=XGHnTEFB-Q4XphR0AWLXhITqhAZqkqEzuJJq1Ypb-zU,92357
|
62
62
|
lamindb/curators/_spatial.py,sha256=JgveK3aC1kFEplumEwU4Yyj-2tgHGfSAO52lHniJq5s,21212
|
63
63
|
lamindb/integrations/__init__.py,sha256=RWGMYYIzr8zvmNPyVB4m-p4gMDhxdRbjES2Ed23OItw,215
|
64
64
|
lamindb/integrations/_vitessce.py,sha256=nbfOsEO-W4f0BVnpFBDaA45577cfdvQehVx1hAUP544,3978
|
@@ -92,10 +92,11 @@ lamindb/migrations/0077_lamindbv1_part6b.py,sha256=v7k8OZX9o5ppSJU_yhHlIXGTobTm3
|
|
92
92
|
lamindb/migrations/0078_lamindbv1_part6c.py,sha256=RWRXBwyyQ_rFTN5kwstBziV6tqHJcGYI2vsFmuYCCz0,17084
|
93
93
|
lamindb/migrations/0079_alter_rundata_value_json_and_more.py,sha256=yQmbs8yWrFLOVQJqAfzLNMZOqTSnXyG-mQgpO7ls1u8,995
|
94
94
|
lamindb/migrations/0080_polish_lamindbv1.py,sha256=VfCwJtHlBsMPIyFQ2oh24oWkiRXjDvXRpKe5fBZ63aM,17660
|
95
|
+
lamindb/migrations/0081_revert_textfield_collection.py,sha256=uHuJ0W4Ips7BrnQnQBGPMn2eFQz29a1QAdHzN7XlDxo,490
|
95
96
|
lamindb/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
96
97
|
lamindb/setup/__init__.py,sha256=OwZpZzPDv5lPPGXZP7-zK6UdO4FHvvuBh439yZvIp3A,410
|
97
98
|
lamindb/setup/core/__init__.py,sha256=SevlVrc2AZWL3uALbE5sopxBnIZPWZ1IB0NBDudiAL8,167
|
98
|
-
lamindb-1.0.
|
99
|
-
lamindb-1.0.
|
100
|
-
lamindb-1.0.
|
101
|
-
lamindb-1.0.
|
99
|
+
lamindb-1.0.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
100
|
+
lamindb-1.0.4.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
101
|
+
lamindb-1.0.4.dist-info/METADATA,sha256=9Y2mkw39jPE4YdbNPQoGwliwHBVWwN1o15jBG5OwtCU,2611
|
102
|
+
lamindb-1.0.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|