apexdevkit 1.8.10__tar.gz → 1.9.2__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 (38) hide show
  1. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/PKG-INFO +2 -1
  2. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/service.py +3 -3
  3. apexdevkit-1.9.2/apexdevkit/formatter.py +50 -0
  4. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/connector.py +13 -1
  5. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/database.py +53 -1
  6. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/in_memory.py +3 -2
  7. apexdevkit-1.9.2/apexdevkit/repository/mongo.py +79 -0
  8. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/pyproject.toml +2 -1
  9. apexdevkit-1.8.10/apexdevkit/formatter.py +0 -57
  10. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/LICENSE +0 -0
  11. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/README.md +0 -0
  12. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/__init__.py +0 -0
  13. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/annotation/__init__.py +0 -0
  14. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/annotation/deprecate.py +0 -0
  15. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/error.py +0 -0
  16. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/__init__.py +0 -0
  17. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/builder.py +0 -0
  18. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/dependable.py +0 -0
  19. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/docs.py +0 -0
  20. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/resource.py +0 -0
  21. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/response.py +0 -0
  22. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/router.py +0 -0
  23. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/fastapi/schema.py +0 -0
  24. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/__init__.py +0 -0
  25. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/fake.py +0 -0
  26. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/fluent.py +0 -0
  27. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/httpx.py +0 -0
  28. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/json.py +0 -0
  29. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/http/url.py +0 -0
  30. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/py.typed +0 -0
  31. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/__init__.py +0 -0
  32. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/base.py +0 -0
  33. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/interface.py +0 -0
  34. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/repository/sqlite.py +0 -0
  35. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/testing/__init__.py +0 -0
  36. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/testing/database.py +0 -0
  37. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/testing/fake.py +0 -0
  38. {apexdevkit-1.8.10 → apexdevkit-1.9.2}/apexdevkit/testing/rest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.8.10
3
+ Version: 1.9.2
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -10,6 +10,7 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Requires-Dist: fastapi
12
12
  Requires-Dist: httpx
13
+ Requires-Dist: pymongo
13
14
  Description-Content-Type: text/markdown
14
15
 
15
16
  # apexdevkit
@@ -51,10 +51,10 @@ ItemT = TypeVar("ItemT")
51
51
 
52
52
  @dataclass
53
53
  class RestfulRepositoryBuilder(Generic[ItemT]):
54
- formatter: Formatter[ItemT] = field(init=False)
54
+ formatter: Formatter[dict[str, Any], ItemT] = field(init=False)
55
55
  repository: Repository[Any, ItemT] = field(init=False)
56
56
 
57
- def with_formatter(self, formatter: Formatter[ItemT]) -> Self:
57
+ def with_formatter(self, formatter: Formatter[dict[str, Any], ItemT]) -> Self:
58
58
  self.formatter = formatter
59
59
 
60
60
  return self
@@ -70,7 +70,7 @@ class RestfulRepositoryBuilder(Generic[ItemT]):
70
70
 
71
71
  @dataclass
72
72
  class _RestfulRepository(RestfulService, Generic[ItemT]):
73
- formatter: Formatter[ItemT]
73
+ formatter: Formatter[dict[str, Any], ItemT]
74
74
  repository: Repository[Any, ItemT]
75
75
 
76
76
  def create_one(self, item: RawItem) -> RawItem:
@@ -0,0 +1,50 @@
1
+ from copy import deepcopy
2
+ from dataclasses import asdict, dataclass, field
3
+ from typing import Any, Generic, Protocol, Self, TypeVar
4
+
5
+ _SourceT = TypeVar("_SourceT")
6
+ _TargetT = TypeVar("_TargetT")
7
+
8
+
9
+ class Formatter(Protocol[_SourceT, _TargetT]): # pragma: no cover
10
+ def load(self, source: _SourceT) -> _TargetT:
11
+ pass
12
+
13
+ def dump(self, target: _TargetT) -> _SourceT:
14
+ pass
15
+
16
+
17
+ @dataclass
18
+ class ListFormatter(Generic[_SourceT, _TargetT]):
19
+ inner: Formatter[_SourceT, _TargetT]
20
+
21
+ def load(self, source: list[_SourceT]) -> list[_TargetT]:
22
+ return [self.inner.load(item) for item in source]
23
+
24
+ def dump(self, target: list[_TargetT]) -> list[_SourceT]:
25
+ return [self.inner.dump(item) for item in target]
26
+
27
+
28
+ @dataclass
29
+ class DataclassFormatter(Generic[_TargetT]):
30
+ resource: type[_TargetT]
31
+ sub_formatters: dict[str, Formatter[Any, Any]] = field(default_factory=dict)
32
+
33
+ def and_nested(self, **formatters: Formatter[Any, Any]) -> Self:
34
+ return self.with_nested(**formatters)
35
+
36
+ def with_nested(self, **formatters: Formatter[Any, Any]) -> Self:
37
+ self.sub_formatters.update(formatters)
38
+
39
+ return self
40
+
41
+ def load(self, raw: dict[str, Any]) -> _TargetT:
42
+ raw = deepcopy(raw)
43
+
44
+ for key, formatter in self.sub_formatters.items():
45
+ raw[key] = formatter.load(raw.pop(key))
46
+
47
+ return self.resource(**raw)
48
+
49
+ def dump(self, item: _TargetT) -> dict[str, Any]:
50
+ return asdict(item) # type: ignore
@@ -1,7 +1,9 @@
1
1
  import sqlite3
