apexdevkit 2.1.3__tar.gz → 2.2.1__tar.gz

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 (53) hide show
  1. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/PKG-INFO +2 -2
  2. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/__init__.py +1 -1
  3. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/annotation/deprecate.py +3 -3
  4. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/date.py +16 -16
  5. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/__init__.py +2 -2
  6. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/builder.py +6 -3
  7. apexdevkit-2.2.1/apexdevkit/fastapi/resource.py +103 -0
  8. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/router.py +21 -11
  9. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/schema.py +0 -3
  10. apexdevkit-2.2.1/apexdevkit/fastapi/service.py +68 -0
  11. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/query/generator.py +16 -16
  12. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/__init__.py +5 -8
  13. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/core/__init__.py +4 -8
  14. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/core/database.py +1 -1
  15. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/in_memory/__init__.py +1 -1
  16. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/in_memory/repository.py +4 -1
  17. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/in_memory/store.py +4 -2
  18. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/__init__.py +1 -1
  19. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/connector.py +6 -3
  20. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/field.py +17 -10
  21. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/mssql/repository.py +2 -2
  22. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/mssql/table.py +6 -3
  23. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/sqlite/__init__.py +1 -1
  24. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/sqlite/repository.py +6 -3
  25. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/sqlite/table.py +6 -3
  26. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/server.py +7 -4
  27. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/sync/__init__.py +4 -4
  28. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/sync/observer.py +5 -3
  29. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/sync/source.py +7 -7
  30. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/sync/sync.py +12 -4
  31. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/testing.py +6 -4
  32. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/pyproject.toml +61 -38
  33. apexdevkit-2.1.3/apexdevkit/fastapi/docs.py +0 -15
  34. apexdevkit-2.1.3/apexdevkit/fastapi/resource.py +0 -319
  35. apexdevkit-2.1.3/apexdevkit/fastapi/service.py +0 -118
  36. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/LICENSE +0 -0
  37. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/README.md +0 -0
  38. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/annotation/__init__.py +0 -0
  39. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/error.py +0 -0
  40. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/dependable.py +0 -0
  41. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/name.py +0 -0
  42. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/fastapi/response.py +0 -0
  43. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/formatter.py +0 -0
  44. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/key_fn.py +0 -0
  45. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/py.typed +0 -0
  46. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/query/__init__.py +11 -11
  47. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/query/query.py +0 -0
  48. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/core/decorator.py +0 -0
  49. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/core/interface.py +0 -0
  50. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/core/mixin.py +0 -0
  51. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/in_memory/mixin.py +0 -0
  52. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/repository/sql/mssql/__init__.py +1 -1
  53. {apexdevkit-2.1.3 → apexdevkit-2.2.1}/apexdevkit/sync/target.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apexdevkit
3
- Version: 2.1.3
3
+ Version: 2.2.1
4
4
  Summary: Apex Development Tools for python.
5
5
  License-File: LICENSE
6
6
  Author: Apex Dev
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.15
14
14
  Requires-Dist: fastapi
15
15
  Requires-Dist: pydantic
16
16
  Requires-Dist: pymssql (==2.*)
17
- Requires-Dist: pypebbles (==0.3.*)
17
+ Requires-Dist: pypebbles (==0.4.*)
18
18
  Requires-Dist: python-dotenv
19
19
  Requires-Dist: sentry-sdk[fastapi]
20
20
  Requires-Dist: uvicorn
@@ -2,8 +2,8 @@ from dotenv import load_dotenv
2
2
  from fastapi import APIRouter, FastAPI, HTTPException
3
3
 
4
4
  __all__ = [
5
- "FastAPI",
6
5
  "APIRouter",
6
+ "FastAPI",
7
7
  "HTTPException",
8
8
  "load_dotenv",
9
9
  ]
@@ -25,7 +25,7 @@ def deprecated(warning: str) -> Callable[[F], F]:
25
25
  if inspect.isfunction(obj) or inspect.ismethod(obj):
26
26
  return _wrap_function(obj, warning)
27
27
  if inspect.isclass(obj):
