apexdevkit 1.22.1__tar.gz → 1.23.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 (54) hide show
  1. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/PKG-INFO +2 -2
  2. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/annotation/deprecate.py +2 -1
  3. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/error.py +2 -1
  4. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/builder.py +2 -1
  5. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/dependable.py +3 -2
  6. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/resource.py +2 -1
  7. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/response.py +2 -1
  8. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/schema.py +6 -5
  9. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/service.py +4 -4
  10. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fluent.py +2 -1
  11. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/formatter.py +2 -3
  12. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/fluent.py +5 -5
  13. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/httpx/client.py +2 -1
  14. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/query/generator.py +2 -1
  15. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/query/query.py +1 -1
  16. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/base.py +2 -1
  17. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/connector.py +5 -5
  18. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/database.py +4 -2
  19. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/decorator.py +2 -1
  20. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/in_memory.py +6 -5
  21. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/interface.py +2 -2
  22. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/mssql.py +30 -11
  23. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/repository.py +18 -5
  24. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/sql.py +15 -3
  25. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/sqlite.py +6 -5
  26. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/server.py +2 -1
  27. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/synchronization.py +2 -1
  28. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/testing/database.py +3 -3
  29. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/testing/fake.py +13 -13
  30. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/testing/rest.py +2 -1
  31. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/pyproject.toml +18 -12
  32. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/LICENSE +0 -0
  33. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/README.md +0 -0
  34. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/__init__.py +0 -0
  35. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/annotation/__init__.py +0 -0
  36. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/environment.py +0 -0
  37. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/__init__.py +0 -0
  38. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/docs.py +0 -0
  39. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/name.py +0 -0
  40. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/request.py +0 -0
  41. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/fastapi/router.py +0 -0
  42. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/__init__.py +0 -0
  43. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/fake.py +0 -0
  44. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/httpx/__init__.py +0 -0
  45. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/httpx/hooks.py +0 -0
  46. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/json.py +0 -0
  47. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/http/url.py +0 -0
  48. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/id.py +0 -0
  49. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/key_fn.py +0 -0
  50. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/py.typed +0 -0
  51. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/query/__init__.py +0 -0
  52. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/repository/__init__.py +0 -0
  53. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/testing/__init__.py +0 -0
  54. {apexdevkit-1.22.1 → apexdevkit-1.23.1}/apexdevkit/value.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: apexdevkit
3
- Version: 1.22.1
3
+ Version: 1.23.1
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
7
- Requires-Python: >=3.11,<4.0
7
+ Requires-Python: >=3.11
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
@@ -1,5 +1,6 @@
1
1
  import inspect
2
- from typing import Any, Callable, TypeVar, cast
2
+ from collections.abc import Callable
3
+ from typing import Any, TypeVar, cast
3
4
  from warnings import warn
4
5
 
5
6
  F = TypeVar("F", bound=Callable[..., Any])
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from dataclasses import dataclass, field
4
- from typing import Any, Callable, Self
5
+ from typing import Any, Self
5
6
 
6
7
  Criteria = Callable[[Any], str]
7
8
 
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
+ from collections.abc import Mapping
4
5
  from dataclasses import dataclass, field
5
- from typing import Any, Mapping, Self
6
+ from typing import Any, Self
6
7
 
7
8
  from fastapi import APIRouter, FastAPI
8
9
  from starlette.middleware.cors import CORSMiddleware
@@ -1,5 +1,6 @@
1
+ from collections.abc import Callable
1
2
  from dataclasses import dataclass
2
- from typing import Annotated, Any, Callable, Protocol
3
+ from typing import Annotated, Any, Protocol
3
4
 
4
5
  from fastapi import Depends, Path
5
6
  from fastapi.requests import Request
@@ -52,7 +53,7 @@ class ParentDependency:
52
53
  try:
53
54
  return builder.with_parent(parent_id)
54
55
  except DoesNotExistError as e:
55
- raise ApiError(404, RestfulResponse(self.parent).not_found(e))
56
+ raise ApiError(404, RestfulResponse(self.parent).not_found(e)) from e
56
57
 
57
58
  return Annotated[RestfulServiceBuilder, Depends(_)]
58
59
 
@@ -1,6 +1,7 @@
1
1
  import re
2
+ from collections.abc import Callable
2
3
  from dataclasses import dataclass
3
- from typing import Any, Callable, Union
4
+ from typing import Any, Union
4
5
 
5
6
  from pydantic import BaseModel
6
7
  from starlette.responses import JSONResponse
@@ -1,5 +1,6 @@
1
+ from collections.abc import Iterable
1
2
  from dataclasses import dataclass
