kstlib 2.3.0__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.3.0/src/kstlib.egg-info → kstlib-2.3.1}/PKG-INFO +1 -1
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/config/loader.py +71 -3
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/limits.py +63 -45
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/meta.py +1 -1
- {kstlib-2.3.0 → kstlib-2.3.1/src/kstlib.egg-info}/PKG-INFO +1 -1
- {kstlib-2.3.0 → kstlib-2.3.1}/LICENSE.md +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/MANIFEST.in +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/README.md +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/pyproject.toml +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/setup.cfg +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/__main__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/channels/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/channels/base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/channels/email.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/channels/slack.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/manager.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/alerts/throttle.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/callback.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/check.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/config.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/errors.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/providers/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/providers/base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/providers/oauth2.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/providers/oidc.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/session.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/auth/token.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cache/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cache/decorator.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cache/strategies.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/app.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/check.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/common.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/login.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/logout.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/providers.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/status.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/token.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/auth/whoami.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/config.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/attach.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/common.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/list_sessions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/logs.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/start.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/status.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/ops/stop.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/call.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/list.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/rapi/show.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/common.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/decrypt.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/doctor.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/encrypt.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/commands/secrets/shred.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/cli/common.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/config/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/config/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/config/export.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/config/sops.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/aiosqlcipher.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/cipher.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/database.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/db/pool.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/helpers/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/helpers/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/helpers/time_trigger.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/kstlib.conf.yml +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/logging/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/logging/manager.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/builder.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/filesystem.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transport.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transports/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transports/gmail.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transports/resend.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transports/ses.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/mail/transports/smtp.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/metrics/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/metrics/decorators.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/metrics/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/_styles.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/cell.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/config.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/delivery.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/image.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/kv.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/list.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/metric.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/monitoring.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/renderer.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/service.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/table.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/monitoring/types.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/container.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/manager.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/tmux.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ops/validators.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/runner.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/steps/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/steps/_base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/steps/callable.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/steps/python.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/steps/shell.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/pipeline/validators.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/py.typed +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/rapi/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/rapi/client.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/rapi/config.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/rapi/credentials.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/rapi/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/circuit_breaker.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/heartbeat.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/rate_limiter.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/shutdown.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/resilience/watchdog.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/base.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/environment.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/keyring.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/kms.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/kwargs.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/providers/sops.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/resolver.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secrets/sensitive.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secure/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secure/fs.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/secure/permissions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ssl.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/chain.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/config.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/primitives.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/transform/validators.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ui/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ui/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ui/panels.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ui/spinner.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/ui/tables.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/dict.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/formatting.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/http_trace.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/lazy.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/secure_delete.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/serialization.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/text.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/utils/validators.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/websocket/__init__.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/websocket/exceptions.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/websocket/manager.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib/websocket/models.py +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib.egg-info/SOURCES.txt +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib.egg-info/dependency_links.txt +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib.egg-info/entry_points.txt +0 -0
- {kstlib-2.3.0 → kstlib-2.3.1}/src/kstlib.egg-info/requires.txt +0 -0
- {kstlib-2.3.0 → 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.
|
|
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>
|
|
@@ -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()
|
|
@@ -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:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kstlib
|
|
3
|
-
Version: 2.3.
|
|
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>
|
|
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
|
|
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
|
|
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
|
|
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
|