apexdevkit 1.14.1__tar.gz → 1.14.3__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 (42) hide show
  1. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/PKG-INFO +1 -1
  2. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/builder.py +4 -2
  3. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/dependable.py +20 -22
  4. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/resource.py +1 -1
  5. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/response.py +1 -1
  6. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/schema.py +1 -1
  7. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/service.py +18 -14
  8. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fluent.py +1 -1
  9. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/formatter.py +7 -6
  10. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/fake.py +1 -1
  11. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/httpx.py +1 -1
  12. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/key_fn.py +1 -1
  13. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/__init__.py +2 -2
  14. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/connector.py +3 -3
  15. apexdevkit-1.14.3/apexdevkit/repository/database.py +94 -0
  16. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/in_memory.py +69 -33
  17. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/mongo.py +1 -1
  18. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/sqlite.py +1 -1
  19. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/testing/database.py +6 -6
  20. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/testing/fake.py +2 -2
  21. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/testing/rest.py +3 -3
  22. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/pyproject.toml +1 -1
  23. apexdevkit-1.14.1/apexdevkit/repository/database.py +0 -90
  24. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/LICENSE +0 -0
  25. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/README.md +0 -0
  26. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/__init__.py +0 -0
  27. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/annotation/__init__.py +0 -0
  28. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/annotation/deprecate.py +0 -0
  29. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/environment.py +0 -0
  30. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/error.py +0 -0
  31. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/__init__.py +0 -0
  32. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/docs.py +0 -0
  33. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/fastapi/router.py +0 -0
  34. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/__init__.py +0 -0
  35. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/fluent.py +0 -0
  36. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/json.py +0 -0
  37. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/http/url.py +0 -0
  38. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/py.typed +0 -0
  39. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/base.py +0 -0
  40. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/decorator.py +0 -0
  41. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/repository/interface.py +0 -0
  42. {apexdevkit-1.14.1 → apexdevkit-1.14.3}/apexdevkit/testing/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.14.1
3
+ Version: 1.14.3
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from abc import ABC, abstractmethod
2
4
  from dataclasses import dataclass, field
3
5
  from typing import Any, Self
@@ -53,12 +55,12 @@ class RestfulServiceBuilder(ABC):
53
55
  parent_id: str = field(init=False)
54
56
  user: Any = field(init=False)
55
57
 
56
- def with_user(self, user: Any) -> "RestfulServiceBuilder":
58
+ def with_user(self, user: Any) -> RestfulServiceBuilder:
57
59
  self.user = user
58
60
 
59
61
  return self
60
62
 
61
- def with_parent(self, identity: str) -> "RestfulServiceBuilder":
63
+ def with_parent(self, identity: str) -> RestfulServiceBuilder:
62
64
  self.parent_id = identity
63
65
 
64
66
  return self
@@ -1,5 +1,5 @@
1
- from dataclasses import dataclass, field
2
- from typing import Annotated, Any, Callable, Protocol, Self
1
+ from dataclasses import dataclass
2
+ from typing import Annotated, Any, Callable, Protocol
3
3
 
4
4
  from fastapi import Depends, Path
5
5
  from fastapi.requests import Request
@@ -23,7 +23,7 @@ class _Dependency(Protocol):
23
23
  pass
24
24
 
25
25
 
26
- @dataclass
26
+ @dataclass(frozen=True)
27
27
  class ServiceDependency:
28
28
  dependency: _Dependency
29
29
 
@@ -36,7 +36,7 @@ class ServiceDependency:
36
36
  return Annotated[RestfulService, Depends(_)]
37
37
 
38
38
 
39
- @dataclass
39
+ @dataclass(frozen=True)
40
40
  class ParentDependency:
41
41
  parent: RestfulName
42
42
  dependency: _Dependency
@@ -56,7 +56,7 @@ class ParentDependency:
56
56
  return Annotated[RestfulServiceBuilder, Depends(_)]
57
57
 
58
58
 
59
- @dataclass
59
+ @dataclass(frozen=True)
60
60
  class UserDependency:
61
61
  extract_user: Callable[..., Any]
62
62
  dependency: _Dependency
@@ -71,7 +71,7 @@ class UserDependency:
71
71
  return Annotated[RestfulServiceBuilder, Depends(_)]
72
72
 
73
73
 
74
- @dataclass
74
+ @dataclass(frozen=True)
75
75
  class InfraDependency:
76
76
  infra: RestfulServiceBuilder
77
77
 
@@ -82,26 +82,24 @@ class InfraDependency:
82
82
  return Annotated[RestfulServiceBuilder, Depends(_)]
83
83
 
84
84
 
85
- @dataclass
85
+ @dataclass(frozen=True)
86
86
  class DependableBuilder:
87
- dependency: _Dependency = field(init=False)
87
+ dependency: _Dependency | None = None
88
88
 
89
- def from_infra(self, value: RestfulServiceBuilder) -> Self:
90
- self.dependency = InfraDependency(value)
89
+ def from_infra(self, value: RestfulServiceBuilder) -> "DependableBuilder":
90
+ return DependableBuilder(InfraDependency(value))
91
91
 
92
- return self
92
+ def with_parent(self, value: RestfulName) -> "DependableBuilder":
93
+ if self.dependency is None:
94
+ raise RuntimeError("RestfulServiceBuilder type not set")
95
+ return DependableBuilder(ParentDependency(value, self.dependency))
93
96
 
94
- def with_parent(self, value: RestfulName) -> Self:
95
- dependable = DependableBuilder()
96
- dependable.dependency = ParentDependency(value, self.dependency)
97
-
98
- return dependable
99
-
100
- def with_user(self, extract_user: Callable[..., Any]) -> Self:
101
- dependable = DependableBuilder()
102
- dependable.dependency = UserDependency(extract_user, self.dependency)
103
-
104
- return dependable
97
+ def with_user(self, extract_user: Callable[..., Any]) -> "DependableBuilder":
98
+ if self.dependency is None:
99
+ raise RuntimeError("RestfulServiceBuilder type not set")
100
+ return DependableBuilder(UserDependency(extract_user, self.dependency))
105
101
 
106
102
  def as_dependable(self) -> type[RestfulService]:
103
+ if self.dependency is None:
104
+ raise RuntimeError("RestfulServiceBuilder type not set")
107
105
  return ServiceDependency(self.dependency).as_dependable()
@@ -10,7 +10,7 @@ _Response = JSONResponse | dict[str, Any]
10
10
  _Endpoint = Callable[..., _Response]
11
11
 
12
12
 
13
- @dataclass
13
+ @dataclass(frozen=True)
14
14
  class RestfulResource:
15
15
  response: RestfulResponse
16
16
 
@@ -7,7 +7,7 @@ from apexdevkit.error import DoesNotExistError, ExistsError, ForbiddenError
7
7
  from apexdevkit.testing import RestfulName
8
8
 
9
9
 
10
- @dataclass
10
+ @dataclass(frozen=True)
11
11
  class RestfulResponse:
12
12
  name: RestfulName
13
13
 
@@ -24,7 +24,7 @@ class SchemaFields(ABC):
24
24
  pass
25
25
 
26
26
 
27
- @dataclass
27
+ @dataclass(frozen=True)
28
28
  class RestfulSchema:
29
29
  name: RestfulName
30
30
  fields: SchemaFields
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from abc import ABC
2
- from dataclasses import dataclass, field
3
- from typing import Any, Dict, Generic, Iterable, Self, TypeVar
4
+ from dataclasses import dataclass
5
+ from typing import Any, Dict, Generic, Iterable, TypeVar
4
6
 
5
7
  from apexdevkit.formatter import Formatter
6
8
  from apexdevkit.repository.decorator import BatchRepositoryDecorator
@@ -50,26 +52,28 @@ class RestfulService(ABC): # pragma: no cover
50
52
  ItemT = TypeVar("ItemT")
51
53
 
52
54
 
53
- @dataclass
55
+ @dataclass(frozen=True)
54
56
  class RestfulRepositoryBuilder(Generic[ItemT]):
55
- formatter: Formatter[dict[str, Any], ItemT] = field(init=False)
56
- repository: Repository[ItemT] = field(init=False)
57
-
58
- def with_formatter(self, formatter: Formatter[dict[str, Any], ItemT]) -> Self:
59
- self.formatter = formatter
60
-
61
- return self
57
+ formatter: Formatter[dict[str, Any], ItemT] | None = None
58
+ repository: Repository[ItemT] | None = None
62
59
 