2
2
  from dataclasses import dataclass
3
3
  from functools import cached_property
4
- from typing import ContextManager
4
+ from typing import Any, ContextManager
5
+
6
+ from pymongo import MongoClient
5
7
 
6
8
  from apexdevkit.repository import Connection
7
9
 
@@ -30,3 +32,13 @@ class SqliteInMemoryConnector:
30
32
  connection.row_factory = sqlite3.Row
31
33
 
32
34
  return connection
35
+
36
+
37
+ @dataclass
38
+ class MongoDBConnector:
39
+ dsn: str
40
+
41
+ def connect(self) -> ContextManager[MongoClient[Any]]:
42
+ connection: MongoClient[Any] = MongoClient(self.dsn)
43
+
44
+ return connection
@@ -1,7 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
- from typing import Any, ContextManager, Iterable, Protocol, Self
4
+ from typing import Any, ContextManager, Dict, Iterable, Iterator, Protocol, Self
5
+
6
+ from pymongo import MongoClient
7
+ from pymongo.collection import Collection, ReturnDocument
8
+ from pymongo.results import DeleteResult, InsertOneResult
5
9
 
6
10
  _RawData = dict[str, Any]
7
11
 
@@ -42,11 +46,59 @@ class Database:
42
46
  return [dict(raw or {}) for raw in raw]
43
47
 
44
48
 
49
+ @dataclass
50
+ class MongoDatabase:
51
+ connector: MongoConnector
52
+ database_name: str
53
+ collection_name: str
54
+
55
+ def collection(self, client: MongoClient[Any]) -> Collection[Any]:
56
+ return client[self.database_name][self.collection_name]
57
+
58
+ def __iter__(self) -> Iterator[Any]:
59
+ with self.connector.connect() as client:
60
+ for raw in self.collection(client).find():
61
+ yield raw
62
+
63
+ def __len__(self) -> int:
64
+ with self.connector.connect() as client:
65
+ return self.collection(client).count_documents({})
66
+
67
+ def create(self, item: Dict[str, Any]) -> InsertOneResult:
68
+ with self.connector.connect() as client:
69
+ return self.collection(client).insert_one(item)
70
+
71
+ def read(self, item_id: str) -> Dict[str, Any] | None:
72
+ with self.connector.connect() as client:
73
+ return self.collection(client).find_one({"id": item_id})
74
+
75
+ def update(self, item_id: str, item: Dict[str, Any]) -> Any:
76
+ with self.connector.connect() as client:
77
+ return self.collection(client).find_one_and_update(
78
+ {"id": item_id},
79
+ {"$set": item},
80
+ return_document=ReturnDocument.AFTER,
81
+ )
82
+
83
+ def delete(self, item_id: str) -> DeleteResult:
84
+ with self.connector.connect() as client:
85
+ return self.collection(client).delete_one({"id": item_id})
86
+
87
+ def delete_all(self) -> DeleteResult:
88
+ with self.connector.connect() as client:
89
+ return self.collection(client).delete_many({})
90
+
91
+
45
92
  class Connector(Protocol): # pragma: no cover
46
93
  def connect(self) -> ContextManager[Connection]:
47
94
  pass
48
95
 
49
96
 
97
+ class MongoConnector(Protocol): # pragma: no cover
98
+ def connect(self) -> ContextManager[MongoClient[Any]]:
99
+ pass
100
+
101
+
50
102
  class Connection(Protocol): # pragma: no cover
51
103
  def cursor(self) -> Cursor:
52
104
  pass
@@ -13,12 +13,13 @@ class _Item(Protocol): # pragma: no cover
13
13
 
14
14
 
15
15
  ItemT = TypeVar("ItemT", bound=_Item)
16
+ _Raw = dict[str, Any]
16
17
 
17
18
 
18
19
  @dataclass
19
20
  class InMemoryRepository(Generic[ItemT]):
20
- formatter: Formatter[ItemT]
21
- items: dict[str, dict[str, Any]] = field(default_factory=dict)
21
+ formatter: Formatter[_Raw, ItemT]
22
+ items: dict[str, _Raw] = field(default_factory=dict)
22
23
 
23
24
  _uniques: list[Criteria] = field(init=False, default_factory=list)
24
25
  _search_by: list[str] = field(init=False, default_factory=list)