28
- return cast(F, _deprecate_class(obj, warning))
28
+ return cast("F", _deprecate_class(obj, warning))
29
29
  raise TypeError(
30
30
  f"Unsupported type for deprecation: {type(obj)}"
31
31
  ) # pragma: no cover
@@ -38,7 +38,7 @@ def _wrap_function[F: Callable[..., Any]](func: F, warning: str) -> F:
38
38
  warn(warning, category=DeprecationWarning, stacklevel=2)
39
39
  return func(*args, **kwargs)
40
40
 
41
- return cast(F, wrapper)
41
+ return cast("F", wrapper)
42
42
 
43
43
 
44
44
  def _deprecate_class(cls: type, warning: str) -> type:
@@ -47,4 +47,4 @@ def _deprecate_class(cls: type, warning: str) -> type:
47
47
  warn(warning, category=DeprecationWarning, stacklevel=2)
48
48
  super().__init__(*args, **kwargs)
49
49
 
50
- return cast(type, DeprecatedClass)
50
+ return cast("type", DeprecatedClass)
@@ -36,28 +36,28 @@ class DateTime:
36
36
  # Date in format of "yyyy-mm-dd"
37
37
  @classmethod
38
38
  def from_date(cls, date: str, time_zone: ZoneInfo | None = None) -> DateTime:
39
- if time_zone is None:
40
- time_zone = ZoneInfo("UTC")
41
-
42
- dt = datetime.strptime(date, "%Y-%m-%d")
43
- dt = dt.replace(tzinfo=time_zone)
44
- timestamp_ms = int(dt.timestamp() * 1000)
45
-
46
- return cls(timestamp_ms=timestamp_ms)
39
+ return cls(
40
+ timestamp_ms=int(
41
+ datetime.strptime(date, "%Y-%m-%d")
42
+ .replace(tzinfo=time_zone or ZoneInfo("UTC"))
43
+ .timestamp()
44
+ * 1000
45
+ )
46
+ )
47
47
 
48
48
  # Date in format of "yyyy-mm-ddTHH:MM:SS"
49
49
  @classmethod
50
50
  def from_date_time(
51
51
  cls, date_time: str, time_zone: ZoneInfo | None = None
52
52
  ) -> DateTime:
53
- if time_zone is None:
54
- time_zone = ZoneInfo("UTC")
55
-
56
- dt = datetime.strptime(date_time, "%Y-%m-%dT%H:%M:%S")
57
- dt = dt.replace(tzinfo=time_zone)
58
- timestamp_ms = int(dt.timestamp() * 1000)
59
-
60
- return cls(timestamp_ms=timestamp_ms)
53
+ return cls(
54
+ timestamp_ms=int(
55
+ datetime.strptime(date_time, "%Y-%m-%dT%H:%M:%S")
56
+ .replace(tzinfo=time_zone or ZoneInfo("UTC"))
57
+ .timestamp()
58
+ * 1000
59
+ )
60
+ )
61
61
 
62
62
  @classmethod
63
63
  def from_timestamp(cls, timestamp: float) -> DateTime:
@@ -5,9 +5,9 @@ from apexdevkit.fastapi.service import RestfulRepository
5
5
 
6
6
  __all__ = [
7
7
  "FastApiBuilder",
8
- "RestfulServiceBuilder",
9
- "inject",
10
8
  "RestfulRepository",
11
9
  "RestfulRouter",
10
+ "RestfulServiceBuilder",
12
11
  "RouterWithHiddenUnderscoreRoutes",
12
+ "inject",
13
13
  ]
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from collections.abc import Mapping, MutableSet
5
4
  from dataclasses import dataclass, field
6
- from typing import Any, Protocol, Self
5
+ from typing import TYPE_CHECKING, Any, Protocol, Self
7
6
 
8
7
  from fastapi import APIRouter, FastAPI
9
8
  from fastapi.middleware.cors import CORSMiddleware
@@ -12,7 +11,11 @@ from fastapi.responses import JSONResponse
12
11
  from apexdevkit.annotation import deprecated