2
- from typing import Any, Iterable
3
+ from typing import Any
3
4
 
4
5
  from apexdevkit.error import DoesNotExistError, ExistsError, ForbiddenError
5
6
  from apexdevkit.fastapi.name import RestfulName
@@ -1,7 +1,8 @@
1
1
  from abc import ABC, abstractmethod
2
+ from collections.abc import Callable, Iterable
2
3
  from dataclasses import dataclass
3
4
  from functools import cached_property
4
- from typing import Any, Callable, Iterable, List
5
+ from typing import Any
5
6
 
6
7
  from pydantic import BaseModel, create_model
7
8
 
@@ -39,10 +40,10 @@ class RestfulSchema:
39
40
  )
40
41
 
41
42
  self._schema_for("Item", {self.name.singular: schema})
42
- self._schema_for("Collection", {self.name.plural: List[schema], "count": int})
43
- self._schema_for("CreateMany", {self.name.plural: List[create_schema]})
44
- self._schema_for("UpdateMany", {self.name.plural: List[update_many_item]})
45
- self._schema_for("ReplaceMany", {self.name.plural: List[replace_schema]})
43
+ self._schema_for("Collection", {self.name.plural: list[schema], "count": int})
44
+ self._schema_for("CreateMany", {self.name.plural: list[create_schema]})
45
+ self._schema_for("UpdateMany", {self.name.plural: list[update_many_item]})
46
+ self._schema_for("ReplaceMany", {self.name.plural: list[replace_schema]})
46
47
 
47
48
  def _schema_for(self, action: str, fields: dict[str, Any]) -> type[BaseModel]:
48
49
  if action not in self.schemas:
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- from abc import ABC
3
+ from collections.abc import Iterable, Mapping
4
4
  from dataclasses import dataclass
5
5
  from functools import cached_property
6
- from typing import Any, Dict, Generic, Iterable, Mapping, TypeVar
6
+ from typing import Any, Generic, TypeVar
7
7
 
8
8
  from apexdevkit.formatter import Formatter
9
9
  from apexdevkit.query.query import FooterOptions, QueryOptions, Summary
@@ -14,7 +14,7 @@ RawItem = Mapping[str, Any]
14
14
  RawCollection = Iterable[RawItem]
15
15
 
16
16
 
17
- class _RawItemWithId(Dict[str, Any]):
17
+ class _RawItemWithId(dict[str, Any]):
18
18
  def __post_init__(self) -> None:
19
19
  assert "id" in self
20
20
 
@@ -22,7 +22,7 @@ class _RawItemWithId(Dict[str, Any]):
22
22
  RawCollectionWithId = Iterable[_RawItemWithId]
23
23
 
24
24
 
25
- class RestfulService(ABC): # pragma: no cover
25
+ class RestfulService: # pragma: no cover
26
26
  def create_one(self, item: RawItem) -> RawItem:
27
27
  raise NotImplementedError(self.create_one.__name__)
28
28
 
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from dataclasses import dataclass
4
- from typing import Any, Callable, Generic, TypeVar
5
+ from typing import Any, Generic, TypeVar
5
6
 
6
7
  ItemT = TypeVar("ItemT")
7
8
 
@@ -1,11 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import pickle
4
+ from collections.abc import Mapping
4
5
  from copy import deepcopy
5
6
  from dataclasses import asdict, dataclass, field, fields, is_dataclass
6
- from typing import Any, Generic, Mapping, Protocol, Self, TypeVar, get_args
7
-
8
- from typing_extensions import get_type_hints
7
+ from typing import Any, Generic, Protocol, Self, TypeVar, get_args, get_type_hints
9
8
 
10
9
  from apexdevkit.fluent import FluentDict
11
10
  from apexdevkit.value import Value
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from enum import Enum, auto
5
- from typing import Any, Protocol, Type
5
+ from typing import Any, Protocol
6
6
 
7
7
  from apexdevkit.http.json import JsonDict
8
8
 
@@ -103,25 +103,25 @@ class FluentHttpRequest:
103
103
  class FluentHttpResponse:
104
104
  response: HttpResponse
105
105
 
106
- def on_bad_request(self, raises: Exception | Type[Exception]) -> FluentHttpResponse:
106
+ def on_bad_request(self, raises: Exception | type[Exception]) -> FluentHttpResponse:
107
107
  if self.response.code() == 400:
108
108
  raise raises
109
109
 
110
110
  return self
111
111
 
112
- def on_conflict(self, raises: Exception | Type[Exception]) -> FluentHttpResponse:
112
+ def on_conflict(self, raises: Exception | type[Exception]) -> FluentHttpResponse:
113
113
  if self.response.code() == 409:
