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.

Files changed (75) hide show
  1. iceaxe/__init__.py +20 -0
  2. iceaxe/__tests__/__init__.py +0 -0
  3. iceaxe/__tests__/benchmarks/__init__.py +0 -0
  4. iceaxe/__tests__/benchmarks/test_bulk_insert.py +45 -0
  5. iceaxe/__tests__/benchmarks/test_select.py +114 -0
  6. iceaxe/__tests__/conf_models.py +133 -0
  7. iceaxe/__tests__/conftest.py +204 -0
  8. iceaxe/__tests__/docker_helpers.py +208 -0
  9. iceaxe/__tests__/helpers.py +268 -0
  10. iceaxe/__tests__/migrations/__init__.py +0 -0
  11. iceaxe/__tests__/migrations/conftest.py +36 -0
  12. iceaxe/__tests__/migrations/test_action_sorter.py +237 -0
  13. iceaxe/__tests__/migrations/test_generator.py +140 -0
  14. iceaxe/__tests__/migrations/test_generics.py +91 -0
  15. iceaxe/__tests__/mountaineer/__init__.py +0 -0
  16. iceaxe/__tests__/mountaineer/dependencies/__init__.py +0 -0
  17. iceaxe/__tests__/mountaineer/dependencies/test_core.py +76 -0
  18. iceaxe/__tests__/schemas/__init__.py +0 -0
  19. iceaxe/__tests__/schemas/test_actions.py +1265 -0
  20. iceaxe/__tests__/schemas/test_cli.py +25 -0
  21. iceaxe/__tests__/schemas/test_db_memory_serializer.py +1571 -0
  22. iceaxe/__tests__/schemas/test_db_serializer.py +435 -0
  23. iceaxe/__tests__/schemas/test_db_stubs.py +190 -0
  24. iceaxe/__tests__/test_alias.py +83 -0
  25. iceaxe/__tests__/test_base.py +52 -0
  26. iceaxe/__tests__/test_comparison.py +383 -0
  27. iceaxe/__tests__/test_field.py +11 -0
  28. iceaxe/__tests__/test_helpers.py +9 -0
  29. iceaxe/__tests__/test_modifications.py +151 -0
  30. iceaxe/__tests__/test_queries.py +764 -0
  31. iceaxe/__tests__/test_queries_str.py +173 -0
  32. iceaxe/__tests__/test_session.py +1511 -0
  33. iceaxe/__tests__/test_text_search.py +287 -0
  34. iceaxe/alias_values.py +67 -0
  35. iceaxe/base.py +351 -0
  36. iceaxe/comparison.py +560 -0
  37. iceaxe/field.py +263 -0
  38. iceaxe/functions.py +1432 -0
  39. iceaxe/generics.py +140 -0
  40. iceaxe/io.py +107 -0
  41. iceaxe/logging.py +91 -0
  42. iceaxe/migrations/__init__.py +5 -0
  43. iceaxe/migrations/action_sorter.py +98 -0
  44. iceaxe/migrations/cli.py +228 -0
  45. iceaxe/migrations/client_io.py +62 -0
  46. iceaxe/migrations/generator.py +404 -0
  47. iceaxe/migrations/migration.py +86 -0
  48. iceaxe/migrations/migrator.py +101 -0
  49. iceaxe/modifications.py +176 -0
  50. iceaxe/mountaineer/__init__.py +10 -0
  51. iceaxe/mountaineer/cli.py +74 -0
  52. iceaxe/mountaineer/config.py +46 -0
  53. iceaxe/mountaineer/dependencies/__init__.py +6 -0
  54. iceaxe/mountaineer/dependencies/core.py +67 -0
  55. iceaxe/postgres.py +133 -0
  56. iceaxe/py.typed +0 -0
  57. iceaxe/queries.py +1459 -0
  58. iceaxe/queries_str.py +294 -0
  59. iceaxe/schemas/__init__.py +0 -0
  60. iceaxe/schemas/actions.py +864 -0
  61. iceaxe/schemas/cli.py +30 -0
  62. iceaxe/schemas/db_memory_serializer.py +711 -0
  63. iceaxe/schemas/db_serializer.py +347 -0
  64. iceaxe/schemas/db_stubs.py +529 -0
  65. iceaxe/session.py +860 -0
  66. iceaxe/session_optimized.c +12207 -0
  67. iceaxe/session_optimized.cpython-313-darwin.so +0 -0
  68. iceaxe/session_optimized.pyx +212 -0
  69. iceaxe/sql_types.py +149 -0
  70. iceaxe/typing.py +73 -0
  71. iceaxe-0.8.3.dist-info/METADATA +262 -0
  72. iceaxe-0.8.3.dist-info/RECORD +75 -0
  73. iceaxe-0.8.3.dist-info/WHEEL +6 -0
  74. iceaxe-0.8.3.dist-info/licenses/LICENSE +21 -0
  75. 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
+ """