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