114
114
  raise raises
115
115
 
116
116
  return self
117
117
 
118
- def on_not_found(self, raises: Exception | Type[Exception]) -> FluentHttpResponse:
118
+ def on_not_found(self, raises: Exception | type[Exception]) -> FluentHttpResponse:
119
119
  if self.response.code() == 404:
120
120
  raise raises
121
121
 
122
122
  return self
123
123
 
124
- def on_failure(self, raises: Type[Exception]) -> FluentHttpResponse:
124
+ def on_failure(self, raises: type[Exception]) -> FluentHttpResponse:
125
125
  if self.response.code() < 200 or self.response.code() > 299:
126
126
  raise raises(self.response.raw(), self.response.code())
127
127
 
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable, Iterator, Mapping
3
4
  from dataclasses import dataclass, field
4
- from typing import Any, Callable, Iterator, Mapping
5
+ from typing import Any
5
6
 
6
7
  import httpx
7
8
 
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict
4
+ from collections.abc import Iterable, Mapping
4
5
  from dataclasses import dataclass, field
5
- from typing import Any, ClassVar, Generic, Iterable, Mapping, Protocol, TypeVar
6
+ from typing import Any, ClassVar, Generic, Protocol, TypeVar
6
7
 
7
8
  from apexdevkit.annotation import deprecated
8
9
  from apexdevkit.error import ForbiddenError
@@ -59,7 +59,7 @@ class SummaryExtractor:
59
59
  def _value(self, value: Any) -> NumericValue | DateValue | StringValue | NullValue:
60
60
  if value is None:
61
61
  return NullValue()
62
- if isinstance(value, (int, float, Decimal)):
62
+ if isinstance(value, int | float | Decimal):
63
63
  return NumericValue.from_decimal(Decimal(value))