63
- def with_repository(self, repository: Repository[ItemT]) -> Self:
64
- self.repository = repository
60
+ def with_formatter(
61
+ self, formatter: Formatter[dict[str, Any], ItemT]
62
+ ) -> RestfulRepositoryBuilder[ItemT]:
63
+ return RestfulRepositoryBuilder[ItemT](formatter, self.repository)
65
64
 
66
- return self
65
+ def with_repository(
66
+ self, repository: Repository[ItemT]
67
+ ) -> RestfulRepositoryBuilder[ItemT]:
68
+ return RestfulRepositoryBuilder[ItemT](self.formatter, repository)
67
69
 
68
70
  def build(self) -> RestfulService:
71
+ if self.formatter is None or self.repository is None:
72
+ raise RuntimeError("Formatter or repository not provided.")
69
73
  return _RestfulRepository(self.formatter, self.repository)
70
74
 
71
75
 
72
- @dataclass
76
+ @dataclass(frozen=True)
73
77
  class _RestfulRepository(RestfulService, Generic[ItemT]):
74
78
  formatter: Formatter[dict[str, Any], ItemT]
75
79
  repository: Repository[ItemT]
@@ -26,7 +26,7 @@ class FluentDict(dict[str, ItemT]):
26
26
  return FluentDict[ItemT]({k: v for k, v in self.items() if k in keys})
27
27
 
28
28
 
29
- @dataclass
29
+ @dataclass(frozen=True)
30
30
  class FluentElement(Generic[ItemT]):
31
31
  value: ItemT
32
32
 
@@ -1,9 +1,11 @@
1
+ import pickle
1
2
  from copy import deepcopy
2
3
  from dataclasses import asdict, dataclass, field
3
4
  from typing import Any, Generic, Protocol, Self, TypeVar
4
5
 
5
6
  _SourceT = TypeVar("_SourceT")
6
7
  _TargetT = TypeVar("_TargetT")
8
+ _ItemT = TypeVar("_ItemT")
7
9
 
8
10
 
9
11
  class Formatter(Protocol[_SourceT, _TargetT]): # pragma: no cover
@@ -14,13 +16,12 @@ class Formatter(Protocol[_SourceT, _TargetT]): # pragma: no cover
14
16
  pass
15
17
 
16
18
 
17
- @dataclass
18
- class NoFormatter(Formatter[Any, Any]):
19
- def load(self, source: Any) -> Any:
20
- return source
19
+ class PickleFormatter(Generic[_ItemT]):
20
+ def dump(self, item: _ItemT) -> bytes:
21
+ return pickle.dumps(item)
21
22
 
22
- def dump(self, target: Any) -> Any:
23
- return target
23
+ def load(self, raw: bytes) -> _ItemT:
24
+ return pickle.loads(raw) # type: ignore
24
25
 
25
26
 
26
27
  @dataclass
@@ -7,7 +7,7 @@ from apexdevkit.http.fluent import HttpMethod, HttpResponse
7
7
  from apexdevkit.http.json import JsonDict
8
8
 
9
9
 
10
- @dataclass
10
+ @dataclass(frozen=True)
11
11
  class FakeResponse:
12
12
  content: Any = field(default_factory=dict)
13
13
  status_code: int = 200
@@ -46,7 +46,7 @@ class Httpx:
46
46
  )
47
47
 
48
48
 
49
- @dataclass
49
+ @dataclass(frozen=True)
50
50
  class _HttpxResponse:
51
51
  inner: httpx.Response
52
52
 
@@ -4,7 +4,7 @@ from dataclasses import dataclass
4
4
  from typing import Any
5
5
 
6
6
 
7
- @dataclass
7
+ @dataclass(frozen=True)
8
8
  class AttributeKey:
9
9
  name: str
10
10
 
@@ -7,7 +7,7 @@ from apexdevkit.repository.database import (
7
7
  DatabaseCommand,
8
8
  )
9
9
  from apexdevkit.repository.in_memory import (
10
- InMemoryKeyValueStore,
10
+ InMemoryByteStore,
11
11
  InMemoryRepository,
12
12
  KeyValueStore,
13
13
  )
