statezero 0.1.0b4__py3-none-any.whl → 0.1.0b5__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.

Potentially problematic release.


This version of statezero might be problematic. Click here for more details.

@@ -1,104 +1,126 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union, Literal, Protocol
4
+ from typing import (
5
+ Any,
6
+ Callable,
7
+ Dict,
8
+ List,
9
+ Optional,
10
+ Set,
11
+ Tuple,
12
+ Type,
13
+ Union,
14
+ Literal,
15
+ Protocol,
16
+ )
5
17
 
6
18
  from statezero.core.classes import ModelSchemaMetadata, SchemaFieldMetadata
7
- from statezero.core.types import (ActionType, ORMField, ORMModel, ORMQuerySet, RequestType)
19
+ from statezero.core.types import (
20
+ ActionType,
21
+ ORMField,
22
+ ORMModel,
23
+ ORMQuerySet,
24
+ RequestType,
25
+ )
26
+
8
27
 
9
28
  class AbstractORMProvider(ABC):
10
29
  """
11
30
  A merged ORM engine interface that combines both query building (filtering,
12
31
  ordering, aggregation, etc.) and ORM provider responsibilities (queryset assembly,
13
32
  event signal registration, model graph construction, etc.).
33
+
34
+ UPDATED: All query manipulation methods now take queryset parameters and return
35
+ new querysets instead of mutating internal state.
14
36
  """
15
37
 
16
- # === Query Engine Methods ===
38
+ # === Query Engine Methods (UPDATED: Now stateless) ===
17
39
 
18
40
  @abstractmethod
19
- def get_fields(self) -> Set[str]:
41
+ def get_fields(self, model: ORMModel) -> Set[str]:
20
42
  """
21
43
  Get all of the model fields - doesn't apply permissions check.
22
44
  """
23
45
  pass
24
46
 
25
47
  @abstractmethod
26
- def filter_node(self, node: Dict[str, Any]) -> None:
48
+ def filter_node(self, queryset: ORMQuerySet, node: Dict[str, Any]) -> ORMQuerySet:
27
49
  """
28
- Apply filter/and/or/not logic to the current query.
50
+ Apply filter/and/or/not logic to the queryset and return new queryset.
29
51
  """
30
52
  pass
31
53
 
32
54
  @abstractmethod
33
- def search_node(self, search_query: str, search_fields: Set[str]) -> None:
55
+ def search_node(
56
+ self, queryset: ORMQuerySet, search_query: str, search_fields: Set[str]
57
+ ) -> ORMQuerySet:
34
58
  """
35
- Apply search to the current query.
59
+ Apply search to the queryset and return new queryset.
36
60
  """
37
61
  pass
38
62
 
39
63
  @abstractmethod
40
- def create(self, data: Dict[str, Any]) -> Any:
41
- """Create a new record."""
42
- pass
43
-
44
- @abstractmethod
45
- def update(self, node: Dict[str, Any]) -> int:
64
+ def exclude_node(self, queryset: ORMQuerySet, node: Dict[str, Any]) -> ORMQuerySet:
46
65
  """
47
- Update records (by filter or primary key).
48
- Returns the number of rows updated.
66
+ Apply exclude logic to the queryset and return new queryset.
49
67
  """
50
68
  pass
51
69
 
52
70
  @abstractmethod
53
- def delete(self, node: Dict[str, Any]) -> int:
71
+ def order_by(self, queryset: ORMQuerySet, order_list: List[str]) -> ORMQuerySet:
54
72
  """
55
- Delete records (by filter or primary key).
56
- Returns the number of rows deleted.
73
+ Order the queryset based on a list of fields and return new queryset.
57
74
  """
58
75
  pass
59
76
 
60
77
  @abstractmethod