64
64
  if self.aggregation.name and (
65
65
  "date" in self.aggregation.name.lower()
@@ -1,4 +1,5 @@
1
- from typing import Any, Generic, Iterator
1
+ from collections.abc import Iterator
2
+ from typing import Any, Generic
2
3
 
3
4
  from apexdevkit.repository.interface import ItemT, Repository
4
5
 
@@ -4,7 +4,7 @@ import sqlite3
4
4
  from contextlib import AbstractContextManager
5
5
  from dataclasses import dataclass
6
6
  from functools import cached_property
7
- from typing import Any, ContextManager
7
+ from typing import Any
8
8
 
9
9
  import pymssql
10
10
  from pymssql import Connection as _Connection
@@ -17,7 +17,7 @@ from apexdevkit.repository import Connection
17
17
  class SqliteFileConnector:
18
18
  dsn: str
19
19
 
20
- def connect(self) -> ContextManager[Connection]:
20
+ def connect(self) -> AbstractContextManager[Connection]:
21
21
  connection = sqlite3.connect(self.dsn)
22
22
  connection.row_factory = sqlite3.Row
23
23
 
@@ -28,11 +28,11 @@ class SqliteFileConnector:
28
28
  class SqliteInMemoryConnector:
29
29
  dsn: str = ":memory:"
30
30
 
31
- def connect(self) -> ContextManager[Connection]:
31
+ def connect(self) -> AbstractContextManager[Connection]:
32
32
  return self._connection
33
33
 
34
34
  @cached_property
35
- def _connection(self) -> ContextManager[Connection]:
35
+ def _connection(self) -> AbstractContextManager[Connection]:
36
36
  connection = sqlite3.connect(self.dsn, check_same_thread=False)
37
37
  connection.row_factory = sqlite3.Row
38
38
 
@@ -48,7 +48,7 @@ class MsSqlConnector:
48
48
  db_tds_version = "7.0"
49
49
  db_port: str | None = None
50
50
 
51
- def connect(self) -> ContextManager[Connection]:
51
+ def connect(self) -> AbstractContextManager[Connection]:
52
52
  return ConnectionContextManager(self._connection())
53
53
 
54
54
  def _connection(self) -> Connection:
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable, Mapping
4
+ from contextlib import AbstractContextManager
3
5
  from copy import deepcopy
4
6
  from dataclasses import dataclass, field
5
- from typing import Any, ContextManager, Iterable, Mapping, Protocol
7
+ from typing import Any, Protocol
6
8
 
7
9
  _RawData = Mapping[str, Any]
8
10
 
@@ -45,7 +47,7 @@ class Database:
45
47
 
46
48
 
47
49
  class Connector(Protocol): # pragma: no cover
48
- def connect(self) -> ContextManager[Connection]:
50
+ def connect(self) -> AbstractContextManager[Connection]:
49
51
  pass
50
52
 
51
53
 
@@ -1,5 +1,6 @@
1
+ from collections.abc import Iterable, Iterator
1
2
  from dataclasses import dataclass
2
- from typing import Any, Generic, Iterable, Iterator
3
+ from typing import Any, Generic
3
4
 
4
5
  from apexdevkit.repository.interface import ItemT, Repository
5
6
 
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable, Iterable, Iterator
3
4
  from contextlib import suppress
4
5
  from dataclasses import dataclass, field
5
- from typing import Any, Callable, Generic, Iterable, Iterator, Protocol, Self
6
+ from typing import Any, Generic, Protocol, Self
6
7
 
7
8
  from apexdevkit.error import DoesNotExistError, ExistsError
8
9
  from apexdevkit.formatter import Formatter, PickleFormatter
@@ -131,14 +132,14 @@ class _SingleKeyRepository(RepositoryBase[ItemT]):
131
132
  def delete(self, item_id: str) -> None:
132
133
  try:
133
134
  self.store.drop(item_id)
134
- except KeyError:
135
- raise DoesNotExistError(item_id)
135
+ except KeyError as e:
136
+ raise DoesNotExistError(item_id) from e
136
137
 
137
138
  def read(self, item_id: str) -> ItemT:
138
139
  try:
139
140
  return self.store.get(item_id)
140
- except KeyError:
141
- raise DoesNotExistError(item_id)
141
+ except KeyError as e:
142
+ raise DoesNotExistError(item_id) from e
142
143
 
143
144
  def __iter__(self) -> Iterator[ItemT]:
144
145
  return iter(self.store.values())
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Iterable
4
- from typing import Any, Iterator, Protocol, TypeVar
3
+ from collections.abc import Iterable, Iterator
4
+ from typing import Any, Protocol, TypeVar
5
5
 
6
6
  ItemT = TypeVar("ItemT")
7
7
 
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable, Iterator, Mapping
3
4
  from dataclasses import dataclass
4
- from typing import Any, Generic, Iterable, Iterator, Mapping, TypeVar
5
+ from typing import Any, Generic, TypeVar
5
6
 
6
7
  from pymssql.exceptions import DatabaseError, OperationalError
7
8
 
@@ -27,8 +28,8 @@ class MsSqlRepository(RepositoryBase[ItemT]):
27
28
 
28
29
  try:
29
30
  return int(raw["n_items"])
30
- except KeyError:
31
- raise UnknownError(raw)
31
+ except KeyError as e:
32
+ raise UnknownError(raw) from e
32
33
 
33
34
  def delete(self, item_id: str) -> None:
34
35
  self.db.execute(self.table.delete(item_id)).fetch_none()
@@ -40,19 +41,17 @@ class MsSqlRepository(RepositoryBase[ItemT]):
40
41
  try:
41
42
  return self.table.load(self.db.execute(self.table.insert(item)).fetch_one())
42
43
  except DatabaseError as e:
43
- e = MssqlException(e)
44
+ if MssqlException(e).is_duplication():
45
+ raise self.table.exists(item) from e
44
46
 
45
- if e.is_duplication():
46
- raise self.table.exists(item)
47
-
48
- raise UnknownError(e.message)
47
+ raise UnknownError(MssqlException(e).message) from e
49
48
 
50
49
  def read(self, item_id: str) -> ItemT:
51
50
  try:
52
51
  raw = self.db.execute(self.table.select(item_id)).fetch_one()
53
52
  except OperationalError as e:
54
53
  if "Conversion failed" in str(e):
55
- raise DoesNotExistError(item_id)
54
+ raise DoesNotExistError(item_id) from e
56
55
  else:
57
56
  raise e
58
57
 
@@ -151,6 +150,7 @@ class MsSqlTableBuilder(Generic[ItemT]):
151
150
  table: str | None = None
152
151
  formatter: Formatter[Mapping[str, Any], ItemT] | None = None
153
152
  fields: list[_SqlField] | None = None
153
+ custom_filters: list[str] | None = None
154
154
 
155
155
  def with_username(self, value: str) -> MsSqlTableBuilder[ItemT]:
156
156
  return MsSqlTableBuilder[ItemT](
@@ -159,6 +159,7 @@ class MsSqlTableBuilder(Generic[ItemT]):
159
159
  self.table,
160
160
  self.formatter,
161
161
  self.fields,
162
+ self.custom_filters,
162
163
  )
163
164
 
164
165
  def with_schema(self, value: str) -> MsSqlTableBuilder[ItemT]:
@@ -168,6 +169,7 @@ class MsSqlTableBuilder(Generic[ItemT]):
168
169
  self.table,
169
170
  self.formatter,
170
171
  self.fields,
172
+ self.custom_filters,
171
173
  )