@@ -20,7 +20,7 @@ __all__ = [
20
20
  "Database",
21
21
  "DatabaseCommand",
22
22
  "InMemoryRepository",
23
- "InMemoryKeyValueStore",
23
+ "InMemoryByteStore",
24
24
  "KeyValueStore",
25
25
  "Repository",
26
26
  "RepositoryBase",
@@ -8,7 +8,7 @@ from pymongo import MongoClient
8
8
  from apexdevkit.repository import Connection
9
9
 
10
10
 
11
- @dataclass
11
+ @dataclass(frozen=True)
12
12
  class SqliteFileConnector:
13
13
  dsn: str
14
14
 
@@ -19,7 +19,7 @@ class SqliteFileConnector:
19
19
  return connection
20
20
 
21
21
 
22
- @dataclass
22
+ @dataclass(frozen=True)
23
23
  class SqliteInMemoryConnector:
24
24
  dsn: str = ":memory:"
25
25
 
@@ -34,7 +34,7 @@ class SqliteInMemoryConnector:
34
34
  return connection
35
35
 
36
36
 
37
- @dataclass
37
+ @dataclass(frozen=True)
38
38
  class PyMongoConnector:
39
39
  dsn: str
40
40
 
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+ from dataclasses import dataclass, field
5
+ from typing import Any, ContextManager, Iterable, Protocol
6
+
7
+ _RawData = dict[str, Any]
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Database:
12
+ connector: Connector
13
+
14
+ def execute(self, command: DatabaseCommand) -> _CommandExecutor:
15
+ return Database._CommandExecutor(self.connector, command)
16
+
17
+ @dataclass(frozen=True)
18
+ class _CommandExecutor:
19
+ connector: Connector
20
+ command: DatabaseCommand
21
+
22
+ def fetch_none(self) -> None:
23
+ with self.connector.connect() as connection:
24
+ cursor: Cursor = connection.cursor()
25
+ cursor.execute(self.command.value, self.command.payload)
26
+ cursor.close()
27
+
28
+ def fetch_one(self) -> _RawData:
29
+ with self.connector.connect() as connection:
30
+ cursor: Cursor = connection.cursor()
31
+ cursor.execute(self.command.value, self.command.payload)
32
+ raw = cursor.fetchone()
33
+ cursor.close()
34
+
35
+ return dict(raw or {})
36
+
37
+ def fetch_all(self) -> Iterable[_RawData]:
38
+ with self.connector.connect() as connection:
39
+ cursor: Cursor = connection.cursor()
40
+ cursor.execute(self.command.value, self.command.payload)
41
+ raw = cursor.fetchall()
42
+ cursor.close()
43
+
44
+ return [dict(raw or {}) for raw in raw]
45
+
46
+
47
+ class Connector(Protocol): # pragma: no cover
48
+ def connect(self) -> ContextManager[Connection]:
49
+ pass
50
+
51
+
52
+ class Connection(Protocol): # pragma: no cover
53
+ def cursor(self) -> Cursor:
54
+ pass
55
+
56
+
57
+ class Cursor(Protocol): # pragma: no cover
58
+ def execute(self, *args: Any, **kwargs: Any) -> Any:
59
+ pass
60
+
61
+ def executemany(self, *args: Any, **kwargs: Any) -> Any:
62
+ pass
63
+
64
+ def fetchone(self, *args: Any, **kwargs: Any) -> Any:
65
+ pass
66
+
67
+ def fetchall(self, *args: Any, **kwargs: Any) -> Any:
68
+ pass
69
+
70
+ def close(self) -> None:
71
+ pass
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class DatabaseCommand:
76
+ value: str = field(default_factory=str)
77
+ payload: _RawData | list[_RawData] = field(default_factory=dict)
78
+
79
+ def with_data(
80
+ self, value: _RawData | None = None, **fields: Any
81
+ ) -> DatabaseCommand:
82
+ assert isinstance(self.payload, dict)
83
+
84
+ payload = deepcopy(self.payload)
85
+ payload.update(value or {})
86
+ payload.update(fields)
87
+
88
+ return DatabaseCommand(self.value, payload)
89
+
90
+ def with_collection(self, value: list[_RawData]) -> DatabaseCommand:
91
+ return DatabaseCommand(self.value, value)
92
+
93
+ def __str__(self) -> str: # pragma: no cover
94
+ return self.value
@@ -1,54 +1,65 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from contextlib import suppress
3
4
  from dataclasses import dataclass, field
4
5
  from typing import Any, Callable, Generic, Iterable, Iterator, Protocol, Self
5
6
 
6
7
  from apexdevkit.error import DoesNotExistError, ExistsError
7
- from apexdevkit.formatter import Formatter, NoFormatter
8
+ from apexdevkit.formatter import Formatter, PickleFormatter
8
9
  from apexdevkit.key_fn import AttributeKey
9
10
  from apexdevkit.repository import RepositoryBase
10
11
  from apexdevkit.repository.interface import ItemT, Repository
11
12
 
12
- KeyFunction = Callable[[Any], str]
13
- _Raw = dict[str, Any]
13
+ _KeyFunction = Callable[[ItemT], str]
14
14
 
15
15
 
16
- @dataclass
16
+ @dataclass(frozen=True)
17
17
  class InMemoryRepository(Generic[ItemT]):
18
- store: KeyValueStore[ItemT] = field(default_factory=lambda: InMemoryKeyValueStore())
19
- keys: list[KeyFunction] = field(default_factory=list)
20
- seeds: list[ItemT] = field(default_factory=list)
21
-
22
- def with_formatter(self, value: Formatter[_Raw, ItemT]) -> Self:
23
- return self.with_store(InMemoryKeyValueStore(value))
24
-
25
- def with_store(self, value: KeyValueStore[ItemT]) -> Self:
26
- self.store = value
27
-
28
- return self
29
-
30
- def and_key(self, function: KeyFunction) -> Self:
18
+ store: KeyValueStore[ItemT] = field(default_factory=lambda: InMemoryByteStore())
19
+ keys: list[_KeyFunction[ItemT]] = field(default_factory=list)
20
+ seeds: frozenset[ItemT] = field(default_factory=frozenset)
21
+
22
+ def with_namespace(self, value: str) -> InMemoryRepository[ItemT]:
23
+ return InMemoryRepository[ItemT](
24
+ store=StoreNamespace(value, self.store),
25
+ keys=self.keys,
26
+ seeds=self.seeds,
27
+ )
28
+
29
+ def with_store(self, value: KeyValueStore[ItemT]) -> InMemoryRepository[ItemT]:
30
+ return InMemoryRepository[ItemT](
31
+ store=value,
32
+ keys=self.keys,
33
+ seeds=self.seeds,
34
+ )
35
+
36
+ def and_key(self, function: _KeyFunction[ItemT]) -> InMemoryRepository[ItemT]:
31
37
  return self.with_key(function)
32
38
 
33
- def with_key(self, function: KeyFunction) -> Self:
34
- self.keys.append(function)
39
+ def with_key(self, function: _KeyFunction[ItemT]) -> InMemoryRepository[ItemT]:
40
+ return InMemoryRepository[ItemT](
41
+ store=self.store,
42
+ keys=[*self.keys, function],
43
+ seeds=self.seeds,
44
+ )
35
45
 
36
- return self
37
-
38
- def and_seeded(self, *items: ItemT) -> Self:
46
+ def and_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
39
47
  return self.with_seeded(*items)
40
48
 
41
- def with_seeded(self, *items: ItemT) -> Self:
42
- self.seeds.extend(items)
43
-
44
- return self
49
+ def with_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
50
+ return InMemoryRepository(
51
+ store=self.store,
52
+ keys=self.keys,
53
+ seeds=self.seeds.union(set(items)),
54
+ )
45
55
 
46
56
  def build(self) -> Repository[ItemT]:
47
57
  return self._seed(self._create())
48
58
 
49
59
  def _seed(self, repository: Repository[ItemT]) -> Repository[ItemT]:
50
60
  for seed in self.seeds:
51
- repository.create(seed)
61
+ with suppress(ExistsError):
62
+ repository.create(seed)
52
63
 
53
64
  return repository
54
65
 
@@ -80,9 +91,10 @@ class KeyValueStore(Protocol[ItemT]): # pragma: no cover
80
91
 
81
92
 
82
93
  @dataclass
83
- class InMemoryKeyValueStore(Generic[ItemT]):
84
- formatter: Formatter[_Raw, ItemT] = field(default_factory=NoFormatter)
85
- items: dict[str, _Raw] = field(default_factory=dict)
94
+ class InMemoryByteStore(Generic[ItemT]):
95
+ formatter: Formatter[bytes, ItemT] = field(default_factory=PickleFormatter)
96
+
97
+ items: dict[str, bytes] = field(default_factory=dict)
86
98
 
87
99
  def count(self) -> int:
88
100
  return len(self.items)
@@ -101,10 +113,34 @@ class InMemoryKeyValueStore(Generic[ItemT]):
101
113
  yield self.formatter.load(raw)
102
114
 
103
115
 
116
+ @dataclass
117
+ class StoreNamespace(Generic[ItemT]):
118
+ name: str
119
+ inner: KeyValueStore[ItemT]
120
+
121
+ def count(self) -> int:
122
+ return self.inner.count()
123
+
124
+ def values(self) -> Iterable[ItemT]:
125
+ return self.inner.values()
126
+
127
+ def set(self, key: str, value: ItemT) -> None:
128
+ self.inner.set(self._expand(key), value)
129
+
130
+ def get(self, key: str) -> ItemT:
131
+ return self.inner.get(self._expand(key))
132
+
133
+ def drop(self, key: str) -> None:
134
+ self.inner.drop(self._expand(key))
135
+
136
+ def _expand(self, key: str) -> str:
137
+ return "-".join([self.name, key])
138
+
139
+
104
140
  @dataclass
105
141
  class _SingleKeyRepository(RepositoryBase[ItemT]):
106
142
  store: KeyValueStore[ItemT]
107
- pk: KeyFunction
143
+ pk: _KeyFunction[ItemT]
108
144
 
109
145
  def bind(self, **kwargs: Any) -> Self: # pragma: no cover
110
146
  return self
@@ -150,7 +186,7 @@ class _SingleKeyRepository(RepositoryBase[ItemT]):
150
186
  class _ManyKeyRepository(RepositoryBase[ItemT]):
151
187
  store: KeyValueStore[ItemT]
152
188
 
153
- keys: list[KeyFunction] = field(default_factory=list)
189
+ keys: list[_KeyFunction[ItemT]] = field(default_factory=list)
154
190
 
155
191
  def bind(self, **kwargs: Any) -> Self: # pragma: no cover
156
192
  return self
@@ -171,7 +207,7 @@ class _ManyKeyRepository(RepositoryBase[ItemT]):
171
207
 
172
208
  error.fire()
173
209
 
174
- def _pk(self, item: ItemT) -> Any:
210
+ def _pk(self, item: ItemT) -> str:
175
211
  return self.keys[0](item)
176
212
 
177
213
  def update(self, item: ItemT) -> None:
@@ -19,7 +19,7 @@ class _Item(Protocol): # pragma: no cover
19
19
  ItemT = TypeVar("ItemT", bound=_Item)
20
20
 
21
21
 
22
- @dataclass
22
+ @dataclass(frozen=True)
23
23
  class MongoRepository(Generic[ItemT]):
24
24
  connector: MongoConnector
25
25
  database_name: str
@@ -9,7 +9,7 @@ from apexdevkit.repository import Database, DatabaseCommand, RepositoryBase
9
9
  from apexdevkit.repository.interface import ItemT
10
10
 
11
11
 
12
- @dataclass
12
+ @dataclass(frozen=True)
13
13
  class SqliteRepository(RepositoryBase[ItemT]):
14
14
  db: Database
15
15
  table: SqlTable[ItemT]
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from contextlib import nullcontext
2
4
  from dataclasses import dataclass, field
3
5
  from typing import Any, ContextManager, Self
@@ -5,15 +7,13 @@ from typing import Any, ContextManager, Self
5
7
  from apexdevkit.repository import DatabaseCommand
6
8
 
7
9
 
8
- @dataclass
10
+ @dataclass(frozen=True)
9
11
  class FakeConnector:
10
12
  commands: list[tuple[str, Any]] = field(default_factory=list)
11
- results: list[Any] = field(init=False, default_factory=list)
12
-
13
- def with_result(self, values: Any) -> Self:
14
- self.results = [values, *self.results]
13
+ results: list[Any] = field(default_factory=list)
15
14
 
16
- return self
15
+ def with_result(self, values: Any) -> FakeConnector:
16
+ return FakeConnector(self.commands, [values, *self.results])
17
17
 
18
18
  def execute(self, command: str, data: Any) -> None:
19
19
  self.commands.append((command, data))
@@ -9,7 +9,7 @@ from apexdevkit.http import JsonDict
9
9
  ItemT = TypeVar("ItemT")
10
10
 
11
11
 
12
- @dataclass
12
+ @dataclass(frozen=True)
13
13
  class Fake:
14
14
  faker: Faker = field(default_factory=Faker)
15
15
 
@@ -50,7 +50,7 @@ class Fake:
50
50
  return bool(self.faker.boolean())
51
51
 
52
52
 
53
- @dataclass
53
+ @dataclass(frozen=True)
54
54
  class FakeResource(Generic[ItemT]):
55
55
  item_type: Type[ItemT] = field()
56
56
  fake: Fake = field(default_factory=Fake)
@@ -8,7 +8,7 @@ from apexdevkit.http import Http, HttpUrl, JsonDict
8
8
  from apexdevkit.http.fluent import HttpMethod, HttpResponse
9
9
 
10
10
 
11
- @dataclass
11
+ @dataclass(frozen=True)
12
12
  class RestResource:
13
13
  http: Http
14
14
  name: RestfulName
@@ -95,13 +95,13 @@ class RestResource:
95
95
  )
