apexdevkit 1.15.5__tar.gz → 1.15.7__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 (46) hide show
  1. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/PKG-INFO +2 -1
  2. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/formatter.py +12 -0
  3. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/__init__.py +2 -0
  4. apexdevkit-1.15.7/apexdevkit/repository/connector.py +112 -0
  5. apexdevkit-1.15.7/apexdevkit/repository/mssql.py +104 -0
  6. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/testing/__init__.py +2 -0
  7. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/testing/fake.py +16 -0
  8. apexdevkit-1.15.7/apexdevkit/value.py +29 -0
  9. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/pyproject.toml +2 -1
  10. apexdevkit-1.15.5/apexdevkit/repository/connector.py +0 -42
  11. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/LICENSE +0 -0
  12. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/README.md +0 -0
  13. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/__init__.py +0 -0
  14. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/annotation/__init__.py +0 -0
  15. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/annotation/deprecate.py +0 -0
  16. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/environment.py +0 -0
  17. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/error.py +0 -0
  18. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/__init__.py +0 -0
  19. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/builder.py +0 -0
  20. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/dependable.py +0 -0
  21. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/docs.py +0 -0
  22. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/resource.py +0 -0
  23. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/response.py +0 -0
  24. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/router.py +0 -0
  25. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/schema.py +0 -0
  26. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fastapi/service.py +0 -0
  27. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/fluent.py +0 -0
  28. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/__init__.py +0 -0
  29. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/fake.py +0 -0
  30. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/fluent.py +0 -0
  31. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/httpx.py +0 -0
  32. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/json.py +0 -0
  33. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/http/url.py +0 -0
  34. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/key_fn.py +0 -0
  35. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/py.typed +0 -0
  36. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/base.py +0 -0
  37. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/database.py +0 -0
  38. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/decorator.py +0 -0
  39. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/in_memory.py +0 -0
  40. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/interface.py +0 -0
  41. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/mongo.py +0 -0
  42. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/repository/sqlite.py +0 -0
  43. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/server.py +0 -0
  44. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/synchronization.py +0 -0
  45. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/testing/database.py +0 -0
  46. {apexdevkit-1.15.5 → apexdevkit-1.15.7}/apexdevkit/testing/rest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.15.5
3
+ Version: 1.15.7
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.13
12
12
  Requires-Dist: fastapi
13
13
  Requires-Dist: httpx
14
14
  Requires-Dist: pymongo
15
+ Requires-Dist: pymssql (>=2.3.1,<3.0.0)
15
16
  Requires-Dist: python-dotenv
16
17
  Requires-Dist: sentry-sdk[fastapi]
17
18
  Requires-Dist: uvicorn
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import pickle
2
4
  from copy import deepcopy
3
5
  from dataclasses import asdict, dataclass, field
4
6
  from typing import Any, Generic, Protocol, Self, TypeVar
5
7
 
8
+ from apexdevkit.value import Value
9
+
6
10
  _SourceT = TypeVar("_SourceT")
7
11
  _TargetT = TypeVar("_TargetT")
8
12
  _ItemT = TypeVar("_ItemT")
@@ -59,3 +63,11 @@ class DataclassFormatter(Generic[_TargetT]):
59
63
 
60
64
  def dump(self, item: _TargetT) -> dict[str, Any]:
61
65
  return asdict(item) # type: ignore
66
+
67
+
68
+ class ValueFormatter:
69
+ def load(self, raw: dict[str, Any]) -> Value:
70
+ return DataclassFormatter(Value).load(raw)
71
+
72
+ def dump(self, value: Value) -> dict[str, Any]:
73
+ return DataclassFormatter(Value).dump(value)
@@ -12,6 +12,7 @@ from apexdevkit.repository.in_memory import (
12
12
  KeyValueStore,
13
13
  )
14
14
  from apexdevkit.repository.interface import Repository
15
+ from apexdevkit.repository.mssql import MsSqlRepository
15
16
 
