persistence-kit 3.13.0__tar.gz → 3.14.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.
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/PKG-INFO +23 -8
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/README.md +21 -7
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/templates.py +45 -15
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/pyproject.toml +1 -1
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/LICENSE +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/common.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/encrypted_middleware.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/encrypted_routes.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/error_handlers.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/exceptions.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/rate_limit.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/route_loader.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/authenticated_user.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/bootstrap/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/bootstrap/configuration.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/bootstrap/seeders.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/bootstrap/startup.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/contracts.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/dynamodb.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/memory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/mongo.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/cache/namespaced.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/contracts/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/contracts/repository.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/contracts/view_repository.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/contracts.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/errors.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/mailer.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/memory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/message.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/mime.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/preview.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/mail/smtp.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/py.typed +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/dynamodb_repo/dynamodb_mapper.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/dynamodb_repo/dynamodb_repo.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/filter_ops.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/memory_repo/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/memory_repo/memory_repo.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/mongo_repo/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/mongo_repo/mongo_mapper.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/mongo_repo/mongo_repo.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/schema_evolve.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/sqlalchemy_dataclass_mapper.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/sqlalchemy_engine.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/sqlalchemy_repo.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/sqlalchemy_repo/table_factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/factory/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/factory/repository_factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/registry/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/registry/entity_registry.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/view/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/view/populating_repository.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/resilience/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/resilience/circuit.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/aggregate.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/api_key.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/base.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/basic.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/bearer.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/login.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/oauth2.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/caching.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/client.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/config.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/contracts.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/errors.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/mapping.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/memory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/payload.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/populate.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/provider.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/registry.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/resolver.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/retry.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/envelope.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/errors.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/ports.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/providers/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/providers/cognito_identity_provider.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/providers/encryption_provider.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/providers/memory_security_provider.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/registration.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/token_verifiers/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/token_verifiers/cognito_jwt_verifier.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/token_verifiers/memory_jwt_verifier.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/app_settings.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/cache_settings.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/constants.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/mail_settings.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/parsers.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/repo_settings.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/contracts.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/errors.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/factory.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/local.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/media.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/routes.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/storage/s3.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/utils/__init__.py +0 -0
- {persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/utils/upsert.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: persistence-kit
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.14.0
|
|
4
4
|
Summary: Reusable persistence and repository toolkit
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
20
|
Classifier: Topic :: Database
|
|
20
21
|
Classifier: Typing :: Typed
|
|
21
22
|
Provides-Extra: all
|
|
@@ -358,10 +359,24 @@ message = mailer.compose(
|
|
|
358
359
|
mailer.dispatch(message)
|
|
359
360
|
```
|
|
360
361
|
|
|
361
|
-
A template is
|
|
362
|
-
(required)
|
|
363
|
-
|
|
364
|
-
and
|
|
362
|
+
A template is a folder in the app's directory, `user_roles_changed/`, with
|
|
363
|
+
`subject.txt` (required) and `body.html` or `body.txt` (at least one). The same
|
|
364
|
+
files can also live loose in the directory as `user_roles_changed.subject.txt`,
|
|
365
|
+
`user_roles_changed.html` and `user_roles_changed.txt`; the folder wins when both
|
|
366
|
+
exist. They are Jinja2, so a layout can be shared with `{% extends "shared/layout.html" %}`,
|
|
367
|
+
resolved from the directory root. HTML is autoescaped, and a variable missing from
|
|
368
|
+
the context fails instead of rendering blank.
|
|
369
|
+
|
|
370
|
+
```text
|
|
371
|
+
templates/mail/
|
|
372
|
+
shared/layout.html
|
|
373
|
+
images/logo.png
|
|
374
|
+
user_roles_changed/
|
|
375
|
+
subject.txt
|
|
376
|
+
body.html
|
|
377
|
+
body.txt
|
|
378
|
+
sample.json
|
|
379
|
+
```
|
|
365
380
|
|
|
366
381
|
`to`, `cc`, `bcc` and `reply_to` take a string, a `MailAddress` or a list of them.
|
|
367
382
|
Blind copies travel in the SMTP envelope only and never appear in the headers.
|
|
@@ -379,8 +394,8 @@ remote images are blocked. `compose` adds it on its own; a missing file or one t
|
|
|
379
394
|
is not an image fails like a missing variable. `MailAttachment(..., content_id=...)`
|
|
380
395
|
does the same for images the caller builds.
|
|
381
396
|
|
|
382
|
-
A template can carry a fourth file, `user_roles_changed.sample.json
|
|
383
|
-
example context. `MailTemplates.names()` lists the templates and
|
|
397
|
+
A template can carry a fourth file, `sample.json` (`user_roles_changed.sample.json`
|
|
398
|
+
when loose), with an example context. `MailTemplates.names()` lists the templates and
|
|
384
399
|
`sample_context(name)` reads that file, so one parametrized test covers them all.
|
|
385
400
|
The same sample drives the preview, which renders without sending:
|
|
386
401
|
|
|
@@ -571,7 +586,7 @@ poetry install --with dev --all-extras
|
|
|
571
586
|
poetry run pytest -q
|
|
572
587
|
```
|
|
573
588
|
|
|
574
|
-
Current baseline: **
|
|
589
|
+
Current baseline: **627 tests passing** (version 3.14.0). Async tests use
|
|
575
590
|
`pytest-asyncio` in strict mode, so each one carries `@pytest.mark.asyncio`.
|
|
576
591
|
|
|
577
592
|
`tests/test_capabilities.py` guards the lazy-import promise: it fails if merely
|
|
@@ -310,10 +310,24 @@ message = mailer.compose(
|
|
|
310
310
|
mailer.dispatch(message)
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
A template is
|
|
314
|
-
(required)
|
|
315
|
-
|
|
316
|
-
and
|
|
313
|
+
A template is a folder in the app's directory, `user_roles_changed/`, with
|
|
314
|
+
`subject.txt` (required) and `body.html` or `body.txt` (at least one). The same
|
|
315
|
+
files can also live loose in the directory as `user_roles_changed.subject.txt`,
|
|
316
|
+
`user_roles_changed.html` and `user_roles_changed.txt`; the folder wins when both
|
|
317
|
+
exist. They are Jinja2, so a layout can be shared with `{% extends "shared/layout.html" %}`,
|
|
318
|
+
resolved from the directory root. HTML is autoescaped, and a variable missing from
|
|
319
|
+
the context fails instead of rendering blank.
|
|
320
|
+
|
|
321
|
+
```text
|
|
322
|
+
templates/mail/
|
|
323
|
+
shared/layout.html
|
|
324
|
+
images/logo.png
|
|
325
|
+
user_roles_changed/
|
|
326
|
+
subject.txt
|
|
327
|
+
body.html
|
|
328
|
+
body.txt
|
|
329
|
+
sample.json
|
|
330
|
+
```
|
|
317
331
|
|
|
318
332
|
`to`, `cc`, `bcc` and `reply_to` take a string, a `MailAddress` or a list of them.
|
|
319
333
|
Blind copies travel in the SMTP envelope only and never appear in the headers.
|
|
@@ -331,8 +345,8 @@ remote images are blocked. `compose` adds it on its own; a missing file or one t
|
|
|
331
345
|
is not an image fails like a missing variable. `MailAttachment(..., content_id=...)`
|
|
332
346
|
does the same for images the caller builds.
|
|
333
347
|
|
|
334
|
-
A template can carry a fourth file, `user_roles_changed.sample.json
|
|
335
|
-
example context. `MailTemplates.names()` lists the templates and
|
|
348
|
+
A template can carry a fourth file, `sample.json` (`user_roles_changed.sample.json`
|
|
349
|
+
when loose), with an example context. `MailTemplates.names()` lists the templates and
|
|
336
350
|
`sample_context(name)` reads that file, so one parametrized test covers them all.
|
|
337
351
|
The same sample drives the preview, which renders without sending:
|
|
338
352
|
|
|
@@ -523,7 +537,7 @@ poetry install --with dev --all-extras
|
|
|
523
537
|
poetry run pytest -q
|
|
524
538
|
```
|
|
525
539
|
|
|
526
|
-
Current baseline: **
|
|
540
|
+
Current baseline: **627 tests passing** (version 3.14.0). Async tests use
|
|
527
541
|
`pytest-asyncio` in strict mode, so each one carries `@pytest.mark.asyncio`.
|
|
528
542
|
|
|
529
543
|
`tests/test_capabilities.py` guards the lazy-import promise: it fails if merely
|
|
@@ -24,6 +24,10 @@ SUBJECT_SUFFIX = ".subject.txt"
|
|
|
24
24
|
HTML_SUFFIX = ".html"
|
|
25
25
|
TEXT_SUFFIX = ".txt"
|
|
26
26
|
SAMPLE_SUFFIX = ".sample.json"
|
|
27
|
+
FOLDER_SUBJECT = "subject.txt"
|
|
28
|
+
FOLDER_HTML = "body.html"
|
|
29
|
+
FOLDER_TEXT = "body.txt"
|
|
30
|
+
FOLDER_SAMPLE = "sample.json"
|
|
27
31
|
IMAGES_DIRECTORY = "images"
|
|
28
32
|
CID_SOURCE = re.compile(r"""\bsrc\s*=\s*["']cid:([^"']+)["']""", re.IGNORECASE)
|
|
29
33
|
|
|
@@ -36,12 +40,21 @@ class RenderedMail:
|
|
|
36
40
|
inline_images: tuple[MailAttachment, ...] = ()
|
|
37
41
|
|
|
38
42
|
|
|
43
|
+
@dataclass(frozen=True, kw_only=True)
|
|
44
|
+
class _TemplateFiles:
|
|
45
|
+
subject: str
|
|
46
|
+
html: str
|
|
47
|
+
text: str
|
|
48
|
+
sample: str
|
|
49
|
+
|
|
50
|
+
|
|
39
51
|
class MailTemplates:
|
|
40
52
|
"""Renders the templates of the application that sends; the kit ships none of its own.
|
|
41
53
|
|
|
42
|
-
A template `name` is
|
|
43
|
-
`
|
|
44
|
-
|
|
54
|
+
A template `name` is a folder `name/` with `subject.txt` (required) and `body.html` or
|
|
55
|
+
`body.txt` (at least one), or the same files loose in `directory` as `name.subject.txt`,
|
|
56
|
+
`name.html` and `name.txt`. HTML is autoescaped, subject and text are not. An optional
|
|
57
|
+
`sample.json` (`name.sample.json` when loose) holds an example context for previews and tests.
|
|
45
58
|
Every `src="cid:file.png"` in the HTML embeds `images/file.png` in the message.
|
|
46
59
|
"""
|
|
47
60
|
|
|
@@ -58,15 +71,14 @@ class MailTemplates:
|
|
|
58
71
|
|
|
59
72
|
def render(self, name: str, context: Mapping[str, Any] | None = None) -> RenderedMail:
|
|
60
73
|
values = dict(context or {})
|
|
61
|
-
|
|
74
|
+
files = self._files(name)
|
|
75
|
+
subject = self._render_file(files.subject, values)
|
|
62
76
|
if subject is None:
|
|
63
|
-
raise MailTemplateError(f"Template '{name}' has no {
|
|
64
|
-
html = self._render_file(
|
|
65
|
-
text = self._render_file(
|
|
77
|
+
raise MailTemplateError(f"Template '{name}' has no {files.subject}.")
|
|
78
|
+
html = self._render_file(files.html, values)
|
|
79
|
+
text = self._render_file(files.text, values)
|
|
66
80
|
if html is None and text is None:
|
|
67
|
-
raise MailTemplateError(
|
|
68
|
-
f"Template '{name}' needs {name}{HTML_SUFFIX} or {name}{TEXT_SUFFIX}."
|
|
69
|
-
)
|
|
81
|
+
raise MailTemplateError(f"Template '{name}' needs {files.html} or {files.text}.")
|
|
70
82
|
return RenderedMail(
|
|
71
83
|
subject=" ".join(subject.split()),
|
|
72
84
|
html=html,
|
|
@@ -76,23 +88,41 @@ class MailTemplates:
|
|
|
76
88
|
|
|
77
89
|
def names(self) -> list[str]:
|
|
78
90
|
"""Every template in the directory, identified by its subject file."""
|
|
79
|
-
|
|
91
|
+
loose = (
|
|
80
92
|
path.name.removesuffix(SUBJECT_SUFFIX)
|
|
81
93
|
for path in self._directory.glob(f"*{SUBJECT_SUFFIX}")
|
|
82
94
|
)
|
|
95
|
+
folders = (path.parent.name for path in self._directory.glob(f"*/{FOLDER_SUBJECT}"))
|
|
96
|
+
return sorted({*loose, *folders})
|
|
83
97
|
|
|
84
98
|
def sample_context(self, name: str) -> dict[str, Any]:
|
|
85
|
-
|
|
99
|
+
relative = self._files(name).sample
|
|
100
|
+
path = self._directory / relative
|
|
86
101
|
if not path.is_file():
|
|
87
|
-
raise MailTemplateError(f"Template '{name}' has no {
|
|
102
|
+
raise MailTemplateError(f"Template '{name}' has no {relative}.")
|
|
88
103
|
try:
|
|
89
104
|
sample = json.loads(path.read_text(encoding="utf-8"))
|
|
90
105
|
except json.JSONDecodeError as exc:
|
|
91
|
-
raise MailTemplateError(f"{
|
|
106
|
+
raise MailTemplateError(f"{relative} is not valid JSON: {exc}") from exc
|
|
92
107
|
if not isinstance(sample, dict):
|
|
93
|
-
raise MailTemplateError(f"{
|
|
108
|
+
raise MailTemplateError(f"{relative} must hold a JSON object.")
|
|
94
109
|
return sample
|
|
95
110
|
|
|
111
|
+
def _files(self, name: str) -> _TemplateFiles:
|
|
112
|
+
if (self._directory / name).is_dir():
|
|
113
|
+
return _TemplateFiles(
|
|
114
|
+
subject=f"{name}/{FOLDER_SUBJECT}",
|
|
115
|
+
html=f"{name}/{FOLDER_HTML}",
|
|
116
|
+
text=f"{name}/{FOLDER_TEXT}",
|
|
117
|
+
sample=f"{name}/{FOLDER_SAMPLE}",
|
|
118
|
+
)
|
|
119
|
+
return _TemplateFiles(
|
|
120
|
+
subject=f"{name}{SUBJECT_SUFFIX}",
|
|
121
|
+
html=f"{name}{HTML_SUFFIX}",
|
|
122
|
+
text=f"{name}{TEXT_SUFFIX}",
|
|
123
|
+
sample=f"{name}{SAMPLE_SUFFIX}",
|
|
124
|
+
)
|
|
125
|
+
|
|
96
126
|
def _inline_images(self, name: str, html: str | None) -> tuple[MailAttachment, ...]:
|
|
97
127
|
if html is None:
|
|
98
128
|
return ()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/api/encrypted_middleware.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/bootstrap/configuration.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/contracts/view_repository.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/memory_repo/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository/mongo_repo/__init__.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/repository_factory/__init__.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/__init__.py
RENAMED
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/restclient/auth/api_key.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/__init__.py
RENAMED
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/envelope.py
RENAMED
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/encrypted/errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/security/providers/__init__.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
|
{persistence_kit-3.13.0 → persistence_kit-3.14.0}/persistence_kit/settings/cache_settings.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
|