172
174
 
173
175
  def with_table(self, value: str) -> MsSqlTableBuilder[ItemT]:
@@ -177,6 +179,7 @@ class MsSqlTableBuilder(Generic[ItemT]):
177
179
  value,
178
180
  self.formatter,
179
181
  self.fields,
182
+ self.custom_filters,
180
183
  )
181
184
 
182
185
  def with_formatter(
@@ -188,6 +191,7 @@ class MsSqlTableBuilder(Generic[ItemT]):
188
191
  self.table,
189
192
  value,
190
193
  self.fields,
194
+ self.custom_filters,
191
195
  )
192
196
 
193
197
  def with_fields(self, value: Iterable[_SqlField]) -> MsSqlTableBuilder[ItemT]:
@@ -213,17 +217,32 @@ class MsSqlTableBuilder(Generic[ItemT]):
213
217
  self.table,
214
218
  self.formatter,
215
219
  key_list,
220
+ self.custom_filters,
221
+ )
222
+
223
+ def with_custom_filters(self, filters: Iterable[str]) -> MsSqlTableBuilder[ItemT]:
224
+ return MsSqlTableBuilder[ItemT](
225
+ self.username,
226
+ self.schema,
227
+ self.table,
228
+ self.formatter,
229
+ self.fields,
230
+ list(filters),
216
231
  )
217
232
 
218
233
  def build(self) -> SqlTable[ItemT]:
219
234
  if not self.schema or not self.table or not self.formatter or not self.fields:
220
235
  raise ValueError("Cannot build sql table.")
221
236
 
237
+ field_manager = SqlFieldManager.Builder().with_fields(self.fields)
238
+ if self.custom_filters and len(self.custom_filters) > 0:
239
+ field_manager = field_manager.with_custom_filters(self.custom_filters)
240
+
222
241
  return DefaultSqlTable(
223
242
  self.schema,
224
243
  self.table,
225
244
  self.formatter,
226
- SqlFieldManager.Builder().with_fields(self.fields).for_mssql().build(),
245
+ field_manager.for_mssql().build(),
227
246
  self.username,
228
247
  )
229
248
 
@@ -253,7 +272,7 @@ class DefaultSqlTable(SqlTable[ItemT]):
253
272
  [f"%({key.name})s" for key in self.fields if key.include_in_insert]
254
273
  )
255
274
  try:
256
- self.fields.id
275
+ _ = self.fields.id
257
276
  output = ", ".join(
258
277
  ["[" + field.name + "] AS " + field.name for field in self.fields]
259
278
  )
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable, Iterator
3
4
  from dataclasses import dataclass, field
4
- from typing import Callable, Generic, Iterator, TypeVar
5
+ from typing import Generic, TypeVar
5
6
 
6
7
  from apexdevkit.error import DoesNotExistError
7
8
  from apexdevkit.formatter import Formatter
@@ -75,22 +76,34 @@ class MultipleRepositoryBuilder(Generic[ItemT]):
75
76
  self,
76
77
  repository: Repository[ItemT],
77
78
  condition: Callable[[ItemT], bool] = lambda item: True,
78
- formatter: Formatter[ItemT, ItemT] = NoFormatter[ItemT](),
79
+ formatter: Formatter[ItemT, ItemT] | None = None,
79
80
  id_prefix: str = "",
80
81
  ) -> MultipleRepositoryBuilder[ItemT]:
81
82
  return MultipleRepositoryBuilder[ItemT](
82
83
  self.repositories
83
- + [_InnerRepository(repository, condition, formatter, id_prefix)]
84
+ + [
85
+ _InnerRepository(
86
+ repository,
87
+ condition,
88
+ formatter or NoFormatter[ItemT](),
89
+ id_prefix,
90
+ )
91
+ ]
84
92
  )
85
93
 
86
94
  def and_repository(
87
95
  self,
88
96
  repository: Repository[ItemT],
89
97
  condition: Callable[[ItemT], bool] = lambda item: True,
90
- formatter: Formatter[ItemT, ItemT] = NoFormatter[ItemT](),
98
+ formatter: Formatter[ItemT, ItemT] | None = None,
91
99
  id_prefix: str = "",
92
100
  ) -> MultipleRepositoryBuilder[ItemT]:
93
- return self.with_repository(repository, condition, formatter, id_prefix)
101
+ return self.with_repository(
102
+ repository,
103
+ condition,
104
+ formatter or NoFormatter[ItemT](),
105
+ id_prefix,
106
+ )
94
107
 
