apexdevkit 1.29.1__tar.gz → 1.29.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 (65) hide show
  1. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/PKG-INFO +1 -1
  2. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/service.py +3 -5
  3. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/formatter.py +13 -13
  4. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/__init__.py +9 -5
  5. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/core/__init__.py +9 -3
  6. apexdevkit-1.29.3/apexdevkit/repository/core/decorator.py +63 -0
  7. apexdevkit-1.29.3/apexdevkit/repository/core/interface.py +42 -0
  8. apexdevkit-1.29.3/apexdevkit/repository/core/mixin.py +24 -0
  9. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/core/multi.py +3 -2
  10. apexdevkit-1.29.3/apexdevkit/repository/in_memory/__init__.py +10 -0
  11. apexdevkit-1.29.3/apexdevkit/repository/in_memory/mixin.py +20 -0
  12. apexdevkit-1.29.3/apexdevkit/repository/in_memory/repository.py +69 -0
  13. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/in_memory/store.py +2 -16
  14. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/sql/mssql.py +10 -5
  15. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/sql/sqlite.py +10 -4
  16. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/pyproject.toml +1 -1
  17. apexdevkit-1.29.1/apexdevkit/repository/core/decorator.py +0 -37
  18. apexdevkit-1.29.1/apexdevkit/repository/core/interface.py +0 -55
  19. apexdevkit-1.29.1/apexdevkit/repository/in_memory/__init__.py +0 -8
  20. apexdevkit-1.29.1/apexdevkit/repository/in_memory/builder.py +0 -62
  21. apexdevkit-1.29.1/apexdevkit/repository/in_memory/multi_key.py +0 -53
  22. apexdevkit-1.29.1/apexdevkit/repository/in_memory/single_key.py +0 -51
  23. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/LICENSE +0 -0
  24. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/README.md +0 -0
  25. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/__init__.py +0 -0
  26. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/annotation/__init__.py +0 -0
  27. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/annotation/deprecate.py +0 -0
  28. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/date.py +0 -0
  29. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/environment.py +0 -0
  30. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/error.py +0 -0
  31. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/__init__.py +0 -0
  32. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/builder.py +0 -0
  33. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/dependable.py +0 -0
  34. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/docs.py +0 -0
  35. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/name.py +0 -0
  36. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/resource.py +0 -0
  37. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/response.py +0 -0
  38. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/router.py +0 -0
  39. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fastapi/schema.py +0 -0
  40. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/fluent.py +0 -0
  41. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/__init__.py +0 -0
  42. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/fake.py +0 -0
  43. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/fluent.py +0 -0
  44. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/httpx/__init__.py +0 -0
  45. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/httpx/client.py +0 -0
  46. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/httpx/hooks.py +0 -0
  47. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/json.py +0 -0
  48. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/http/url.py +0 -0
  49. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/id.py +0 -0
  50. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/key_fn.py +0 -0
  51. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/py.typed +0 -0
  52. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/query/__init__.py +0 -0
  53. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/query/generator.py +0 -0
  54. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/query/query.py +0 -0
  55. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/core/database.py +0 -0
  56. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/sql/__init__.py +0 -0
  57. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/sql/connector.py +0 -0
  58. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/repository/sql/field.py +0 -0
  59. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/server.py +0 -0
  60. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/synchronization.py +0 -0
  61. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/testing/__init__.py +0 -0
  62. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/testing/database.py +0 -0
  63. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/testing/fake.py +0 -0
  64. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/testing/rest.py +0 -0
  65. {apexdevkit-1.29.1 → apexdevkit-1.29.3}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apexdevkit
3
- Version: 1.29.1
3
+ Version: 1.29.3
4
4
  Summary: Apex Development Tools for python.
5
5
  License-File: LICENSE
6
6
  Author: Apex Dev
@@ -3,10 +3,11 @@ from __future__ import annotations
3
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, Generic, TypeVar
6
+ from typing import Any, Generic
7
7
 
8
8
  from apexdevkit.formatter import Formatter
