lamindb 0.77.2__py3-none-any.whl → 1.0rc1__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 +39 -32
- lamindb/_artifact.py +95 -64
- lamindb/_can_curate.py +19 -10
- lamindb/_collection.py +51 -49
- lamindb/_feature.py +9 -9
- lamindb/_finish.py +99 -86
- lamindb/_from_values.py +20 -17
- lamindb/_is_versioned.py +2 -1
- lamindb/_parents.py +23 -16
- lamindb/_query_manager.py +3 -3
- lamindb/_query_set.py +85 -18
- lamindb/_record.py +121 -46
- lamindb/_run.py +3 -3
- lamindb/_save.py +14 -8
- lamindb/{_feature_set.py → _schema.py} +34 -31
- lamindb/_storage.py +2 -1
- lamindb/_transform.py +51 -23
- lamindb/_ulabel.py +17 -8
- lamindb/_view.py +15 -14
- lamindb/base/__init__.py +24 -0
- lamindb/base/fields.py +281 -0
- lamindb/base/ids.py +103 -0
- lamindb/base/types.py +51 -0
- lamindb/base/users.py +30 -0
- lamindb/base/validation.py +67 -0
- lamindb/core/__init__.py +19 -14
- lamindb/core/_context.py +297 -228
- lamindb/core/_data.py +44 -49
- lamindb/core/_describe.py +41 -31
- lamindb/core/_django.py +59 -44
- lamindb/core/_feature_manager.py +192 -168
- lamindb/core/_label_manager.py +22 -22
- lamindb/core/_mapped_collection.py +17 -14
- lamindb/core/_settings.py +1 -12
- lamindb/core/_sync_git.py +56 -9
- lamindb/core/_track_environment.py +1 -1
- lamindb/core/datasets/_core.py +5 -6
- lamindb/core/exceptions.py +0 -7
- lamindb/core/fields.py +1 -1
- lamindb/core/loaders.py +18 -2
- lamindb/core/{schema.py → relations.py} +22 -19
- lamindb/core/storage/_anndata_accessor.py +1 -2
- lamindb/core/storage/_backed_access.py +2 -1
- lamindb/core/storage/_tiledbsoma.py +40 -13
- lamindb/core/storage/objects.py +1 -1
- lamindb/core/storage/paths.py +13 -8
- lamindb/core/subsettings/__init__.py +0 -2
- lamindb/core/types.py +2 -23
- lamindb/core/versioning.py +11 -7
- lamindb/{_curate.py → curators/__init__.py} +700 -57
- lamindb/curators/_spatial.py +528 -0
- lamindb/integrations/_vitessce.py +1 -3
- lamindb/migrations/0052_squashed.py +1261 -0
- lamindb/migrations/0053_alter_featureset_hash_alter_paramvalue_created_by_and_more.py +57 -0
- lamindb/migrations/0054_alter_feature_previous_runs_and_more.py +35 -0
- lamindb/migrations/0055_artifact_type_artifactparamvalue_and_more.py +61 -0
- lamindb/migrations/0056_rename_ulabel_ref_is_name_artifactulabel_label_ref_is_name_and_more.py +22 -0
- lamindb/migrations/0057_link_models_latest_report_and_others.py +356 -0
- lamindb/migrations/0058_artifact__actions_collection__actions.py +22 -0
- lamindb/migrations/0059_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +31 -0
- lamindb/migrations/0060_alter_artifact__actions.py +22 -0
- lamindb/migrations/0061_alter_collection_meta_artifact_alter_run_environment_and_more.py +45 -0
- lamindb/migrations/0062_add_is_latest_field.py +32 -0
- lamindb/migrations/0063_populate_latest_field.py +45 -0
- lamindb/migrations/0064_alter_artifact_version_alter_collection_version_and_more.py +33 -0
- lamindb/migrations/0065_remove_collection_feature_sets_and_more.py +22 -0
- lamindb/migrations/0066_alter_artifact__feature_values_and_more.py +352 -0
- lamindb/migrations/0067_alter_featurevalue_unique_together_and_more.py +20 -0
- lamindb/migrations/0068_alter_artifactulabel_unique_together_and_more.py +20 -0
- lamindb/migrations/0069_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +1294 -0
- lamindb/migrations/0069_squashed.py +1770 -0
- lamindb/migrations/0070_lamindbv1_migrate_data.py +78 -0
- lamindb/migrations/0071_lamindbv1_migrate_schema.py +741 -0
- lamindb/migrations/0072_remove_user__branch_code_remove_user_aux_and_more.py +148 -0
- lamindb/migrations/0073_merge_ourprojects.py +945 -0
- lamindb/migrations/0074_lamindbv1_part4.py +374 -0
- lamindb/migrations/0075_lamindbv1_part5.py +276 -0
- lamindb/migrations/0076_lamindbv1_part6.py +621 -0
- lamindb/migrations/0077_lamindbv1_part6b.py +228 -0
- lamindb/migrations/0078_lamindbv1_part6c.py +468 -0
- lamindb/migrations/0079_alter_rundata_value_json_and_more.py +36 -0
- lamindb/migrations/__init__.py +0 -0
- lamindb/models.py +4064 -0
- {lamindb-0.77.2.dist-info → lamindb-1.0rc1.dist-info}/METADATA +15 -20
- lamindb-1.0rc1.dist-info/RECORD +100 -0
- {lamindb-0.77.2.dist-info → lamindb-1.0rc1.dist-info}/WHEEL +1 -1
- lamindb/core/subsettings/_transform_settings.py +0 -21
- lamindb-0.77.2.dist-info/RECORD +0 -63
- {lamindb-0.77.2.dist-info → lamindb-1.0rc1.dist-info}/LICENSE +0 -0
lamindb/_transform.py
CHANGED
@@ -1,26 +1,31 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import warnings
|
3
4
|
from typing import TYPE_CHECKING
|
4
5
|
|
5
6
|
from lamin_utils import logger
|
6
7
|
from lamindb_setup.core._docs import doc_args
|
7
|
-
|
8
|
+
|
9
|
+
from lamindb.models import Run, Transform
|
8
10
|
|
9
11
|
from ._parents import _view_parents
|
10
12
|
from ._run import delete_run_artifacts
|
13
|
+
from .core._settings import settings
|
11
14
|
from .core.exceptions import InconsistentKey
|
12
15
|
from .core.versioning import message_update_key_in_version_family, process_revises
|
13
16
|
|
14
17
|
if TYPE_CHECKING:
|
15
|
-
from
|
18
|
+
from lamindb.base.types import TransformType
|
16
19
|
|
17
20
|
|
18
21
|
def __init__(transform: Transform, *args, **kwargs):
|
19
22
|
if len(args) == len(transform._meta.concrete_fields):
|
20
23
|
super(Transform, transform).__init__(*args, **kwargs)
|
21
24
|
return None
|
22
|
-
name: str | None = kwargs.pop("name") if "name" in kwargs else None
|
23
25
|
key: str | None = kwargs.pop("key") if "key" in kwargs else None
|
26
|
+
description: str | None = (
|
27
|
+
kwargs.pop("description") if "description" in kwargs else None
|
28
|
+
)
|
24
29
|
revises: Transform | None = kwargs.pop("revises") if "revises" in kwargs else None
|
25
30
|
version: str | None = kwargs.pop("version") if "version" in kwargs else None
|
26
31
|
type: TransformType | None = kwargs.pop("type") if "type" in kwargs else "pipeline"
|
@@ -28,47 +33,77 @@ def __init__(transform: Transform, *args, **kwargs):
|
|
28
33
|
reference_type: str | None = (
|
29
34
|
kwargs.pop("reference_type") if "reference_type" in kwargs else None
|
30
35
|
)
|
31
|
-
|
32
|
-
|
33
|
-
|
36
|
+
using_key = (
|
37
|
+
kwargs.pop("using_key") if "using_key" in kwargs else settings._using_key
|
38
|
+
)
|
39
|
+
if "name" in kwargs:
|
40
|
+
if key is None:
|
41
|
+
key = kwargs.pop("name")
|
42
|
+
warnings.warn(
|
43
|
+
f"`name` will be removed soon, please pass '{key}' to `key` instead",
|
44
|
+
FutureWarning,
|
45
|
+
stacklevel=2,
|
46
|
+
)
|
47
|
+
else:
|
48
|
+
# description wasn't exist, so no check necessary
|
49
|
+
description = kwargs.pop("name")
|
50
|
+
warnings.warn(
|
51
|
+
f"`name` will be removed soon, please pass '{description}' to `description` instead",
|
52
|
+
FutureWarning,
|
53
|
+
stacklevel=2,
|
54
|
+
)
|
34
55
|
# below is internal use that we'll hopefully be able to eliminate
|
35
56
|
uid: str | None = kwargs.pop("uid") if "uid" in kwargs else None
|
36
57
|
if not len(kwargs) == 0:
|
37
58
|
raise ValueError(
|
38
|
-
"Only
|
59
|
+
"Only key, description, version, type, revises, reference, "
|
39
60
|
f"reference_type can be passed, but you passed: {kwargs}"
|
40
61
|
)
|
41
62
|
if revises is None:
|
42
63
|
# need to check uid before checking key
|
43
64
|
if uid is not None:
|
44
65
|
revises = (
|
45
|
-
Transform.
|
66
|
+
Transform.objects.using(using_key)
|
67
|
+
.filter(uid__startswith=uid[:-4], is_latest=True)
|
46
68
|
.order_by("-created_at")
|
47
69
|
.first()
|
48
70
|
)
|
49
71
|
elif key is not None:
|
50
|
-
|
51
|
-
Transform.
|
72
|
+
candidate_for_revises = (
|
73
|
+
Transform.objects.using(using_key)
|
74
|
+
.filter(key=key, is_latest=True)
|
52
75
|
.order_by("-created_at")
|
53
76
|
.first()
|
54
77
|
)
|
78
|
+
if candidate_for_revises is not None:
|
79
|
+
revises = candidate_for_revises
|
80
|
+
if candidate_for_revises.source_code is None:
|
81
|
+
# no source code was yet saved, return the same transform
|
82
|
+
logger.important(
|
83
|
+
"no source code was yet saved, returning existing transform with same key"
|
84
|
+
)
|
85
|
+
uid = revises.uid
|
55
86
|
if revises is not None and uid is not None and uid == revises.uid:
|
56
87
|
from ._record import init_self_from_db, update_attributes
|
57
88
|
|
89
|
+
if revises.key != key:
|
90
|
+
logger.warning("ignoring inconsistent key")
|
58
91
|
init_self_from_db(transform, revises)
|
59
|
-
update_attributes(transform, {"
|
92
|
+
update_attributes(transform, {"description": description})
|
60
93
|
return None
|
61
94
|
if revises is not None and key is not None and revises.key != key:
|
62
95
|
note = message_update_key_in_version_family(
|
63
96
|
suid=revises.stem_uid,
|
64
97
|
existing_key=revises.key,
|
65
98
|
new_key=key,
|
66
|
-
registry="
|
99
|
+
registry="Transform",
|
67
100
|
)
|
68
101
|
raise InconsistentKey(
|
69
|
-
f"`key` is {key}, but `revises.key` is '{revises.key}'\n\nEither do *not* pass `key`.\n\n{note}"
|
102
|
+
f"`key` is '{key}', but `revises.key` is '{revises.key}'\n\nEither do *not* pass `key`.\n\n{note}"
|
70
103
|
)
|
71
|
-
new_uid, version,
|
104
|
+
new_uid, version, key, description, revises = process_revises(
|
105
|
+
revises, version, key, description, Transform
|
106
|
+
)
|
72
107
|
# this is only because the user-facing constructor allows passing a uid
|
73
108
|
# most others don't
|
74
109
|
if uid is None:
|
@@ -78,7 +113,7 @@ def __init__(transform: Transform, *args, **kwargs):
|
|
78
113
|
has_consciously_provided_uid = True
|
79
114
|
super(Transform, transform).__init__(
|
80
115
|
uid=uid,
|
81
|
-
|
116
|
+
description=description,
|
82
117
|
key=key,
|
83
118
|
type=type,
|
84
119
|
version=version,
|
@@ -90,13 +125,6 @@ def __init__(transform: Transform, *args, **kwargs):
|
|
90
125
|
|
91
126
|
|
92
127
|
def delete(self) -> None:
|
93
|
-
_source_code_artifact = None
|
94
|
-
if self._source_code_artifact is not None:
|
95
|
-
_source_code_artifact = self._source_code_artifact
|
96
|
-
self._source_code_artifact = None
|
97
|
-
self.save()
|
98
|
-
if _source_code_artifact is not None:
|
99
|
-
_source_code_artifact.delete(permanent=True)
|
100
128
|
# query all runs and delete their artifacts
|
101
129
|
runs = Run.filter(transform=self)
|
102
130
|
for run in runs:
|
@@ -116,7 +144,7 @@ def latest_run(self) -> Run:
|
|
116
144
|
def view_lineage(self, with_successors: bool = False, distance: int = 5):
|
117
145
|
return _view_parents(
|
118
146
|
record=self,
|
119
|
-
field="
|
147
|
+
field="key",
|
120
148
|
with_children=with_successors,
|
121
149
|
distance=distance,
|
122
150
|
attr_name="predecessors",
|
lamindb/_ulabel.py
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
|
5
3
|
import lamindb_setup as ln_setup
|
6
|
-
from
|
7
|
-
from lnschema_core import ULabel
|
4
|
+
from lamin_utils import logger
|
8
5
|
|
9
|
-
from .
|
6
|
+
from lamindb.models import ULabel
|
10
7
|
|
11
|
-
|
12
|
-
from lnschema_core.types import ListLike
|
8
|
+
from ._utils import attach_func_to_class_method
|
13
9
|
|
14
10
|
|
15
11
|
def __init__(self, *args, **kwargs):
|
@@ -19,7 +15,9 @@ def __init__(self, *args, **kwargs):
|
|
19
15
|
# now we proceed with the user-facing constructor
|
20
16
|
if len(args) > 0:
|
21
17
|
raise ValueError("Only one non-keyword arg allowed")
|
22
|
-
name: str
|
18
|
+
name: str = kwargs.pop("name") if "name" in kwargs else None
|
19
|
+
type: str | None = kwargs.pop("type") if "type" in kwargs else None
|
20
|
+
is_type: str | None = kwargs.pop("is_type") if "is_type" in kwargs else None
|
23
21
|
description: str | None = (
|
24
22
|
kwargs.pop("description") if "description" in kwargs else None
|
25
23
|
)
|
@@ -31,8 +29,19 @@ def __init__(self, *args, **kwargs):
|
|
31
29
|
raise ValueError(
|
32
30
|
"Only name, description, reference, reference_type are valid keyword arguments"
|
33
31
|
)
|
32
|
+
if is_type:
|
33
|
+
if name.endswith("s"):
|
34
|
+
logger.warning(
|
35
|
+
"`name` ends with 's', in case you're naming with plural, consider the singular for a type name"
|
36
|
+
)
|
37
|
+
if name[0].islower():
|
38
|
+
logger.warning(
|
39
|
+
"`name` starts with lowercase, in case you're naming a type, consider starting with uppercase"
|
40
|
+
)
|
34
41
|
super(ULabel, self).__init__(
|
35
42
|
name=name,
|
43
|
+
type=type,
|
44
|
+
is_type=is_type,
|
36
45
|
description=description,
|
37
46
|
reference=reference,
|
38
47
|
reference_type=reference_type,
|
lamindb/_view.py
CHANGED
@@ -5,13 +5,12 @@ import importlib
|
|
5
5
|
import inspect
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
-
from IPython.display import HTML, display
|
9
8
|
from lamin_utils import colors, logger
|
10
9
|
from lamindb_setup import settings
|
11
10
|
from lamindb_setup._init_instance import get_schema_module_name
|
12
|
-
from lnschema_core import Feature, Record
|
13
11
|
|
14
12
|
from lamindb.core import FeatureValue, ParamValue
|
13
|
+
from lamindb.models import Feature, Record
|
15
14
|
|
16
15
|
from ._feature import convert_pandas_dtype_to_lamin_dtype
|
17
16
|
|
@@ -24,6 +23,8 @@ is_run_from_ipython = getattr(builtins, "__IPYTHON__", False)
|
|
24
23
|
def display_df_with_descriptions(
|
25
24
|
df: pd.DataFrame, descriptions: dict[str, str] | None = None
|
26
25
|
):
|
26
|
+
from IPython.display import HTML, display
|
27
|
+
|
27
28
|
if descriptions is None:
|
28
29
|
display(df)
|
29
30
|
return None
|
@@ -96,7 +97,7 @@ def display_df_with_descriptions(
|
|
96
97
|
def view(
|
97
98
|
df: pd.DataFrame | None = None,
|
98
99
|
limit: int = 7,
|
99
|
-
|
100
|
+
modules: str | None = None,
|
100
101
|
registries: list[str] | None = None,
|
101
102
|
) -> None:
|
102
103
|
"""View metadata.
|
@@ -104,8 +105,8 @@ def view(
|
|
104
105
|
Args:
|
105
106
|
df: A DataFrame to display.
|
106
107
|
limit: Display the latest `n` records
|
107
|
-
|
108
|
-
`None` and displays all
|
108
|
+
modules: schema module to view. Default's to
|
109
|
+
`None` and displays all registry modules.
|
109
110
|
registries: List of Record names. Defaults to
|
110
111
|
`None` and lists all registries.
|
111
112
|
|
@@ -127,13 +128,13 @@ def view(
|
|
127
128
|
else:
|
128
129
|
show = logger.print
|
129
130
|
|
130
|
-
if
|
131
|
-
|
131
|
+
if modules is not None:
|
132
|
+
module_names = [modules]
|
132
133
|
else:
|
133
|
-
|
134
|
+
module_names = ["core"] + list(settings.instance.modules)
|
134
135
|
|
135
|
-
for
|
136
|
-
schema_module = importlib.import_module(get_schema_module_name(
|
136
|
+
for module_name in module_names:
|
137
|
+
schema_module = importlib.import_module(get_schema_module_name(module_name))
|
137
138
|
# the below is necessary because a schema module might not have been
|
138
139
|
# explicitly accessed
|
139
140
|
importlib.reload(schema_module)
|
@@ -145,7 +146,7 @@ def view(
|
|
145
146
|
and issubclass(registry, Record)
|
146
147
|
and registry is not Record
|
147
148
|
}
|
148
|
-
if
|
149
|
+
if module_name == "core":
|
149
150
|
all_registries.update({FeatureValue, ParamValue})
|
150
151
|
if registries is not None:
|
151
152
|
filtered_registries = {
|
@@ -155,9 +156,9 @@ def view(
|
|
155
156
|
}
|
156
157
|
else:
|
157
158
|
filtered_registries = all_registries
|
158
|
-
if len(
|
159
|
-
section = f"* module: {colors.green(colors.bold(
|
160
|
-
section_no_color = f"* module: {
|
159
|
+
if len(module_names) > 1:
|
160
|
+
section = f"* module: {colors.green(colors.bold(module_name))} *"
|
161
|
+
section_no_color = f"* module: {module_name} *"
|
161
162
|
logger.print("*" * len(section_no_color))
|
162
163
|
logger.print(section)
|
163
164
|
logger.print("*" * len(section_no_color))
|
lamindb/base/__init__.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
"""Base library.
|
2
|
+
|
3
|
+
Is available also when no instance is connected.
|
4
|
+
|
5
|
+
Modules:
|
6
|
+
|
7
|
+
.. autosummary::
|
8
|
+
:toctree: .
|
9
|
+
|
10
|
+
types
|
11
|
+
|
12
|
+
Utils:
|
13
|
+
|
14
|
+
.. autosummary::
|
15
|
+
:toctree: .
|
16
|
+
|
17
|
+
doc_args
|
18
|
+
deprecated
|
19
|
+
|
20
|
+
"""
|
21
|
+
|
22
|
+
from lamindb_setup.core import deprecated, doc_args
|
23
|
+
|
24
|
+
from . import types
|
lamindb/base/fields.py
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
from django.db import models
|
2
|
+
|
3
|
+
|
4
|
+
class CharField(models.CharField):
|
5
|
+
"""Custom `CharField` with default values for `blank`, `default`, and `max_length`.
|
6
|
+
|
7
|
+
Django default values for `CharField` are `blank=False`, `default=""`, undefined `max_length`.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, max_length: int = 255, **kwargs):
|
11
|
+
kwargs["max_length"] = max_length # Set max_length in kwargs
|
12
|
+
kwargs.setdefault("blank", True)
|
13
|
+
kwargs.setdefault("default", None)
|
14
|
+
super().__init__(**kwargs) # Pass all arguments as kwargs
|
15
|
+
|
16
|
+
|
17
|
+
class TextField(models.TextField):
|
18
|
+
"""Custom `TextField` with default values for `blank` and `default`.
|
19
|
+
|
20
|
+
Django default values for `TextField` are `blank=False`, `default=''`.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self, *args, **kwargs):
|
24
|
+
kwargs.setdefault("blank", True)
|
25
|
+
kwargs.setdefault("default", None)
|
26
|
+
super().__init__(*args, **kwargs)
|
27
|
+
|
28
|
+
|
29
|
+
class ForeignKey(models.ForeignKey):
|
30
|
+
"""Custom `ForeignKey` with default values for `blank`.
|
31
|
+
|
32
|
+
Django default value for `ForeignKey` `blank=False`.
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, *args, **kwargs):
|
36
|
+
kwargs.setdefault("blank", True)
|
37
|
+
super().__init__(*args, **kwargs)
|
38
|
+
|
39
|
+
|
40
|
+
class BooleanField(models.BooleanField):
|
41
|
+
"""Custom `BooleanField` with default values for `blank` and `default`.
|
42
|
+
|
43
|
+
Django default values for `BooleanField` are `blank=False`, `default=False`.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init__(self, *args, **kwargs):
|
47
|
+
kwargs.setdefault("blank", True)
|
48
|
+
kwargs.setdefault("default", None)
|
49
|
+
super().__init__(*args, **kwargs)
|
50
|
+
|
51
|
+
|
52
|
+
class DateField(models.DateField):
|
53
|
+
"""Custom `DateField` with default values for `blank`.
|
54
|
+
|
55
|
+
Django default values for `DateField` are `blank=False`.
|
56
|
+
"""
|
57
|
+
|
58
|
+
def __init__(self, *args, **kwargs):
|
59
|
+
kwargs.setdefault("blank", True)
|
60
|
+
super().__init__(*args, **kwargs)
|
61
|
+
|
62
|
+
|
63
|
+
class DateTimeField(models.DateTimeField):
|
64
|
+
"""Custom `DateTimeField` with default values for `blank`.
|
65
|
+
|
66
|
+
Django default values for `DateTimeField` are `blank=False`.
|
67
|
+
"""
|
68
|
+
|
69
|
+
def __init__(self, *args, **kwargs):
|
70
|
+
kwargs.setdefault("blank", True)
|
71
|
+
super().__init__(*args, **kwargs)
|
72
|
+
|
73
|
+
|
74
|
+
class BigIntegerField(models.BigIntegerField):
|
75
|
+
"""Custom `BigIntegerField` with default values for `blank`.
|
76
|
+
|
77
|
+
Django default values for `BigIntegerField` are `blank=False`.
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(self, *args, **kwargs):
|
81
|
+
kwargs.setdefault("blank", True)
|
82
|
+
kwargs.setdefault("default", None)
|
83
|
+
super().__init__(*args, **kwargs)
|
84
|
+
|
85
|
+
|
86
|
+
class IntegerField(models.IntegerField):
|
87
|
+
"""Custom `IntegerField` with default values for `blank`.
|
88
|
+
|
89
|
+
Django default values for `IntegerField` are `blank=False`.
|
90
|
+
"""
|
91
|
+
|
92
|
+
def __init__(self, *args, **kwargs):
|
93
|
+
kwargs.setdefault("blank", True)
|
94
|
+
super().__init__(*args, **kwargs)
|
95
|
+
|
96
|
+
|
97
|
+
class OneToOneField(models.OneToOneField):
|
98
|
+
"""Custom `OneToOneField` with default values for `blank`.
|
99
|
+
|
100
|
+
Django default values for `OneToOneField` are `blank=False`.
|
101
|
+
"""
|
102
|
+
|
103
|
+
def __init__(self, *args, **kwargs):
|
104
|
+
kwargs.setdefault("blank", True)
|
105
|
+
super().__init__(*args, **kwargs)
|
106
|
+
|
107
|
+
|
108
|
+
class FloatField(models.FloatField):
|
109
|
+
"""Custom `FloatField` with default values for `blank`.
|
110
|
+
|
111
|
+
Django default values for `FloatField` are `blank=False`.
|
112
|
+
"""
|
113
|
+
|
114
|
+
def __init__(self, *args, **kwargs):
|
115
|
+
kwargs.setdefault("blank", True)
|
116
|
+
super().__init__(*args, **kwargs)
|
117
|
+
|
118
|
+
|
119
|
+
class DecimalField(models.DecimalField):
|
120
|
+
"""Custom `DecimalField` with default values for `blank`.
|
121
|
+
|
122
|
+
Django default values for `DecimalField` are `blank=False`.
|
123
|
+
"""
|
124
|
+
|
125
|
+
def __init__(self, *args, **kwargs):
|
126
|
+
kwargs.setdefault("blank", True)
|
127
|
+
super().__init__(*args, **kwargs)
|
128
|
+
|
129
|
+
|
130
|
+
class JSONField(models.JSONField):
|
131
|
+
"""Custom `JSONField` with default values for `blank`.
|
132
|
+
|
133
|
+
Django default values for `JSONField` are `blank=False`.
|
134
|
+
"""
|
135
|
+
|
136
|
+
def __init__(self, *args, **kwargs):
|
137
|
+
kwargs.setdefault("blank", True)
|
138
|
+
super().__init__(*args, **kwargs)
|
139
|
+
|
140
|
+
|
141
|
+
class DurationField(models.DurationField):
|
142
|
+
"""Custom `DurationField` with default values for `blank`.
|
143
|
+
|
144
|
+
Django default values for `DurationField` are `blank=False`.
|
145
|
+
"""
|
146
|
+
|
147
|
+
def __init__(self, *args, **kwargs):
|
148
|
+
kwargs.setdefault("blank", True)
|
149
|
+
super().__init__(*args, **kwargs)
|
150
|
+
|
151
|
+
|
152
|
+
class URLField(models.URLField):
|
153
|
+
"""Custom `URLField` with default values for `blank`.
|
154
|
+
|
155
|
+
Django default values for `URLField` are `blank=False`.
|
156
|
+
"""
|
157
|
+
|
158
|
+
def __init__(self, *args, **kwargs):
|
159
|
+
kwargs.setdefault("blank", True)
|
160
|
+
super().__init__(*args, **kwargs)
|
161
|
+
|
162
|
+
|
163
|
+
class EmailField(models.EmailField):
|
164
|
+
"""Custom `EmailField` with default values for `blank`.
|
165
|
+
|
166
|
+
Django default values for `EmailField` are `blank=False`.
|
167
|
+
"""
|
168
|
+
|
169
|
+
def __init__(self, *args, **kwargs):
|
170
|
+
kwargs.setdefault("blank", True)
|
171
|
+
super().__init__(*args, **kwargs)
|
172
|
+
|
173
|
+
|
174
|
+
class TimeField(models.TimeField):
|
175
|
+
"""Custom `TimeField` with default values for `blank`.
|
176
|
+
|
177
|
+
Django default values for `TimeField` are `blank=False`.
|
178
|
+
"""
|
179
|
+
|
180
|
+
def __init__(self, *args, **kwargs):
|
181
|
+
kwargs.setdefault("blank", True)
|
182
|
+
super().__init__(*args, **kwargs)
|
183
|
+
|
184
|
+
|
185
|
+
class SlugField(models.SlugField):
|
186
|
+
"""Custom `SlugField` with default values for `blank`.
|
187
|
+
|
188
|
+
Django default values for `SlugField` are `blank=False`.
|
189
|
+
"""
|
190
|
+
|
191
|
+
def __init__(self, *args, **kwargs):
|
192
|
+
kwargs.setdefault("blank", True)
|
193
|
+
super().__init__(*args, **kwargs)
|
194
|
+
|
195
|
+
|
196
|
+
class UUIDField(models.UUIDField):
|
197
|
+
"""Custom `UUIDField` with default values for `blank`.
|
198
|
+
|
199
|
+
Django default values for `UUIDField` are `blank=False`.
|
200
|
+
"""
|
201
|
+
|
202
|
+
def __init__(self, *args, **kwargs):
|
203
|
+
kwargs.setdefault("blank", True)
|
204
|
+
super().__init__(*args, **kwargs)
|
205
|
+
|
206
|
+
|
207
|
+
class PositiveIntegerField(models.PositiveIntegerField):
|
208
|
+
"""Custom `PositiveIntegerField` with default values for `blank`.
|
209
|
+
|
210
|
+
Django default values for `PositiveIntegerField` are `blank=False`.
|
211
|
+
"""
|
212
|
+
|
213
|
+
def __init__(self, *args, **kwargs):
|
214
|
+
kwargs.setdefault("blank", True)
|
215
|
+
super().__init__(*args, **kwargs)
|
216
|
+
|
217
|
+
|
218
|
+
class PositiveSmallIntegerField(models.PositiveSmallIntegerField):
|
219
|
+
"""Custom `PositiveSmallIntegerField` with default values for `blank`.
|
220
|
+
|
221
|
+
Django default values for `PositiveSmallIntegerField` are `blank=False`.
|
222
|
+
"""
|
223
|
+
|
224
|
+
def __init__(self, *args, **kwargs):
|
225
|
+
kwargs.setdefault("blank", True)
|
226
|
+
super().__init__(*args, **kwargs)
|
227
|
+
|
228
|
+
|
229
|
+
class SmallIntegerField(models.SmallIntegerField):
|
230
|
+
"""Custom `SmallIntegerField` with default values for `blank`.
|
231
|
+
|
232
|
+
Django default values for `SmallIntegerField` are `blank=False`.
|
233
|
+
"""
|
234
|
+
|
235
|
+
def __init__(self, *args, **kwargs):
|
236
|
+
kwargs.setdefault("blank", True)
|
237
|
+
super().__init__(*args, **kwargs)
|
238
|
+
|
239
|
+
|
240
|
+
class BinaryField(models.BinaryField):
|
241
|
+
"""Custom `BinaryField` with default values for `blank`.
|
242
|
+
|
243
|
+
Django default values for `BinaryField` are `blank=False`.
|
244
|
+
"""
|
245
|
+
|
246
|
+
def __init__(self, *args, **kwargs):
|
247
|
+
kwargs.setdefault("blank", True)
|
248
|
+
super().__init__(*args, **kwargs)
|
249
|
+
|
250
|
+
|
251
|
+
class GenericIPAddressField(models.GenericIPAddressField):
|
252
|
+
"""Custom `GenericIPAddressField` with default values for `blank`.
|
253
|
+
|
254
|
+
Django default values for `GenericIPAddressField` are `blank=False`.
|
255
|
+
"""
|
256
|
+
|
257
|
+
def __init__(self, *args, **kwargs):
|
258
|
+
kwargs.setdefault("blank", True)
|
259
|
+
super().__init__(*args, **kwargs)
|
260
|
+
|
261
|
+
|
262
|
+
class FileField(models.FileField):
|
263
|
+
"""Custom `FileField` with default values for `blank`.
|
264
|
+
|
265
|
+
Django default values for `FileField` are `blank=False`.
|
266
|
+
"""
|
267
|
+
|
268
|
+
def __init__(self, *args, **kwargs):
|
269
|
+
kwargs.setdefault("blank", True)
|
270
|
+
super().__init__(*args, **kwargs)
|
271
|
+
|
272
|
+
|
273
|
+
class ImageField(models.ImageField):
|
274
|
+
"""Custom `ImageField` with default values for `blank`.
|
275
|
+
|
276
|
+
Django default values for `ImageField` are `blank=False`.
|
277
|
+
"""
|
278
|
+
|
279
|
+
def __init__(self, *args, **kwargs):
|
280
|
+
kwargs.setdefault("blank", True)
|
281
|
+
super().__init__(*args, **kwargs)
|
lamindb/base/ids.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
"""IDs.
|
2
|
+
|
3
|
+
Base generators:
|
4
|
+
|
5
|
+
.. autosummary::
|
6
|
+
:toctree: .
|
7
|
+
|
8
|
+
base26
|
9
|
+
base62
|
10
|
+
base64
|
11
|
+
|
12
|
+
8 base62 characters:
|
13
|
+
|
14
|
+
======= ===========
|
15
|
+
n p_collision
|
16
|
+
======= ===========
|
17
|
+
100k 2e-05
|
18
|
+
1M 2e-03
|
19
|
+
======= ===========
|
20
|
+
|
21
|
+
12 base62 characters:
|
22
|
+
|
23
|
+
======= ===========
|
24
|
+
n p_collision
|
25
|
+
======= ===========
|
26
|
+
100M 2e-06
|
27
|
+
1B 2e-04
|
28
|
+
======= ===========
|
29
|
+
|
30
|
+
20 base62 characters (62**20=7e+35) roughly matches UUID (2*122=5e+36):
|
31
|
+
|
32
|
+
======= ===========
|
33
|
+
n p_collision
|
34
|
+
======= ===========
|
35
|
+
3e15 1e-6
|
36
|
+
======= ===========
|
37
|
+
|
38
|
+
"""
|
39
|
+
|
40
|
+
import secrets
|
41
|
+
import string
|
42
|
+
|
43
|
+
|
44
|
+
def base64(n_char: int) -> str:
|
45
|
+
"""Random Base64 string."""
|
46
|
+
alphabet = string.digits + string.ascii_letters.swapcase() + "_" + "-"
|
47
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
48
|
+
return id
|
49
|
+
|
50
|
+
|
51
|
+
def base62(n_char: int) -> str:
|
52
|
+
"""Random Base62 string."""
|
53
|
+
alphabet = string.digits + string.ascii_letters.swapcase()
|
54
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
55
|
+
return id
|
56
|
+
|
57
|
+
|
58
|
+
# the following cannot be serialized by Django
|
59
|
+
# class Base62:
|
60
|
+
# def __init__(self, n_char: int):
|
61
|
+
# self.n_char = n_char
|
62
|
+
|
63
|
+
# def __call__(self):
|
64
|
+
# return base62(self.n_char)
|
65
|
+
|
66
|
+
|
67
|
+
def base26(n_char: int):
|
68
|
+
"""ASCII lowercase."""
|
69
|
+
alphabet = string.ascii_lowercase
|
70
|
+
id = "".join(secrets.choice(alphabet) for i in range(n_char))
|
71
|
+
return id
|
72
|
+
|
73
|
+
|
74
|
+
def base62_4() -> str:
|
75
|
+
return base62(4)
|
76
|
+
|
77
|
+
|
78
|
+
def base62_8() -> str:
|
79
|
+
return base62(8)
|
80
|
+
|
81
|
+
|
82
|
+
def base62_12() -> str:
|
83
|
+
return base62(12)
|
84
|
+
|
85
|
+
|
86
|
+
def base62_14() -> str:
|
87
|
+
return base62(14)
|
88
|
+
|
89
|
+
|
90
|
+
def base62_16() -> str:
|
91
|
+
return base62(16)
|
92
|
+
|
93
|
+
|
94
|
+
def base62_18() -> str:
|
95
|
+
return base62(18)
|
96
|
+
|
97
|
+
|
98
|
+
def base62_20() -> str:
|
99
|
+
return base62(20)
|
100
|
+
|
101
|
+
|
102
|
+
def base62_24() -> str:
|
103
|
+
return base62(24)
|