95
108
  def build(self) -> MultipleRepository[ItemT]:
96
109
  return MultipleRepository[ItemT](self.repositories)
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterator, Mapping
3
4
  from dataclasses import dataclass, field
4
- from typing import Any, Iterator, Mapping
5
+ from typing import Any
5
6
 
6
7
 
7
8
  @dataclass(frozen=True)
@@ -120,6 +121,7 @@ class SqlFieldBuilder:
120
121
  @dataclass
121
122
  class SqlFieldManager:
122
123
  fields: list[_SqlField]
124
+ custom_filters: list[str]
123
125
  key_formatter: str
124
126
  value_formatter: str
125
127
 
@@ -209,7 +211,7 @@ class SqlFieldManager:
209
211
  )
210
212
 
211
213
  def _general_filters(self) -> str:
212
- statements: list[str] = []
214
+ statements: list[str] = [] + self.custom_filters
213
215
  for key in self.fields:
214
216
  if key.is_filter:
215
217
  inner_statements: list[str] = []
@@ -237,6 +239,8 @@ class SqlFieldManager:
237
239
 
238
240
  @dataclass
239
241
  class Builder:
242
+ custom_filters: list[str] = field(default_factory=list)
243
+
240
244
  fields: list[_SqlField] = field(init=False)
241
245
  key_formatter: str = field(init=False)
242
246
  value_formatter: str = field(init=False)
@@ -246,6 +250,11 @@ class SqlFieldManager:
246
250
 
247
251
  return self
248
252
 
253
+ def with_custom_filters(self, fields: list[str]) -> SqlFieldManager.Builder:
254
+ self.custom_filters = fields
255
+
256
+ return self
257
+
249
258
  def for_sqlite(self) -> SqlFieldManager.Builder:
250
259
  self.key_formatter = "x"
251
260
  self.value_formatter = ":x"
@@ -260,5 +269,8 @@ class SqlFieldManager:
260
269
 
261
270
  def build(self) -> SqlFieldManager:
262
271
  return SqlFieldManager(
263
- self.fields, self.key_formatter, self.value_formatter
272
+ self.fields,
273
+ self.custom_filters,
274
+ self.key_formatter,
275
+ self.value_formatter,
264
276
  )
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable, Iterator, Mapping
3
4
  from dataclasses import dataclass
4
5
  from sqlite3 import IntegrityError
5
- from typing import Any, Generic, Iterable, Iterator, Mapping
6
+ from typing import Any, Generic
6
7
 
7
8
  from apexdevkit.error import DoesNotExistError, ExistsError
8
9
  from apexdevkit.formatter import Formatter
@@ -28,8 +29,8 @@ class SqliteRepository(RepositoryBase[ItemT]):
28
29
 
29
30
  try:
30
31
  return int(raw["n_items"])
31
- except KeyError:
32
- raise UnknownError(raw)
32
+ except KeyError as e:
33
+ raise UnknownError(raw) from e
33
34
 
34
35
  def create(self, item: ItemT) -> ItemT:
35
36
  try:
@@ -183,7 +184,7 @@ class _DefaultSqlTable(SqlTable[ItemT]):
183
184
 
184
185
  return DatabaseCommand(f"""
185
186
  SELECT
186
- {columns}
187
+ {columns}
187
188
  FROM {self.table_name.upper()}
188
189
  {self.fields.where_statement(include_id=True)};
189
190
  """).with_data(self.fields.with_fixed({self.fields.id: item_id}))
@@ -198,7 +199,7 @@ class _DefaultSqlTable(SqlTable[ItemT]):
198
199
 
199
200
  return DatabaseCommand(f"""
200
201
  SELECT
201
- {columns}
202
+ {columns}
202
203
  FROM {self.table_name.upper()}
203
204
  WHERE {duplicates};
204
205
  """).with_data({key: raw[key] for key in raw if key in self.fields.composite})
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging.config
4
+ from collections.abc import Callable
4
5
  from dataclasses import dataclass, field
5
- from typing import Any, Callable
6
+ from typing import Any
6
7
 
7
8
  import sentry_sdk
8
9
  import uvicorn
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable, Iterator
3
4
  from dataclasses import dataclass
4
- from typing import Generic, Iterable, Iterator, Protocol, TypeVar
5
+ from typing import Generic, Protocol, TypeVar
5
6
 
6
7
  ItemT = TypeVar("ItemT")
7
8
 
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from contextlib import nullcontext
3
+ from contextlib import AbstractContextManager, nullcontext
4
4
  from dataclasses import dataclass, field
5
- from typing import Any, ContextManager, Self
5
+ from typing import Any, Self
6
6
 