9
9
  from apexdevkit.repository import BruteForceBatch, Repository
10
+ from apexdevkit.repository.core import ItemT
10
11
 
11
12
  RawItem = Mapping[str, Any]
12
13
  RawCollection = Iterable[RawItem]
@@ -58,9 +59,6 @@ class RestfulService: # pragma: no cover
58
59
  raise NotImplementedError(self.delete_one.__name__)
59
60
 
60
61
 
61
- ItemT = TypeVar("ItemT")
62
-
63
-
64
62
  @dataclass(frozen=True)
65
63
  class RestfulRepository(RestfulService, Generic[ItemT]):
66
64
  repository: Repository[ItemT]
@@ -69,7 +67,7 @@ class RestfulRepository(RestfulService, Generic[ItemT]):
69
67
 
70
68
  @cached_property
71
69
  def _batch(self) -> BruteForceBatch[ItemT]:
72
- return BruteForceBatch(self.repository)
70
+ return BruteForceBatch(inner=self.repository)
73
71
 
74
72
  def create_one(self, item: RawItem) -> RawItem:
75
73
  return self.formatter.dump(self.repository.create(self.formatter.load(item)))
@@ -85,8 +85,8 @@ class DataclassFormatter(Generic[_TargetT]):
85
85
 
86
86
  return self
87
87
 
