apexdevkit 1.32.6__tar.gz → 1.32.8__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.32.6 → apexdevkit-1.32.8}/PKG-INFO +1 -1
  2. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/sync/observer.py +4 -3
  3. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/sync/source.py +21 -10
  4. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/sync/sync.py +70 -6
  5. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/sync/target.py +21 -17
  6. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/pyproject.toml +1 -1
  7. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/LICENSE +0 -0
  8. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/README.md +0 -0
  9. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/__init__.py +0 -0
  10. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/annotation/__init__.py +0 -0
  11. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/annotation/deprecate.py +0 -0
  12. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/date.py +0 -0
  13. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/environment.py +0 -0
  14. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/error.py +0 -0
  15. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/__init__.py +0 -0
  16. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/builder.py +0 -0
  17. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/dependable.py +0 -0
  18. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/docs.py +0 -0
  19. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/name.py +0 -0
  20. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/resource.py +0 -0
  21. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/response.py +0 -0
  22. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/router.py +0 -0
  23. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/schema.py +0 -0
  24. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fastapi/service.py +0 -0
  25. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/fluent.py +0 -0
  26. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/formatter.py +0 -0
  27. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/__init__.py +0 -0
  28. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/fake.py +0 -0
  29. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/fluent.py +0 -0
  30. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/httpx/__init__.py +0 -0
  31. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/httpx/client.py +0 -0
  32. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/httpx/hooks.py +0 -0
  33. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/json.py +0 -0
  34. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/http/url.py +0 -0
  35. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/key_fn.py +0 -0
  36. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/py.typed +0 -0
  37. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/query/__init__.py +0 -0
  38. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/query/generator.py +0 -0
  39. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/query/query.py +0 -0
  40. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/__init__.py +0 -0
  41. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/__init__.py +0 -0
  42. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/database.py +0 -0
  43. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/decorator.py +0 -0
  44. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/interface.py +0 -0
  45. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/mixin.py +0 -0
  46. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/core/multi.py +0 -0
  47. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/in_memory/__init__.py +0 -0
  48. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/in_memory/mixin.py +0 -0
  49. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/in_memory/repository.py +0 -0
  50. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/in_memory/store.py +0 -0
  51. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/sql/__init__.py +0 -0
  52. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/sql/connector.py +0 -0
  53. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/sql/field.py +0 -0
  54. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/sql/mssql.py +0 -0
  55. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/repository/sql/sqlite.py +0 -0
  56. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/security/__init__.py +0 -0
  57. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/security/core.py +0 -0
  58. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/security/hashing.py +0 -0
  59. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/server.py +0 -0
  60. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/sync/__init__.py +0 -0
  61. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/testing/__init__.py +0 -0
  62. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/testing/database.py +0 -0
  63. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/testing/fake.py +0 -0
  64. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/testing/rest.py +0 -0
  65. {apexdevkit-1.32.6 → apexdevkit-1.32.8}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apexdevkit
3
- Version: 1.32.6
3
+ Version: 1.32.8
4
4
  Summary: Apex Development Tools for python.
5
5
  License-File: LICENSE
6
6
  Author: Apex Dev
@@ -68,22 +68,23 @@ class SyncObserver(Protocol): # pragma: no cover
68
68
 
69
69
  @dataclass(frozen=True)
70
70
  class DefaultLog:
71
+ name: str = "items"
71
72
  echo: Callable[[str], None] = print
72
73
 
73
74
  def before_prune(self, n_items: int) -> None:
74
- self.echo(f"Total # of absent items: {n_items}")
75
+ self.echo(f"Total # of absent {self.name}: {n_items}")
75
76
 
76
77
  def after_prune(self) -> None:
77
78
  self.echo("Pruning completed")
78
79
 
79
80
  def before_load(self, n_items: int) -> None:
80
- self.echo(f"Total # of new items: {n_items}")
81
+ self.echo(f"Total # of new {self.name}: {n_items}")
81
82
 
82
83
  def after_load(self) -> None:
83
84
  self.echo("Loading completed")
84
85
 
85
86
  def before_update(self, n_items: int) -> None:
86
- self.echo(f"Total # of updates: {n_items}")
87
+ self.echo(f"Total # of changed {self.name}: {n_items}")
87
88
 
88
89
  def after_update(self) -> None:
89
90
  self.echo("Updating completed")
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable
4
4
  from dataclasses import dataclass, field
5
- from typing import Generic, Protocol, TypeVar
5
+ from typing import Any, Generic, Protocol, TypeVar
6
6
 
7
7
  from apexdevkit.key_fn import AttributeKey, KeyFn
8
8
 
@@ -48,9 +48,9 @@ class SourceDecorator(Generic[T]): # pragma: no cover
48
48
 
49
49
  @dataclass(frozen=True, kw_only=True)
50
50
  class SourcePreSet(Generic[T]):
51
- removals: Iterable[T]
52
- additions: Iterable[T]
53
- changes: Iterable[T]
51
+ removals: Iterable[T] = field(default_factory=list)
52
+ additions: Iterable[T] = field(default_factory=list)
53
+ changes: Iterable[T] = field(default_factory=list)
54
54
 