7
7
  from apexdevkit.repository import DatabaseCommand
8
8
 
@@ -27,7 +27,7 @@ class FakeConnector:
27
27
  def fetchall(self) -> list[dict[str, Any]]:
28
28
  return self.results.pop() # type: ignore
29
29
 
30
- def connect(self) -> ContextManager[Self]:
30
+ def connect(self) -> AbstractContextManager[Self]:
31
31
  return nullcontext(self)
32
32
 
33
33
  def cursor(self) -> Self:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import random
4
4
  from dataclasses import dataclass, field
5
5
  from functools import cached_property
6
- from typing import Any, Generic, Type, TypeVar
6
+ from typing import Any, Generic, TypeVar
7
7
 
8
8
  from faker import Faker
9
9
 
@@ -70,7 +70,7 @@ class Fake:
70
70
 
71
71
  @dataclass(frozen=True)
72
72
  class FakeResource(Generic[ItemT]):
73
- item_type: Type[ItemT] = field()
73
+ item_type: type[ItemT] = field()
74
74
  fake: Fake = field(default_factory=Fake)
75
75
 
76
76
  @cached_property
@@ -89,7 +89,7 @@ class FakeResource(Generic[ItemT]):
89
89
 
90
90
  @dataclass(frozen=True)
91
91
  class FakeValue(FakeResource[Value]):
92
- item_type: Type[Value] = field(default=Value)
92
+ item_type: type[Value] = field(default=Value)
93
93
 
94
94
  @cached_property
95
95
  def _raw(self) -> dict[str, Any]:
@@ -101,7 +101,7 @@ class FakeValue(FakeResource[Value]):
101
101
 
102
102
  @dataclass(frozen=True)
103
103
  class FakeNumericValue(FakeResource[NumericValue]):
104
- item_type: Type[NumericValue] = field(default=NumericValue)
104
+ item_type: type[NumericValue] = field(default=NumericValue)
105
105
 
106
106
  @cached_property
107
107
  def _raw(self) -> dict[str, Any]:
@@ -113,7 +113,7 @@ class FakeNumericValue(FakeResource[NumericValue]):
113
113
 
114
114
  @dataclass(frozen=True)
115
115
  class FakeStringValue(FakeResource[StringValue]):
116
- item_type: Type[StringValue] = field(default=StringValue)
116
+ item_type: type[StringValue] = field(default=StringValue)
117
117
 
118
118
  @cached_property
119
119
  def _raw(self) -> dict[str, Any]:
@@ -124,7 +124,7 @@ class FakeStringValue(FakeResource[StringValue]):
124
124
 
125
125
  @dataclass(frozen=True)
126
126
  class FakeDateValue(FakeResource[DateValue]):
127
- item_type: Type[DateValue] = field(default=DateValue)
127
+ item_type: type[DateValue] = field(default=DateValue)
128
128
 
129
129
  @cached_property
130
130
  def _raw(self) -> dict[str, Any]:
@@ -134,7 +134,7 @@ class FakeDateValue(FakeResource[DateValue]):
134
134
  @dataclass(frozen=True)
135
135
  class FakeLeaf(FakeResource[Leaf]):
136
136
  values: list[NumericValue | StringValue | DateValue] = field(default_factory=list)
137
- item_type: Type[Leaf] = field(default=Leaf)
137
+ item_type: type[Leaf] = field(default=Leaf)
138
138
 
139
139
  @cached_property
140
140
  def _raw(self) -> dict[str, Any]:
@@ -147,7 +147,7 @@ class FakeLeaf(FakeResource[Leaf]):
147
147
  @dataclass(frozen=True)
148
148
  class FakeOperator(FakeResource[Operator]):
149
149
  operands: list[Operator | Leaf] = field(default_factory=list)
150
- item_type: Type[Operator] = field(default=Operator)
150
+ item_type: type[Operator] = field(default=Operator)
151
151
 
152
152
  @cached_property
153
153
  def _raw(self) -> dict[str, Any]:
@@ -160,7 +160,7 @@ class FakeOperator(FakeResource[Operator]):
160
160
  @dataclass(frozen=True)
161
161
  class FakeSort(FakeResource[Sort]):
162
162
  is_descending: bool | None = None
163
- item_type: Type[Sort] = field(default=Sort)
163
+ item_type: type[Sort] = field(default=Sort)
164
164
 
165
165
  @cached_property
166
166
  def _raw(self) -> dict[str, Any]:
@@ -174,7 +174,7 @@ class FakeSort(FakeResource[Sort]):
174
174
 
175
175
  @dataclass(frozen=True)