61
- def get(self, node: Dict[str, Any]) -> Any:
78
+ def select_related(
79
+ self, queryset: ORMQuerySet, related_fields: List[str]
80
+ ) -> ORMQuerySet:
62
81
  """
63
- Retrieve a single record. Raises an error if multiple or none are found.
82
+ Optimize the queryset by eager loading the given related fields and return new queryset.
64
83
  """
65
84
  pass
66
85
 
67
86
  @abstractmethod
68
- def get_or_create(self, node: Dict[str, Any]) -> Tuple[Any, bool]:
87
+ def prefetch_related(
88
+ self, queryset: ORMQuerySet, related_fields: List[str]
89
+ ) -> ORMQuerySet:
69
90
  """
70
- Retrieve a record if it exists, otherwise create it.
71
- Returns a tuple of (instance, created_flag).
91
+ Optimize the queryset by prefetching the given related fields and return new queryset.
72
92
  """
73
93
  pass
74
94
 
75
95
  @abstractmethod
76
- def update_or_create(self, node: Dict[str, Any]) -> Tuple[Any, bool]:
96
+ def select_fields(self, queryset: ORMQuerySet, fields: List[str]) -> ORMQuerySet:
77
97
  """
78
- Update a record if it exists or create it if it doesn't.
79
- Returns a tuple of (instance, created_flag).
98
+ Select only specific fields from the queryset and return new queryset.
80
99
  """
81
100
  pass
82
101
 
83
102
  @abstractmethod
84
- def first(self) -> Any:
85
- """Return the first record from the current query."""
86
- pass
87
-
88
- @abstractmethod
89
- def last(self) -> Any:
90
- """Return the last record from the current query."""
103
+ def fetch_list(
104
+ self,
105
+ queryset: ORMQuerySet,
106
+ offset: Optional[int] = None,
107
+ limit: Optional[int] = None,
108
+ req: Optional[RequestType] = None,
109
+ permissions: Optional[List[Type]] = None,
110
+ ) -> ORMQuerySet:
111
+ """
112
+ Return a sliced queryset based on pagination with permission checks.
113
+ """
91
114
  pass
92
115
 
93
- @abstractmethod
94
- def exists(self) -> bool:
95
- """Return True if the current query has any results; otherwise False."""
96
- pass
116
+ # === Aggregate Methods (UPDATED: Take queryset parameter) ===
97
117
 
98
118
  @abstractmethod
99
- def aggregate(self, agg_list: List[Dict[str, Any]]) -> Dict[str, Any]:
119
+ def aggregate(
120
+ self, queryset: ORMQuerySet, agg_list: List[Dict[str, Any]]
121
+ ) -> Dict[str, Any]:
100
122
  """
101
- Aggregate the current query based on the provided functions.
123
+ Aggregate the queryset based on the provided functions.
102
124
  Example:
103
125
  [
104
126
  {'function': 'count', 'field': 'id', 'alias': 'id_count'},
@@ -108,60 +130,155 @@ class AbstractORMProvider(ABC):
108
130
  pass
109
131
 
110
132
  @abstractmethod
111
- def count(self, field: str) -> int:
133
+ def count(self, queryset: ORMQuerySet, field: str) -> int:
112
134
  """Count the number of records for the given field."""
113
135
  pass
114
136
 
115
137
  @abstractmethod
116
- def sum(self, field: str) -> Any:
138
+ def sum(self, queryset: ORMQuerySet, field: str) -> Any:
117
139
  """Sum the values of the given field."""
118
140
  pass
119
141
 
120
142
  @abstractmethod
121
- def avg(self, field: str) -> Any:
143
+ def avg(self, queryset: ORMQuerySet, field: str) -> Any:
122
144
  """Calculate the average of the given field."""
123
145
  pass
124
146
 
125
147
  @abstractmethod
126
- def min(self, field: str) -> Any:
148
+ def min(self, queryset: ORMQuerySet, field: str) -> Any:
127
149
  """Find the minimum value for the given field."""
128
150
  pass
129
151
 
130
152
  @abstractmethod
131
- def max(self, field: str) -> Any:
153
+ def max(self, queryset: ORMQuerySet, field: str) -> Any:
132
154
  """Find the maximum value for the given field."""
133
155
  pass
134
156
 
135
157
  @abstractmethod
136
- def order_by(self, order_list: List[Dict[str, str]]) -> None:
158
+ def first(self, queryset: ORMQuerySet) -> Any:
159
+ """Return the first record from the queryset."""
160
+ pass
161
+
162
+ @abstractmethod
163
+ def last(self, queryset: ORMQuerySet) -> Any:
164
+ """Return the last record from the queryset."""
165
+ pass
166
+
167
+ @abstractmethod
168
+ def exists(self, queryset: ORMQuerySet) -> bool:
169
+ """Return True if the queryset has any results; otherwise False."""
170
+ pass
171
+
172
+ # === CRUD Methods (UPDATED: Take model or queryset parameters explicitly) ===
173
+
174
+ @abstractmethod
175
+ def create(
176
+ self, model: Type[ORMModel], data: Dict[str, Any], *args, **kwargs
177
+ ) -> Any:
178
+ """Create a new record using the model class."""
179
+ pass
180
+
181
+ @abstractmethod
182
+ def update(
183
+ self,
184
+ queryset: ORMQuerySet,
185
+ node: Dict[str, Any],
186
+ req: RequestType,
187
+ permissions: List[Type],
188
+ readable_fields: Optional[Set[str]] = None,
189
+ ) -> Tuple[int, List[Any]]:
137
190
  """