13
12
  from apexdevkit.error import ApiError
14
13
  from apexdevkit.fastapi.dependable import DependableBuilder
15
- from apexdevkit.fastapi.service import RestfulService
14
+
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Mapping, MutableSet
17
+
18
+ from apexdevkit.fastapi.service import RestfulService
16
19
 
17
20
 
18
21
  @dataclass
@@ -0,0 +1,103 @@
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+
5
+ from fastapi.responses import JSONResponse
6
+
7
+ from apexdevkit.error import DoesNotExistError, ExistsError, ForbiddenError
8
+ from apexdevkit.fastapi.response import RestfulResponse
9
+
10
+ _Response = JSONResponse | dict[str, Any]
11
+ _Endpoint = Callable[..., _Response]
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class RestfulResource:
16
+ response: RestfulResponse
17
+
18
+ def create_one_endpoint(self, Service, Item) -> _Endpoint:
19
+ def endpoint(service: Service, item: Item) -> _Response:
20
+ try:
21
+ result = service.create_one(item)
22
+ except ExistsError as e:
23
+ return JSONResponse(self.response.exists(e), 409)
24
+ except ForbiddenError as e:
25
+ return JSONResponse(self.response.forbidden(e), 403)
26
+
27
+ return self.response.created_one(result)
28
+
29
+ return endpoint
30
+
31
+ def read_one_endpoint(self, Service, ItemId) -> _Endpoint:
32
+ def endpoint(service: Service, item_id: ItemId) -> _Response:
33
+ try:
34
+ result = service.read_one(item_id)
35
+ except DoesNotExistError as e:
36
+ return JSONResponse(self.response.not_found(e), 404)
37
+ except ForbiddenError as e:
38
+ return JSONResponse(self.response.forbidden(e), 403)
39
+
40
+ return self.response.found_one(result)
41
+
42
+ return endpoint
43
+
44
+ def read_many_endpoint(self, Service, QueryParams) -> _Endpoint:
45
+ def endpoint(service: Service, params: QueryParams) -> _Response:
46
+ try:
47
+ result = list(service.read_many(**dict(params)))
48
+ except ForbiddenError as e:
49
+ return JSONResponse(self.response.forbidden(e), 403)
50
+
51
+ return self.response.found_many(result)
52
+
53
+ return endpoint
54
+
55
+ def read_all_endpoint(self, Service) -> _Endpoint:
56
+ def endpoint(service: Service) -> _Response:
57
+ try:
58
+ result = list(service.read_all())
59
+ except ForbiddenError as e:
60
+ return JSONResponse(self.response.forbidden(e), 403)
61
+
62
+ return self.response.found_many(result)
63
+
64
+ return endpoint
65
+
66
+ def update_one_endpoint(self, Service, ItemId, Updates) -> _Endpoint:
67
+ def endpoint(service: Service, item_id: ItemId, updates: Updates) -> _Response:
68
+ try:
69
+ service.update_one(item_id, **updates)
70
+ except DoesNotExistError as e:
71
+ return JSONResponse(self.response.not_found(e), 404)
72
+ except ForbiddenError as e:
73
+ return JSONResponse(self.response.forbidden(e), 403)
74
+
75
+ return self.response.ok()
76
+
77
+ return endpoint
78
+
79
+ def replace_one_endpoint(self, Service, Item) -> _Endpoint:
80
+ def endpoint(service: Service, item: Item) -> _Response:
81
+ try:
82
+ service.replace_one(item)
83
+ except DoesNotExistError as e:
84
+ return JSONResponse(self.response.not_found(e), 404)
85
+ except ForbiddenError as e:
86
+ return JSONResponse(self.response.forbidden(e), 403)
87
+
88
+ return self.response.ok()
89
+
90
+ return endpoint
91
+
92
+ def delete_one_endpoint(self, Service, ItemId) -> _Endpoint:
93
+ def endpoint(service: Service, item_id: ItemId) -> _Response:
94
+ try:
95
+ service.delete_one(item_id)
96
+ except DoesNotExistError as e:
97
+ return JSONResponse(self.response.not_found(e), 404)
98
+ except ForbiddenError as e:
99
+ return JSONResponse(self.response.forbidden(e), 403)
100
+
101
+ return self.response.ok()
102
+
103
+ return endpoint
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import dataclass, field
3
3
  from enum import Enum
