kstlib 2.2.1__tar.gz → 2.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kstlib-2.2.1/src/kstlib.egg-info → kstlib-2.3.1}/PKG-INFO +1 -1
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/__init__.py +5 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/config/__init__.py +2 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/config/loader.py +113 -3
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/kstlib.conf.yml +23 -1
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/limits.py +63 -45
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/builder.py +362 -6
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/meta.py +1 -1
- {kstlib-2.2.1 → kstlib-2.3.1/src/kstlib.egg-info}/PKG-INFO +1 -1
- {kstlib-2.2.1 → kstlib-2.3.1}/LICENSE.md +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/MANIFEST.in +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/README.md +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/pyproject.toml +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/setup.cfg +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/__main__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/channels/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/channels/base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/channels/email.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/channels/slack.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/manager.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/alerts/throttle.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/callback.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/check.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/config.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/errors.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/providers/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/providers/base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/providers/oauth2.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/providers/oidc.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/session.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/auth/token.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cache/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cache/decorator.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cache/strategies.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/app.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/check.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/common.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/login.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/logout.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/providers.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/status.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/token.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/whoami.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/config.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/attach.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/common.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/logs.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/start.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/status.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/stop.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/call.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/list.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/show.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/common.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/shred.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/cli/common.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/config/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/config/export.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/config/sops.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/aiosqlcipher.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/cipher.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/database.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/db/pool.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/helpers/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/helpers/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/helpers/time_trigger.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/logging/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/logging/manager.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/filesystem.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transport.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transports/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transports/gmail.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transports/resend.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transports/ses.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/mail/transports/smtp.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/metrics/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/metrics/decorators.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/metrics/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/_styles.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/cell.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/config.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/delivery.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/image.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/kv.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/list.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/metric.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/monitoring.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/renderer.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/service.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/table.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/monitoring/types.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/container.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/manager.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/tmux.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ops/validators.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/runner.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/steps/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/steps/_base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/steps/callable.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/steps/python.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/steps/shell.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/pipeline/validators.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/py.typed +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/rapi/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/rapi/client.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/rapi/config.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/rapi/credentials.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/rapi/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/circuit_breaker.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/heartbeat.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/rate_limiter.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/shutdown.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/resilience/watchdog.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/base.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/environment.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/keyring.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/kms.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/kwargs.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/providers/sops.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/resolver.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secrets/sensitive.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secure/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secure/fs.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/secure/permissions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ssl.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/chain.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/config.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/primitives.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/transform/validators.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ui/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ui/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ui/panels.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ui/spinner.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/ui/tables.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/dict.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/formatting.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/http_trace.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/lazy.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/secure_delete.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/serialization.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/text.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/utils/validators.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/websocket/__init__.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/websocket/exceptions.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/websocket/manager.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib/websocket/models.py +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib.egg-info/SOURCES.txt +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib.egg-info/dependency_links.txt +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib.egg-info/entry_points.txt +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib.egg-info/requires.txt +0 -0
- {kstlib-2.2.1 → kstlib-2.3.1}/src/kstlib.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kstlib
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Config-driven helpers for Python projects (dynamic config, secure secrets, preset logging, and more…)
|
|
5
5
|
Author-email: Michel TRUONG <michel.truong@gmail.com>
|
|
6
6
|
Maintainer-email: Michel TRUONG <michel.truong@gmail.com>
|
|
@@ -69,6 +69,7 @@ __all__ = [
|
|
|
69
69
|
"ops",
|
|
70
70
|
"pipeline",
|
|
71
71
|
"rapi",
|
|
72
|
+
"reload_config",
|
|
72
73
|
"require_config",
|
|
73
74
|
"resilience",
|
|
74
75
|
"secrets",
|
|
@@ -117,6 +118,7 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
|
117
118
|
"load_config": ("kstlib.config.loader", "load_config"),
|
|
118
119
|
"load_from_env": ("kstlib.config.loader", "load_from_env"),
|
|
119
120
|
"load_from_file": ("kstlib.config.loader", "load_from_file"),
|
|
121
|
+
"reload_config": ("kstlib.config.loader", "reload_config"),
|
|
120
122
|
"require_config": ("kstlib.config.loader", "require_config"),
|
|
121
123
|
# Logging
|
|
122
124
|
"LogManager": ("kstlib.logging", "LogManager"),
|
|
@@ -250,6 +252,9 @@ if TYPE_CHECKING:
|
|
|
250
252
|
from kstlib.config.loader import (
|
|
251
253
|
load_from_file as load_from_file,
|
|
252
254
|
)
|
|
255
|
+
from kstlib.config.loader import (
|
|
256
|
+
reload_config as reload_config,
|
|
257
|
+
)
|
|
253
258
|
from kstlib.config.loader import (
|
|
254
259
|
require_config as require_config,
|
|
255
260
|
)
|
|
@@ -34,6 +34,7 @@ from kstlib.config.loader import (
|
|
|
34
34
|
load_config,
|
|
35
35
|
load_from_env,
|
|
36
36
|
load_from_file,
|
|
37
|
+
reload_config,
|
|
37
38
|
require_config,
|
|
38
39
|
)
|
|
39
40
|
from kstlib.config.sops import (
|
|
@@ -71,6 +72,7 @@ __all__ = [
|
|
|
71
72
|
"load_config",
|
|
72
73
|
"load_from_env",
|
|
73
74
|
"load_from_file",
|
|
75
|
+
"reload_config",
|
|
74
76
|
"require_config",
|
|
75
77
|
"reset_decryptor",
|
|
76
78
|
]
|
|
@@ -318,6 +318,76 @@ def _load_any_config_file(
|
|
|
318
318
|
return data
|
|
319
319
|
|
|
320
320
|
|
|
321
|
+
def _normalize_includes(raw: Any, source_path: pathlib.Path) -> list[str]:
|
|
322
|
+
"""Normalize the raw ``include`` YAML value to a clean list of paths.
|
|
323
|
+
|
|
324
|
+
Handles the common footgun where an author leaves ``include:`` with an
|
|
325
|
+
empty body (or comments out every entry) and gets a cryptic
|
|
326
|
+
``TypeError: 'NoneType' object is not iterable`` at iteration time.
|
|
327
|
+
Normalization rules:
|
|
328
|
+
|
|
329
|
+
1. Missing key, ``None``, ``[]``, empty string, or whitespace-only
|
|
330
|
+
string produces an empty list silently. Commenting out a key is a
|
|
331
|
+
legitimate editing pattern, not an error worth logging.
|
|
332
|
+
2. A non-empty string is wrapped as a single-element list after
|
|
333
|
+
``strip()``.
|
|
334
|
+
3. A list whose individual items are ``None``, empty, or whitespace-only
|
|
335
|
+
has those entries dropped with a single ``WARNING`` log message
|
|
336
|
+
naming the source file, the drop count, and the original indices.
|
|
337
|
+
4. Any other top-level type (``int``, ``dict``, ...) or any non-string
|
|
338
|
+
list item raises :exc:`ConfigFormatError`, consistent with the rest
|
|
339
|
+
of this loader.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
raw: Raw value of the ``include`` key popped from the YAML mapping.
|
|
343
|
+
source_path: Path of the YAML file that declared the include, used
|
|
344
|
+
in log and error messages for actionable feedback.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Cleaned list of include path strings. Never ``None`` and never
|
|
348
|
+
containing ``None`` or empty entries.
|
|
349
|
+
|
|
350
|
+
Raises:
|
|
351
|
+
ConfigFormatError: If ``raw`` is not ``None``, ``str``, or ``list``,
|
|
352
|
+
or if any list element is not a ``str``.
|
|
353
|
+
|
|
354
|
+
"""
|
|
355
|
+
if raw is None:
|
|
356
|
+
return []
|
|
357
|
+
if isinstance(raw, str):
|
|
358
|
+
stripped = raw.strip()
|
|
359
|
+
return [stripped] if stripped else []
|
|
360
|
+
if not isinstance(raw, list):
|
|
361
|
+
raise ConfigFormatError(f"include must be string or list, got {type(raw).__name__} (file: {source_path})")
|
|
362
|
+
|
|
363
|
+
cleaned: list[str] = []
|
|
364
|
+
dropped: list[int] = []
|
|
365
|
+
for idx, item in enumerate(raw):
|
|
366
|
+
if item is None:
|
|
367
|
+
dropped.append(idx)
|
|
368
|
+
continue
|
|
369
|
+
if not isinstance(item, str):
|
|
370
|
+
raise ConfigFormatError(f"include[{idx}] must be string, got {type(item).__name__} (file: {source_path})")
|
|
371
|
+
stripped = item.strip()
|
|
372
|
+
if not stripped:
|
|
373
|
+
dropped.append(idx)
|
|
374
|
+
continue
|
|
375
|
+
cleaned.append(stripped)
|
|
376
|
+
|
|
377
|
+
if dropped:
|
|
378
|
+
# Lazy import: keep loader import graph shallow (see kstlib.limits
|
|
379
|
+
# circular import fix).
|
|
380
|
+
import logging
|
|
381
|
+
|
|
382
|
+
logging.getLogger(__name__).warning(
|
|
383
|
+
"include in %s: dropped %d empty entries at indices %s",
|
|
384
|
+
source_path,
|
|
385
|
+
len(dropped),
|
|
386
|
+
dropped,
|
|
387
|
+
)
|
|
388
|
+
return cleaned
|
|
389
|
+
|
|
390
|
+
|
|
321
391
|
def _load_with_includes(
|
|
322
392
|
path: pathlib.Path,
|
|
323
393
|
loaded_paths: set[pathlib.Path] | None = None,
|
|
@@ -365,9 +435,7 @@ def _load_with_includes(
|
|
|
365
435
|
loaded_paths.add(path)
|
|
366
436
|
|
|
367
437
|
data = _load_any_config_file(path, encoding, sops_decrypt=sops_decrypt)
|
|
368
|
-
includes = data.pop("include", [])
|
|
369
|
-
if isinstance(includes, str):
|
|
370
|
-
includes = [includes]
|
|
438
|
+
includes = _normalize_includes(data.pop("include", []), path)
|
|
371
439
|
|
|
372
440
|
# Get real extension (handles .sops.yml -> .yml)
|
|
373
441
|
parent_ext = get_real_extension(path) if is_sops_file(path) else path.suffix.lower()
|
|
@@ -1064,6 +1132,47 @@ def clear_config() -> None:
|
|
|
1064
1132
|
_default_loader.cache = None
|
|
1065
1133
|
|
|
1066
1134
|
|
|
1135
|
+
def reload_config(filename: str = CONFIG_FILENAME) -> Box:
|
|
1136
|
+
"""Force-reload the singleton configuration from disk.
|
|
1137
|
+
|
|
1138
|
+
Equivalent to ``clear_config()`` followed by ``get_config()``, but explicit
|
|
1139
|
+
and discoverable in a single import. Designed for interactive sessions
|
|
1140
|
+
(Jupyter, REPL) where the underlying YAML files have been edited and the
|
|
1141
|
+
cached config needs to be refreshed without restarting the kernel.
|
|
1142
|
+
|
|
1143
|
+
Args:
|
|
1144
|
+
filename: Config filename to search for. Defaults to
|
|
1145
|
+
``kstlib.conf.yml``.
|
|
1146
|
+
|
|
1147
|
+
Returns:
|
|
1148
|
+
Freshly loaded ``Box`` configuration object. The singleton cache is
|
|
1149
|
+
updated in-place, so subsequent ``get_config()`` calls return the same
|
|
1150
|
+
fresh object.
|
|
1151
|
+
|
|
1152
|
+
Raises:
|
|
1153
|
+
ConfigFileNotFoundError: If no configuration file is found in any
|
|
1154
|
+
search location.
|
|
1155
|
+
|
|
1156
|
+
Note:
|
|
1157
|
+
When to use which:
|
|
1158
|
+
|
|
1159
|
+
- ``reload_config()``: explicit one-shot refresh (Jupyter/REPL).
|
|
1160
|
+
- ``get_config(force_reload=True)``: same effect, but the intent is
|
|
1161
|
+
hidden in a kwarg.
|
|
1162
|
+
- ``clear_config()``: only flushes the cache; the next
|
|
1163
|
+
``get_config()`` call triggers the actual reload. Useful in tests
|
|
1164
|
+
that want to isolate the cache boundary.
|
|
1165
|
+
|
|
1166
|
+
Examples:
|
|
1167
|
+
>>> from kstlib.config import reload_config
|
|
1168
|
+
>>> cfg = reload_config() # doctest: +SKIP
|
|
1169
|
+
>>> cfg.mail.default # doctest: +SKIP
|
|
1170
|
+
'corporate'
|
|
1171
|
+
|
|
1172
|
+
"""
|
|
1173
|
+
return get_config(filename=filename, force_reload=True)
|
|
1174
|
+
|
|
1175
|
+
|
|
1067
1176
|
__all__ = [
|
|
1068
1177
|
"AutoDiscoveryConfig",
|
|
1069
1178
|
"ConfigLoader",
|
|
@@ -1072,5 +1181,6 @@ __all__ = [
|
|
|
1072
1181
|
"load_config",
|
|
1073
1182
|
"load_from_env",
|
|
1074
1183
|
"load_from_file",
|
|
1184
|
+
"reload_config",
|
|
1075
1185
|
"require_config",
|
|
1076
1186
|
]
|
|
@@ -358,6 +358,21 @@ mail:
|
|
|
358
358
|
# Leave null to force callers to pass transport= or preset= explicitly.
|
|
359
359
|
default: null
|
|
360
360
|
|
|
361
|
+
# SSL/TLS configuration for mail transports.
|
|
362
|
+
#
|
|
363
|
+
# Values here override the root ``ssl:`` section (bottom of this file)
|
|
364
|
+
# for mail only, and are themselves overridden by ``ssl_verify`` and
|
|
365
|
+
# ``ssl_ca_bundle`` keys set inside an individual preset. Each key
|
|
366
|
+
# cascades independently: you can set ``verify: false`` here and still
|
|
367
|
+
# provide ``ssl_ca_bundle`` at the preset level.
|
|
368
|
+
#
|
|
369
|
+
# Security: setting ``verify: false`` at any level emits a WARNING log
|
|
370
|
+
# at transport build time. Prefer ``ca_bundle: /path/to/private-ca.pem``
|
|
371
|
+
# for internal PKI rather than disabling verification outright.
|
|
372
|
+
ssl:
|
|
373
|
+
verify: true
|
|
374
|
+
ca_bundle: null
|
|
375
|
+
|
|
361
376
|
# Named transport presets. Each preset declares a "transport" field
|
|
362
377
|
# (smtp or resend) and backend-specific parameters. Define your own
|
|
363
378
|
# presets here and reference them via MailBuilder(preset="name").
|
|
@@ -368,11 +383,18 @@ mail:
|
|
|
368
383
|
# transport: smtp
|
|
369
384
|
# host: smtp-secure.corp.local
|
|
370
385
|
# port: 25
|
|
371
|
-
# login:
|
|
386
|
+
# login: svc_mail
|
|
372
387
|
# password: "secret"
|
|
373
388
|
# starttls: false
|
|
374
389
|
# ssl: false
|
|
375
390
|
# timeout: 30
|
|
391
|
+
# # Optional SSL overrides for this preset (highest priority in the cascade):
|
|
392
|
+
# ssl_verify: false
|
|
393
|
+
# ssl_ca_bundle: /etc/ssl/certs/corp-ca.pem
|
|
394
|
+
# # Optional envelope defaults (sender / reply_to only, never to/cc/bcc):
|
|
395
|
+
# defaults:
|
|
396
|
+
# sender: "Service Notifications <notify@corp.local>"
|
|
397
|
+
# reply_to: "Service Notifications <notify@corp.local>"
|
|
376
398
|
#
|
|
377
399
|
# transactional:
|
|
378
400
|
# transport: resend
|
|
@@ -16,52 +16,26 @@ Example:
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
from collections.abc import Mapping
|
|
20
|
-
from dataclasses import dataclass
|
|
21
|
-
from typing import Any
|
|
22
|
-
|
|
23
|
-
from kstlib.utils.formatting import format_bytes, parse_size_string
|
|
24
|
-
|
|
25
|
-
__all__ = [
|
|
26
|
-
"HARD_MAX_CONFIG_FILE_SIZE",
|
|
27
|
-
"HARD_MAX_DATETIME_FORMAT_LENGTH",
|
|
28
|
-
"HARD_MAX_DISPLAY_VALUE_LENGTH",
|
|
29
|
-
"HARD_MAX_ENDPOINT_REF_LENGTH",
|
|
30
|
-
"HARD_MAX_EPOCH_TIMESTAMP",
|
|
31
|
-
"HARD_MAX_PIPELINE_STEPS",
|
|
32
|
-
"HARD_MAX_PIPELINE_TIMEOUT",
|
|
33
|
-
"HARD_MAX_RAPI_UPLOAD_SIZE",
|
|
34
|
-
"HARD_MAX_STEP_ARGS",
|
|
35
|
-
"HARD_MAX_TIMEZONE_LENGTH",
|
|
36
|
-
"HARD_MIN_EPOCH_TIMESTAMP",
|
|
37
|
-
"HARD_MIN_PIPELINE_TIMEOUT",
|
|
38
|
-
"AlertsLimits",
|
|
39
|
-
"CacheLimits",
|
|
40
|
-
"DatabaseLimits",
|
|
41
|
-
"MailLimits",
|
|
42
|
-
"PipelineLimits",
|
|
43
|
-
"RapiLimits",
|
|
44
|
-
"RapiRenderConfig",
|
|
45
|
-
"ResilienceLimits",
|
|
46
|
-
"SopsLimits",
|
|
47
|
-
"WebSocketLimits",
|
|
48
|
-
"clamp_with_limits",
|
|
49
|
-
"get_alerts_limits",
|
|
50
|
-
"get_cache_limits",
|
|
51
|
-
"get_db_limits",
|
|
52
|
-
"get_mail_limits",
|
|
53
|
-
"get_pipeline_limits",
|
|
54
|
-
"get_rapi_limits",
|
|
55
|
-
"get_rapi_render_config",
|
|
56
|
-
"get_resilience_limits",
|
|
57
|
-
"get_sops_limits",
|
|
58
|
-
"get_websocket_limits",
|
|
59
|
-
]
|
|
60
|
-
|
|
61
19
|
# =============================================================================
|
|
62
|
-
#
|
|
20
|
+
# Pre-import constants (hard limits + defaults)
|
|
21
|
+
#
|
|
22
|
+
# Defined BEFORE any cross-module import to break a latent circular import.
|
|
23
|
+
# During `kstlib.limits` initialisation, importing `kstlib.utils.formatting`
|
|
24
|
+
# transitively triggers `kstlib.config.__init__`, which re-enters
|
|
25
|
+
# `kstlib.limits` from two points:
|
|
26
|
+
# - `kstlib.config.loader` -> reads HARD_MAX_CONFIG_FILE_SIZE
|
|
27
|
+
# - `kstlib.config.sops` -> reads DEFAULT_MAX_SOPS_CACHE_ENTRIES and
|
|
28
|
+
# HARD_MAX_SOPS_CACHE_ENTRIES
|
|
29
|
+
# Both the HARD_* block below and the DEFAULT_* block that follows must
|
|
30
|
+
# therefore be defined before the cross-module import. If these constants
|
|
31
|
+
# sat at their usual position (after imports), Python would raise
|
|
32
|
+
# ImportError on the cycle re-entry into a still-initialising kstlib.limits.
|
|
33
|
+
#
|
|
34
|
+
# See: fix-circular-import-mail request (2026-04-24).
|
|
63
35
|
# =============================================================================
|
|
64
36
|
|
|
37
|
+
# Hard limits (deep defense - cannot be exceeded via config)
|
|
38
|
+
|
|
65
39
|
#: Absolute maximum attachment size (25 MiB) - protects against memory exhaustion.
|
|
66
40
|
HARD_MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024
|
|
67
41
|
|
|
@@ -240,9 +214,7 @@ HARD_MIN_EPOCH_TIMESTAMP = 0
|
|
|
240
214
|
#: Maximum valid epoch timestamp (year 2100) - protects against overflow.
|
|
241
215
|
HARD_MAX_EPOCH_TIMESTAMP = 4102444800
|
|
242
216
|
|
|
243
|
-
# =============================================================================
|
|
244
217
|
# Default limits (used when config is not available)
|
|
245
|
-
# =============================================================================
|
|
246
218
|
|
|
247
219
|
DEFAULT_MAX_ATTACHMENT_SIZE = 25 * 1024 * 1024 # 25 MiB
|
|
248
220
|
DEFAULT_MAX_ATTACHMENTS = 20
|
|
@@ -291,6 +263,52 @@ DEFAULT_WS_DISCONNECT_ALERT_INTERVAL = 300.0 # seconds between throttled alerts
|
|
|
291
263
|
DEFAULT_PIPELINE_TIMEOUT = 300.0 # seconds (5 minutes)
|
|
292
264
|
DEFAULT_PIPELINE_ON_ERROR = "fail_fast"
|
|
293
265
|
|
|
266
|
+
# =============================================================================
|
|
267
|
+
# Cross-module imports (intentionally after pre-import constants, see note above)
|
|
268
|
+
# =============================================================================
|
|
269
|
+
|
|
270
|
+
from collections.abc import Mapping # noqa: E402
|
|
271
|
+
from dataclasses import dataclass # noqa: E402
|
|
272
|
+
from typing import Any # noqa: E402
|
|
273
|
+
|
|
274
|
+
from kstlib.utils.formatting import format_bytes, parse_size_string # noqa: E402
|
|
275
|
+
|
|
276
|
+
__all__ = [
|
|
277
|
+
"HARD_MAX_CONFIG_FILE_SIZE",
|
|
278
|
+
"HARD_MAX_DATETIME_FORMAT_LENGTH",
|
|
279
|
+
"HARD_MAX_DISPLAY_VALUE_LENGTH",
|
|
280
|
+
"HARD_MAX_ENDPOINT_REF_LENGTH",
|
|
281
|
+
"HARD_MAX_EPOCH_TIMESTAMP",
|
|
282
|
+
"HARD_MAX_PIPELINE_STEPS",
|
|
283
|
+
"HARD_MAX_PIPELINE_TIMEOUT",
|
|
284
|
+
"HARD_MAX_RAPI_UPLOAD_SIZE",
|
|
285
|
+
"HARD_MAX_STEP_ARGS",
|
|
286
|
+
"HARD_MAX_TIMEZONE_LENGTH",
|
|
287
|
+
"HARD_MIN_EPOCH_TIMESTAMP",
|
|
288
|
+
"HARD_MIN_PIPELINE_TIMEOUT",
|
|
289
|
+
"AlertsLimits",
|
|
290
|
+
"CacheLimits",
|
|
291
|
+
"DatabaseLimits",
|
|
292
|
+
"MailLimits",
|
|
293
|
+
"PipelineLimits",
|
|
294
|
+
"RapiLimits",
|
|
295
|
+
"RapiRenderConfig",
|
|
296
|
+
"ResilienceLimits",
|
|
297
|
+
"SopsLimits",
|
|
298
|
+
"WebSocketLimits",
|
|
299
|
+
"clamp_with_limits",
|
|
300
|
+
"get_alerts_limits",
|
|
301
|
+
"get_cache_limits",
|
|
302
|
+
"get_db_limits",
|
|
303
|
+
"get_mail_limits",
|
|
304
|
+
"get_pipeline_limits",
|
|
305
|
+
"get_rapi_limits",
|
|
306
|
+
"get_rapi_render_config",
|
|
307
|
+
"get_resilience_limits",
|
|
308
|
+
"get_sops_limits",
|
|
309
|
+
"get_websocket_limits",
|
|
310
|
+
]
|
|
311
|
+
|
|
294
312
|
|
|
295
313
|
@dataclass(frozen=True, slots=True)
|
|
296
314
|
class MailLimits:
|