@@ -0,0 +1,79 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, Generic, Iterator, Protocol, TypeVar
5
+
6
+ from apexdevkit.error import DoesNotExistError, ExistsError
7
+ from apexdevkit.repository.database import MongoDatabase
8
+
9
+
10
+ class _Item(Protocol): # pragma: no cover
11
+ @property
12
+ def id(self) -> Any:
13
+ pass
14
+
15
+
16
+ ItemT = TypeVar("ItemT", bound=_Item)
17
+
18
+
19
+ @dataclass
20
+ class MongoDBRepository(Generic[ItemT]):
21
+ database: MongoDatabase
22
+ table: MongoTable[ItemT]
23
+
24
+ def __iter__(self) -> Iterator[ItemT]:
25
+ for raw in self.database:
26
+ yield self.table.load(raw)
27
+
28
+ def __len__(self) -> int:
29
+ return len(self.database)
30
+
31
+ def create(self, item: ItemT) -> ItemT:
32
+ try:
33
+ self.read(item.id)
34
+ raise ExistsError(item).with_duplicate(
35
+ lambda i: f"_Item with id<{i.id}> already exists."
36
+ )
37
+ except DoesNotExistError:
38
+ self.database.create(self.table.to_dict(item))
39
+ return item
40
+
41
+ def create_many(self, items: list[ItemT]) -> list[ItemT]:
42
+ return [self.create(item) for item in items]
43
+
44
+ def read(self, item_id: str) -> ItemT:
45
+ raw = self.database.read(item_id)
46
+
47
+ if not raw:
48
+ raise DoesNotExistError(item_id)
49
+
50
+ return self.table.load(raw)
51
+
52
+ def update(self, item: ItemT) -> None:
53
+ self.database.update(
54
+ self.table.get_id(item),
55
+ self.table.to_dict(item),
56
+ )
57
+
58
+ def update_many(self, items: list[ItemT]) -> None:
59
+ for item in items:
60
+ self.update(item)
61
+
62
+ def delete(self, item_id: str) -> None:
63
+ result = self.database.delete(item_id)
64
+ if result.deleted_count == 0:
65
+ raise DoesNotExistError(item_id)
66
+
67
+ def delete_all(self) -> None:
68
+ self.database.delete_all()
69
+
70
+
71
+ class MongoTable(Generic[ItemT]):
72
+ def to_dict(self, item: ItemT) -> Dict[str, Any]:
73
+ raise NotImplementedError("Not implemented")
74
+
75
+ def load(self, data: Dict[str, Any]) -> ItemT:
76
+ raise NotImplementedError("Not implemented")
77
+
78
+ def get_id(self, item: ItemT) -> str:
79
+ raise NotImplementedError("Not implemented")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.8.10"
3
+ version = "1.9.2"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
@@ -9,6 +9,7 @@ readme = "README.md"
9
9
  python = "^3.11"
10
10
  httpx = "*"
11
11
  fastapi = "*"
12
+ pymongo = "*"
12
13
 
13
14
  [tool.poetry.group.dev.dependencies]
14
15
  pytest = "*"
@@ -1,57 +0,0 @@
1
- from copy import deepcopy
2
- from dataclasses import asdict, dataclass, field
3
- from typing import Any, Generic, Protocol, Self, TypeVar
4
-
5
- ItemT = TypeVar("ItemT")
6
-
7
-
8
- class Formatter(Protocol[ItemT]): # pragma: no cover
9
- def load(self, raw: dict[str, Any]) -> ItemT:
10
- pass
11
-
12
- def dump(self, item: ItemT) -> dict[str, Any]:
13
- pass
14
-
15
-
16
- @dataclass
17
- class ListFormatter(Generic[ItemT]):
18
- inner: Formatter[ItemT]
19
-
20
- def load(self, raw: list[dict[str, Any]]) -> list[ItemT]:
21
- result = []
22
- for item in raw:
23
- result.append(self.inner.load(item))
24
- return result
25
-
26
- def dump(self, items: list[ItemT]) -> list[dict[str, Any]]:
27
- result: list[dict[str, Any]] = []
28
- for item in items:
29
- result.append(self.inner.dump(item))
30
- return result
31
-
32
-
33
- @dataclass
34
- class DataclassFormatter(Generic[ItemT]):
35
- resource: type[ItemT]
36
- sub_formatters: dict[str, Formatter[Any] | ListFormatter[Any]] = field(
37
- default_factory=dict
38
- )
39
-
40
- def and_nested(self, **formatters: Formatter[Any] | ListFormatter[Any]) -> Self:
41
- return self.with_nested(**formatters)
42
-
43
- def with_nested(self, **formatters: Formatter[Any] | ListFormatter[Any]) -> Self:
44
- self.sub_formatters.update(formatters)
45
-
46
- return self
47
-
48
- def load(self, raw: dict[str, Any]) -> ItemT:
49
- raw = deepcopy(raw)
50
-
51
- for key, formatter in self.sub_formatters.items():
52
- raw[key] = formatter.load(raw.pop(key))
53
-
54
- return self.resource(**raw)
55
-
56
- def dump(self, item: ItemT) -> dict[str, Any]:
57
- return asdict(item) # type: ignore
File without changes
File without changes