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.
Files changed (89) hide show
  1. lamindb/__init__.py +39 -32
  2. lamindb/_artifact.py +95 -64
  3. lamindb/_can_curate.py +19 -10
  4. lamindb/_collection.py +51 -49
  5. lamindb/_feature.py +9 -9
  6. lamindb/_finish.py +99 -86
  7. lamindb/_from_values.py +20 -17
  8. lamindb/_is_versioned.py +2 -1
  9. lamindb/_parents.py +23 -16
  10. lamindb/_query_manager.py +3 -3
  11. lamindb/_query_set.py +85 -18
  12. lamindb/_record.py +121 -46
  13. lamindb/_run.py +3 -3
  14. lamindb/_save.py +14 -8
  15. lamindb/{_feature_set.py → _schema.py} +34 -31
  16. lamindb/_storage.py +2 -1
  17. lamindb/_transform.py +51 -23
  18. lamindb/_ulabel.py +17 -8
  19. lamindb/_view.py +15 -14
  20. lamindb/base/__init__.py +24 -0
  21. lamindb/base/fields.py +281 -0
  22. lamindb/base/ids.py +103 -0
  23. lamindb/base/types.py +51 -0
  24. lamindb/base/users.py +30 -0
  25. lamindb/base/validation.py +67 -0
  26. lamindb/core/__init__.py +19 -14
  27. lamindb/core/_context.py +297 -228
  28. lamindb/core/_data.py +44 -49
  29. lamindb/core/_describe.py +41 -31
  30. lamindb/core/_django.py +59 -44
  31. lamindb/core/_feature_manager.py +192 -168
  32. lamindb/core/_label_manager.py +22 -22
  33. lamindb/core/_mapped_collection.py +17 -14
  34. lamindb/core/_settings.py +1 -12
  35. lamindb/core/_sync_git.py +56 -9
  36. lamindb/core/_track_environment.py +1 -1
  37. lamindb/core/datasets/_core.py +5 -6
  38. lamindb/core/exceptions.py +0 -7
  39. lamindb/core/fields.py +1 -1
  40. lamindb/core/loaders.py +18 -2
  41. lamindb/core/{schema.py → relations.py} +22 -19
  42. lamindb/core/storage/_anndata_accessor.py +1 -2
  43. lamindb/core/storage/_backed_access.py +2 -1
  44. lamindb/core/storage/_tiledbsoma.py +40 -13
  45. lamindb/core/storage/objects.py +1 -1
  46. lamindb/core/storage/paths.py +13 -8
  47. lamindb/core/subsettings/__init__.py +0 -2
  48. lamindb/core/types.py +2 -23
  49. lamindb/core/versioning.py +11 -7
  50. lamindb/{_curate.py → curators/__init__.py} +700 -57
  51. lamindb/curators/_spatial.py +528 -0
  52. lamindb/integrations/_vitessce.py +1 -3
  53. lamindb/migrations/0052_squashed.py +1261 -0
  54. lamindb/migrations/0053_alter_featureset_hash_alter_paramvalue_created_by_and_more.py +57 -0
  55. lamindb/migrations/0054_alter_feature_previous_runs_and_more.py +35 -0
  56. lamindb/migrations/0055_artifact_type_artifactparamvalue_and_more.py +61 -0
  57. lamindb/migrations/0056_rename_ulabel_ref_is_name_artifactulabel_label_ref_is_name_and_more.py +22 -0
  58. lamindb/migrations/0057_link_models_latest_report_and_others.py +356 -0
  59. lamindb/migrations/0058_artifact__actions_collection__actions.py +22 -0
  60. lamindb/migrations/0059_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +31 -0
  61. lamindb/migrations/0060_alter_artifact__actions.py +22 -0
  62. lamindb/migrations/0061_alter_collection_meta_artifact_alter_run_environment_and_more.py +45 -0
  63. lamindb/migrations/0062_add_is_latest_field.py +32 -0
  64. lamindb/migrations/0063_populate_latest_field.py +45 -0
  65. lamindb/migrations/0064_alter_artifact_version_alter_collection_version_and_more.py +33 -0
  66. lamindb/migrations/0065_remove_collection_feature_sets_and_more.py +22 -0
  67. lamindb/migrations/0066_alter_artifact__feature_values_and_more.py +352 -0
  68. lamindb/migrations/0067_alter_featurevalue_unique_together_and_more.py +20 -0
  69. lamindb/migrations/0068_alter_artifactulabel_unique_together_and_more.py +20 -0
  70. lamindb/migrations/0069_alter_artifact__accessor_alter_artifact__hash_type_and_more.py +1294 -0
  71. lamindb/migrations/0069_squashed.py +1770 -0
  72. lamindb/migrations/0070_lamindbv1_migrate_data.py +78 -0
  73. lamindb/migrations/0071_lamindbv1_migrate_schema.py +741 -0
  74. lamindb/migrations/0072_remove_user__branch_code_remove_user_aux_and_more.py +148 -0
  75. lamindb/migrations/0073_merge_ourprojects.py +945 -0
  76. lamindb/migrations/0074_lamindbv1_part4.py +374 -0
  77. lamindb/migrations/0075_lamindbv1_part5.py +276 -0
  78. lamindb/migrations/0076_lamindbv1_part6.py +621 -0
  79. lamindb/migrations/0077_lamindbv1_part6b.py +228 -0
  80. lamindb/migrations/0078_lamindbv1_part6c.py +468 -0
  81. lamindb/migrations/0079_alter_rundata_value_json_and_more.py +36 -0
  82. lamindb/migrations/__init__.py +0 -0
  83. lamindb/models.py +4064 -0
  84. {lamindb-0.77.2.dist-info → lamindb-1.0rc1.dist-info}/METADATA +15 -20
  85. lamindb-1.0rc1.dist-info/RECORD +100 -0
  86. {lamindb-0.77.2.dist-info → lamindb-1.0rc1.dist-info}/WHEEL +1 -1
  87. lamindb/core/subsettings/_transform_settings.py +0 -21
  88. lamindb-0.77.2.dist-info/RECORD +0 -63
  89. {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
- from lnschema_core.models import Run, Transform
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 lnschema_core.types import TransformType
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
- if "is_new_version_of" in kwargs:
32
- logger.warning("`is_new_version_of` will be removed soon, please use `revises`")
33
- revises = kwargs.pop("is_new_version_of")
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 name, key, version, type, revises, reference, "
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.filter(uid__startswith=uid[:-4], is_latest=True)
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
- revises = (
51
- Transform.filter(key=key, is_latest=True)
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, {"name": name})
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="Artifact",
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, name, revises = process_revises(revises, version, name, Transform)
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
- name=name,
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="name",
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 lamindb_setup.core._docs import doc_args
7
- from lnschema_core import ULabel
4
+ from lamin_utils import logger
8
5
 
9
- from ._utils import attach_func_to_class_method
6
+ from lamindb.models import ULabel
10
7
 
11
- if TYPE_CHECKING:
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 | None = kwargs.pop("name") if "name" in kwargs else None
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
- schema: str | None = None,
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
- schema: Schema module to view. Default's to
108
- `None` and displays all schema modules.
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 schema is not None:
131
- schema_names = [schema]
131
+ if modules is not None:
132
+ module_names = [modules]
132
133
  else:
133
- schema_names = ["core"] + list(settings.instance.schema)
134
+ module_names = ["core"] + list(settings.instance.modules)
134
135
 
135
- for schema_name in schema_names:
136
- schema_module = importlib.import_module(get_schema_module_name(schema_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 schema_name == "core":
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(schema_names) > 1:
159
- section = f"* module: {colors.green(colors.bold(schema_name))} *"
160
- section_no_color = f"* module: {schema_name} *"
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))
@@ -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)