domainkit 0.1.0__tar.gz → 0.2.0__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.
- {domainkit-0.1.0 → domainkit-0.2.0}/PKG-INFO +42 -95
- {domainkit-0.1.0 → domainkit-0.2.0}/README.md +38 -94
- {domainkit-0.1.0 → domainkit-0.2.0}/pyproject.toml +7 -1
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/protection.py +3 -2
- domainkit-0.2.0/src/domainkit/management/commands/init.py +108 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/dot-env-tpl +3 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/dot-gitignore-tpl +11 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/manage.py-tpl +15 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/pyproject.toml-tpl +17 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/__init__.py-tpl +1 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/asgi.py-tpl +5 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/__init__.py-tpl +1 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/base.py-tpl +13 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/local.py-tpl +11 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/production.py-tpl +16 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/test.py-tpl +7 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/config/urls.py-tpl +8 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/src/domains/__init__.py-tpl +1 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/tests/__init__.py-tpl +1 -0
- domainkit-0.2.0/src/domainkit/management/templates/project/tests/test_health.py-tpl +8 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/formatting.py +3 -2
- {domainkit-0.1.0 → domainkit-0.2.0}/LICENSE +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/apps/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/apps/config.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/apps/registry.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/architecture.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/conf/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/conf/env.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/conf/global_settings.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/conf/lazy.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/context.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/apps.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/management/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/management/commands/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/management/commands/auditlog.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/recorder.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/settings.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/tables.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/apps.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/middleware.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/settings.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/tables.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/idempotency/tasks.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/admin.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/apps.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/messages.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/runworker.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/metrics.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/outbox.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/registry.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/settings.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/tables.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/worker.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/connections.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/drivers.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/migration_env/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/migration_env/env.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/migration_env/script.py.mako +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/migrations.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/orm.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/repository.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/types.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/db/uow.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/_dataclass.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/events.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/exceptions.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/messages.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/model.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/pagination.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/repository.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/ddd/uow.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/deprecation.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/exceptions.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/application.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/errors.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/health.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/metrics.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/middleware.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/pagination.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/http/ratelimit.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/log.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/base.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/check.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/makemigrations.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/migrate.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/runserver.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/shell.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/showmigrations.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/commands/startdomain.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/api/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/api/dependencies.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/api/routes.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/api/schemas.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/application/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/application/commands.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/application/handlers.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/application/services.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/application/unit_of_work.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/apps.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/domain/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/domain/events.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/domain/exceptions.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/domain/model.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/domain/repositories.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/infrastructure/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/infrastructure/orm.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/infrastructure/repositories.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/infrastructure/unit_of_work.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/management/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/management/commands/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/management/commands/{{ aggregates }}.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/settings.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/__init__.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/fakes.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/test_api.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/test_model.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/test_services.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/test_settings.py-tpl +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/py.typed +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/reporting.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/testing.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/tracing.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/__init__.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/module_loading.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/rates.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/readiness.py +0 -0
- {domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/utils/text.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: domainkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A Django-style framework for domain-driven FastAPI services.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -19,14 +19,17 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Provides-Extra: metrics
|
|
20
20
|
Provides-Extra: postgres
|
|
21
21
|
Provides-Extra: sqlite
|
|
22
|
+
Provides-Extra: testing
|
|
22
23
|
Provides-Extra: tracing
|
|
23
24
|
Requires-Dist: aiosqlite (>=0.22.1,<0.23.0) ; extra == "sqlite"
|
|
24
25
|
Requires-Dist: alembic (>=1.20.0,<2.0.0)
|
|
25
26
|
Requires-Dist: asyncpg (>=0.31.0,<0.32.0) ; extra == "postgres"
|
|
26
27
|
Requires-Dist: fastapi (>=0.141.1,<0.142.0)
|
|
28
|
+
Requires-Dist: httpx2 (>=2.13.0,<3.0.0) ; extra == "testing"
|
|
27
29
|
Requires-Dist: opentelemetry-api (>=1.45.1,<2.0.0) ; extra == "tracing"
|
|
28
30
|
Requires-Dist: prometheus-client (>=0.26.0,<0.27.0) ; extra == "metrics"
|
|
29
31
|
Requires-Dist: pydantic (>=2.14.1,<3.0.0)
|
|
32
|
+
Requires-Dist: pytest (>=9.1.1,<10.0.0) ; extra == "testing"
|
|
30
33
|
Requires-Dist: python-dotenv (>=1.2.4,<2.0.0)
|
|
31
34
|
Requires-Dist: sqlalchemy[asyncio] (>=2.0.54,<2.1)
|
|
32
35
|
Requires-Dist: uvicorn[standard] (>=0.54.0,<0.55.0)
|
|
@@ -46,7 +49,7 @@ A Django-style framework for domain-driven FastAPI services. It provides:
|
|
|
46
49
|
- optional apps for reliable messaging, audit trails, and idempotent requests
|
|
47
50
|
|
|
48
51
|
```bash
|
|
49
|
-
pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, tracing
|
|
52
|
+
pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, tracing, testing
|
|
50
53
|
```
|
|
51
54
|
|
|
52
55
|
## Documentation
|
|
@@ -60,105 +63,36 @@ pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, traci
|
|
|
60
63
|
|
|
61
64
|
## Quick start
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
```bash
|
|
67
|
+
python3.13 -m venv .venv && . .venv/bin/activate
|
|
68
|
+
pip install "domainkit[postgres,sqlite,testing]"
|
|
69
|
+
domainkit init billingAPI billing # the name sets the variable prefix: BILLING_API_*
|
|
70
|
+
cd billing
|
|
71
|
+
python manage.py check
|
|
72
|
+
python manage.py startdomain invoices --aggregate Invoice # prints how to install it
|
|
73
|
+
python manage.py makemigrations -m "create invoices" && python manage.py migrate
|
|
74
|
+
pytest
|
|
75
|
+
python manage.py runserver # http://127.0.0.1:8000/docs
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`domainkit init` creates a project that passes `check` and its tests as it is:
|
|
66
79
|
|
|
67
80
|
```text
|
|
68
81
|
billing/
|
|
69
|
-
pyproject.toml
|
|
82
|
+
pyproject.toml the name, dependencies, and the pytest plugin
|
|
70
83
|
manage.py
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
src/config/settings/local
|
|
74
|
-
src/config/
|
|
75
|
-
src/config/
|
|
76
|
-
src/
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
```toml
|
|
81
|
-
# pyproject.toml
|
|
82
|
-
[project]
|
|
83
|
-
name = "billingAPI"
|
|
84
|
-
version = "0.1.0"
|
|
85
|
-
requires-python = ">=3.13"
|
|
86
|
-
dependencies = ["domainkit[sqlite]"]
|
|
87
|
-
|
|
88
|
-
[tool.pytest.ini_options]
|
|
89
|
-
pythonpath = ["src"]
|
|
90
|
-
addopts = "-p domainkit.testing" # the database and client fixtures
|
|
91
|
-
domainkit_settings = "config.settings.test"
|
|
84
|
+
.env selects config.settings.local on this machine
|
|
85
|
+
.gitignore
|
|
86
|
+
src/config/settings/ base, local, test, and production settings
|
|
87
|
+
src/config/urls.py the root router: mount each domain's router here
|
|
88
|
+
src/config/asgi.py the application, for uvicorn
|
|
89
|
+
src/domains/ the bounded contexts
|
|
90
|
+
tests/test_health.py
|
|
92
91
|
```
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
from pathlib import Path
|
|
98
|
-
|
|
99
|
-
if __name__ == "__main__":
|
|
100
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent / "src"))
|
|
101
|
-
from domainkit.management import execute_from_command_line
|
|
102
|
-
|
|
103
|
-
execute_from_command_line(sys.argv)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
```python
|
|
107
|
-
# src/config/settings/local.py
|
|
108
|
-
from pathlib import Path
|
|
109
|
-
|
|
110
|
-
from domainkit.conf.env import Env
|
|
111
|
-
|
|
112
|
-
env = Env()
|
|
113
|
-
BASE_DIR = Path(__file__).resolve().parents[3]
|
|
114
|
-
|
|
115
|
-
DEBUG = True
|
|
116
|
-
INSTALLED_DOMAINS = ["domainkit.contrib.messaging"]
|
|
117
|
-
ROOT_ROUTER = "config.urls.router"
|
|
118
|
-
ASGI_APPLICATION = "config.asgi:application"
|
|
119
|
-
DATABASES = {
|
|
120
|
-
"default": {
|
|
121
|
-
"URL": env.str("DATABASE_URL", f"sqlite+aiosqlite:///{BASE_DIR / 'db.sqlite3'}")
|
|
122
|
-
},
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
```python
|
|
127
|
-
# src/config/settings/test.py
|
|
128
|
-
from config.settings.local import *
|
|
129
|
-
|
|
130
|
-
DEBUG = False
|
|
131
|
-
ALLOWED_HOSTS = ["testserver"]
|
|
132
|
-
DATABASES = {"default": {"URL": "sqlite+aiosqlite:///:memory:"}}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
```python
|
|
136
|
-
# src/config/urls.py
|
|
137
|
-
from fastapi import APIRouter
|
|
138
|
-
|
|
139
|
-
from domainkit.http.health import router as health_router
|
|
140
|
-
|
|
141
|
-
router = APIRouter()
|
|
142
|
-
router.include_router(health_router)
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
```python
|
|
146
|
-
# src/config/asgi.py
|
|
147
|
-
from domainkit.http import get_asgi_application
|
|
148
|
-
|
|
149
|
-
application = get_asgi_application()
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Then:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
export BILLING_API_SETTINGS_MODULE=config.settings.local # or put it in .env
|
|
156
|
-
python manage.py check
|
|
157
|
-
python manage.py startdomain invoices --aggregate Invoice # prints how to install it
|
|
158
|
-
python manage.py makemigrations -m "create invoices" && python manage.py migrate
|
|
159
|
-
pytest # the generated tests
|
|
160
|
-
python manage.py runserver # http://127.0.0.1:8000/docs
|
|
161
|
-
```
|
|
93
|
+
The production settings read `BILLING_API_ALLOWED_HOSTS` and
|
|
94
|
+
`BILLING_API_DATABASE_URL` from the environment, and the deployment checks
|
|
95
|
+
refuse to start without them, or without verified TLS to the database.
|
|
162
96
|
|
|
163
97
|
## Developing domainkit
|
|
164
98
|
|
|
@@ -171,11 +105,24 @@ poetry run pytest --cov # random order; coverage gate in pyprojec
|
|
|
171
105
|
poetry run mutmut run "domainkit.http*" && poetry run mutmut results
|
|
172
106
|
```
|
|
173
107
|
|
|
108
|
+
The database tests use SQLite unless `DOMAINKIT_TEST_POSTGRES_URL` names a
|
|
109
|
+
PostgreSQL server; each test then creates a database there and drops it.
|
|
110
|
+
Use a throwaway server:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
docker run -d --rm --name domainkit-postgres -e POSTGRES_PASSWORD=postgres \
|
|
114
|
+
-p 127.0.0.1:55432:5432 postgres:18-alpine
|
|
115
|
+
DOMAINKIT_TEST_POSTGRES_URL=postgresql+asyncpg://postgres:postgres@127.0.0.1:55432/postgres \
|
|
116
|
+
poetry run pytest
|
|
117
|
+
```
|
|
118
|
+
|
|
174
119
|
CI (`.github/workflows/ci.yml`) runs the same checks on every push and pull
|
|
175
120
|
request:
|
|
176
121
|
|
|
177
122
|
- lint and strict typing
|
|
178
123
|
- the tests on Python 3.13 and 3.14
|
|
124
|
+
- the tests again with PostgreSQL 18, including row locking, the audit
|
|
125
|
+
triggers, and server-side timeouts
|
|
179
126
|
- a wheel build, installed and imported in a clean environment
|
|
180
127
|
- pip-audit, and a gitleaks scan of the whole history
|
|
181
128
|
|
|
@@ -8,7 +8,7 @@ A Django-style framework for domain-driven FastAPI services. It provides:
|
|
|
8
8
|
- optional apps for reliable messaging, audit trails, and idempotent requests
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
|
-
pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, tracing
|
|
11
|
+
pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, tracing, testing
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Documentation
|
|
@@ -22,105 +22,36 @@ pip install "domainkit[postgres]" # extras: postgres, sqlite, metrics, traci
|
|
|
22
22
|
|
|
23
23
|
## Quick start
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
```bash
|
|
26
|
+
python3.13 -m venv .venv && . .venv/bin/activate
|
|
27
|
+
pip install "domainkit[postgres,sqlite,testing]"
|
|
28
|
+
domainkit init billingAPI billing # the name sets the variable prefix: BILLING_API_*
|
|
29
|
+
cd billing
|
|
30
|
+
python manage.py check
|
|
31
|
+
python manage.py startdomain invoices --aggregate Invoice # prints how to install it
|
|
32
|
+
python manage.py makemigrations -m "create invoices" && python manage.py migrate
|
|
33
|
+
pytest
|
|
34
|
+
python manage.py runserver # http://127.0.0.1:8000/docs
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`domainkit init` creates a project that passes `check` and its tests as it is:
|
|
28
38
|
|
|
29
39
|
```text
|
|
30
40
|
billing/
|
|
31
|
-
pyproject.toml
|
|
41
|
+
pyproject.toml the name, dependencies, and the pytest plugin
|
|
32
42
|
manage.py
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
src/config/settings/local
|
|
36
|
-
src/config/
|
|
37
|
-
src/config/
|
|
38
|
-
src/
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
```toml
|
|
43
|
-
# pyproject.toml
|
|
44
|
-
[project]
|
|
45
|
-
name = "billingAPI"
|
|
46
|
-
version = "0.1.0"
|
|
47
|
-
requires-python = ">=3.13"
|
|
48
|
-
dependencies = ["domainkit[sqlite]"]
|
|
49
|
-
|
|
50
|
-
[tool.pytest.ini_options]
|
|
51
|
-
pythonpath = ["src"]
|
|
52
|
-
addopts = "-p domainkit.testing" # the database and client fixtures
|
|
53
|
-
domainkit_settings = "config.settings.test"
|
|
43
|
+
.env selects config.settings.local on this machine
|
|
44
|
+
.gitignore
|
|
45
|
+
src/config/settings/ base, local, test, and production settings
|
|
46
|
+
src/config/urls.py the root router: mount each domain's router here
|
|
47
|
+
src/config/asgi.py the application, for uvicorn
|
|
48
|
+
src/domains/ the bounded contexts
|
|
49
|
+
tests/test_health.py
|
|
54
50
|
```
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
from pathlib import Path
|
|
60
|
-
|
|
61
|
-
if __name__ == "__main__":
|
|
62
|
-
sys.path.insert(0, str(Path(__file__).resolve().parent / "src"))
|
|
63
|
-
from domainkit.management import execute_from_command_line
|
|
64
|
-
|
|
65
|
-
execute_from_command_line(sys.argv)
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
```python
|
|
69
|
-
# src/config/settings/local.py
|
|
70
|
-
from pathlib import Path
|
|
71
|
-
|
|
72
|
-
from domainkit.conf.env import Env
|
|
73
|
-
|
|
74
|
-
env = Env()
|
|
75
|
-
BASE_DIR = Path(__file__).resolve().parents[3]
|
|
76
|
-
|
|
77
|
-
DEBUG = True
|
|
78
|
-
INSTALLED_DOMAINS = ["domainkit.contrib.messaging"]
|
|
79
|
-
ROOT_ROUTER = "config.urls.router"
|
|
80
|
-
ASGI_APPLICATION = "config.asgi:application"
|
|
81
|
-
DATABASES = {
|
|
82
|
-
"default": {
|
|
83
|
-
"URL": env.str("DATABASE_URL", f"sqlite+aiosqlite:///{BASE_DIR / 'db.sqlite3'}")
|
|
84
|
-
},
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
```python
|
|
89
|
-
# src/config/settings/test.py
|
|
90
|
-
from config.settings.local import *
|
|
91
|
-
|
|
92
|
-
DEBUG = False
|
|
93
|
-
ALLOWED_HOSTS = ["testserver"]
|
|
94
|
-
DATABASES = {"default": {"URL": "sqlite+aiosqlite:///:memory:"}}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
```python
|
|
98
|
-
# src/config/urls.py
|
|
99
|
-
from fastapi import APIRouter
|
|
100
|
-
|
|
101
|
-
from domainkit.http.health import router as health_router
|
|
102
|
-
|
|
103
|
-
router = APIRouter()
|
|
104
|
-
router.include_router(health_router)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
```python
|
|
108
|
-
# src/config/asgi.py
|
|
109
|
-
from domainkit.http import get_asgi_application
|
|
110
|
-
|
|
111
|
-
application = get_asgi_application()
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Then:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
export BILLING_API_SETTINGS_MODULE=config.settings.local # or put it in .env
|
|
118
|
-
python manage.py check
|
|
119
|
-
python manage.py startdomain invoices --aggregate Invoice # prints how to install it
|
|
120
|
-
python manage.py makemigrations -m "create invoices" && python manage.py migrate
|
|
121
|
-
pytest # the generated tests
|
|
122
|
-
python manage.py runserver # http://127.0.0.1:8000/docs
|
|
123
|
-
```
|
|
52
|
+
The production settings read `BILLING_API_ALLOWED_HOSTS` and
|
|
53
|
+
`BILLING_API_DATABASE_URL` from the environment, and the deployment checks
|
|
54
|
+
refuse to start without them, or without verified TLS to the database.
|
|
124
55
|
|
|
125
56
|
## Developing domainkit
|
|
126
57
|
|
|
@@ -133,11 +64,24 @@ poetry run pytest --cov # random order; coverage gate in pyprojec
|
|
|
133
64
|
poetry run mutmut run "domainkit.http*" && poetry run mutmut results
|
|
134
65
|
```
|
|
135
66
|
|
|
67
|
+
The database tests use SQLite unless `DOMAINKIT_TEST_POSTGRES_URL` names a
|
|
68
|
+
PostgreSQL server; each test then creates a database there and drops it.
|
|
69
|
+
Use a throwaway server:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
docker run -d --rm --name domainkit-postgres -e POSTGRES_PASSWORD=postgres \
|
|
73
|
+
-p 127.0.0.1:55432:5432 postgres:18-alpine
|
|
74
|
+
DOMAINKIT_TEST_POSTGRES_URL=postgresql+asyncpg://postgres:postgres@127.0.0.1:55432/postgres \
|
|
75
|
+
poetry run pytest
|
|
76
|
+
```
|
|
77
|
+
|
|
136
78
|
CI (`.github/workflows/ci.yml`) runs the same checks on every push and pull
|
|
137
79
|
request:
|
|
138
80
|
|
|
139
81
|
- lint and strict typing
|
|
140
82
|
- the tests on Python 3.13 and 3.14
|
|
83
|
+
- the tests again with PostgreSQL 18, including row locking, the audit
|
|
84
|
+
triggers, and server-side timeouts
|
|
141
85
|
- a wheel build, installed and imported in a clean environment
|
|
142
86
|
- pip-audit, and a gitleaks scan of the whole history
|
|
143
87
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "domainkit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "A Django-style framework for domain-driven FastAPI services."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -35,6 +35,12 @@ postgres = ["asyncpg (>=0.31.0,<0.32.0)"]
|
|
|
35
35
|
sqlite = ["aiosqlite (>=0.22.1,<0.23.0)"]
|
|
36
36
|
metrics = ["prometheus-client (>=0.26.0,<0.27.0)"]
|
|
37
37
|
tracing = ["opentelemetry-api (>=1.45.1,<2.0.0)"]
|
|
38
|
+
# The domainkit.testing pytest plugin; Starlette's TestClient needs httpx2.
|
|
39
|
+
testing = ["pytest (>=9.1.1,<10.0.0)", "httpx2 (>=2.13.0,<3.0.0)"]
|
|
40
|
+
|
|
41
|
+
# Runs commands before a project exists: "domainkit init billingAPI".
|
|
42
|
+
[project.scripts]
|
|
43
|
+
domainkit = "domainkit.management:execute_from_command_line"
|
|
38
44
|
|
|
39
45
|
[project.urls]
|
|
40
46
|
Homepage = "https://github.com/karimmattar/domainkit"
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Revoking ``UPDATE`` and ``DELETE`` from the application's role is undone by
|
|
4
4
|
the next broad ``GRANT``. These triggers refuse updates, deletes, and
|
|
5
|
-
truncation for every role
|
|
6
|
-
example, to apply a retention policy). Call them from a project
|
|
5
|
+
truncation for every role, the table owner included; only the owner can drop
|
|
6
|
+
them (for example, to apply a retention policy). Call them from a project
|
|
7
|
+
revision::
|
|
7
8
|
|
|
8
9
|
from domainkit.contrib.audit.protection import protect, unprotect
|
|
9
10
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""``init``: create a project from the project template (Django's ``startproject``).
|
|
2
|
+
|
|
3
|
+
It runs before any project exists, through the ``domainkit`` program the
|
|
4
|
+
package installs::
|
|
5
|
+
|
|
6
|
+
domainkit init billingAPI # creates ./billingAPI
|
|
7
|
+
domainkit init billingAPI billing # or names the directory
|
|
8
|
+
|
|
9
|
+
The project gets settings for local work, tests, and production; its routes
|
|
10
|
+
and ASGI application; ``manage.py``; and a first test. Its name sets the
|
|
11
|
+
environment variable prefix (``billingAPI``: ``BILLING_API_``).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from argparse import ArgumentParser
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Final
|
|
18
|
+
|
|
19
|
+
from domainkit import __version__
|
|
20
|
+
from domainkit.conf.env import prefix_for
|
|
21
|
+
from domainkit.management.base import BaseCommand, CommandError
|
|
22
|
+
from domainkit.management.commands.startdomain import render
|
|
23
|
+
from domainkit.utils.formatting import format_python
|
|
24
|
+
|
|
25
|
+
TEMPLATE_DIR: Final = Path(__file__).resolve().parents[1] / "templates" / "project"
|
|
26
|
+
DOTFILE: Final = "dot-"
|
|
27
|
+
"""Template files named ``dot-<name>`` become ``.<name>`` (``.env``, ``.gitignore``)."""
|
|
28
|
+
_PROJECT_NAME: Final = re.compile(r"[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?")
|
|
29
|
+
_RELEASE: Final = re.compile(r"(\d+)\.(\d+)\.\d+")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def requirement(version: str) -> str:
|
|
33
|
+
"""Depend on this release up to the next one that may break it.
|
|
34
|
+
|
|
35
|
+
Before 1.0 a minor release may break (``>=0.1.1,<0.2``); after it, a
|
|
36
|
+
major one. A version without release numbers (a source checkout) pins
|
|
37
|
+
nothing.
|
|
38
|
+
"""
|
|
39
|
+
extras = "domainkit[postgres,sqlite]"
|
|
40
|
+
match = _RELEASE.fullmatch(version)
|
|
41
|
+
if match is None:
|
|
42
|
+
return extras
|
|
43
|
+
major, minor = int(match[1]), int(match[2])
|
|
44
|
+
upper = f"0.{minor + 1}" if major == 0 else f"{major + 1}"
|
|
45
|
+
return f"{extras}>={version},<{upper}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Command(BaseCommand):
|
|
49
|
+
"""Generate a project directory that passes ``check`` and its tests."""
|
|
50
|
+
|
|
51
|
+
help = "Create a project: settings, routes, manage.py, and a first test."
|
|
52
|
+
requires_setup = False
|
|
53
|
+
|
|
54
|
+
def add_arguments(self, parser: ArgumentParser) -> None:
|
|
55
|
+
"""Accept the project name and an optional directory."""
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"name",
|
|
58
|
+
help="project name, as in [project].name; it sets the environment "
|
|
59
|
+
"variable prefix (billingAPI: BILLING_API_)",
|
|
60
|
+
)
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"directory",
|
|
63
|
+
nargs="?",
|
|
64
|
+
type=Path,
|
|
65
|
+
help="where to create it (default: ./<name>); empty or missing",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def handle(self, *, name: str, directory: Path | None, **options: object) -> None:
|
|
69
|
+
"""Render the template, then print the next steps."""
|
|
70
|
+
if not _PROJECT_NAME.fullmatch(name):
|
|
71
|
+
raise CommandError(
|
|
72
|
+
f"{name!r} is not a valid project name: use letters, digits, '.', "
|
|
73
|
+
"'-', and '_', starting and ending with a letter or digit."
|
|
74
|
+
)
|
|
75
|
+
prefix = prefix_for(name)
|
|
76
|
+
if not prefix[0].isalpha():
|
|
77
|
+
raise CommandError(
|
|
78
|
+
f"{name!r} must start with a letter: it prefixes the project's "
|
|
79
|
+
f"environment variables ({prefix}...)."
|
|
80
|
+
)
|
|
81
|
+
target = (directory or Path(name)).resolve()
|
|
82
|
+
if target.exists() and (not target.is_dir() or any(target.iterdir())):
|
|
83
|
+
raise CommandError(f"{target} already exists and is not empty.")
|
|
84
|
+
context = {
|
|
85
|
+
"project": name,
|
|
86
|
+
"prefix": prefix,
|
|
87
|
+
"dependency": requirement(__version__),
|
|
88
|
+
}
|
|
89
|
+
for path in render(TEMPLATE_DIR, target, context):
|
|
90
|
+
if path.name.startswith(DOTFILE):
|
|
91
|
+
path.rename(path.with_name(f".{path.name.removeprefix(DOTFILE)}"))
|
|
92
|
+
format_python([target])
|
|
93
|
+
self.write(_next_steps(name, target))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _next_steps(name: str, target: Path) -> str:
|
|
97
|
+
"""Explain how to install and run the new project."""
|
|
98
|
+
return "\n".join(
|
|
99
|
+
[
|
|
100
|
+
f"Created {name} in {target}.",
|
|
101
|
+
"Next steps:",
|
|
102
|
+
f" cd {target.relative_to(Path.cwd(), walk_up=True)}",
|
|
103
|
+
' pip install "domainkit[postgres,sqlite,testing]" # or: uv sync',
|
|
104
|
+
" python manage.py check",
|
|
105
|
+
" python manage.py startdomain <name>",
|
|
106
|
+
" pytest",
|
|
107
|
+
]
|
|
108
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Run the project's commands: python manage.py <command>."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
ROOT = Path(__file__).resolve().parent
|
|
8
|
+
|
|
9
|
+
if __name__ == "__main__":
|
|
10
|
+
# Find pyproject.toml and .env whatever the working directory.
|
|
11
|
+
os.environ.setdefault("DOMAINKIT_PROJECT_ROOT", str(ROOT))
|
|
12
|
+
sys.path.insert(0, str(ROOT / "src"))
|
|
13
|
+
from domainkit.management import execute_from_command_line
|
|
14
|
+
|
|
15
|
+
execute_from_command_line(sys.argv)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "{{ project }}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
requires-python = ">=3.13"
|
|
5
|
+
dependencies = ["{{ dependency }}"]
|
|
6
|
+
|
|
7
|
+
[dependency-groups]
|
|
8
|
+
dev = ["domainkit[testing]"] # pytest and httpx2, for the fixtures
|
|
9
|
+
|
|
10
|
+
[tool.pytest.ini_options]
|
|
11
|
+
pythonpath = ["src"]
|
|
12
|
+
addopts = "-p domainkit.testing" # the database and client fixtures
|
|
13
|
+
domainkit_settings = "config.settings.test"
|
|
14
|
+
|
|
15
|
+
[tool.ruff.lint.per-file-ignores]
|
|
16
|
+
# Each settings module builds on base.py with a star import.
|
|
17
|
+
"src/config/settings/*.py" = ["F403", "F405"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Project configuration: settings, routes, and the ASGI application."""
|
domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/__init__.py-tpl
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Settings modules; {{ prefix }}SETTINGS_MODULE selects one."""
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Settings shared by every environment."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from domainkit.conf.env import Env
|
|
6
|
+
|
|
7
|
+
env = Env()
|
|
8
|
+
BASE_DIR = Path(__file__).resolve().parents[3]
|
|
9
|
+
|
|
10
|
+
PROJECT_NAME = "{{ project }}"
|
|
11
|
+
INSTALLED_DOMAINS = ["domainkit.contrib.messaging"]
|
|
12
|
+
ROOT_ROUTER = "config.urls.router"
|
|
13
|
+
ASGI_APPLICATION = "config.asgi:application"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Settings for development on this machine (selected by .env)."""
|
|
2
|
+
|
|
3
|
+
from config.settings.base import *
|
|
4
|
+
from config.settings.base import BASE_DIR, env
|
|
5
|
+
|
|
6
|
+
DEBUG = True
|
|
7
|
+
DATABASES = {
|
|
8
|
+
"default": {
|
|
9
|
+
"URL": env.str("DATABASE_URL", f"sqlite+aiosqlite:///{BASE_DIR / 'db.sqlite3'}")
|
|
10
|
+
},
|
|
11
|
+
}
|
domainkit-0.2.0/src/domainkit/management/templates/project/src/config/settings/production.py-tpl
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Settings for production: addresses and secrets come from the environment.
|
|
2
|
+
|
|
3
|
+
Set {{ prefix }}SETTINGS_MODULE=config.settings.production,
|
|
4
|
+
{{ prefix }}ALLOWED_HOSTS (comma-separated), and {{ prefix }}DATABASE_URL.
|
|
5
|
+
Startup fails when one is missing, and the deployment checks refuse DEBUG,
|
|
6
|
+
wildcard hosts, and network databases without verified TLS.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from config.settings.base import *
|
|
10
|
+
from config.settings.base import env
|
|
11
|
+
|
|
12
|
+
ENVIRONMENT = "production"
|
|
13
|
+
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
|
|
14
|
+
DATABASES = {
|
|
15
|
+
"default": {"URL": env.str("DATABASE_URL"), "SSLMODE": "verify-full"},
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The bounded contexts; add one with: python manage.py startdomain <name>."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""The project's tests."""
|
|
@@ -18,9 +18,10 @@ def format_python(paths: Iterable[Path]) -> bool:
|
|
|
18
18
|
return False
|
|
19
19
|
targets = [str(path) for path in paths]
|
|
20
20
|
ruff = [sys.executable, "-m", "ruff"]
|
|
21
|
+
# No cache: it would land in a project that was just generated.
|
|
21
22
|
for arguments in (
|
|
22
|
-
["check", "--fix", "--select", "I", "--quiet", *targets],
|
|
23
|
-
["format", "--quiet", *targets],
|
|
23
|
+
["check", "--fix", "--select", "I", "--quiet", "--no-cache", *targets],
|
|
24
|
+
["format", "--quiet", "--no-cache", *targets],
|
|
24
25
|
):
|
|
25
26
|
subprocess.run([*ruff, *arguments], check=False) # noqa: S603 - fixed program, generated paths.
|
|
26
27
|
return True
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/management/commands/__init__.py
RENAMED
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/audit/management/commands/auditlog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/__init__.py
RENAMED
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/messages.py
RENAMED
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/contrib/messaging/management/commands/runworker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/package/apps.py-tpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/__init__.py-tpl
RENAMED
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/fakes.py-tpl
RENAMED
|
File without changes
|
{domainkit-0.1.0 → domainkit-0.2.0}/src/domainkit/management/templates/domain/tests/test_api.py-tpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|