4
- from typing import Annotated, Any, Protocol, Self, TypeVar
4
+ from typing import Annotated, Any, Protocol, Self
5
5
 
6
6
  from fastapi import APIRouter, Depends, Path, Query
7
7
  from fastapi.responses import JSONResponse
@@ -47,8 +47,6 @@ class RouterWithHiddenUnderscoreRoutes(APIRouter):
47
47
 
48
48
  _Response = JSONResponse | dict[str, Any]
49
49
 
50
- T = TypeVar("T")
51
-
52
50
 
53
51
  class Dependency(Protocol): # pragma: no cover
54
52
  def as_dependable(self) -> type[RestfulService]:
@@ -123,11 +121,12 @@ class RestfulRouter:
123
121
  def with_create_one(
124
122
  self,
125
123
  dependency: Dependency | None = None,
124
+ *,
126
125
  is_documented: bool = True,
127
126
  ) -> Self:
128
127
  self.router.add_api_route(
129
128
  "",
130
- self.resource.create_one(
129
+ self.resource.create_one_endpoint(
131
130
  Service=self._resolve(dependency),
132
131
  Item=Annotated[
133
132
  RawItem,
@@ -147,11 +146,12 @@ class RestfulRouter:
147
146
  def with_read_one(
148
147
  self,
149
148
  dependency: Dependency | None = None,
149
+ *,
150
150
  is_documented: bool = True,
151
151
  ) -> Self:
152
152
  self.router.add_api_route(
153
153
  self.item_path,
154
- self.resource.read_one(
154
+ self.resource.read_one_endpoint(
155
155
  Service=self._resolve(dependency),
156
156
  ItemId=Annotated[
157
157
  str,
@@ -172,14 +172,18 @@ class RestfulRouter:
172
172
  self,
173
173
  query: FluentDict[Any],
174
174
  dependency: Dependency | None = None,
175
+ *,
175
176
  is_documented: bool = True,
176
177
  ) -> Self:
177
178
  self.router.add_api_route(
178
179
  "",
179
- self.resource.read_many(
180
+ self.resource.read_many_endpoint(
180
181
  Service=self._resolve(dependency),
181
182
  QueryParams=Annotated[
182
- Schema(self.name.singular).optional_schema_for("ReadMany", query),
183
+ Schema(self.name.singular).optional_schema_for(
184
+ action="ReadMany",
185
+ fields=query,
186
+ ),
183
187
  Query(),
184
188
  ],
185
189
  ),
@@ -196,11 +200,14 @@ class RestfulRouter:
196
200
  def with_read_all(
197
201
  self,
198
202
  dependency: Dependency | None = None,
203
+ *,
199
204
  is_documented: bool = True,
200
205
  ) -> Self:
201
206
  self.router.add_api_route(
202
207
  "",
203
- self.resource.read_all(Service=self._resolve(dependency)),
208
+ self.resource.read_all_endpoint(
209
+ Service=self._resolve(dependency),
210
+ ),
204
211
  methods=["GET"],
205
212
  status_code=200,
206
213
  responses={},
@@ -214,11 +221,12 @@ class RestfulRouter:
214
221
  def with_update_one(
215
222
  self,
216
223
  dependency: Dependency | None = None,
224
+ *,
217
225
  is_documented: bool = True,
218
226
  ) -> Self:
219
227
  self.router.add_api_route(
220
228
  self.item_path,
221
- self.resource.update_one(
229
+ self.resource.update_one_endpoint(
222
230
  Service=self._resolve(dependency),
223
231
  ItemId=Annotated[
224
232
  str,
@@ -242,11 +250,12 @@ class RestfulRouter:
242
250
  def with_replace_one(
243
251
  self,
244
252
  dependency: Dependency | None = None,
253
+ *,
245
254
  is_documented: bool = True,
246
255
  ) -> Self:
247
256
  self.router.add_api_route(
248
257
  "",
249
- self.resource.replace_one(
258
+ self.resource.replace_one_endpoint(
250
259
  Service=self._resolve(dependency),
251
260
  Item=Annotated[
252
261
  RawItem,
@@ -266,11 +275,12 @@ class RestfulRouter:
266
275
  def with_delete_one(
267
276
  self,
268
277
  dependency: Dependency | None = None,
278
+ *,
269
279
  is_documented: bool = True,
270
280
  ) -> Self:
271
281
  self.router.add_api_route(
272
282
  self.item_path,
273
- self.resource.delete_one(
283
+ self.resource.delete_one_endpoint(
274
284
  Service=self._resolve(dependency),
275
285
  ItemId=Annotated[
276
286
  str,
@@ -11,9 +11,6 @@ from apexdevkit.fastapi.name import RestfulName
11
11
 
12
12
 
13
13
  class SchemaFields(ABC):
14
- def id(self) -> FluentDict[type]:
15
- return self.readable().select("id")
16
-
17
14
  def writable(self) -> FluentDict[type]:
18
15
  return self.readable().drop("id")
19
16
 
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable, Mapping
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from apexdevkit.repository import Entity, Repository
8
+
9
+ if TYPE_CHECKING:
10
+ from apexdevkit.formatter import Formatter
11
+
12
+ RawItem = Mapping[str, Any]
13
+ RawCollection = Iterable[RawItem]
14
+
15
+
16
+ class RestfulService: # pragma: no cover
17
+ def create_one(self, item: RawItem) -> RawItem:
18
+ raise NotImplementedError(self.create_one.__name__)
19
+
20
+ def read_one(self, item_id: str) -> RawItem:
21
+ raise NotImplementedError(self.read_one.__name__)
22
+
23
+ def read_many(self, **params: Any) -> RawCollection:
24
+ raise NotImplementedError(self.read_many.__name__)
25
+
26
+ def read_all(self) -> RawCollection:
27
+ raise NotImplementedError(self.read_all.__name__)
28
+
29
+ def update_one(self, item_id: str, **with_fields: Any) -> RawItem:
30
+ raise NotImplementedError(self.update_one.__name__)
31
+
32
+ def replace_one(self, item: RawItem) -> RawItem:
33
+ raise NotImplementedError(self.replace_one.__name__)
34
+
35
+ def delete_one(self, item_id: str) -> None:
36
+ raise NotImplementedError(self.delete_one.__name__)
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class RestfulRepository[ItemT: Entity](RestfulService):
41
+ repository: Repository[ItemT]
42
+
43
+ formatter: Formatter[Mapping[str, Any], ItemT]
44
+
45
+ def create_one(self, item: RawItem) -> RawItem:
46
+ return self.formatter.dump(self.repository.create(self.formatter.load(item)))
47
+
48
+ def read_one(self, item_id: str) -> RawItem:
49
+ return self.formatter.dump(self.repository.read(item_id))
50
+
51
+ def read_all(self) -> RawCollection:
52
+ return [self.formatter.dump(item) for item in self.repository]
53
+
54
+ def update_one(self, item_id: str, **with_fields: Any) -> RawItem:
55
+ data = dict(self.formatter.dump(self.repository.read(item_id)))
56
+ data.update(**with_fields)
57
+
58
+ self.repository.update(self.formatter.load(data))
59
+
60
+ return data
61
+
62
+ def replace_one(self, item: RawItem) -> RawItem:
63
+ self.repository.update(self.formatter.load(item))
64
+
65
+ return item
66
+
67
+ def delete_one(self, item_id: str) -> None:
68
+ self.repository.delete(item_id)
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict
4
- from collections.abc import Iterable, Mapping
5
4
  from dataclasses import dataclass, field
6
- from typing import Any, ClassVar, Protocol, TypeVar
5
+ from typing import TYPE_CHECKING, Any, ClassVar, Protocol
7
6
 
8
7
  from apexdevkit.annotation import deprecated
9
8
  from apexdevkit.error import ForbiddenError
@@ -24,44 +23,45 @@ from apexdevkit.query.query import (
24
23
  )
25
24
  from apexdevkit.repository import Database, DatabaseCommand
26
25
 
27
- ItemT = TypeVar("ItemT", covariant=True)
26
+ if TYPE_CHECKING:
27
+ from collections.abc import Iterable, Mapping
28
28
 
29
29
 
30
- class _Loader(Protocol[ItemT]):
31
- def load(self, raw: Mapping[str, Any]) -> ItemT:
30
+ class _Loader[T](Protocol):
31
+ def load(self, raw: Mapping[str, Any]) -> T:
32
32
  pass
33
33
 
34
34
 
35
35
  @dataclass
36
- class DefaultMsSqlFilter[ItemT]:
36
+ class DefaultMsSqlFilter[T]:
37
37
  _database: Database = field(init=False)
38
38
  _query_builder: MsSqlQueryBuilder = field(init=False)
39
39
  _options: QueryOptions = field(init=False)
40
- _formatter: _Loader[ItemT] = field(init=False)
40
+ _formatter: _Loader[T] = field(init=False)
41
41
 
42
- def with_database(self, value: Database) -> DefaultMsSqlFilter[ItemT]:
42
+ def with_database(self, value: Database) -> DefaultMsSqlFilter[T]:
43
43
  self._database = value
44
44
 
45
45
  return self
46
46
 
47
- def with_query_builder(self, value: MsSqlQueryBuilder) -> DefaultMsSqlFilter[ItemT]:
47
+ def with_query_builder(self, value: MsSqlQueryBuilder) -> DefaultMsSqlFilter[T]:
48
48
  self._query_builder = value
49
49
 
50
50
  return self
51
51
 
52
- def with_options(self, value: QueryOptions) -> DefaultMsSqlFilter[ItemT]:
52
+ def with_options(self, value: QueryOptions) -> DefaultMsSqlFilter[T]:
53
53
  self._options = value
54
54
 
55
55
  return self
56
56
 
57
- def with_formatter(self, value: _Loader[ItemT]) -> DefaultMsSqlFilter[ItemT]:
57
+ def with_formatter(self, value: _Loader[T]) -> DefaultMsSqlFilter[T]:
58
58
  self._formatter = value
59
59
 
60
60
  return self
61
61
 
62
- def retrieve(self) -> Iterable[ItemT]:
62
+ def retrieve(self) -> Iterable[T]:
63
63
  if not any(sort.name == "id" for sort in self._options.ordering):
64
- self._options.ordering.append(Sort("id", False))
64
+ self._options.ordering.append(Sort(name="id", is_descending=False))
65
65
 
66
66
  result = self._database.execute(
67
67
  self._query_builder.filter(self._options)
@@ -101,7 +101,7 @@ class DefaultMsSqlAggregate:
101
101
  for aggregation in self._options.aggregations
102
102
  for value in [
103
103
  result[
104
- f"{aggregation.name if aggregation.name else 'general'}"
104
+ f"{aggregation.name or 'general'}"
105
105
  f"_{aggregation.aggregation.value.lower()}"
106
106
  ]
107
107
  ]
@@ -215,7 +215,7 @@ class MsSqlField:
215
215
 
216
216
  return result
217
217
 
218
- def as_order_part(self, is_descending: bool = False) -> str:
218
+ def as_order_part(self, *, is_descending: bool = False) -> str:
219
219
  result = self.name
220
220
 
221
221
  if is_descending:
@@ -278,7 +278,7 @@ class MsSqlOrderGenerator:
278
278
  return f"ORDER BY {clause}"
279
279
 
280
280
  def generate_one(self, item: Sort) -> str:
281
- return self.field_for(item.name).as_order_part(item.is_descending)
281
+ return self.field_for(item.name).as_order_part(is_descending=item.is_descending)
282
282
 
283
283
  def field_for(self, name: str) -> MsSqlField:
284
284
  for f in self.fields:
@@ -19,23 +19,20 @@ from .in_memory import (
19
19
  from .sql import MsSqlRepository, SqliteRepository
20
20
 
21
21
  __all__ = [
22
- "Entity",
23
- # Core
24
22
  "BruteForceBatch",
23
+ "CacheMixin",
25
24
  "Connection",
26
25
  "Connector",
27
26
  "Cursor",
28
27
  "Database",
29
28
  "DatabaseCommand",
30
- "NoRepository",
31
- "Repository",
32
- "RepositoryDecorator",
33
- # In-memory
34
- "CacheMixin",
29
+ "Entity",
35
30
  "InMemoryByteStore",
36
31
  "InMemoryRepository",
37
32
  "KeyValueStore",
38
- # SQL
39
33
  "MsSqlRepository",
34
+ "NoRepository",
35
+ "Repository",
36
+ "RepositoryDecorator",
40
37
  "SqliteRepository",
41
38
  ]
@@ -11,22 +11,18 @@ from .interface import Entity, ItemT, KeyFn, Repository
11
11
  from .mixin import ContainsMixin
12
12
 
13
13
  __all__ = [
14
- # Database
14
+ "BruteForceBatch",
15
15
  "Connection",
16
16
  "ConnectionContextManager",
17
17
  "Connector",
18
+ "ContainsMixin",
18
19
  "Cursor",
19
20
  "Database",
20
21
  "DatabaseCommand",
21
- # Decorator
22
- "RepositoryDecorator",
23
- "BruteForceBatch",
24
- # Interface
25
22
  "Entity",
26
23
  "ItemT",
27
24
  "KeyFn",
28
- "Repository",
29
25
  "NoRepository",
30
- # Mixin
31
- "ContainsMixin",
26
+ "Repository",
27
+ "RepositoryDecorator",
32
28
  ]
@@ -106,6 +106,6 @@ class ConnectionContextManager(AbstractContextManager[Connection]):
106
106
  def __enter__(self) -> Connection:
107
107
  return self.connection
108
108
 
109
- def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
109
+ def __exit__(self, exc_type: object, exc_value: object, traceback: object) -> None:
110
110
  self.connection.cursor().close()
111
111
  self.connection.close()
@@ -4,7 +4,7 @@ from .store import InMemoryByteStore, KeyValueStore
4
4
 
5
5
  __all__ = [
6
6
  "CacheMixin",
7
- "InMemoryRepository",
8
7
  "InMemoryByteStore",
8
+ "InMemoryRepository",
9
9
  "KeyValueStore",
10
10
  ]
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Iterator
4
3
  from contextlib import suppress
5
4
  from dataclasses import dataclass, field
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from apexdevkit.error import DoesNotExistError, ExistsError
8
8
  from apexdevkit.key_fn import AttributeKey
@@ -10,6 +10,9 @@ from apexdevkit.repository.core import ContainsMixin, ItemT, KeyFn, Repository
10
10
 
11
11
  from .store import InMemoryByteStore, KeyValueStore
12
12
 
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Iterator
15
+
13
16
 
14
17
  @dataclass(frozen=True)
15
18
  class InMemoryRepository(ContainsMixin, Repository[ItemT]):
@@ -1,11 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Iterable
4
3
  from dataclasses import dataclass, field
5
- from typing import Protocol
4
+ from typing import TYPE_CHECKING, Protocol
6
5
 
7
6
  from apexdevkit.formatter import Formatter, PickleFormatter
8
7
 
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Iterable
10
+
9
11
 
10
12
  class KeyValueStore[ItemT](Protocol): # pragma: no cover
11
13
  def count(self) -> int:
@@ -3,9 +3,9 @@ from .mssql import MsSqlRepository
3
3
  from .sqlite import SqliteRepository
4
4
 
5
5
  __all__ = [
6
+ "MsSqlRepository",
6
7
  "NotNone",
7
8
  "SqlFieldBuilder",
8
9
  "SqlFieldManager",
9
- "MsSqlRepository",
10
10
  "SqliteRepository",
11
11
  ]