138
- Order the query based on a list of fields.
139
- Each dict should contain 'field' and optionally 'direction' ('asc' or 'desc').
191
+ Update records in the queryset.
192
+ Returns tuple of (number of rows updated, updated instances).
140
193
  """
141
194
  pass
142
195
 
143
196
  @abstractmethod
144
- def select_related(self, related_fields: List[str]) -> None:
197
+ def delete(
198
+ self,
199
+ queryset: ORMQuerySet,
200
+ node: Dict[str, Any],
201
+ req: RequestType,
202
+ permissions: List[Type],
203
+ ) -> Tuple[int, Any]:
145
204
  """
146
- Optimize the query by eager loading the given related fields.
205
+ Delete records in the queryset.
206
+ Returns tuple of (number of rows deleted, deleted instance data).
147
207
  """
148
208
  pass
149
209
 
150
210
  @abstractmethod
151
- def prefetch_related(self, related_fields: List[str]) -> None:
211
+ def get(
212
+ self,
213
+ queryset: ORMQuerySet,
214
+ node: Dict[str, Any],
215
+ req: RequestType,
216
+ permissions: List[Type],
217
+ ) -> Any:
152
218
  """
153
- Optimize the query by prefetching the given related fields.
219
+ Retrieve a single record from the queryset.
220
+ Raises an error if multiple or none are found.
154
221
  """
155
222
  pass
156
223
 
157
224
  @abstractmethod
158
- def fetch_list(self, offset: int, limit: int) -> List[Any]:
225
+ def get_or_create(
226
+ self,
227
+ queryset: ORMQuerySet,
228
+ node: Dict[str, Any],
229
+ serializer: Any,
230
+ req: RequestType,
231
+ permissions: List[Type],
232
+ create_fields_map: Dict[str, Set[str]],
233
+ ) -> Tuple[Any, bool]:
159
234
  """