55
55
  def absent(self) -> Iterable[T]:
56
56
  return self.removals
@@ -109,17 +109,28 @@ class SourceDiscriminator(Generic[T]):
109
109
  current: Iterable[T]
110
110
  latest: Iterable[T]
111
111
 
112
- key_fn: KeyFn = AttributeKey("id")
112
+ key_fn: KeyFn | None = None
113
113
 
114
114
  def absent(self) -> Iterable[T]:
115
- index = {self.key_fn(item) for item in self.latest}
115
+ index = {self.key_of(item) for item in self.latest}
116
116
 
117
- return [item for item in self.current if self.key_fn(item) not in index]
117
+ return [item for item in self.current if self.key_of(item) not in index]
118
118
 
119
119
  def new(self) -> Iterable[T]:
120
- index = {self.key_fn(item) for item in self.current}
120
+ index = {self.key_of(item) for item in self.current}
121
121
 
122
- return [item for item in self.latest if self.key_fn(item) not in index]
122
+ return [item for item in self.latest if self.key_of(item) not in index]
123
123
 
124
124
  def updates(self) -> Iterable[T]:
125
- return set(self.latest).difference(self.current)
125
+ index = {self.key_of(item) for item in self.current}
126
+
127
+ return [
128
+ item
129
+ for item in set(self.latest).difference(self.current)
130
+ if self.key_of(item) in index
131
+ ]
132
+
133
+ def key_of(self, item: Any) -> str:
134
+ key_fn = self.key_fn or AttributeKey("id")
135
+
136
+ return key_fn(item)
@@ -4,9 +4,11 @@ from collections.abc import Callable, Collection, Iterable
4
4
  from dataclasses import dataclass, field, replace
5
5
  from typing import Any, Generic, Self, TypeVar
6
6
 
7
+ from apexdevkit.key_fn import AttributeKey, KeyFn
8
+
7
9
  from .observer import DefaultLog, ObservableSync
8
- from .source import EmptySource, Source
9
- from .target import NoTarget, Target
10
+ from .source import EmptySource, Source, SourceDiscriminator, SourcePreSet
11
+ from .target import IterableTarget, NoTarget, Target
10
12
 
11
13
  T = TypeVar("T")
12
14
 
@@ -18,6 +20,12 @@ class Sync(ObservableSync, Generic[T]):
18
20
 
19
21
  dry_run: bool = False
20
22
 
23
+ def purge(self) -> _Purge[T]:
24
+ return _Purge[T](sync=self)
25
+
26
+ def discriminate(self, latest: Iterable[T]) -> _Discriminate[T]:
27
+ return _Discriminate[T](sync=self, latest=latest)
28
+
21
29
  def and_target(self, value: Target[T]) -> Sync[T]:
22
30
  return self.with_target(value)
23
31
 
@@ -30,8 +38,8 @@ class Sync(ObservableSync, Generic[T]):
30
38
  def with_source(self, value: Source[T]) -> Sync[T]:
31
39
  return replace(self, source=value)
32
40
 
33
- def with_log(self, using: Callable[[str], None]) -> Self:
34
- return self.attach(DefaultLog(echo=using))
41
+ def with_log(self, using: Callable[[str], None], named: str = "items") -> Self:
42
+ return self.attach(DefaultLog(name=named, echo=using))
35
43
 
36
44
  def dry(self, *, when: bool = True) -> Sync[T]:
37
45
  return replace(self, dry_run=when)
@@ -74,6 +82,58 @@ class Sync(ObservableSync, Generic[T]):
74
82
  )
75
83
 
76
84
 
85
+ @dataclass(frozen=True, kw_only=True)
86
+ class _Purge(Generic[T]):
87
+ sync: Sync[T]
88
+
89
+ def target(self, value: IterableTarget[T]) -> _Purge[T]:
90
+ return _Purge[T](
91
+ sync=replace(
92
+ self.sync,
93
+ target=value,
94
+ source=SourcePreSet[T](removals=list(value)),
95
+ )
96
+ )
97
+
98
+ def __call__(self) -> None:
99
+ self.run()
100
+
101
+ def run(self) -> None:
102
+ self.sync.prune()
103
+
104
+
105
+ @dataclass
106
+ class _Discriminate(Generic[T]):
107
+ sync: Sync[T]
108
+ latest: Iterable[T]
109
+
110
+ current: Iterable[T] = field(default_factory=list)
111
+
112
+ def against(self, target: IterableTarget[T]) -> _Discriminate[T]:
113
+ return self.target(target).and_current(list(target))
114
+
115
+ def target(self, value: Target[T]) -> _Discriminate[T]:
116
+ return replace(self, sync=self.sync.with_target(value))
117
+
118
+ def and_current(self, value: Iterable[T]) -> _Discriminate[T]:
119
+ return replace(self, current=value)
120
+
121
+ def default(self) -> Sync[T]:
122
+ return self.using_attribute(name="id")
123
+
124
+ def using_attribute(self, name: str) -> Sync[T]:
125
+ return self.using(key_fn=AttributeKey(name))
126
+
127
+ def using(self, key_fn: KeyFn) -> Sync[T]:
128
+ return self.sync.with_source(
129
+ SourceDiscriminator(
130
+ current=self.current,
131
+ latest=self.latest,
132
+ key_fn=key_fn,
133
+ )
134
+ )
135
+
136
+
77
137
  @dataclass(frozen=True, kw_only=True)