176
176
  class FakePage(FakeResource[Page]):
177
- item_type: Type[Page] = field(default=Page)
177
+ item_type: type[Page] = field(default=Page)
178
178
 
179
179
  @cached_property
180
180
  def _raw(self) -> dict[str, Any]:
@@ -188,7 +188,7 @@ class FakePage(FakeResource[Page]):
188
188
  @dataclass(frozen=True)
189
189
  class FakeFilter(FakeResource[Filter]):
190
190
  args: list[NumericValue | StringValue] = field(default_factory=list)
191
- item_type: Type[Filter] = field(default=Filter)
191
+ item_type: type[Filter] = field(default=Filter)
192
192
 
193
193
  @cached_property
194
194
  def _raw(self) -> dict[str, Any]:
@@ -203,7 +203,7 @@ class FakeQueryOptions(FakeResource[QueryOptions]):
203
203
  condition: Operator | None = None
204
204
  ordering: list[Sort] = field(default_factory=list)
205
205
  paging: Page | None = None
206
- item_type: Type[QueryOptions] = field(default=QueryOptions)
206
+ item_type: type[QueryOptions] = field(default=QueryOptions)
207
207
 
208
208
  @cached_property
209
209
  def _raw(self) -> dict[str, Any]:
@@ -217,7 +217,7 @@ class FakeQueryOptions(FakeResource[QueryOptions]):
217
217
 
218
218
  @dataclass(frozen=True)
219
219
  class FakeAggregationOption(FakeResource[AggregationOption]):
220
- item_type: Type[AggregationOption] = field(default=AggregationOption)
220
+ item_type: type[AggregationOption] = field(default=AggregationOption)
221
221
 
222
222
  @cached_property
223
223
  def _raw(self) -> dict[str, Any]:
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Iterable
3
4
  from dataclasses import dataclass
4
5
  from functools import cached_property
5
- from typing import Any, Iterable, Self
6
+ from typing import Any, Self
6
7
 
7
8
  from apexdevkit.fastapi.name import RestfulName
8
9
  from apexdevkit.fastapi.request import HttpRequest
@@ -1,16 +1,19 @@
1
- [tool.poetry]
1
+ [project]
2
2
  name = "apexdevkit"
3
- version = "1.22.1"
3
+ version = "1.23.1"
4
4
  description = "Apex Development Tools for python."
5
- authors = ["Apex Dev <dev@apex.ge>"]
6
5
  readme = "README.md"
6
+ authors = [
7
+ { name = "Apex Dev", email = "dev@apex.ge" }
8
+ ]
9
+ dynamic = ["dependencies"]
10
+ requires-python = ">=3.11"
7
11
 
8
12
  [tool.poetry.dependencies]
9
- python = "^3.11"
10
13
  httpx = "*"
11
14
  fastapi = "*"
12
15
  uvicorn = "*"
13
- sentry-sdk = {extras = ["fastapi"], version = "*"}
16
+ sentry-sdk = { extras = ["fastapi"], version = "*" }
14
17
  python-dotenv = "*"
15
18
  pymssql = "2.3.2"
16
19
 
@@ -22,14 +25,12 @@ pytest-cov = "*"
22
25
  pytest-recording = "*"
23
26
  coverage = "*"
24
27
  faker = "*"
25
- mongomock = "*"
26
28
 
27
29
  [tool.poetry.group.lint.dependencies]
28
30
  mypy = "*"
29
31
  ruff = "*"
30
32
 
31
33
  [tool.mypy]
32
- python_version = "3.11"
33
34
  ignore_missing_imports = true
34
35
  strict = true
35
36
  exclude = [
@@ -38,7 +39,6 @@ exclude = [
38
39
  ]
39
40
 
40
41
  [tool.ruff]
41
- target-version = "py311"
42
42
  line-length = 88
43
43
 
44
44
  exclude = [
@@ -48,10 +48,16 @@ exclude = [
48
48
  "venv",
49
49
  ]
50
50
 
51
- lint.select = ["E", "F", "I"]
52
- lint.ignore = []
53
- lint.fixable = ["A", "B", "C", "D", "E", "F", "I"]
54
- lint.unfixable = []
51
+ [tool.ruff.lint]
52
+ select = [
53
+ "B", # flake8-bugbear
54
+ "C4", # flake8-comprehensions
55
+ "E", # pycodestyle errors
56
+ "F", # pyflakes
57
+ "I", # isort
58
+ "UP", # pyupgrade
59
+ "W", # pycodestyle warnings
60
+ ]
55
61
 
56
62
  [tool.ruff.lint.mccabe]
57
63
  max-complexity = 10
File without changes
File without changes