16
17
  __all__ = [
17
18
  "Connection",
@@ -24,4 +25,5 @@ __all__ = [
24
25
  "KeyValueStore",
25
26
  "Repository",
26
27
  "RepositoryBase",
28
+ "MsSqlRepository",
27
29
  ]
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ import sqlite3
4
+ from contextlib import AbstractContextManager
5
+ from dataclasses import dataclass
6
+ from functools import cached_property
7
+ from typing import Any, ContextManager
8
+
9
+ import pymssql
10
+ from pymongo import MongoClient
11
+ from pymssql import Connection as _Connection
12
+ from pymssql import Cursor
13
+
14
+ from apexdevkit.repository import Connection
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class SqliteFileConnector:
19
+ dsn: str
20
+
21
+ def connect(self) -> ContextManager[Connection]:
22
+ connection = sqlite3.connect(self.dsn)
23
+ connection.row_factory = sqlite3.Row
24
+
25
+ return connection
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class SqliteInMemoryConnector:
30
+ dsn: str = ":memory:"
31
+
32
+ def connect(self) -> ContextManager[Connection]:
33
+ return self._connection
34
+
35
+ @cached_property
36
+ def _connection(self) -> ContextManager[Connection]:
37
+ connection = sqlite3.connect(self.dsn, check_same_thread=False)
38
+ connection.row_factory = sqlite3.Row
39
+
40
+ return connection
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class PyMongoConnector:
45
+ dsn: str
46
+
47
+ def connect(self) -> ContextManager[MongoClient[Any]]:
48
+ return MongoClient(self.dsn)
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class MsSqlConnector:
53
+ db_host: str
54
+ db_user: str
55
+ db_password: str
56
+ db_name: str
57
+ db_tds_version = "7.0"
58
+
59
+ def connect(self) -> ContextManager[Connection]:
60
+ return ConnectionContextManager(self._connection())
61
+
62
+ def _connection(self) -> Connection:
63
+ return MsSqlConnectionAdapter(
64
+ pymssql.connect(
65
+ tds_version=self.db_tds_version,
66
+ server=self.db_host,
67
+ user=self.db_user,
68
+ password=self.db_password,
69
+ database=self.db_name,
70
+ as_dict=True,
71
+ autocommit=True,
72
+ )
73
+ )
74
+
75
+
76
+ class ConnectionContextManager(AbstractContextManager[Connection]):
77
+ def __init__(self, connection: Connection) -> None:
78
+ self.connection = connection
79
+
80
+ def __enter__(self) -> Connection:
81
+ return self.connection
82
+
83
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
84
+ self.connection.cursor().close()
85
+
86
+
87
+ @dataclass
88
+ class MsSqlConnectionAdapter:
89
+ connection: _Connection
90
+
91
+ def cursor(self) -> MsSqlCursorAdapter:
92
+ return MsSqlCursorAdapter(self.connection.cursor())
93
+
94
+
95
+ @dataclass
96
+ class MsSqlCursorAdapter:
97
+ cursor: Cursor
98
+
99
+ def execute(self, *args: Any, **kwargs: Any) -> Any:
100
+ self.cursor.execute(*args, **kwargs) # type : ignore
101
+
102
+ def executemany(self, *args: Any, **kwargs: Any) -> Any:
103
+ self.cursor.executemany(*args, **kwargs) # type : ignore
104
+
105
+ def fetchone(self, *args: Any, **kwargs: Any) -> Any:
106
+ return self.cursor.fetchone() # type : ignore
107
+
108
+ def fetchall(self, *args: Any, **kwargs: Any) -> Any:
109
+ return self.cursor.fetchall() # type : ignore
110
+
111
+ def close(self) -> None:
112
+ self.cursor.close() # type : ignore
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Iterator, Protocol, TypeVar
5
+
6
+ from pymssql.exceptions import DatabaseError
7
+
8
+ from apexdevkit.error import DoesNotExistError, ExistsError
9
+ from apexdevkit.repository import Database, DatabaseCommand, RepositoryBase
10
+
11
+ ItemT = TypeVar("ItemT")
12
+
13
+
14
+ @dataclass
15
+ class MsSqlRepository(RepositoryBase[ItemT]):
16
+ db: Database
17
+ table: SqlTable[ItemT]
18
+
19
+ def __iter__(self) -> Iterator[ItemT]:
20
+ for raw in self.db.execute(self.table.select_all()).fetch_all():
21
+ yield self.table.load(raw)
22
+
23
+ def __len__(self) -> int:
24
+ raw = self.db.execute(self.table.count_all()).fetch_one()
25
+
26
+ try:
27
+ return int(raw["n_items"])
28
+ except KeyError:
29
+ raise UnknownError(raw)
30
+
31
+ def delete(self, item_id: str) -> None:
32
+ self.db.execute(self.table.delete(item_id)).fetch_none()
33
+
34
+ def delete_all(self) -> None:
35
+ self.db.execute(self.table.delete_all()).fetch_none()
36
+
37
+ def create(self, item: ItemT) -> ItemT:
38
+ try:
39
+ return self.table.load(self.db.execute(self.table.insert(item)).fetch_one())
40
+ except DatabaseError as e:
41
+ e = MssqlException(e)
42
+
43
+ if e.is_duplication():
44
+ raise self.table.exists(item)
45
+
46
+ raise UnknownError(e.message)
47
+
48
+ def read(self, item_id: str) -> ItemT:
49
+ raw = self.db.execute(self.table.select(item_id)).fetch_one()
50
+
51
+ if not raw:
52
+ raise DoesNotExistError(item_id)
53
+
54
+ return self.table.load(raw)
55
+
56
+ def update(self, item: ItemT) -> None:
57
+ self.db.execute(self.table.update(item)).fetch_none()
58
+
59
+
60
+ class SqlTable(Protocol[ItemT]): # pragma: no cover
61
+ def count_all(self) -> DatabaseCommand:
62
+ pass
63
+
64
+ def insert(self, item: ItemT) -> DatabaseCommand:
65
+ pass
66
+
67
+ def select(self, item_id: str) -> DatabaseCommand:
68
+ pass
69
+
70
+ def select_all(self) -> DatabaseCommand:
71
+ pass
72
+
73
+ def delete(self, item_id: str) -> DatabaseCommand:
74
+ pass
75
+
76
+ def delete_all(self) -> DatabaseCommand:
77
+ pass
78
+
79
+ def update(self, item: ItemT) -> DatabaseCommand:
80
+ pass
81
+
82
+ def load(self, data: dict[str, Any]) -> ItemT:
83
+ pass
84
+
85
+ def exists(self, duplicate: ItemT) -> ExistsError:
86
+ pass
87
+
88
+
89
+ @dataclass
90
+ class MssqlException:
91
+ code: int
92
+ message: str
93
+
94
+ def __init__(self, e: DatabaseError):
95
+ self.code = e.args[0]
96
+ self.message = e.args[1].decode()
97
+
98
+ def is_duplication(self) -> bool:
99
+ return self.code in [2627, 70003]
100
+
101
+
102
+ @dataclass
103
+ class UnknownError(Exception):
104
+ raw: Any
@@ -1,4 +1,5 @@
1
1
  from apexdevkit.testing.database import FakeConnector
2
+ from apexdevkit.testing.fake import FakeValue
2
3
  from apexdevkit.testing.rest import RestCollection, RestfulName, RestResource
3
4
 
4
5
  __all__ = [
@@ -6,4 +7,5 @@ __all__ = [
6
7
  "RestCollection",
7
8
  "RestfulName",
8
9
  "RestResource",
10
+ "FakeValue",
9
11
  ]
@@ -1,3 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ import random
1
4
  from dataclasses import dataclass, field
2
5
  from functools import cached_property
3
6
  from typing import Any, Generic, Type, TypeVar
@@ -5,6 +8,7 @@ from typing import Any, Generic, Type, TypeVar
5
8
  from faker import Faker
6
9
 
7
10
  from apexdevkit.http import JsonDict
11
+ from apexdevkit.value import Value
8
12
 
9
13
  ItemT = TypeVar("ItemT")
10
14
 
@@ -67,3 +71,15 @@ class FakeResource(Generic[ItemT]):
67
71
 
68
72
  def entity(self, **fields: Any) -> ItemT:
69
73
  return self.item_type(**self.json().merge(JsonDict(fields)))
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class FakeValue(FakeResource[Value]):
78
+ item_type: Type[Value] = field(default=Value)
79
+
80
+ @cached_property
81
+ def _raw(self) -> dict[str, Any]:
82
+ return {
83
+ "value": self.fake.number(),
84
+ "exponent": random.choice([10, 100, 1000]),
85
+ }
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from decimal import Decimal
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Value:
9
+ value: int = 0
10
+ exponent: int = 1
11
+
12
+ def as_decimal(self) -> Decimal:
13
+ return Decimal(self.value) / Decimal(self.exponent)
14
+
15
+ @classmethod
16
+ def from_string(cls, decimal_str: str) -> Value:
17
+ integer_part, _, fractional_part = decimal_str.partition(".")
18
+ value = int(integer_part + fractional_part)
19
+ exponent = 10 ** len(fractional_part)
20
+ return cls(value, exponent)
21
+
22
+ def add(self, other: Value) -> Value:
23
+ return Value(self.value + self._adjusted(other), self.exponent)
24
+
25
+ def subtract(self, other: Value) -> Value:
26
+ return Value(self.value - self._adjusted(other), self.exponent)
27
+
28
+ def _adjusted(self, other: Value) -> int:
29
+ return int(Decimal(other.value) / Decimal(other.exponent) * self.exponent)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.15.5"
3
+ version = "1.15.7"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
@@ -13,6 +13,7 @@ pymongo = "*"
13
13
  uvicorn = "*"
14
14
  sentry-sdk = {extras = ["fastapi"], version = "*"}
15
15
  python-dotenv = "*"
16
+ pymssql = "^2.3.1"
16
17
 
17
18
  [tool.poetry.group.dev.dependencies]
18
19
  pytest = "*"
@@ -1,42 +0,0 @@
1
- import sqlite3
2
- from dataclasses import dataclass
3
- from functools import cached_property
4
- from typing import Any, ContextManager
5
-
6
- from pymongo import MongoClient
7
-
8
- from apexdevkit.repository import Connection
9
-
10
-
11
- @dataclass(frozen=True)
12
- class SqliteFileConnector:
13
- dsn: str
14
-
15
- def connect(self) -> ContextManager[Connection]:
16
- connection = sqlite3.connect(self.dsn)
17
- connection.row_factory = sqlite3.Row
18
-
19
- return connection
20
-
21
-
22
- @dataclass(frozen=True)
23
- class SqliteInMemoryConnector:
24
- dsn: str = ":memory:"
25
-
26
- def connect(self) -> ContextManager[Connection]:
27
- return self._connection
28
-
29
- @cached_property
30
- def _connection(self) -> ContextManager[Connection]:
31
- connection = sqlite3.connect(self.dsn, check_same_thread=False)
32
- connection.row_factory = sqlite3.Row
33
-
34
- return connection
35
-
36
-
37
- @dataclass(frozen=True)
38
- class PyMongoConnector:
39
- dsn: str
40
-
41
- def connect(self) -> ContextManager[MongoClient[Any]]:
42
- return MongoClient(self.dsn)
File without changes
File without changes