160
- Return a list of records (as dicts or objects) based on pagination.
235
+ Retrieve a record if it exists, otherwise create it.
236
+ Returns a tuple of (instance, created_flag).
161
237
  """
162
238
  pass
163
239
 
164
- # === ORM Provider Methods ===
240
+ @abstractmethod
241
+ def update_or_create(
242
+ self,
243
+ queryset: ORMQuerySet,
244
+ node: Dict[str, Any],
245
+ req: RequestType,
246
+ serializer: Any,
247
+ permissions: List[Type],
248
+ update_fields_map: Dict[str, Set[str]],
249
+ create_fields_map: Dict[str, Set[str]],
250
+ ) -> Tuple[Any, bool]:
251
+ """
252
+ Update a record if it exists or create it if it doesn't.
253
+ Returns a tuple of (instance, created_flag).
254
+ """
255
+ pass
256
+
257
+ @abstractmethod
258
+ def update_instance(
259
+ self,
260
+ model: Type[ORMModel],
261
+ ast: Dict[str, Any],
262
+ req: RequestType,
263
+ permissions: List[Type],
264
+ serializer: Any,
265
+ fields_map: Dict[str, Set[str]],
266
+ ) -> Any:
267
+ """Update a single model instance by filter."""
268
+ pass
269
+
270
+ @abstractmethod
271
+ def delete_instance(
272
+ self,
273
+ model: Type[ORMModel],
274
+ ast: Dict[str, Any],
275
+ req: RequestType,
276
+ permissions: List[Type],
277
+ ) -> int:
278
+ """Delete a single model instance by filter."""
279
+ pass
280
+
281
+ # === ORM Provider Methods (Unchanged - these are utility methods) ===
165
282
 
166
283
  @abstractmethod
167
284
  def get_queryset(
@@ -205,29 +322,32 @@ class AbstractORMProvider(ABC):
205
322
  pass
206
323
 
207
324
  @abstractmethod
208
- def get_user(self, request: RequestType): # returns User
325
+ def get_user(self, request: RequestType): # returns User
209
326
  """
210
327
  Get the request user.
211
328
  """
212
329
  pass
213
330
 
214
331
  @abstractmethod
215
- def build_model_graph(self, model: ORMModel) -> None: # type:ignore
332
+ def build_model_graph(self, model: ORMModel) -> Any: # type:ignore
216
333
  """
217
334
  Construct a graph representation of model relationships.
218
335
  """
219
336
  pass
220
337
 
221
338
 
339
+ # === Other Abstract Classes (Unchanged) ===
340
+
341
+
222
342
  class AbstractCustomQueryset(ABC):
223
343
  @abstractmethod
224
344
  def get_queryset(self, request: Optional[RequestType] = None) -> Any:
225
345
  """
226
346
  Return a custom queryset (e.g. a custom SQLAlchemy Query or Django QuerySet).
227
-
347
+
228
348
  Args:
229
349
  request: The current request object, which may contain user information
230
-
350
+
231
351
  Returns:
232
352
  A custom queryset
233
353
  """
@@ -302,7 +422,7 @@ class AbstractEventEmitter(ABC):
302
422
  ) -> None:
303
423
  """
304
424
  Emit an event to the specified namespace with the given event type and data.
305
-
425
+
306
426
  Parameters:
307
427
  -----------
308
428
  namespace: str
@@ -406,24 +526,31 @@ class AbstractPermission(ABC):
406
526
  """
407
527
  pass
408
528
 
529
+
409
530
  class AbstractSearchProvider(ABC):
410
531
  """Base class for search providers in StateZero."""
411
-
532
+
412
533
  @abstractmethod
413
- def search(self, queryset: ORMQuerySet, query: str, search_fields: Union[Set[str], Literal["__all__"]]) -> ORMQuerySet:
534
+ def search(
535
+ self,
536
+ queryset: ORMQuerySet,
537
+ query: str,
538
+ search_fields: Union[Set[str], Literal["__all__"]],
539
+ ) -> ORMQuerySet:
414
540
  """
415
541
  Apply search filtering to a queryset.
416
-
542
+
417
543
  Args:
418
544
  queryset: Django queryset
419
545
  query: The search query string
420
546
  search_fields: Set of field names to search in
421
-
547
+
422
548
  Returns:
423
549
  Filtered queryset with search applied
424
550
  """
425
551
  pass
426
552
 
553
+
427
554
  class AbstractQueryOptimizer(ABC):
428
555
  """
