apexdevkit 1.32.7__tar.gz → 1.33.1__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.7 → apexdevkit-1.33.1}/PKG-INFO +1 -1
  2. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/sql/connector.py +7 -6
  3. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/sql/mssql.py +5 -3
  4. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/sql/sqlite.py +5 -3
  5. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/sync/observer.py +4 -3
  6. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/sync/source.py +18 -7
  7. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/sync/sync.py +63 -10
  8. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/pyproject.toml +1 -1
  9. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/LICENSE +0 -0
  10. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/README.md +0 -0
  11. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/__init__.py +0 -0
  12. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/annotation/__init__.py +0 -0
  13. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/annotation/deprecate.py +0 -0
  14. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/date.py +0 -0
  15. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/environment.py +0 -0
  16. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/error.py +0 -0
  17. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/__init__.py +0 -0
  18. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/builder.py +0 -0
  19. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/dependable.py +0 -0
  20. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/docs.py +0 -0
  21. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/name.py +0 -0
  22. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/resource.py +0 -0
  23. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/response.py +0 -0
  24. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/router.py +0 -0
  25. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/schema.py +0 -0
  26. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fastapi/service.py +0 -0
  27. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/fluent.py +0 -0
  28. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/formatter.py +0 -0
  29. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/__init__.py +0 -0
  30. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/fake.py +0 -0
  31. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/fluent.py +0 -0
  32. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/httpx/__init__.py +0 -0
  33. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/httpx/client.py +0 -0
  34. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/httpx/hooks.py +0 -0
  35. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/json.py +0 -0
  36. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/http/url.py +0 -0
  37. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/key_fn.py +0 -0
  38. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/py.typed +0 -0
  39. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/query/__init__.py +0 -0
  40. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/query/generator.py +0 -0
  41. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/query/query.py +0 -0
  42. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/__init__.py +0 -0
  43. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/__init__.py +0 -0
  44. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/database.py +0 -0
  45. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/decorator.py +0 -0
  46. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/interface.py +0 -0
  47. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/mixin.py +0 -0
  48. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/core/multi.py +0 -0
  49. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/in_memory/__init__.py +0 -0
  50. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/in_memory/mixin.py +0 -0
  51. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/in_memory/repository.py +0 -0
  52. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/in_memory/store.py +0 -0
  53. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/sql/__init__.py +0 -0
  54. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/repository/sql/field.py +0 -0
  55. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/security/__init__.py +0 -0
  56. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/security/core.py +0 -0
  57. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/security/hashing.py +0 -0
  58. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/server.py +0 -0
  59. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/sync/__init__.py +0 -0
  60. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/sync/target.py +0 -0
  61. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/testing/__init__.py +0 -0
  62. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/testing/database.py +0 -0
  63. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/testing/fake.py +0 -0
  64. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/testing/rest.py +0 -0
  65. {apexdevkit-1.32.7 → apexdevkit-1.33.1}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apexdevkit
3
- Version: 1.32.7
3
+ Version: 1.33.1
4
4
  Summary: Apex Development Tools for python.
5
5
  License-File: LICENSE
6
6
  Author: Apex Dev
@@ -8,13 +8,14 @@ from typing import Any
8
8
 
9
9
  import pymssql
10
10
 
11
+ from apexdevkit.environment import environment_variable
11
12
  from apexdevkit.repository import Connection
12
13
  from apexdevkit.repository.core.database import ConnectionContextManager
13
14
 
14
15
 
15
16
  @dataclass(frozen=True)
16
17
  class SqliteFileConnector:
17
- dsn: str
18
+ dsn: str = environment_variable("DSN")
18
19
 
19
20
  def connect(self) -> AbstractContextManager[Connection]:
20
21
  connection = sqlite3.connect(self.dsn)
@@ -40,12 +41,12 @@ class SqliteInMemoryConnector:
40
41
 
41
42
  @dataclass(frozen=True)
42
43
  class MsSqlConnector:
43
- db_host: str
44
- db_user: str
45
- db_password: str
46
- db_name: str
44
+ db_host: str = environment_variable("DB_HOST")
45
+ db_user: str = environment_variable("DB_USER")
46
+ db_password: str = environment_variable("DB_PASSWORD")
47
+ db_name: str = environment_variable("DB_NAME")
48
+ db_port: str = environment_variable("DB_PORT", default="1433")
47
49
  db_tds_version = "7.0"
48
- db_port: str = "1433"
49
50
 
50
51
  def connect(self) -> AbstractContextManager[Connection]:
51
52
  return ConnectionContextManager(self._connection())
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable, Iterator, Mapping
4
- from dataclasses import dataclass
4
+ from dataclasses import dataclass, field
5
5
  from typing import Any, Generic
6
6
 
7
7
  from pymssql.exceptions import DatabaseError, OperationalError
@@ -16,14 +16,16 @@ from apexdevkit.repository.core import (
16
16
  Repository,
17
17
  )
18
18
 
19
+ from .connector import MsSqlConnector
19
20
  from .field import NotNone, SqlFieldManager, _SqlField
20
21
 
21
22
 
22
- @dataclass
23
+ @dataclass(frozen=True, kw_only=True)
23
24
  class MsSqlRepository(ContainsMixin[ItemT], Repository[ItemT]):
24
- db: Database
25
25
  table: SqlTable[ItemT]
26
26
 
27
+ db: Database = field(default_factory=lambda: Database(connector=MsSqlConnector()))
28
+
27
29
  def __iter__(self) -> Iterator[ItemT]:
28
30
  for raw in self.db.execute(self.table.select_all()).fetch_all():
29
31
  yield self.table.load(raw)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Iterable, Iterator, Mapping
4
- from dataclasses import dataclass
4
+ from dataclasses import dataclass, field
5
5
  from sqlite3 import IntegrityError
6
6
  from typing import Any, Generic
7
7
 
@@ -15,14 +15,16 @@ from apexdevkit.repository.core import (
15
15
  Repository,
16
16
  )
17
17
 
18
+ from .connector import SqliteFileConnector
18
19
  from .field import NotNone, SqlFieldManager, _SqlField
19
20
 
20
21
 
21
- @dataclass(frozen=True)
22
+ @dataclass(frozen=True, kw_only=True)
22
23
  class SqliteRepository(ContainsMixin[ItemT], Repository[ItemT]):
23
- db: Database
24
24
  table: SqlTable[ItemT]
25
25
 
26
+ db: Database = field(default_factory=lambda: Database(SqliteFileConnector()))
27
+
26
28
  def __iter__(self) -> Iterator[ItemT]:
27
29
  for raw in self.db.execute(self.table.select_all()).fetch_all():
28
30
  yield self.table.load(raw)
@@ -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
 
@@ -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,8 +4,10 @@ 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, SourcePreSet
10
+ from .source import EmptySource, Source, SourceDiscriminator, SourcePreSet
9
11
  from .target import IterableTarget, NoTarget, Target
10
12
 
11
13
  T = TypeVar("T")
@@ -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,19 +38,12 @@ 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)
38
46
 
39
- def purge(self, target: IterableTarget[T]) -> None:
40
- (
41
- self.with_target(target)
42
- .and_source(SourcePreSet[T](removals=target))
43
- .run(load=False, update=False)
44
- )
45
-
46
47
  def run(self, prune: bool = True, load: bool = True, update: bool = True) -> None:
47
48
  if prune:
48
49
  self.prune()
@@ -81,6 +82,58 @@ class Sync(ObservableSync, Generic[T]):
81
82
  )
82
83
 
83
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
+
84
137
  @dataclass(frozen=True, kw_only=True)
85
138
  class SyncProbe:
86
139
  source: Collection[Any]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "apexdevkit"
3
- version = "1.32.7"
3
+ version = "1.33.1"
4
4
  description = "Apex Development Tools for python."
5
5
  readme = "README.md"
6
6
  authors = [
File without changes
File without changes