96
96
 
97
97
 
98
- @dataclass
98
+ @dataclass(frozen=True)
99
99
  class RestCollection(RestResource):
100
100
  def sub_resource(self, name: str) -> RestItem:
101
101
  return RestItem(self.http.with_endpoint(self.name.plural), RestfulName(name))
102
102
 
103
103
 
104
- @dataclass
104
+ @dataclass(frozen=True)
105
105
  class RestItem(RestResource):
106
106
  def sub_resource(self, name: str) -> RestItem:
107
107
  return RestItem(self.http.with_endpoint(self.name.singular), RestfulName(name))
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.14.1"
3
+ version = "1.14.3"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
@@ -1,90 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass, field
4
- from typing import Any, ContextManager, Iterable, Protocol, Self
5
-
6
- _RawData = dict[str, Any]
7
-
8
-
9
- @dataclass
10
- class Database:
11
- connector: Connector
12
-
13
- command: DatabaseCommand = field(init=False)
14
-
15
- def execute(self, command: DatabaseCommand) -> Self:
16
- self.command = command
17
-
18
- return self
19
-
20
- def fetch_none(self) -> None:
21
- with self.connector.connect() as connection:
22
- cursor: Cursor = connection.cursor()
23
- cursor.execute(self.command.value, self.command.payload)
24
- cursor.close()
25
-
26
- def fetch_one(self) -> _RawData:
27
- with self.connector.connect() as connection:
28
- cursor: Cursor = connection.cursor()
29
- cursor.execute(self.command.value, self.command.payload)
30
- raw = cursor.fetchone()
31
- cursor.close()
32
-
33
- return dict(raw or {})
34
-
35
- def fetch_all(self) -> Iterable[_RawData]:
36
- with self.connector.connect() as connection:
37
- cursor: Cursor = connection.cursor()
38
- cursor.execute(self.command.value, self.command.payload)
39
- raw = cursor.fetchall()
40
- cursor.close()
41
-
42
- return [dict(raw or {}) for raw in raw]
43
-
44
-
45
- class Connector(Protocol): # pragma: no cover
46
- def connect(self) -> ContextManager[Connection]:
47
- pass
48
-
49
-
50
- class Connection(Protocol): # pragma: no cover
51
- def cursor(self) -> Cursor:
52
- pass
53
-
54
-
55
- class Cursor(Protocol): # pragma: no cover
56
- def execute(self, *args: Any, **kwargs: Any) -> Any:
57
- pass
58
-
59
- def executemany(self, *args: Any, **kwargs: Any) -> Any:
60
- pass
61
-
62
- def fetchone(self, *args: Any, **kwargs: Any) -> Any:
63
- pass
64
-
65
- def fetchall(self, *args: Any, **kwargs: Any) -> Any:
66
- pass
67
-
68
- def close(self) -> None:
69
- pass
70
-
71
-
72
- @dataclass
73
- class DatabaseCommand:
74
- value: str
75
- payload: _RawData | list[_RawData] = field(init=False, default_factory=dict)
76
-
77
- def with_data(self, value: _RawData | None = None, **fields: Any) -> Self:
78
- assert isinstance(self.payload, dict)
79
- self.payload.update(value or {})
80
- self.payload.update(fields)
81
-
82
- return self
83
-
84
- def with_collection(self, value: list[_RawData]) -> Self:
85
- self.payload = value
86
-
87
- return self
88
-
89
- def __str__(self) -> str: # pragma: no cover
90
- return self.value
File without changes
File without changes