iceaxe 0.8.3__cp313-cp313-macosx_11_0_arm64.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.
Potentially problematic release.
This version of iceaxe might be problematic. Click here for more details.
- iceaxe/__init__.py +20 -0
- iceaxe/__tests__/__init__.py +0 -0
- iceaxe/__tests__/benchmarks/__init__.py +0 -0
- iceaxe/__tests__/benchmarks/test_bulk_insert.py +45 -0
- iceaxe/__tests__/benchmarks/test_select.py +114 -0
- iceaxe/__tests__/conf_models.py +133 -0
- iceaxe/__tests__/conftest.py +204 -0
- iceaxe/__tests__/docker_helpers.py +208 -0
- iceaxe/__tests__/helpers.py +268 -0
- iceaxe/__tests__/migrations/__init__.py +0 -0
- iceaxe/__tests__/migrations/conftest.py +36 -0
- iceaxe/__tests__/migrations/test_action_sorter.py +237 -0
- iceaxe/__tests__/migrations/test_generator.py +140 -0
- iceaxe/__tests__/migrations/test_generics.py +91 -0
- iceaxe/__tests__/mountaineer/__init__.py +0 -0
- iceaxe/__tests__/mountaineer/dependencies/__init__.py +0 -0
- iceaxe/__tests__/mountaineer/dependencies/test_core.py +76 -0
- iceaxe/__tests__/schemas/__init__.py +0 -0
- iceaxe/__tests__/schemas/test_actions.py +1265 -0
- iceaxe/__tests__/schemas/test_cli.py +25 -0
- iceaxe/__tests__/schemas/test_db_memory_serializer.py +1571 -0
- iceaxe/__tests__/schemas/test_db_serializer.py +435 -0
- iceaxe/__tests__/schemas/test_db_stubs.py +190 -0
- iceaxe/__tests__/test_alias.py +83 -0
- iceaxe/__tests__/test_base.py +52 -0
- iceaxe/__tests__/test_comparison.py +383 -0
- iceaxe/__tests__/test_field.py +11 -0
- iceaxe/__tests__/test_helpers.py +9 -0
- iceaxe/__tests__/test_modifications.py +151 -0
- iceaxe/__tests__/test_queries.py +764 -0
- iceaxe/__tests__/test_queries_str.py +173 -0
- iceaxe/__tests__/test_session.py +1511 -0
- iceaxe/__tests__/test_text_search.py +287 -0
- iceaxe/alias_values.py +67 -0
- iceaxe/base.py +351 -0
- iceaxe/comparison.py +560 -0
- iceaxe/field.py +263 -0
- iceaxe/functions.py +1432 -0
- iceaxe/generics.py +140 -0
- iceaxe/io.py +107 -0
- iceaxe/logging.py +91 -0
- iceaxe/migrations/__init__.py +5 -0
- iceaxe/migrations/action_sorter.py +98 -0
- iceaxe/migrations/cli.py +228 -0
- iceaxe/migrations/client_io.py +62 -0
- iceaxe/migrations/generator.py +404 -0
- iceaxe/migrations/migration.py +86 -0
- iceaxe/migrations/migrator.py +101 -0
- iceaxe/modifications.py +176 -0
- iceaxe/mountaineer/__init__.py +10 -0
- iceaxe/mountaineer/cli.py +74 -0
- iceaxe/mountaineer/config.py +46 -0
- iceaxe/mountaineer/dependencies/__init__.py +6 -0
- iceaxe/mountaineer/dependencies/core.py +67 -0
- iceaxe/postgres.py +133 -0
- iceaxe/py.typed +0 -0
- iceaxe/queries.py +1459 -0
- iceaxe/queries_str.py +294 -0
- iceaxe/schemas/__init__.py +0 -0
- iceaxe/schemas/actions.py +864 -0
- iceaxe/schemas/cli.py +30 -0
- iceaxe/schemas/db_memory_serializer.py +711 -0
- iceaxe/schemas/db_serializer.py +347 -0
- iceaxe/schemas/db_stubs.py +529 -0
- iceaxe/session.py +860 -0
- iceaxe/session_optimized.c +12207 -0
- iceaxe/session_optimized.cpython-313-darwin.so +0 -0
- iceaxe/session_optimized.pyx +212 -0
- iceaxe/sql_types.py +149 -0
- iceaxe/typing.py +73 -0
- iceaxe-0.8.3.dist-info/METADATA +262 -0
- iceaxe-0.8.3.dist-info/RECORD +75 -0
- iceaxe-0.8.3.dist-info/WHEEL +6 -0
- iceaxe-0.8.3.dist-info/licenses/LICENSE +21 -0
- iceaxe-0.8.3.dist-info/top_level.txt +1 -0
iceaxe/field.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
from json import dumps as json_dumps
|
|
2
|
+
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
4
|
+
Any,
|
|
5
|
+
Callable,
|
|
6
|
+
Concatenate,
|
|
7
|
+
Generic,
|
|
8
|
+
ParamSpec,
|
|
9
|
+
Type,
|
|
10
|
+
TypeVar,
|
|
11
|
+
Unpack,
|
|
12
|
+
cast,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from pydantic import Field as PydanticField
|
|
16
|
+
from pydantic.fields import FieldInfo, _FieldInfoInputs
|
|
17
|
+
from pydantic_core import PydanticUndefined
|
|
18
|
+
|
|
19
|
+
from iceaxe.comparison import ComparisonBase
|
|
20
|
+
from iceaxe.postgres import PostgresFieldBase
|
|
21
|
+
from iceaxe.queries_str import QueryIdentifier, QueryLiteral
|
|
22
|
+
from iceaxe.sql_types import ColumnType
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from iceaxe.base import TableBase
|
|
26
|
+
|
|
27
|
+
P = ParamSpec("P")
|
|
28
|
+
|
|
29
|
+
_Unset: Any = PydanticUndefined
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DBFieldInputs(_FieldInfoInputs, total=False):
|
|
33
|
+
primary_key: bool
|
|
34
|
+
autoincrement: bool
|
|
35
|
+
postgres_config: PostgresFieldBase | None
|
|
36
|
+
foreign_key: str | None
|
|
37
|
+
unique: bool
|
|
38
|
+
index: bool
|
|
39
|
+
check_expression: str | None
|
|
40
|
+
is_json: bool
|
|
41
|
+
explicit_type: ColumnType | None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DBFieldInfo(FieldInfo):
|
|
45
|
+
"""
|
|
46
|
+
Extended field information for database fields, building upon Pydantic's FieldInfo.
|
|
47
|
+
This class adds database-specific attributes and functionality for field configuration
|
|
48
|
+
in SQL databases, particularly PostgreSQL.
|
|
49
|
+
|
|
50
|
+
This class is used internally by the Field constructor to store metadata about
|
|
51
|
+
database columns, including constraints, foreign keys, and PostgreSQL-specific
|
|
52
|
+
configurations.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
primary_key: bool = False
|
|
56
|
+
"""
|
|
57
|
+
Indicates if this field serves as the primary key for the table.
|
|
58
|
+
When True, this field will be used as the unique identifier for rows.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
autoincrement: bool = False
|
|
62
|
+
"""
|
|
63
|
+
Controls whether the field should automatically increment.
|
|
64
|
+
By default, this is True for primary key fields that have no default value set.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
postgres_config: PostgresFieldBase | None = None
|
|
68
|
+
"""
|
|
69
|
+
Custom PostgreSQL configuration for the field.
|
|
70
|
+
Allows for type-specific customization of PostgreSQL parameters.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
foreign_key: str | None = None
|
|
74
|
+
"""
|
|
75
|
+
Specifies a foreign key relationship to another table.
|
|
76
|
+
Format should be "table_name.column_name" if set.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
unique: bool = False
|
|
80
|
+
"""
|
|
81
|
+
When True, enforces that all values in this column must be unique.
|
|
82
|
+
Creates a unique constraint in the database.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
index: bool = False
|
|
86
|
+
"""
|
|
87
|
+
When True, creates an index on this column to optimize query performance.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
check_expression: str | None = None
|
|
91
|
+
"""
|
|
92
|
+
SQL expression for a CHECK constraint on this column.
|
|
93
|
+
Allows for custom validation rules at the database level.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
is_json: bool = False
|
|
97
|
+
"""
|
|
98
|
+
Indicates if this field should be stored as JSON in the database.
|
|
99
|
+
When True, the field's value will be JSON serialized before storage.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
explicit_type: ColumnType | None = None
|
|
103
|
+
"""
|
|
104
|
+
Explicitly specify the SQL column type for this field.
|
|
105
|
+
When set, this type takes precedence over automatic type inference.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
def __init__(self, **kwargs: Unpack[DBFieldInputs]):
|
|
109
|
+
"""
|
|
110
|
+
Initialize a new DBFieldInfo instance with the given field configuration.
|
|
111
|
+
|
|
112
|
+
:param kwargs: Keyword arguments that configure the field's behavior.
|
|
113
|
+
Includes all standard Pydantic field options plus database-specific options.
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
# The super call should persist all kwargs as _attributes_set
|
|
117
|
+
# We're intentionally passing kwargs that we know aren't in the
|
|
118
|
+
# base typehinted dict
|
|
119
|
+
super().__init__(**kwargs) # type: ignore
|
|
120
|
+
self.primary_key = kwargs.pop("primary_key", False)
|
|
121
|
+
self.autoincrement = kwargs.pop(
|
|
122
|
+
"autoincrement", (self.primary_key and self.default is None)
|
|
123
|
+
)
|
|
124
|
+
self.postgres_config = kwargs.pop("postgres_config", None)
|
|
125
|
+
self.foreign_key = kwargs.pop("foreign_key", None)
|
|
126
|
+
self.unique = kwargs.pop("unique", False)
|
|
127
|
+
self.index = kwargs.pop("index", False)
|
|
128
|
+
self.check_expression = kwargs.pop("check_expression", None)
|
|
129
|
+
self.is_json = kwargs.pop("is_json", False)
|
|
130
|
+
self.explicit_type = kwargs.pop("explicit_type", None)
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def extend_field(
|
|
134
|
+
cls,
|
|
135
|
+
field: FieldInfo,
|
|
136
|
+
primary_key: bool,
|
|
137
|
+
postgres_config: PostgresFieldBase | None,
|
|
138
|
+
foreign_key: str | None,
|
|
139
|
+
unique: bool,
|
|
140
|
+
index: bool,
|
|
141
|
+
check_expression: str | None,
|
|
142
|
+
is_json: bool,
|
|
143
|
+
explicit_type: ColumnType | None,
|
|
144
|
+
):
|
|
145
|
+
"""
|
|
146
|
+
Helper function to extend a Pydantic FieldInfo with database-specific attributes.
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
return cls(
|
|
150
|
+
primary_key=primary_key,
|
|
151
|
+
postgres_config=postgres_config,
|
|
152
|
+
foreign_key=foreign_key,
|
|
153
|
+
unique=unique,
|
|
154
|
+
index=index,
|
|
155
|
+
check_expression=check_expression,
|
|
156
|
+
is_json=is_json,
|
|
157
|
+
explicit_type=explicit_type,
|
|
158
|
+
**field._attributes_set, # type: ignore
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def to_db_value(self, value: Any):
|
|
162
|
+
if self.is_json:
|
|
163
|
+
return json_dumps(value)
|
|
164
|
+
return value
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def __get_db_field(_: Callable[Concatenate[Any, P], Any] = PydanticField): # type: ignore
|
|
168
|
+
"""
|
|
169
|
+
Workaround constructor to pass typehints through our function subclass
|
|
170
|
+
to the original PydanticField constructor
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
def func(
|
|
175
|
+
primary_key: bool = False,
|
|
176
|
+
postgres_config: PostgresFieldBase | None = None,
|
|
177
|
+
foreign_key: str | None = None,
|
|
178
|
+
unique: bool = False,
|
|
179
|
+
index: bool = False,
|
|
180
|
+
check_expression: str | None = None,
|
|
181
|
+
is_json: bool = False,
|
|
182
|
+
explicit_type: ColumnType | None = None,
|
|
183
|
+
default: Any = _Unset,
|
|
184
|
+
default_factory: (
|
|
185
|
+
Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
|
|
186
|
+
) = _Unset,
|
|
187
|
+
*args: P.args,
|
|
188
|
+
**kwargs: P.kwargs,
|
|
189
|
+
):
|
|
190
|
+
raw_field = PydanticField(
|
|
191
|
+
default=default, default_factory=default_factory, **kwargs
|
|
192
|
+
) # type: ignore
|
|
193
|
+
|
|
194
|
+
# The Any request is required for us to be able to assign fields to any
|
|
195
|
+
# arbitrary type, like `value: str = Field()`
|
|
196
|
+
return cast(
|
|
197
|
+
Any,
|
|
198
|
+
DBFieldInfo.extend_field(
|
|
199
|
+
raw_field,
|
|
200
|
+
primary_key=primary_key,
|
|
201
|
+
postgres_config=postgres_config,
|
|
202
|
+
foreign_key=foreign_key,
|
|
203
|
+
unique=unique,
|
|
204
|
+
index=index,
|
|
205
|
+
check_expression=check_expression,
|
|
206
|
+
is_json=is_json,
|
|
207
|
+
explicit_type=explicit_type,
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return func
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
T = TypeVar("T")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class DBFieldClassDefinition(Generic[T], ComparisonBase[T]):
|
|
218
|
+
"""
|
|
219
|
+
The returned model when users access a field directly from
|
|
220
|
+
the table class, e.g. `User.id`
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
root_model: Type["TableBase"]
|
|
225
|
+
key: str
|
|
226
|
+
field_definition: DBFieldInfo
|
|
227
|
+
|
|
228
|
+
def __init__(
|
|
229
|
+
self,
|
|
230
|
+
root_model: Type["TableBase"],
|
|
231
|
+
key: str,
|
|
232
|
+
field_definition: DBFieldInfo,
|
|
233
|
+
):
|
|
234
|
+
self.root_model = root_model
|
|
235
|
+
self.key = key
|
|
236
|
+
self.field_definition = field_definition
|
|
237
|
+
|
|
238
|
+
def to_query(self):
|
|
239
|
+
table = QueryIdentifier(self.root_model.get_table_name())
|
|
240
|
+
column = QueryIdentifier(self.key)
|
|
241
|
+
return QueryLiteral(f"{table}.{column}"), []
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
Field = __get_db_field()
|
|
245
|
+
"""
|
|
246
|
+
Create a new database field with optional database-specific configurations.
|
|
247
|
+
|
|
248
|
+
This function extends Pydantic's Field with additional database functionality. It accepts
|
|
249
|
+
all standard Pydantic Field parameters plus all the database-specific parameters defined
|
|
250
|
+
in DBFieldInfo.
|
|
251
|
+
|
|
252
|
+
```python {{sticky: True}}
|
|
253
|
+
from iceaxe import Field
|
|
254
|
+
from iceaxe.base import TableBase
|
|
255
|
+
|
|
256
|
+
class User(TableBase):
|
|
257
|
+
id: int = Field(primary_key=True)
|
|
258
|
+
username: str = Field(unique=True, index=True)
|
|
259
|
+
settings: dict = Field(is_json=True, default_factory=dict)
|
|
260
|
+
department_id: int = Field(foreign_key="departments.id")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
"""
|