python-ddd-framework 0.7.3__py3-none-any.whl → 0.9.0__py3-none-any.whl
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.
- python_ddd_framework/__init__.py +2 -2
- python_ddd_framework/configuration/root.py +2 -0
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md +6 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md +1 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md +6 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/src/host/module.py.jinja +0 -4
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md +6 -0
- python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md +62 -2
- python_ddd_framework/settings/__init__.py +3 -7
- python_ddd_framework/settings/application.py +53 -21
- python_ddd_framework/settings/binding.py +14 -0
- python_ddd_framework/settings/catalog.py +4 -59
- python_ddd_framework/settings/contracts.py +73 -2
- python_ddd_framework/settings/management.py +3 -13
- python_ddd_framework/settings/manager.py +17 -34
- python_ddd_framework/settings/module.py +21 -9
- python_ddd_framework/settings/provider.py +10 -3
- python_ddd_framework/settings/values.py +0 -8
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/METADATA +27 -3
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/RECORD +24 -31
- python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja +0 -1
- python_ddd_framework/settings/changes.py +0 -14
- python_ddd_framework/settings/declarations.py +0 -18
- python_ddd_framework/settings/handlers.py +0 -43
- python_ddd_framework/settings/notifications.py +0 -41
- python_ddd_framework/settings/refresh.py +0 -140
- python_ddd_framework/settings/refresh_module.py +0 -52
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/WHEEL +0 -0
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/entry_points.txt +0 -0
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {python_ddd_framework-0.7.3.dist-info → python_ddd_framework-0.9.0.dist-info}/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt +0 -0
|
@@ -1,32 +1,25 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""条件写入的 owner;读取解析由 SettingProvider 负责,生效时机归业务。"""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
from typing import TYPE_CHECKING, Any, TypeVar, cast
|
|
5
|
+
from typing import Any, TypeVar, cast
|
|
7
6
|
from uuid import UUID
|
|
8
7
|
|
|
9
8
|
from pydantic import ValidationError
|
|
10
9
|
from pydantic_core import PydanticSerializationError
|
|
11
10
|
|
|
12
|
-
from ..notifications import NotificationPayloadError, NotificationPublishStatus, Notifications
|
|
13
11
|
from .catalog import SettingCatalog
|
|
14
|
-
from .changes import SETTINGS_CHANGED, SettingChanged
|
|
15
12
|
from .contracts import SettingDefinition
|
|
16
13
|
from .errors import RuntimeSettingError
|
|
17
14
|
from .provider import SettingProvider
|
|
18
15
|
from .store import RuntimeSettingStore
|
|
19
|
-
from .values import SettingSnapshot, SettingValueSource,
|
|
20
|
-
|
|
21
|
-
if TYPE_CHECKING:
|
|
22
|
-
from .refresh import SettingsRefreshRuntime
|
|
16
|
+
from .values import SettingSnapshot, SettingValueSource, StoredSetting
|
|
23
17
|
|
|
24
18
|
_T = TypeVar("_T")
|
|
25
|
-
_LOGGER = logging.getLogger(__name__)
|
|
26
19
|
|
|
27
20
|
|
|
28
21
|
class SettingManager:
|
|
29
|
-
__slots__ = ("_catalog", "
|
|
22
|
+
__slots__ = ("_catalog", "_provider", "_store")
|
|
30
23
|
|
|
31
24
|
def __init__(
|
|
32
25
|
self, catalog: SettingCatalog, provider: SettingProvider, store: RuntimeSettingStore
|
|
@@ -34,13 +27,11 @@ class SettingManager:
|
|
|
34
27
|
self._catalog = catalog
|
|
35
28
|
self._provider = provider
|
|
36
29
|
self._store = store
|
|
37
|
-
self._refresh: SettingsRefreshRuntime | None = None
|
|
38
|
-
self._notifications: Notifications | None = None
|
|
39
30
|
|
|
40
31
|
async def set_global(self, definition: SettingDefinition[_T], value: _T) -> _T:
|
|
41
32
|
current = await self._provider.snapshot(definition)
|
|
42
33
|
saved = await self.update(definition, value, expected_version=current.version)
|
|
43
|
-
return cast(_T, saved.
|
|
34
|
+
return cast(_T, saved.value)
|
|
44
35
|
|
|
45
36
|
async def reset_global(self, definition: SettingDefinition[Any]) -> None:
|
|
46
37
|
current = await self._provider.snapshot(definition)
|
|
@@ -48,8 +39,8 @@ class SettingManager:
|
|
|
48
39
|
|
|
49
40
|
async def update(
|
|
50
41
|
self, definition: SettingDefinition[_T], value: _T, *, expected_version: UUID | None
|
|
51
|
-
) ->
|
|
52
|
-
self.
|
|
42
|
+
) -> SettingSnapshot[_T]:
|
|
43
|
+
self._require_write(definition, operation="set")
|
|
53
44
|
try:
|
|
54
45
|
validated, serialized = definition._dump_json(value)
|
|
55
46
|
except (TypeError, ValidationError, PydanticSerializationError):
|
|
@@ -60,30 +51,22 @@ class SettingManager:
|
|
|
60
51
|
definition.name, serialized, expected_version=expected_version
|
|
61
52
|
)
|
|
62
53
|
# 返回本次已经提交的值,不在提交后重读,避免把另一次并发写入误报为本次结果。
|
|
63
|
-
|
|
54
|
+
return SettingSnapshot(
|
|
64
55
|
definition.name, validated, SettingValueSource.RUNTIME, saved.version
|
|
65
56
|
)
|
|
66
|
-
return SettingWriteOutcome(snapshot, await self._committed(definition.name))
|
|
67
57
|
|
|
68
58
|
async def reset(
|
|
69
59
|
self, definition: SettingDefinition[_T], *, expected_version: UUID | None
|
|
70
|
-
) ->
|
|
71
|
-
self.
|
|
60
|
+
) -> SettingSnapshot[_T]:
|
|
61
|
+
self._require_write(definition, operation="reset")
|
|
72
62
|
# 默认值先校验,不能在物理删除已经提交之后再抛出配置校验失败。
|
|
73
63
|
fallback = self._provider._resolve(definition, StoredSetting(None, expected_version))
|
|
74
64
|
saved = await self._store.write(definition.name, None, expected_version=expected_version)
|
|
75
|
-
|
|
76
|
-
return SettingWriteOutcome(snapshot, await self._committed(definition.name))
|
|
65
|
+
return SettingSnapshot(definition.name, fallback.value, fallback.source, saved.version)
|
|
77
66
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
try:
|
|
85
|
-
return await self._notifications.publish(SETTINGS_CHANGED, SettingChanged(name=name))
|
|
86
|
-
except NotificationPayloadError:
|
|
87
|
-
# 发布大小/编码限制是独立结果;不能把已提交的写入重新表现为保存失败。
|
|
88
|
-
_LOGGER.warning("Committed setting notification rejected for %s", name)
|
|
89
|
-
return NotificationPublishStatus.REJECTED
|
|
67
|
+
def _require_write(self, definition: SettingDefinition[Any], *, operation: str) -> None:
|
|
68
|
+
self._catalog.require_owned(definition)
|
|
69
|
+
if SettingValueSource.RUNTIME not in definition.allowed_sources:
|
|
70
|
+
raise RuntimeSettingError(
|
|
71
|
+
setting=definition.name, operation=operation, error_type="SourceNotAllowed"
|
|
72
|
+
)
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from dishka import DEFAULT_COMPONENT, DependencyKey, Provider, Scope
|
|
4
4
|
|
|
5
|
+
from ..authorization.module import AuthorizationModule
|
|
5
6
|
from ..lifecycle import ConfigureContext
|
|
6
7
|
from ..modularity import AppModule, ModuleRef
|
|
7
8
|
from ..services.composition import (
|
|
8
9
|
ServiceBindingContext,
|
|
9
10
|
ServiceComposition,
|
|
10
|
-
ServiceDiscoveryContext,
|
|
11
11
|
)
|
|
12
12
|
from ..services.registration import RegistrationDecision, RegistrationKind
|
|
13
13
|
from .binding import _provide_settings
|
|
14
|
-
from .catalog import SettingCatalog, _build_setting_catalog
|
|
14
|
+
from .catalog import SettingCatalog, _build_setting_catalog
|
|
15
|
+
from .errors import SettingDefinitionError
|
|
15
16
|
from .store import RuntimeSettingStore
|
|
16
17
|
|
|
17
18
|
|
|
@@ -19,15 +20,10 @@ class SettingsModule(AppModule):
|
|
|
19
20
|
catalog: SettingCatalog
|
|
20
21
|
|
|
21
22
|
def configure(self, context: ConfigureContext) -> None:
|
|
22
|
-
self.catalog = _build_setting_catalog(context.
|
|
23
|
+
self.catalog = _build_setting_catalog(context.discoveries)
|
|
23
24
|
if self.catalog.definitions:
|
|
24
25
|
context.configure.section("settings")
|
|
25
|
-
context.services.add_composition(
|
|
26
|
-
ServiceComposition(discover=self._discover, bind=self._bind)
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def _discover(self, context: ServiceDiscoveryContext) -> None:
|
|
30
|
-
_contribute_setting_handler_defaults(context.modules, context.services)
|
|
26
|
+
context.services.add_composition(ServiceComposition(bind=self._bind))
|
|
31
27
|
|
|
32
28
|
def _bind(self, context: ServiceBindingContext) -> None:
|
|
33
29
|
provider = Provider()
|
|
@@ -57,3 +53,19 @@ class SettingsManagementModule(AppModule):
|
|
|
57
53
|
"python_ddd_framework.settings.application",
|
|
58
54
|
"python_ddd_framework.settings.permissions",
|
|
59
55
|
)
|
|
56
|
+
|
|
57
|
+
def configure(self, context: ConfigureContext) -> None:
|
|
58
|
+
context.services.add_composition(ServiceComposition(bind=self._bind))
|
|
59
|
+
|
|
60
|
+
def _bind(self, context: ServiceBindingContext) -> None:
|
|
61
|
+
# 全局 discovery 屏障后核对实际权限目录;只读 Settings 无需装配授权服务。
|
|
62
|
+
catalog = context.modules.instance(SettingsModule).catalog
|
|
63
|
+
permissions = context.modules.instance(AuthorizationModule).catalog
|
|
64
|
+
for definition in catalog.definitions:
|
|
65
|
+
for permission in (definition.read_permission, definition.write_permission):
|
|
66
|
+
if permission is not None and permission not in permissions:
|
|
67
|
+
raise SettingDefinitionError(
|
|
68
|
+
setting=definition.name,
|
|
69
|
+
reason=f"permission {permission} is not defined",
|
|
70
|
+
source=catalog.sources.get(definition.name),
|
|
71
|
+
)
|
|
@@ -43,7 +43,7 @@ class SettingProvider:
|
|
|
43
43
|
self._catalog.require_owned(definition)
|
|
44
44
|
stored = (
|
|
45
45
|
await self._store.read(definition.name)
|
|
46
|
-
if self._store is not None
|
|
46
|
+
if self._store is not None and SettingValueSource.RUNTIME in definition.allowed_sources
|
|
47
47
|
else StoredSetting(None, None)
|
|
48
48
|
)
|
|
49
49
|
return self._resolve(definition, stored)
|
|
@@ -56,11 +56,18 @@ class SettingProvider:
|
|
|
56
56
|
value = definition._validate_json(stored.value_json)
|
|
57
57
|
source = SettingValueSource.RUNTIME
|
|
58
58
|
else:
|
|
59
|
-
configured =
|
|
59
|
+
configured = (
|
|
60
|
+
self._configuration.get_path(f"settings.{definition.name}", _MISSING)
|
|
61
|
+
if SettingValueSource.CONFIGURATION in definition.allowed_sources
|
|
62
|
+
else _MISSING
|
|
63
|
+
)
|
|
60
64
|
if configured is not _MISSING:
|
|
61
65
|
value = definition._validate_python(configured)
|
|
62
66
|
source = SettingValueSource.CONFIGURATION
|
|
63
|
-
elif
|
|
67
|
+
elif (
|
|
68
|
+
definition.has_default
|
|
69
|
+
and SettingValueSource.DEFINITION in definition.allowed_sources
|
|
70
|
+
):
|
|
64
71
|
value = definition._default_value()
|
|
65
72
|
source = SettingValueSource.DEFINITION
|
|
66
73
|
else:
|
|
@@ -7,8 +7,6 @@ from enum import Enum
|
|
|
7
7
|
from typing import Generic, TypeVar
|
|
8
8
|
from uuid import UUID
|
|
9
9
|
|
|
10
|
-
from ..notifications import NotificationPublishStatus
|
|
11
|
-
|
|
12
10
|
_T = TypeVar("_T")
|
|
13
11
|
|
|
14
12
|
|
|
@@ -31,9 +29,3 @@ class SettingSnapshot(Generic[_T]):
|
|
|
31
29
|
value: _T | None
|
|
32
30
|
source: SettingValueSource
|
|
33
31
|
version: UUID | None
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass(frozen=True, slots=True)
|
|
37
|
-
class SettingWriteOutcome(Generic[_T]):
|
|
38
|
-
snapshot: SettingSnapshot[_T]
|
|
39
|
-
notification: NotificationPublishStatus
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-ddd-framework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Modular DDD application framework with project CLI and bundled templates
|
|
5
5
|
License-Expression: LicenseRef-Proprietary
|
|
6
6
|
License-File: LICENSE
|
|
@@ -64,7 +64,7 @@ Python DDD Framework combines a module system with FastAPI, Dishka, Pydantic, SQ
|
|
|
64
64
|
| HTTP APIs | Expose selected application services through FastAPI, or write explicit routers. Use OpenAPI, filters, file uploads, downloads, and streaming responses. |
|
|
65
65
|
| Persistence and events | Use async SQLAlchemy repositories, explicit transaction boundaries, module-owned Alembic migrations, seed contributors, and local events before or after commit. |
|
|
66
66
|
| Identity and permissions | Add JWT authentication, refresh sessions, users and roles, permission definitions, and authorization on service methods. |
|
|
67
|
-
| Cache, settings, and locks | Use typed Redis caching, runtime settings with conditional updates and reset,
|
|
67
|
+
| Cache, settings, and locks | Use typed Redis caching, global runtime settings with conditional updates and reset, and explicit Redis-backed business locks. |
|
|
68
68
|
| Background execution | Enqueue durable PostgreSQL jobs with typed payloads, schedule recurring work, run periodic workers, and select in-process or managed subprocess execution. |
|
|
69
69
|
| Real-time communication | Add authenticated WebSocket connections, typed messages, and targeted notifications to connected clients. |
|
|
70
70
|
| Hosting and observability | Manage long-lived integrations with `HostedService`, coordinate shutdown, and use structured logging, correlation, OpenTelemetry tracing, and lifecycle health endpoints. |
|
|
@@ -273,6 +273,12 @@ Edit `backend/app.development.yaml` for local connections, identity settings, lo
|
|
|
273
273
|
|
|
274
274
|
Generated credentials are for local development. Production configuration is supplied separately in `app.production.yaml`. See [configuration](https://github.com/componet-architecture/python-ddd-framework/blob/main/docs/development.md#定义-module-与配置) and [background and logging configuration](https://github.com/componet-architecture/python-ddd-framework/blob/main/docs/development.md#settings后台与日志).
|
|
275
275
|
|
|
276
|
+
### Use global settings
|
|
277
|
+
|
|
278
|
+
Declare a typed `SettingDefinition` and its scanned provider under the owning module's `domain/settings/`, then add `SettingsModule` to that module's dependencies. Inject `SettingProvider` to read values and `SettingManager` for conditional writes/reset. The generated Host already selects Settings persistence and management HTTP; apply its Settings migration before using the store for reads or writes.
|
|
279
|
+
|
|
280
|
+
Values resolve from runtime override, Host configuration, then definition default. Reset clears an override and resumes fallback. Management provides list/read/update/reset with visibility, permissions, and version checks; business code decides when to use the value. Follow the [complete project recipe](docs/development.md#settings-策略与使用) or the generated project's **Global settings** guide for declarations, configuration, permissions, and request bodies. The minimal contract starts with 0.9.0; see [migration notes](#minimal-settings-migration-090).
|
|
281
|
+
|
|
276
282
|
### Run application tests
|
|
277
283
|
|
|
278
284
|
After completing local database setup, run from `backend/`:
|
|
@@ -314,6 +320,24 @@ New `ddd` modules contain the full responsibility directories and necessary comp
|
|
|
314
320
|
|
|
315
321
|
See the [migration guide](docs/development.md#数据库聚合与迁移) and [verification record](docs/status.md#http-回收与迁移来源判定未发布).
|
|
316
322
|
|
|
323
|
+
## Minimal Settings migration (0.9.0)
|
|
324
|
+
|
|
325
|
+
Settings now owns only global definitions, reads, writes, and clearing overrides. Modules own definitions, the Host selects storage, and business code decides when and how to use the values. Source order, source restrictions, management permissions, typed JSON, independent transactions, UUID concurrency tokens, and reset version records remain unchanged. No dependency, database schema, or migration change is required.
|
|
326
|
+
|
|
327
|
+
- Remove `SettingsRefreshModule`, `SettingsNotificationsModule`, `SettingHandlers`, `SettingHandlerDefinition`, and Settings refresh options from Host/module wiring. Remove settings observers and the dedicated `application/setting_handlers` directory. Read through `SettingProvider` where the business needs the value; the framework no longer coordinates application, polling, reconnect compensation, retries, or application status. Generic Notifications and HostedService remain available independently.
|
|
328
|
+
- `SettingManager.update/reset` returns `SettingSnapshot[T]` directly: replace `result.snapshot.value/version` with `result.value/version`. `SettingWriteOutcome`, notification feedback, and handler/application status exports are removed.
|
|
329
|
+
- HTTP update/reset returns the same `SettingView` shape as a read: `name`, `value`, `source`, and `version`. Replace `response.setting` with `response`; remove `saved`, `notification`, and `effect` handling. `SettingWriteResult` is removed. Success confirms persistence only, and stale versions still produce a conflict.
|
|
330
|
+
|
|
331
|
+
Current rules and examples are in the [Settings contract](docs/architecture.md#typed-cache-与-settings) and [usage guide](docs/development.md#settings-策略与使用). Validation and remaining limits are recorded in [status](docs/status.md#settings-最小化未发布).
|
|
332
|
+
|
|
333
|
+
## Settings policies (0.8.0)
|
|
334
|
+
|
|
335
|
+
`SettingDefinition` now accepts `allowed_sources`, `management_access`, `read_permission`, and `write_permission`. Existing definitions retain their source order and management visibility. Generic update/reset now also require `settings.read`, in addition to `settings.write` and any declared per-setting permissions, because their responses contain the saved setting snapshot. Update roles that previously held only write permission.
|
|
336
|
+
|
|
337
|
+
Before excluding `CONFIGURATION` for a setting, remove its entries from YAML, environment variables, dotenv, CLI, explicit configuration and module configuration defaults: composition rejects forbidden inputs even when another value masks them. `HIDDEN` and `READ_ONLY` restrict generic management; internal readers and dedicated application services can still use the existing Provider/Manager subject to source, type and version rules. Defining a per-setting permission does not grant it to existing users or roles; update grants through the application's authorization system. No database migration or dependency change is required for this extension.
|
|
338
|
+
|
|
339
|
+
Version 0.8.0 includes these policies. Follow [Settings setup and usage](docs/development.md#settings-策略与使用) for definition discovery, permission declarations, internal Provider/Manager calls, and HTTP update/reset with version tokens. The [contract](docs/architecture.md#typed-cache-与-settings) owns the rules; [validation evidence](docs/status.md#settings-最小策略扩展) separates implementation results from the later read-only review.
|
|
340
|
+
|
|
317
341
|
## Invocation performance (0.7.2)
|
|
318
342
|
|
|
319
343
|
Function calls avoid regenerating DI wrappers, empty extension chains skip unnecessary work, and completed filter/interceptor chains release execution references sooner. Managed declaration checks continue to detect dynamic method and policy changes.
|
|
@@ -343,7 +367,7 @@ Runnable examples: [HTTP generators](docs/development.md#http-生成器与持续
|
|
|
343
367
|
Capability composition now uses the same `AppModule` model as customer modules. Existing applications must migrate their declarations together; there is no compatibility path.
|
|
344
368
|
|
|
345
369
|
- Declare capability dependencies on the Module that uses them. Selecting a sibling provider in Host does not supply a missing business-module dependency. `ApplicationServicesModule` supplies the usual application-service capabilities; additional capabilities are explicit dependencies.
|
|
346
|
-
- Wrap capability declarations with `NotificationDefinitions`, `HostedServices`, `BackgroundWorkers`,
|
|
370
|
+
- Wrap capability declarations with `NotificationDefinitions`, `HostedServices`, `BackgroundWorkers`, or `BackgroundSchedules` from their owning packages. Use native service registration for providers and `ServiceComposition` for capability discovery and final binding. Keep provider selection in Host.
|
|
347
371
|
- Replace Application invocation methods with `invoke(application, Service.method, ...)`, `invoke_as(application, user, ...)`, `call(application, function, ...)`, and `call_as(application, user, ...)`. Register interception with `add_interceptor(context, ...)` and events with `subscribe_local_event(context, ...)`. Import these functions from `python_ddd_framework`.
|
|
348
372
|
- Read capability catalogs from their Module instance through `application.modules.instance(ModuleType)` or the catalog's DI binding. Generic service diagnostics remain in `application.service_catalog`; readiness is `application.is_ready`.
|
|
349
373
|
- `DataSeedContributor`, `DataSeeder`, and `DataSeedError` belong to `python_ddd_framework.seeding` and remain available from the root package. Domain Modules with contributors depend on `DataSeedingModule`; remove `@application_service` from contributors. Seed is no longer an ApplicationService or part of its catalog. Explicit seed commands retain contributor ordering, per-item UoW, module dependency selection, failure reporting, and cleanup.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
python_ddd_framework/__init__.py,sha256=
|
|
1
|
+
python_ddd_framework/__init__.py,sha256=TuNv1DOY2PLAvwzWzj_j0zSoJmofjjkdOQdqY18SDwI,15402
|
|
2
2
|
python_ddd_framework/application/__init__.py,sha256=RPe_lkegIRim7GcBnXyevOg5KM7bfUIO9YGsqcA0lXk,184
|
|
3
3
|
python_ddd_framework/application/build_spec.py,sha256=Udj1JIgP2oBMCUezmzWzOdjFYBCEE8OxwHSEaDqrMMo,1030
|
|
4
4
|
python_ddd_framework/application/builder.py,sha256=d0vc1D5HvG_aJI5lPQydr-ha0OTNviWlplxlyP99qDs,8080
|
|
@@ -89,7 +89,7 @@ python_ddd_framework/configuration/environment_source.py,sha256=5DFYpLyOXpCkYT4W
|
|
|
89
89
|
python_ddd_framework/configuration/immutability.py,sha256=TRB2caTQtoDUA1bcQt9bPWBl-7u45f5vO30sRJQnKkc,5672
|
|
90
90
|
python_ddd_framework/configuration/input_shape.py,sha256=T97z95F2PQkvNN0PV5Iz1s19UvX1CZGvyV7Ojo7cRLY,2438
|
|
91
91
|
python_ddd_framework/configuration/merge.py,sha256=VE6vtvcyQu8rQaqGAQs23JQmhJFneq6cWd1SZCow1Bk,3702
|
|
92
|
-
python_ddd_framework/configuration/root.py,sha256
|
|
92
|
+
python_ddd_framework/configuration/root.py,sha256=-xmDLRHJWOpLUVKa7i-QABsrp5kCrTtefunvdhGkgvc,8068
|
|
93
93
|
python_ddd_framework/configuration/sources.py,sha256=Xd7Sxk0umHS7h5SRq08NSWGhLmJ3lHVP6tyzmh1F7AA,19233
|
|
94
94
|
python_ddd_framework/configuration/values.py,sha256=4S8Ae2CRRekA9ge0ZxoMJcdJn3XqnF06E6DOaAZZuHc,14591
|
|
95
95
|
python_ddd_framework/configuration/yaml_source.py,sha256=hZhKftmip6rRQUwqQBfTi_d-UYthUqWPbL5dQUvb89Q,2016
|
|
@@ -107,7 +107,7 @@ python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/
|
|
|
107
107
|
python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/__init__.py.jinja,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
108
|
python_ddd_framework/developer_kit/templates/basic/{{cookiecutter.module_name}}/services/default_conversion_service.py.jinja,sha256=YIYXqDycBVoUlLCwwUZ2tcyUk2fKSu-20mI00u5QUhc,342
|
|
109
109
|
python_ddd_framework/developer_kit/templates/module/cookiecutter.json,sha256=edD_zSqmFuOKvMZyxmFFabAavBQJk0tbGO4YrnsgZN0,60
|
|
110
|
-
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md,sha256=
|
|
110
|
+
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/README.md,sha256=nbPgBhWynTZaK6vojFyxf2ARSYAzw6xCsNzAqUtulNo,6537
|
|
111
111
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/__init__.py.jinja,sha256=byPOI4PKtQDi5IQa9tzZZEVYy5zGx9PJdBzPrgcxm64,89
|
|
112
112
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/__init__.py.jinja,sha256=pm9_TqjN20deRELpopxK412O3mra0OZ6QdesPdcsaRU,90
|
|
113
113
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/background_jobs/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
@@ -120,7 +120,6 @@ python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}
|
|
|
120
120
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/module.py.jinja,sha256=qGmq1ggBmxGY2B8H8gB8tEY-uxMuWpyr60l0ZAouho8,1950
|
|
121
121
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/options/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
122
122
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/services/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
123
|
-
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application/setting_handlers/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
124
123
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/__init__.py.jinja,sha256=grSDmU4IKdRYZsqi56aEVvsgx92AYF2k3jlDzANqra4,51
|
|
125
124
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/inputs/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
126
125
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/application_contracts/integration_services/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
@@ -158,8 +157,8 @@ python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}
|
|
|
158
157
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/sqlalchemy/repositories/__init__.py.jinja,sha256=VdqphDwvoB9nhl5Q9vHpSnmMWwDUlDp_YckRkionwWw,46
|
|
159
158
|
python_ddd_framework/developer_kit/templates/module/{{cookiecutter.module_name}}/tests/__init__.py.jinja,sha256=Kbig4VsdpmVUDHx5SLPf7ZZM6S5ftVmzBxBAJIjwvng,40
|
|
160
159
|
python_ddd_framework/developer_kit/templates/project/cookiecutter.json,sha256=vmN799k9eRHAEGeYt8uUjPsusbblvgpNIsXVOsXUno4,314
|
|
161
|
-
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md,sha256=
|
|
162
|
-
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md,sha256=
|
|
160
|
+
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/AGENTS.md,sha256=MVG6FbJckCZYwq-V33AZpH7RCvcv2tKaaN5VcaIzJ6M,13432
|
|
161
|
+
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/README.md,sha256=y_Xg7rKtj6I8U2ChoLj57C5jz75gk2BdqD8F-ZH8IfI,11874
|
|
163
162
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/.dockerignore,sha256=TG053vIzikxG8h53vCcU_s5wcr7p4GEKwsedbrRrSOo,71
|
|
164
163
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/.gitignore,sha256=OF6nwgb-RiP8EAXc5osbk3bsOEYkk0JaV99v3FcglTU,66
|
|
165
164
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/.python-version,sha256=lN0hZ-3c-Wk_uFRrQasUfy9YJrXrdvSzYKhuHeXIXbY,34
|
|
@@ -170,11 +169,11 @@ python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name
|
|
|
170
169
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/pyproject.toml.jinja,sha256=zZ-kDjgfOYo4g35VLoctEEoyvlZm9fEB2ddHW30JzUs,932
|
|
171
170
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/src/host/__init__.py.jinja,sha256=RfLT7dqPnSotCDorSp6Taz9jBYOmepu6De_wZNelWZ8,74
|
|
172
171
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/src/host/main.py.jinja,sha256=SPxJ86SW1dx-BuoWITWifq-6NseoxnVMLTufcFFsxF4,1996
|
|
173
|
-
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/src/host/module.py.jinja,sha256=
|
|
172
|
+
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/src/host/module.py.jinja,sha256=qpM0L69ek8-SXQpJoZ-NkPyfGGIGTRrB1xtrmzpxZF8,3004
|
|
174
173
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/tests/conftest.py.jinja,sha256=I_SlSxioBsCFNJLM52iM-neYBJR71qOUn7C4dI9t-7I,149
|
|
175
174
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/backend/tests/host/test_http.py.jinja,sha256=pmiz6k0oWWrPeW_rVG2Ynibg4j3yuKvgvYFc0K7J_no,2766
|
|
176
|
-
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md,sha256=
|
|
177
|
-
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md,sha256=
|
|
175
|
+
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/architecture.md,sha256=VINiboJya5Ae6wn0UL7pHK-IXX7OIKfI0jbWaOL0F9U,27842
|
|
176
|
+
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/docs/development.md,sha256=V44TFV5BU5dhCautVc8D41h2-QhKgyCk69X3d-dWZgk,57556
|
|
178
177
|
python_ddd_framework/developer_kit/templates/project/{{cookiecutter.project_name}}/scripts/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
179
178
|
python_ddd_framework/developer_kit/wiring.py,sha256=80ZhxpGXK6NP3b_BTTc1kt3oIU21MU70cT9kgm8ZgAE,7835
|
|
180
179
|
python_ddd_framework/diagnostics/__init__.py,sha256=HdgzjQqMJ_SUGKJc5EcQ5PL1rM4GcTeem-L0q1r7vjQ,455
|
|
@@ -390,25 +389,19 @@ python_ddd_framework/services/registration.py,sha256=AsvhVv_szq8TcaXdUMvOY6fNNp8
|
|
|
390
389
|
python_ddd_framework/services/relocation.py,sha256=oMel_8RPFtPHDE9KrUepzx1nJpiug0EaNK7RWEKZFC0,3763
|
|
391
390
|
python_ddd_framework/services/repository.py,sha256=jjjCmpcz4deAZmDNSM44WlDmh9msFO53qN116CHdBSM,1686
|
|
392
391
|
python_ddd_framework/services/runtime.py,sha256=57G5jNjOiexpSqTCogdI18u5xeEK2YglztNqDtFcsZI,9300
|
|
393
|
-
python_ddd_framework/settings/__init__.py,sha256=
|
|
394
|
-
python_ddd_framework/settings/application.py,sha256=
|
|
395
|
-
python_ddd_framework/settings/binding.py,sha256=
|
|
396
|
-
python_ddd_framework/settings/catalog.py,sha256=
|
|
397
|
-
python_ddd_framework/settings/
|
|
398
|
-
python_ddd_framework/settings/contracts.py,sha256=E70lLiFC5G7XoEpmf3rsoW5BnPhzajm-eri0vzn0xuY,4428
|
|
399
|
-
python_ddd_framework/settings/declarations.py,sha256=FeFrgw6fAdjX6bGaajjnO2tsrfxEvKW6sZrRfTAxCEs,526
|
|
392
|
+
python_ddd_framework/settings/__init__.py,sha256=EYm9Ek57X2o1edniVeKn150uKFgSrHF7onKUncGRQnc,867
|
|
393
|
+
python_ddd_framework/settings/application.py,sha256=QMouHpNJUxkmkQ3kRHSxtZ4ssDAsRZitYWiWADuGCH4,5444
|
|
394
|
+
python_ddd_framework/settings/binding.py,sha256=_wDIsKe-p6WuKaQB9NvhbxCm9aYbiN95m7erJS35T8Y,1621
|
|
395
|
+
python_ddd_framework/settings/catalog.py,sha256=IkfZTd2PGUOMTyW7koQEdIXqPdiiPYlNmoTyfxPF4P0,2452
|
|
396
|
+
python_ddd_framework/settings/contracts.py,sha256=v_Wn7kr3_JQYH-rXeusR_YWvtwR3RT_KxQqnvVD7uZI,6899
|
|
400
397
|
python_ddd_framework/settings/definition_discovery.py,sha256=zHN3-coqzdEb--mecv00v7ifWzzCfJRZ-BrFcdVx4u4,3002
|
|
401
398
|
python_ddd_framework/settings/definitions.py,sha256=hkWO0dqOoBmd7pKv-cUsCZaEU0AeUEULVLW1Rl8uwdQ,1687
|
|
402
399
|
python_ddd_framework/settings/errors.py,sha256=ZcU1nMB44_Rq9Wl5bw83s3Y066LxSLrFqOW_gG6ViS4,836
|
|
403
|
-
python_ddd_framework/settings/
|
|
404
|
-
python_ddd_framework/settings/
|
|
405
|
-
python_ddd_framework/settings/
|
|
406
|
-
python_ddd_framework/settings/module.py,sha256=8c9c4pIpzn3XB8xkJqW4AhKkuUS-HYxwfS4px7VeP1Q,2314
|
|
407
|
-
python_ddd_framework/settings/notifications.py,sha256=tVSXjhhJWhkMcOakSmeOfXT1X02Hcx2xRU8Whr8rSig,1700
|
|
400
|
+
python_ddd_framework/settings/management.py,sha256=sfibyZzhrsYg8FwPZCz_mDw6UEBtnP9HJaVu2O8PMmc,1298
|
|
401
|
+
python_ddd_framework/settings/manager.py,sha256=0jc7dQxGgKY-vc3rzVrPevHPZ6BY7y4APaxGd74kKlk,3164
|
|
402
|
+
python_ddd_framework/settings/module.py,sha256=jnCyeiFAnQB2q3Zeyx-HN9xmHbpGouUrfB3K3gJ8ekE,3061
|
|
408
403
|
python_ddd_framework/settings/permissions.py,sha256=2nPyZOZ9G-4mSCIqwNI0C9WUBhP0ZwqNjljP3XZD3PE,553
|
|
409
|
-
python_ddd_framework/settings/provider.py,sha256=
|
|
410
|
-
python_ddd_framework/settings/refresh.py,sha256=jYYqEdTU4cOXyHmdMEdw_94qybp4bzVPRdrM6WF8q6k,5413
|
|
411
|
-
python_ddd_framework/settings/refresh_module.py,sha256=wDxWau4oZCtBiMO6TDXZFbZRcKTiYaTFGWPjZnaFNug,2225
|
|
404
|
+
python_ddd_framework/settings/provider.py,sha256=AvMrdWML_wbbJEqNb9IH8_OcUUX8QbdEpxw4GDibdA4,3216
|
|
412
405
|
python_ddd_framework/settings/sqlalchemy/__init__.py,sha256=G-S-sSsxwfjZxr0mVAyXUBG0SIxC3ZCMtrdpMsZ14YM,158
|
|
413
406
|
python_ddd_framework/settings/sqlalchemy/migrations/0001_settings.py,sha256=wx_GcxijY-43V9DoNNzdvfwg8d5oYWkmeWSe23xtXLA,1045
|
|
414
407
|
python_ddd_framework/settings/sqlalchemy/migrations/0002_physical_delete.py,sha256=JtPmESosI_SIe4ijFuBFX7zvcgsnk-7dtjULipdC6NU,982
|
|
@@ -418,7 +411,7 @@ python_ddd_framework/settings/sqlalchemy/models.py,sha256=qKbRHhdm0uDPUUtLgeVeFv
|
|
|
418
411
|
python_ddd_framework/settings/sqlalchemy/module.py,sha256=NbvxE40aSW1aWf7CbU3GRPN_WrvsQgUXp05hAAoDtcE,1212
|
|
419
412
|
python_ddd_framework/settings/sqlalchemy/store.py,sha256=I9yRtpZBzNEYG0NANFAgVvVTY0_Qnl5JinXf00ebQwE,3597
|
|
420
413
|
python_ddd_framework/settings/store.py,sha256=3tdF-0l0owUdWuNEd0K8OfbXncI38hx0pD6anU5GdmA,442
|
|
421
|
-
python_ddd_framework/settings/values.py,sha256=
|
|
414
|
+
python_ddd_framework/settings/values.py,sha256=smw2jLySVP6aKefqmkOqHp8z9pjc2nGfEofE6uGzMZY,687
|
|
422
415
|
python_ddd_framework/sqlalchemy/__init__.py,sha256=DP6PAJHevoc1i-n9ma8_2CAiPmAvWoN7opqRYzHR4Sg,2054
|
|
423
416
|
python_ddd_framework/sqlalchemy/alembic_runtime/__init__.py,sha256=r-ZHMiEhca5M2xv3plW9U3fHfzt5lVQeDohEFqrTuv0,82
|
|
424
417
|
python_ddd_framework/sqlalchemy/alembic_runtime/env.py,sha256=ArKiDG9n5JOPQG44oSrJUd3o96zyY-b2PPkGysFy674,1389
|
|
@@ -452,9 +445,9 @@ python_ddd_framework/unit_of_work/module.py,sha256=EjsUtCG7w56NmLU_xqBRiwJ65Mw5h
|
|
|
452
445
|
python_ddd_framework/unit_of_work/options.py,sha256=AVcEmwwn39qvj8PLQetBeGzdU_eblABGuNLDIQU5tyI,3365
|
|
453
446
|
python_ddd_framework/validation/__init__.py,sha256=nTN_BgIXMLHeUtMgSj2hpisWc1_sk6UWTapk30oh1us,100
|
|
454
447
|
python_ddd_framework/validation/models.py,sha256=Ydz5ZYgqYMPZEXi9A3DA6beN5lD9AcMaImnxDf1aRXs,1737
|
|
455
|
-
python_ddd_framework-0.
|
|
456
|
-
python_ddd_framework-0.
|
|
457
|
-
python_ddd_framework-0.
|
|
458
|
-
python_ddd_framework-0.
|
|
459
|
-
python_ddd_framework-0.
|
|
460
|
-
python_ddd_framework-0.
|
|
448
|
+
python_ddd_framework-0.9.0.dist-info/licenses/LICENSE,sha256=2sSWLZdtk4EhG2GlwpksXzmyux-G6Qiov0D27lE_bqw,805
|
|
449
|
+
python_ddd_framework-0.9.0.dist-info/licenses/src/python_ddd_framework/background_jobs/pgqueuer/UPSTREAM_LICENSE.txt,sha256=0lCGUd5Pke2Up1Kqn1kRnHcwLlBz60E8xvR7WL4k_kE,1066
|
|
450
|
+
python_ddd_framework-0.9.0.dist-info/WHEEL,sha256=si2p8vW03RnhyBnhWl4tj-ObJa78SpE-c9HsEtblGR0,80
|
|
451
|
+
python_ddd_framework-0.9.0.dist-info/entry_points.txt,sha256=CMy4XBD8IKGUfHwe0YBmHmSJtjfnA3yyDHqsMZCLsc8,447
|
|
452
|
+
python_ddd_framework-0.9.0.dist-info/METADATA,sha256=hk9U10YzJOgUMz5XK2ZTFvqjAwrhoBquHOzoMXqJQgI,71605
|
|
453
|
+
python_ddd_framework-0.9.0.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""所属能力的实现或契约。"""
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"""Settings 变更只通知标识,数据库仍是有效值的唯一权威。"""
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, ConfigDict
|
|
4
|
-
|
|
5
|
-
from ..notifications import NotificationDefinition
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class SettingChanged(BaseModel):
|
|
9
|
-
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
10
|
-
|
|
11
|
-
name: str
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
SETTINGS_CHANGED = NotificationDefinition("settings.changed", SettingChanged)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"""Settings handler 的关系声明;实例和生命周期仍由原生 DI 管理。"""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from ..modularity import AppModule, ModuleDeclaration
|
|
7
|
-
from .handlers import SettingHandlerDefinition
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass(frozen=True, slots=True)
|
|
11
|
-
class SettingHandlers(ModuleDeclaration):
|
|
12
|
-
definitions: tuple[SettingHandlerDefinition[Any], ...]
|
|
13
|
-
|
|
14
|
-
@property
|
|
15
|
-
def required_module(self) -> type[AppModule]:
|
|
16
|
-
from .module import SettingsModule
|
|
17
|
-
|
|
18
|
-
return SettingsModule
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""长期组件的 typed 应用契约;执行上下文由组件自身负责。"""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import inspect
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from typing import Generic, Protocol, TypeVar
|
|
8
|
-
|
|
9
|
-
from .contracts import SettingDefinition
|
|
10
|
-
from .errors import SettingDefinitionError
|
|
11
|
-
from .values import SettingSnapshot
|
|
12
|
-
|
|
13
|
-
_T = TypeVar("_T")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class SettingHandler(Protocol[_T]):
|
|
17
|
-
async def apply(self, snapshot: SettingSnapshot[_T]) -> None: ...
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@dataclass(frozen=True, slots=True)
|
|
21
|
-
class SettingHandlerDefinition(Generic[_T]):
|
|
22
|
-
setting: SettingDefinition[_T]
|
|
23
|
-
handler_type: type[SettingHandler[_T]]
|
|
24
|
-
|
|
25
|
-
def __post_init__(self) -> None:
|
|
26
|
-
if not isinstance(self.setting, SettingDefinition):
|
|
27
|
-
raise SettingDefinitionError(setting="handler", reason="setting must be a definition")
|
|
28
|
-
if (
|
|
29
|
-
not isinstance(self.handler_type, type)
|
|
30
|
-
or inspect.isabstract(self.handler_type)
|
|
31
|
-
or not inspect.iscoroutinefunction(getattr(self.handler_type, "apply", None))
|
|
32
|
-
):
|
|
33
|
-
raise SettingDefinitionError(
|
|
34
|
-
setting=self.setting.name, reason="handler must have an async apply method"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@dataclass(frozen=True, slots=True)
|
|
39
|
-
class SettingApplicationStatus:
|
|
40
|
-
setting: str
|
|
41
|
-
handler: str
|
|
42
|
-
last_applied: SettingSnapshot[object] | None
|
|
43
|
-
error_type: str | None
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"""Settings 是通用通知的使用方,重连和漏消息的补偿由 Settings 自身拥有。"""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from contextlib import AsyncExitStack
|
|
6
|
-
|
|
7
|
-
from ..lifecycle import PreInitializeContext, ShutdownContext
|
|
8
|
-
from ..modularity import AppModule
|
|
9
|
-
from ..notifications import Notifications
|
|
10
|
-
from ..notifications.declarations import NotificationDefinitions
|
|
11
|
-
from ..notifications.module import NotificationsModule
|
|
12
|
-
from .changes import SETTINGS_CHANGED, SettingChanged
|
|
13
|
-
from .manager import SettingManager
|
|
14
|
-
from .refresh import SettingsRefreshRuntime
|
|
15
|
-
from .refresh_module import SettingsRefreshModule
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class SettingsNotificationsModule(AppModule):
|
|
19
|
-
dependencies = (SettingsRefreshModule, NotificationsModule)
|
|
20
|
-
notification_definitions = NotificationDefinitions((SETTINGS_CHANGED,))
|
|
21
|
-
|
|
22
|
-
def __init__(self) -> None:
|
|
23
|
-
self._subscriptions = AsyncExitStack()
|
|
24
|
-
|
|
25
|
-
async def pre_initialize(self, context: PreInitializeContext) -> None:
|
|
26
|
-
refresh = await context.container.get(SettingsRefreshRuntime)
|
|
27
|
-
notifications = await context.container.get(Notifications)
|
|
28
|
-
settings = await context.container.get(SettingManager)
|
|
29
|
-
|
|
30
|
-
async def changed(message: SettingChanged) -> None:
|
|
31
|
-
refresh.request_refresh(message.name)
|
|
32
|
-
|
|
33
|
-
await self._subscriptions.enter_async_context(
|
|
34
|
-
notifications.subscribe(SETTINGS_CHANGED, changed, on_reconnect=refresh.request_refresh)
|
|
35
|
-
)
|
|
36
|
-
settings._notifications = notifications
|
|
37
|
-
|
|
38
|
-
async def shutdown(self, context: ShutdownContext) -> None:
|
|
39
|
-
settings = await context.container.get(SettingManager)
|
|
40
|
-
settings._notifications = None
|
|
41
|
-
await self._subscriptions.aclose()
|