88
- def load(self, raw: Mapping[str, Any]) -> _TargetT:
89
- raw = FluentDict[Any](deepcopy(raw)).select(
88
+ def load(self, source: Mapping[str, Any]) -> _TargetT:
89
+ source = FluentDict[Any](deepcopy(source)).select(
90
90
  *self.resource.__annotations__.keys(),
91
91
  "id",
92
92
  "idempotency_id",
@@ -95,27 +95,27 @@ class DataclassFormatter(Generic[_TargetT]):
95
95
  for key in fields(self.resource): # type: ignore
96
96
  types = get_type_hints(self.resource)
97
97
  key_type = types[key.name]
98
- if key.name not in raw:
98
+ if key.name not in source:
99
99
  continue
100
100
  if key.name in self.sub_formatters:
101
- raw[key.name] = (
102
- self.sub_formatters[key.name].load(raw.pop(key.name))
103
- if raw[key.name]
104
- else raw[key.name]
101
+ source[key.name] = (
102
+ self.sub_formatters[key.name].load(source.pop(key.name))
103
+ if source[key.name]
104
+ else source[key.name]
105
105
  )
106
106
  elif is_dataclass(key_type):
107
- raw[key.name] = DataclassFormatter(key_type).load(raw[key.name]) # type: ignore
107
+ source[key.name] = DataclassFormatter(key_type).load(source[key.name]) # type: ignore
108
108
  else:
109
109
  args = get_args(key_type)
110
110
  if len(args) == 1 and is_dataclass(args[0]):
111
- raw[key.name] = ListFormatter(DataclassFormatter(args[0])).load( # type: ignore
112
- raw[key.name]
111
+ source[key.name] = ListFormatter(DataclassFormatter(args[0])).load( # type: ignore
112
+ source[key.name]
113
113
  )
114
114
 
115
- return self.resource(**raw)
115
+ return self.resource(**source)
116
116
 
117
- def dump(self, item: _TargetT) -> Mapping[str, Any]:
118
- return asdict(item) # type: ignore
117
+ def dump(self, target: _TargetT) -> Mapping[str, Any]:
118
+ return asdict(target) # type: ignore
119
119
 
120
120
 
121
121
  class ValueFormatter:
@@ -1,22 +1,25 @@
1
- from apexdevkit.repository.core import (
1
+ from .core import (
2
2
  BruteForceBatch,
3
3
  Connection,
4
4
  Connector,
5
5
  Cursor,
6
6
  Database,
7
7
  DatabaseCommand,
8
+ NoRepository,
8
9
  Repository,
9
- RepositoryBase,
10
10
  RepositoryDecorator,
11
11
  )
12
- from apexdevkit.repository.in_memory import (
12
+ from .core.interface import Entity
13
+ from .in_memory import (
14
+ CacheMixin,
13
15
  InMemoryByteStore,
14
16
  InMemoryRepository,
15
17
  KeyValueStore,
16
18
  )
17
- from apexdevkit.repository.sql import MsSqlRepository, SqliteRepository
19
+ from .sql import MsSqlRepository, SqliteRepository
18
20
 
19
21
  __all__ = [
22
+ "Entity",
20
23
  # Core
21
24
  "BruteForceBatch",
22
25
  "Connection",
@@ -24,10 +27,11 @@ __all__ = [
24
27
  "Cursor",
25
28
  "Database",
26
29
  "DatabaseCommand",
30
+ "NoRepository",
27
31
  "Repository",
28
- "RepositoryBase",
29
32
  "RepositoryDecorator",
30
33
  # In-memory
34
+ "CacheMixin",
31
35
  "InMemoryByteStore",
32
36
  "InMemoryRepository",
33
37
  "KeyValueStore",
@@ -6,8 +6,9 @@ from .database import (
6
6
  Database,
7
7
  DatabaseCommand,
8
8
  )
9
- from .decorator import BruteForceBatch, RepositoryDecorator
10
- from .interface import Repository, RepositoryBase
9
+ from .decorator import BruteForceBatch, NoRepository, RepositoryDecorator
10
+ from .interface import Entity, ItemT, KeyFn, Repository
11
+ from .mixin import ContainsMixin
11
12
 
12
13
  __all__ = [
13
14
  # Database
@@ -21,6 +22,11 @@ __all__ = [
21
22
  "RepositoryDecorator",
22
23
  "BruteForceBatch",
23
24
  # Interface
25
+ "Entity",
26
+ "ItemT",
27
+ "KeyFn",
24
28
  "Repository",
25
- "RepositoryBase",
29
+ "NoRepository",
30
+ # Mixin
31
+ "ContainsMixin",
26
32
  ]
@@ -0,0 +1,63 @@
1
+ from collections.abc import Iterable, Iterator
2
+ from dataclasses import dataclass, field
3
+
4
+ from .interface import ItemT, Repository
5
+
6
+
7
+ class NoRepository(Repository[ItemT]): # pragma: no cover
8
+ def create(self, item: ItemT) -> ItemT:
9
+ raise NotImplementedError
10
+
11
+ def read(self, item_id: str) -> ItemT:
12
+ raise NotImplementedError
13
+
14
+ def update(self, item: ItemT) -> None:
15
+ raise NotImplementedError
16
+
17
+ def delete(self, item_id: str) -> None:
18
+ raise NotImplementedError
19
+
20
+ def __iter__(self) -> Iterator[ItemT]:
21
+ raise NotImplementedError
22
+
23
+ def __len__(self) -> int:
24
+ raise NotImplementedError
25
+
26
+ def __contains__(self, item: object) -> bool:
27
+ raise NotImplementedError
28
+
29
+
30
+ @dataclass(frozen=True, kw_only=True)
31
+ class RepositoryDecorator(Repository[ItemT]): # pragma: no cover
32
+ inner: Repository[ItemT] = field(default_factory=NoRepository)
33
+
34
+ def create(self, item: ItemT) -> ItemT:
35
+ return self.inner.create(item)
36
+
37
+ def read(self, item_id: str) -> ItemT:
38
+ return self.inner.read(item_id)
39
+
40
+ def update(self, item: ItemT) -> None:
41
+ self.inner.update(item)
42
+
43
+ def delete(self, item_id: str) -> None:
44
+ self.inner.delete(item_id)
45
+
46
+ def __iter__(self) -> Iterator[ItemT]:
47
+ return iter(self.inner)
48
+
49
+ def __len__(self) -> int:
50
+ return len(self.inner)
51
+
52
+ def __contains__(self, item: object) -> bool:
53
+ return item in self.inner
54
+
55
+
56
+ @dataclass(frozen=True, kw_only=True)
57
+ class BruteForceBatch(RepositoryDecorator[ItemT]):
58
+ def create_many(self, items: Iterable[ItemT]) -> Iterable[ItemT]:
59
+ return [self.inner.create(item) for item in items]
60
+
61
+ def update_many(self, items: Iterable[ItemT]) -> None:
62
+ for item in items:
63
+ self.inner.update(item)
@@ -0,0 +1,42 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Collection, Iterable
4
+ from dataclasses import dataclass, field
5
+ from typing import Protocol, TypeVar
6
+ from uuid import uuid4
7
+
8
+
9
+ def _uuid() -> str:
10
+ return str(uuid4())
11
+
12
+
13
+ @dataclass(frozen=True, kw_only=True)
14
+ class Entity:
15
+ id: str = field(default_factory=_uuid)
16
+ idempotency_id: str | None = field(default=None)
17
+
18
+
19
+ ItemT = TypeVar("ItemT", bound=Entity)
20
+ KeyFn = Callable[[ItemT], str]
21
+
22
+
23
+ class Repository(Collection[ItemT], Protocol[ItemT]): # pragma: no cover
24
+ def create(self, item: ItemT) -> ItemT:
25
+ pass
26
+
27
+ def read(self, item_id: str) -> ItemT:
28
+ pass
29
+
30
+ def update(self, item: ItemT) -> None:
31
+ pass
32
+
33
+ def delete(self, item_id: str) -> None:
34
+ pass
35
+
36
+
37
+ class BatchRepository(Repository[ItemT], Protocol[ItemT]): # pragma: no cover
38
+ def create_many(self, items: Iterable[ItemT]) -> Iterable[ItemT]:
39
+ pass
40
+
41
+ def update_many(self, items: Iterable[ItemT]) -> None:
42
+ pass
@@ -0,0 +1,24 @@
1
+ from typing import Generic
2
+
3
+ from apexdevkit.error import DoesNotExistError
4
+ from apexdevkit.repository.core import Entity, ItemT
5
+
6
+
7
+ class ContainsMixin(Generic[ItemT]):
8
+ def __contains__(self, item: object) -> bool:
9
+ match item:
10
+ case Entity():
11
+ return self.contains_id(item.id)
12
+ case _:
13
+ return False
14
+
15
+ def contains_id(self, value: str) -> bool:
16
+ try:
17
+ self.read(value)
18
+ except DoesNotExistError:
19
+ return False
20
+
21
+ return True
22
+
23
+ def read(self, item_id: str) -> ItemT:
24
+ raise NotImplementedError
@@ -7,7 +7,8 @@ from typing import Generic
7
7
  from apexdevkit.error import DoesNotExistError
8
8
  from apexdevkit.formatter import Formatter
9
9
 
10
- from .interface import ItemT, Repository, RepositoryBase
10
+ from . import ContainsMixin
11
+ from .interface import ItemT, Repository
11
12
 
12
13
 
13
14
  @dataclass(frozen=True)
@@ -20,7 +21,7 @@ class NoFormatter(Generic[ItemT]):
20
21
 
21
22
 
22
23
  @dataclass(frozen=True)
23
- class MultipleRepository(RepositoryBase[ItemT]):
24
+ class MultipleRepository(ContainsMixin[ItemT], Repository[ItemT]):
24
25
  repositories: list[_InnerRepository[ItemT]]
25
26
 
26
27
  def create(self, item: ItemT) -> ItemT:
@@ -0,0 +1,10 @@
1
+ from .mixin import CacheMixin
2
+ from .repository import InMemoryRepository
3
+ from .store import InMemoryByteStore, KeyValueStore
4
+
5
+ __all__ = [
6
+ "CacheMixin",
7
+ "InMemoryRepository",
8
+ "InMemoryByteStore",
9
+ "KeyValueStore",
10
+ ]
@@ -0,0 +1,20 @@
1
+ from collections import defaultdict
2
+ from collections.abc import MutableMapping
3
+ from dataclasses import dataclass
4
+ from functools import cached_property
5
+ from typing import Any
6
+
7
+ from .store import InMemoryByteStore, KeyValueStore
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class CacheMixin:
12
+ @cached_property
13
+ def _entries(self) -> MutableMapping[str, KeyValueStore[Any]]:
14
+ return defaultdict(InMemoryByteStore)
15
+
16
+ def store_for(self, name: str) -> KeyValueStore[Any]:
17
+ return self._entries[name]
18
+
19
+ def clear(self) -> None:
20
+ self._entries.clear()
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterator
4
+ from contextlib import suppress
5
+ from dataclasses import dataclass, field
6
+
7
+ from apexdevkit.error import DoesNotExistError, ExistsError
8
+ from apexdevkit.key_fn import AttributeKey
9
+ from apexdevkit.repository.core import ContainsMixin, ItemT, KeyFn, Repository
10
+
11
+ from .store import InMemoryByteStore, KeyValueStore
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class InMemoryRepository(ContainsMixin[ItemT], Repository[ItemT]):
16
+ store: KeyValueStore[ItemT] = field(default_factory=InMemoryByteStore)
17
+ keys: list[KeyFn[ItemT]] = field(default_factory=lambda: [AttributeKey("id")])
18
+
19
+ def and_key(self, function: KeyFn[ItemT]) -> InMemoryRepository[ItemT]:
20
+ return self.with_key(function)
21
+
22
+ def with_key(self, function: KeyFn[ItemT]) -> InMemoryRepository[ItemT]:
23
+ self.keys.append(function)
24
+
25
+ return self
26
+
27
+ def and_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
28
+ return self.with_seeded(*items)
29
+
30
+ def with_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
31
+ for seed in items:
32
+ with suppress(ExistsError):
33
+ self.create(seed)
34
+
35
+ return self
36
+
37
+ def create(self, item: ItemT) -> ItemT:
38
+ self._ensure_does_not_exist(item)
39
+ self.store.set(item.id, item)
40
+
41
+ return item
42
+
43
+ def _ensure_does_not_exist(self, new: ItemT) -> None:
44
+ for existing in self:
45
+ for key in self.keys:
46
+ if key(new) == key(existing):
47
+ ExistsError(existing).with_duplicate(key).fire()
48
+
49
+ def update(self, item: ItemT) -> None:
50
+ self.delete(item.id)
51
+ self.create(item)
52
+
53
+ def delete(self, item_id: str) -> None:
54
+ item = self.read(item_id)
55
+ self.store.drop(item.id)
56
+
57
+ def read(self, item_id: str) -> ItemT:
58
+ for key in self.keys:
59
+ for item in self:
60
+ if key(item) == str(item_id):
61
+ return item
62
+
63
+ raise DoesNotExistError(item_id)
64
+
65
+ def __iter__(self) -> Iterator[ItemT]:
66
+ return iter(self.store.values())
67
+
68
+ def __len__(self) -> int:
69
+ return self.store.count()
@@ -1,9 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections import defaultdict
4
- from collections.abc import Iterable, MutableMapping
3
+ from collections.abc import Iterable
5
4
  from dataclasses import dataclass, field
6
- from typing import Any, Generic, Protocol
5
+ from typing import Generic, Protocol
7
6
 
8
7
  from apexdevkit.formatter import Formatter, PickleFormatter
9
8
  from apexdevkit.repository.core.interface import ItemT
@@ -47,16 +46,3 @@ class InMemoryByteStore(Generic[ItemT]):
47
46
  def values(self) -> Iterable[ItemT]:
48
47
  for raw in self.items.values():
49
48
  yield self.formatter.load(raw)
50
-
51
- @dataclass
52
- class Cache:
53
- items: MutableMapping[str, InMemoryByteStore[Any]] = field(init=False)
54
-
55
- def __post_init__(self) -> None:
56
- self.items = defaultdict(InMemoryByteStore)
57
-
58
- def store_for(self, name: str) -> InMemoryByteStore[Any]:
59
- return self.items[name]
60
-
61
- def clear(self) -> None:
62
- self.items.clear()
@@ -2,20 +2,25 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable, Iterator, Mapping
4
4
  from dataclasses import dataclass
5
- from typing import Any, Generic, TypeVar
5
+ from typing import Any, Generic
6
6
 
7
7
  from pymssql.exceptions import DatabaseError, OperationalError
8
8
 
9
9
  from apexdevkit.error import DoesNotExistError, ExistsError
10
10
  from apexdevkit.formatter import Formatter
11
- from apexdevkit.repository import Database, DatabaseCommand, RepositoryBase
12
- from apexdevkit.repository.sql.field import NotNone, SqlFieldManager, _SqlField
11
+ from apexdevkit.repository.core import (
12
+ ContainsMixin,
13
+ Database,
14
+ DatabaseCommand,
15
+ ItemT,
16
+ Repository,
17
+ )
13
18
 
14
- ItemT = TypeVar("ItemT")
19
+ from .field import NotNone, SqlFieldManager, _SqlField
15
20
 
16
21
 
17
22
  @dataclass
18
- class MsSqlRepository(RepositoryBase[ItemT]):
23
+ class MsSqlRepository(ContainsMixin[ItemT], Repository[ItemT]):
19
24
  db: Database
20
25
  table: SqlTable[ItemT]
21
26
 
@@ -7,13 +7,19 @@ from typing import Any, Generic
7
7
 
8
8
  from apexdevkit.error import DoesNotExistError, ExistsError
9
9
  from apexdevkit.formatter import Formatter
10
- from apexdevkit.repository import Database, DatabaseCommand, RepositoryBase
11
- from apexdevkit.repository.core.interface import ItemT
12
- from apexdevkit.repository.sql.field import NotNone, SqlFieldManager, _SqlField
10
+ from apexdevkit.repository.core import (
11
+ ContainsMixin,
12
+ Database,
13
+ DatabaseCommand,
14
+ ItemT,
15
+ Repository,
16
+ )
17
+
18
+ from .field import NotNone, SqlFieldManager, _SqlField
13
19
 
14
20
 
15
21
  @dataclass(frozen=True)
16
- class SqliteRepository(RepositoryBase[ItemT]):
22
+ class SqliteRepository(ContainsMixin[ItemT], Repository[ItemT]):
17
23
  db: Database
18
24
  table: SqlTable[ItemT]
19
25
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "apexdevkit"
3
- version = "1.29.1"
3
+ version = "1.29.3"
4
4
  description = "Apex Development Tools for python."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,37 +0,0 @@
1
- from collections.abc import Iterable, Iterator
2
- from dataclasses import dataclass
3
- from typing import Generic
4
-
5
- from .interface import ItemT, Repository
6
-
7
-
8
- @dataclass
9
- class RepositoryDecorator(Generic[ItemT]): # pragma: no cover
10
- inner: Repository[ItemT]
11
-
12
- def create(self, item: ItemT) -> ItemT:
13
- return self.inner.create(item)
14
-
15
- def read(self, item_id: str) -> ItemT:
16
- return self.inner.read(item_id)
17
-
18
- def update(self, item: ItemT) -> None:
19
- self.inner.update(item)
20
-
21
- def delete(self, item_id: str) -> None:
22
- self.inner.delete(item_id)
23
-
24
- def __iter__(self) -> Iterator[ItemT]:
25
- return self.inner.__iter__()
26
-
27
- def __len__(self) -> int:
28
- return self.inner.__len__()
29
-
30
-
31
- class BruteForceBatch(RepositoryDecorator[ItemT]):
32
- def create_many(self, items: Iterable[ItemT]) -> Iterable[ItemT]:
33
- return [self.inner.create(item) for item in items]
34
-
35
- def update_many(self, items: Iterable[ItemT]) -> None:
36
- for item in items:
37
- self.inner.update(item)
@@ -1,55 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from collections.abc import Callable, Iterable, Iterator
4
- from typing import Generic, Protocol, TypeVar
5
-
6
- ItemT = TypeVar("ItemT")
7
- KeyFn = Callable[[ItemT], str]
8
-
9
-
10
- class Repository(Protocol[ItemT]): # pragma: no cover
11
- def create(self, item: ItemT) -> ItemT:
12
- pass
13
-
14
- def read(self, item_id: str) -> ItemT:
15
- pass
16
-
17
- def update(self, item: ItemT) -> None:
18
- pass
19
-
20
- def delete(self, item_id: str) -> None:
21
- pass
22
-
23
- def __iter__(self) -> Iterator[ItemT]:
24
- pass
25
-
26
- def __len__(self) -> int:
27
- pass
28
-
29
-
30
- class BatchRepository(Repository[ItemT], Protocol[ItemT]): # pragma: no cover
31
- def create_many(self, items: Iterable[ItemT]) -> Iterable[ItemT]:
32
- pass
33
-
34
- def update_many(self, items: Iterable[ItemT]) -> None:
35
- pass
36
-
37
-
38
- class RepositoryBase(Generic[ItemT]): # pragma: no cover
39
- def create(self, item: ItemT) -> ItemT:
40
- raise NotImplementedError
41
-
42
- def read(self, item_id: str) -> ItemT:
43
- raise NotImplementedError
44
-
45
- def update(self, item: ItemT) -> None:
46
- raise NotImplementedError
47
-
48
- def delete(self, item_id: str) -> None:
49
- raise NotImplementedError
50
-
51
- def __iter__(self) -> Iterator[ItemT]:
52
- raise NotImplementedError
53
-
54
- def __len__(self) -> int:
55
- raise NotImplementedError
@@ -1,8 +0,0 @@
1
- from apexdevkit.repository.in_memory.builder import InMemoryRepository
2
- from apexdevkit.repository.in_memory.store import InMemoryByteStore, KeyValueStore
3
-
4
- __all__ = [
5
- "InMemoryRepository",
6
- "InMemoryByteStore",
7
- "KeyValueStore",
8
- ]
@@ -1,62 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from contextlib import suppress
4
- from dataclasses import dataclass, field
5
- from typing import Generic
6
-
7
- from apexdevkit.error import ExistsError
8
- from apexdevkit.key_fn import AttributeKey
9
- from apexdevkit.repository.core.interface import ItemT, KeyFn, Repository
10
-
11
- from .multi_key import MultiKeyRepository
12
- from .single_key import SingleKeyRepository
13
- from .store import InMemoryByteStore, KeyValueStore
14
-
15
-
16
- @dataclass(frozen=True)
17
- class InMemoryRepository(Generic[ItemT]):
18
- store: KeyValueStore[ItemT] = field(default_factory=lambda: InMemoryByteStore())
19
- keys: list[KeyFn[ItemT]] = field(default_factory=list)
20
- seeds: frozenset[ItemT] = field(default_factory=frozenset)
21
-
22
- def with_store(self, value: KeyValueStore[ItemT]) -> InMemoryRepository[ItemT]:
23
- return InMemoryRepository(store=value, keys=self.keys, seeds=self.seeds)
24
-
25
- def and_key(self, function: KeyFn[ItemT]) -> InMemoryRepository[ItemT]:
26
- return self.with_key(function)
27
-
28
- def with_key(self, function: KeyFn[ItemT]) -> InMemoryRepository[ItemT]:
29
- return InMemoryRepository(
30
- store=self.store,
31
- keys=[*self.keys, function],
32
- seeds=self.seeds,
33
- )
34
-
35
- def and_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
36
- return self.with_seeded(*items)
37
-
38
- def with_seeded(self, *items: ItemT) -> InMemoryRepository[ItemT]:
39
- return InMemoryRepository(
40
- store=self.store,
41
- keys=self.keys,
42
- seeds=self.seeds.union(set(items)),
43
- )
44
-
45
- def build(self) -> Repository[ItemT]:
46
- return self._seed(self._create())
47
-
48
- def _seed(self, repository: Repository[ItemT]) -> Repository[ItemT]:
49
- for seed in self.seeds:
50
- with suppress(ExistsError):
51
- repository.create(seed)
52
-
53
- return repository
54
-
55
- def _create(self) -> Repository[ItemT]:
56
- match len(self.keys):
57
- case 0:
58
- return SingleKeyRepository(self.store, pk=AttributeKey("id"))
59
- case 1:
60
- return SingleKeyRepository(self.store, pk=self.keys[0])
61
- case _:
62
- return MultiKeyRepository(self.store, keys=self.keys)
@@ -1,53 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from collections.abc import Iterator
4
- from dataclasses import dataclass, field
5
-
6
- from apexdevkit.error import DoesNotExistError, ExistsError
7
- from apexdevkit.repository.core.interface import ItemT, KeyFn, RepositoryBase
8
-
9
- from .store import KeyValueStore
10
-
11
-
12
- @dataclass
13
- class MultiKeyRepository(RepositoryBase[ItemT]):
14
- store: KeyValueStore[ItemT]
15
-
16
- keys: list[KeyFn[ItemT]] = field(default_factory=list)
17
-
18
- def create(self, item: ItemT) -> ItemT:
19
- self._ensure_does_not_exist(item)
20
- self.store.set(self._pk(item), item)
21
-
22
- return item
23
-
24
- def _ensure_does_not_exist(self, new: ItemT) -> None:
25
- for existing in self:
26
- for key in self.keys:
27
- if key(new) == key(existing):
28
- ExistsError(existing).with_duplicate(key).fire()
29
-
30
- def _pk(self, item: ItemT) -> str:
31
- return self.keys[0](item)
32
-
33
- def update(self, item: ItemT) -> None:
34
- self.delete(self._pk(item))
35
- self.create(item)
36
-
37
- def delete(self, item_id: str) -> None:
38
- item = self.read(item_id)
39
- self.store.drop(self._pk(item))
40
-
41
- def read(self, item_id: str) -> ItemT:
42
- for key in self.keys:
43
- for item in self:
44
- if key(item) == str(item_id):
45
- return item
46
-
47
- raise DoesNotExistError(item_id)
48
-
49
- def __iter__(self) -> Iterator[ItemT]:
50
- return iter(self.store.values())
51
-
52
- def __len__(self) -> int:
53
- return self.store.count()
@@ -1,51 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from collections.abc import Iterator
4
- from dataclasses import dataclass
5
-
6
- from apexdevkit.error import DoesNotExistError, ExistsError
7
- from apexdevkit.repository.core.interface import ItemT, KeyFn, RepositoryBase
8
-
9
- from .store import KeyValueStore
10
-
11
-
12
- @dataclass
13
- class SingleKeyRepository(RepositoryBase[ItemT]):
14
- store: KeyValueStore[ItemT]
15
- pk: KeyFn[ItemT]
16
-
17
- def create(self, item: ItemT) -> ItemT:
18
- self._ensure_does_not_exist(item)
19
- self.store.set(self.pk(item), item)
20
-
21
- return item
22
-
23
- def _ensure_does_not_exist(self, new: ItemT) -> None:
24
- try:
25
- existing = self.store.get(self.pk(new))
26
- except KeyError:
27
- return
28
-
29
- ExistsError(existing).with_duplicate(self.pk).fire()
30
-
31
- def update(self, item: ItemT) -> None:
32
- self.delete(self.pk(item))
33
- self.create(item)
34
-
35
- def delete(self, item_id: str) -> None:
36
- try:
37
- self.store.drop(item_id)
38
- except KeyError as e:
39
- raise DoesNotExistError(item_id) from e
40
-
41
- def read(self, item_id: str) -> ItemT:
42
- try:
43
- return self.store.get(item_id)
44
- except KeyError as e:
45
- raise DoesNotExistError(item_id) from e
46
-
47
- def __iter__(self) -> Iterator[ItemT]:
48
- return iter(self.store.values())
49
-
50
- def __len__(self) -> int:
51
- return self.store.count()
File without changes
File without changes