429
556
  Abstract Base Class for query optimizers.
@@ -436,7 +563,7 @@ class AbstractQueryOptimizer(ABC):
436
563
  self,
437
564
  depth: Optional[int] = None,
438
565
  fields_per_model: Optional[Dict[str, Set[str]]] = None,
439
- get_model_name_func: Optional[Callable[[Type[ORMModel]], str]] = None
566
+ get_model_name_func: Optional[Callable[[Type[ORMModel]], str]] = None,
440
567
  ):
441
568
  """
442
569
  Initializes the optimizer with common configuration potentially
@@ -462,10 +589,7 @@ class AbstractQueryOptimizer(ABC):
462
589
 
463
590
  @abstractmethod
464
591
  def optimize(
465
- self,
466
- queryset: Any,
467
- fields: Optional[List[str]] = None,
468
- **kwargs: Any
592
+ self, queryset: Any, fields: Optional[List[str]] = None, **kwargs: Any
469
593
  ) -> Any:
470
594
  """
471
595
  Optimizes the given query object.
@@ -170,13 +170,13 @@ class RequestProcessor:
170
170
  )
171
171
 
172
172
  # Create and use the AST parser directly, instead of delegating to ORM provider
173
- self.orm_provider.set_queryset(base_queryset)
174
173
  parser = ASTParser(
175
174
  engine=self.orm_provider,
176
175
  serializer=self.data_serializer,
177
176
  model=model,
178
177
  config=self.config,
179
178
  registry=self.registry,
179
+ base_queryset=base_queryset, # Pass the queryset here
180
180
  serializer_options=serializer_options or {},
181
181
  request=req,
182
182
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: statezero
3
- Version: 0.1.0b4
3
+ Version: 0.1.0b5
4
4
  Summary: Connect your Python backend to a modern JavaScript SPA frontend with 90% less complexity.
5
5
  Author-email: Robert <robert.herring@statezero.dev>
6
6
  Project-URL: homepage, https://www.statezero.dev
@@ -9,7 +9,7 @@ statezero/adaptors/django/exception_handler.py,sha256=cQF1Fm5IjH91ydB54TK9sqXiAO
9
9
  statezero/adaptors/django/f_handler.py,sha256=yvITFj9UAnz8-r-aLEcWoz48tBhZ08-VMq9Fsm2uiN8,12305
10
10
  statezero/adaptors/django/helpers.py,sha256=0Dyq5vboDuTUaH-KpS3oVDjastA9yv6xI6XpBuvRM3I,5974
11
11
  statezero/adaptors/django/middleware.py,sha256=YVr8fkqCk51xJQM-ovtrUiB9Kt9H81cLd9xv4cM9YlM,410
12
- statezero/adaptors/django/orm.py,sha256=G_dhMzX5iaaTSxO4bxSZvxMiBw7mLMWY2GUD0mlkMNg,36304
12
+ statezero/adaptors/django/orm.py,sha256=-9tWH9hW2Ta5AipXysoWy-em7R-ybHKmPLtBX89nYQc,37118
13
13
  statezero/adaptors/django/permissions.py,sha256=fU2c4bKK0zX2uuVB0UazZHTI-5OkiI5-BtPNcPEWmW0,9525
14
14
  statezero/adaptors/django/query_optimizer.py,sha256=-GNqL7Xn8WP8OsLEAAxXpIszSyEwm-l6WjgdkEFzxUM,38541
15
15
  statezero/adaptors/django/schemas.py,sha256=shq8ed9qHCnbCfYVsRxVE7V3R3GhGIKeRRj7dI3r1IU,12728
@@ -27,7 +27,7 @@ statezero/adaptors/django/search_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW
27
27
  statezero/adaptors/django/search_providers/basic_search.py,sha256=5_GJ1r_B6JdIYXL6yYEYn5mik88EolSH5aZvygc_UF0,977
28
28
  statezero/adaptors/django/search_providers/postgres_search.py,sha256=IMoHxzfi-Y3hAxPND4Xc6GatrPs1eXAqpmcfwt5Zr14,2459
29
29
  statezero/core/__init__.py,sha256=Z6RTutAAElLMEjBFphVVmpySPdJBA55j-Uo0BtR7c5E,1040
30
- statezero/core/ast_parser.py,sha256=epJ2pBpzRlKnrSgDMdVNOTRX6NX28khwkf_xOoPH_Uw,35561
30
+ statezero/core/ast_parser.py,sha256=ezEHqVB1Afqw_GSyUs4Dh0W94xFM4aaNEaxyYMk-rr4,38756
31
31
  statezero/core/ast_validator.py,sha256=YZAflPyba0kXWBNhd1Z_XeEk-_zUzM6MkY9zSlX1PMs,11582
32
32
  statezero/core/classes.py,sha256=-rJ8szqGGzsFxE3TvbtYHFHFP9Kg2WP24aYi74Io338,4923
33
33
  statezero/core/config.py,sha256=oOjcudQO0XgEZ60kwkzpcOoTi0QZARS8XWbp9gzgasg,11489
@@ -36,11 +36,11 @@ statezero/core/event_bus.py,sha256=2IFLBHSkLzpm1AX0MfSXSmF2X-lXK-gOoODZCtB2Jdw,6
36
36
  statezero/core/event_emitters.py,sha256=qjMbeUmdn4bG7WiVfqTmNdaflEea5amnTEpOn5X0J44,2046
37
37
  statezero/core/exceptions.py,sha256=_krMHWW9qBbMXvvqFdWf85a3Kayn7XbJczfC3x3gmBI,3330
38
38
  statezero/core/hook_checks.py,sha256=uqtvwRx1qGsF7Vc49elAWdOjMzhuv3RADKY1wiLvhK4,3425
39
- statezero/core/interfaces.py,sha256=HwcFXWJfIAxqYgWbGesn1Th9O8mvqH2r0XP2UyYifEw,15866
40
- statezero/core/process_request.py,sha256=NiG36XEjPMemJI03l5eTNaymyfUbwVgrx78chVcs4Nk,8047
39
+ statezero/core/interfaces.py,sha256=VCekst5esL9Yh739tW8PSedRS_s50qhEzlnJ8pwaxtU,19064
40
+ statezero/core/process_request.py,sha256=dwIeBEVOE8zA-oE1h65XNOGiVqFbbXA7SzTAguLNgZk,8060
41
41
  statezero/core/types.py,sha256=K9x9AU5J6yd2AWvqRz27CeAY6UYfuQoQ7xTEwTijrmM,1982
42
- statezero-0.1.0b4.dist-info/licenses/license.md,sha256=0uKjybTt9K_YbEmYgf25JN292qjjJ-BPofvIZ3wdtX4,7411
43
- statezero-0.1.0b4.dist-info/METADATA,sha256=dr2oqS3zZzB7K_CPWA3AGfi9svC-oCRqQAzBQUHWOx8,7152
44
- statezero-0.1.0b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
- statezero-0.1.0b4.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
46
- statezero-0.1.0b4.dist-info/RECORD,,
42
+ statezero-0.1.0b5.dist-info/licenses/license.md,sha256=0uKjybTt9K_YbEmYgf25JN292qjjJ-BPofvIZ3wdtX4,7411
43
+ statezero-0.1.0b5.dist-info/METADATA,sha256=cHmsrjlDYnl5F-H5HSHcgqpP13UMLfZr9M8gqoC7UZc,7152
44
+ statezero-0.1.0b5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
45
+ statezero-0.1.0b5.dist-info/top_level.txt,sha256=UAuZYPKczradU1kcMQxsGjUzEW0qdgsqzhXyscrcLpw,10
46
+ statezero-0.1.0b5.dist-info/RECORD,,