78
138
  class SyncProbe:
79
139
  source: Collection[Any]
@@ -88,8 +148,12 @@ class SyncProbe:
88
148
 
89
149
  return self
90
150
 
91
- def send(self, using: Callable[[Iterable[Any]], None]) -> SyncProbe:
151
+ def send(self, using: Callable[[Iterable[Any]], Iterable[Any] | None]) -> SyncProbe:
152
+ result = None
92
153
  if self.is_enabled and len(self.source) > 0:
93
- using(self.source)
154
+ result = using(self.source)
155
+
156
+ if result:
157
+ list(result)
94
158
 
95
159
  return self
@@ -2,30 +2,34 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable
4
4
  from dataclasses import dataclass, field
5
- from typing import Any, Generic, Protocol, TypeVar
5
+ from typing import Generic, Protocol, TypeVar
6
6
 
7
- T = TypeVar("T", contravariant=True)
7
+ T = TypeVar("T")
8
8
 
9
9
 
10
10
  class Target(Protocol[T]): # pragma: no cover
11
- def prune(self, source: Iterable[T]) -> Any:
11
+ def prune(self, source: Iterable[T]) -> Iterable[T] | None:
12
12
  pass
13
13
 
14
- def load(self, source: Iterable[T]) -> Any:
14
+ def load(self, source: Iterable[T]) -> Iterable[T] | None:
15
15
  pass
16
16
 
17
- def renew(self, source: Iterable[T]) -> Any:
17
+ def renew(self, source: Iterable[T]) -> Iterable[T] | None:
18
18
  pass
19
19
 
20
20
 
21
+ class IterableTarget(Target[T], Iterable[T], Protocol[T]):
22
+ pass
23
+
24
+
21
25
  class NoTarget(Generic[T]): # pragma: no cover
22
- def prune(self, source: Iterable[T]) -> Any:
26
+ def prune(self, source: Iterable[T]) -> Iterable[T] | None:
23
27
  pass
24
28
 
25
- def load(self, source: Iterable[T]) -> Any:
29
+ def load(self, source: Iterable[T]) -> Iterable[T] | None:
26
30
  pass
27
31
 
28
- def renew(self, source: Iterable[T]) -> Any:
32
+ def renew(self, source: Iterable[T]) -> Iterable[T] | None:
29
33
  pass
30
34
 
31
35
 
@@ -33,14 +37,14 @@ class NoTarget(Generic[T]): # pragma: no cover
33
37
  class TargetDecorator(Generic[T]):
34
38
  inner: Target[T] = field(default_factory=NoTarget)
35
39
 
36
- def prune(self, source: Iterable[T]) -> Any:
37
- self.inner.prune(source)
40
+ def prune(self, source: Iterable[T]) -> Iterable[T] | None:
41
+ return self.inner.prune(source)
38
42
 
39
- def load(self, source: Iterable[T]) -> Any:
40
- self.inner.load(source)
43
+ def load(self, source: Iterable[T]) -> Iterable[T] | None:
44
+ return self.inner.load(source)
41
45
 
42
- def renew(self, source: Iterable[T]) -> Any:
43
- self.inner.renew(source)
46
+ def renew(self, source: Iterable[T]) -> Iterable[T] | None:
47
+ return self.inner.renew(source)
44
48
 
45
49
 
46
50
  class TargetFailing(Generic[T]):
@@ -69,17 +73,17 @@ class TargetFailing(Generic[T]):
69
73
 
70
74
  @dataclass(frozen=True, kw_only=True)
71
75
  class _FailOnPrune(TargetDecorator[T]):
72
- def prune(self, source: Iterable[T]) -> Any: # pragma: no cover
76
+ def prune(self, source: Iterable[T]) -> Iterable[T] | None: # pragma: no cover
73
77
  raise RuntimeError(source)
74
78
 
75
79
 
76
80
  @dataclass(frozen=True, kw_only=True)
77
81
  class _FailOnLoad(TargetDecorator[T]):
78
- def load(self, source: Iterable[T]) -> Any: # pragma: no cover
82
+ def load(self, source: Iterable[T]) -> Iterable[T] | None: # pragma: no cover
79
83
  raise RuntimeError(source)
80
84
 
81
85
 
82
86
  @dataclass(frozen=True, kw_only=True)
83
87
  class _FailOnUpdate(TargetDecorator[T]):
84
- def renew(self, source: Iterable[T]) -> Any: # pragma: no cover
88
+ def renew(self, source: Iterable[T]) -> Iterable[T] | None: # pragma: no cover
85
89
  raise RuntimeError(source)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "apexdevkit"
3
- version = "1.32.6"
3
+ version = "1.32.8"
4
4
  description = "Apex Development Tools for python."
5
5
  readme = "README.md"
6
6
  authors = [
File without changes
File without changes