tigrcorn-config 0.3.16.dev12__tar.gz → 0.3.17.dev1__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.
Files changed (46) hide show
  1. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/PKG-INFO +2 -2
  2. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/pyproject.toml +2 -2
  3. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/governance_surface.py +3 -0
  4. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/__init__.py +16 -0
  5. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/helpers.py +28 -0
  6. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/listeners.py +122 -0
  7. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/mapping.py +60 -0
  8. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/namespace.py +215 -0
  9. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/public_api.py +280 -0
  10. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/public_helpers.py +148 -0
  11. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/load/sources.py +47 -0
  12. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/__init__.py +32 -0
  13. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/app.py +50 -0
  14. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/http.py +74 -0
  15. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/observability.py +26 -0
  16. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/scheduler.py +11 -0
  17. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/security.py +37 -0
  18. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/server.py +195 -0
  19. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/transports.py +120 -0
  20. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config/model/types.py +9 -0
  21. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/negative_surface.py +2 -2
  22. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/PKG-INFO +2 -2
  23. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/SOURCES.txt +18 -3
  24. tigrcorn_config-0.3.17.dev1/src/tigrcorn_config.egg-info/requires.txt +4 -0
  25. tigrcorn_config-0.3.16.dev12/src/tigrcorn_config/load.py +0 -666
  26. tigrcorn_config-0.3.16.dev12/src/tigrcorn_config/model.py +0 -471
  27. tigrcorn_config-0.3.16.dev12/src/tigrcorn_config.egg-info/requires.txt +0 -4
  28. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/LICENSE +0 -0
  29. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/README.md +0 -0
  30. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/setup.cfg +0 -0
  31. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/__init__.py +0 -0
  32. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/audit.py +0 -0
  33. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/defaults.py +0 -0
  34. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/env.py +0 -0
  35. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/files.py +0 -0
  36. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/merge.py +0 -0
  37. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/normalize.py +0 -0
  38. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/observability_surface.py +0 -0
  39. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/origin_surface.py +0 -0
  40. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/policy_surface.py +0 -0
  41. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/profiles.py +0 -0
  42. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/py.typed +0 -0
  43. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/quic_surface.py +0 -0
  44. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config/validate.py +0 -0
  45. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/dependency_links.txt +0 -0
  46. {tigrcorn_config-0.3.16.dev12 → tigrcorn_config-0.3.17.dev1}/src/tigrcorn_config.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrcorn-config
3
- Version: 0.3.16.dev12
3
+ Version: 0.3.17.dev1
4
4
  Summary: Typed configuration models, profiles, environment loading, and validation for the Tigrcorn ASGI/HTTP server stack.
5
5
  Author-email: Jacob Stewart <jacob@swarmauri.com>
6
6
  License: Apache License
@@ -186,7 +186,7 @@ Classifier: Typing :: Typed
186
186
  Requires-Python: <3.15,>=3.10
187
187
  Description-Content-Type: text/markdown
188
188
  License-File: LICENSE
189
- Requires-Dist: tigrcorn-core==0.3.16.dev12
189
+ Requires-Dist: tigrcorn-core==0.3.17.dev1
190
190
  Provides-Extra: yaml
191
191
  Requires-Dist: PyYAML>=6.0; extra == "yaml"
192
192
  Dynamic: license-file
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tigrcorn-config"
7
- version = "0.3.16.dev12"
7
+ version = "0.3.17.dev1"
8
8
  description = "Typed configuration models, profiles, environment loading, and validation for the Tigrcorn ASGI/HTTP server stack."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10,<3.15"
@@ -28,7 +28,7 @@ classifiers = [
28
28
  "Topic :: Software Development :: Libraries :: Python Modules",
29
29
  "Typing :: Typed",
30
30
  ]
31
- dependencies = ["tigrcorn-core==0.3.16.dev12"]
31
+ dependencies = ["tigrcorn-core==0.3.17.dev1"]
32
32
 
33
33
  [project.optional-dependencies]
34
34
  yaml = ["PyYAML>=6.0"]
@@ -223,8 +223,11 @@ APPROVED_LEGACY_UNITTEST_FILES = (
223
223
  'tests/test_stream_probe_fixtures.py',
224
224
  'tests/test_websocket_uix_demo.py',
225
225
  'tests/test_webtransport_bidi_stream_context.py',
226
+ 'tests/test_webtransport_feature_demo_app.py',
226
227
  'tests/test_webtransport_feature_coverage.py',
227
228
  'tests/test_webtransport_mtls_demo.py',
229
+ 'tests/test_webtransport_mtls_demo_cleanup.py',
230
+ 'tests/test_webtransport_mtls_demo_runtime.py',
228
231
  'tests/test_webtransport_operator_surface.py',
229
232
  'tests/test_aioquic_performance_matrix.py',
230
233
  'tests/test_ws_wss_probe_fixture.py',
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from .mapping import config_from_mapping, config_from_source, config_to_dict
4
+ from .namespace import namespace_to_overrides
5
+ from .public_api import build_config
6
+ from .sources import build_config_from_namespace, build_config_from_sources
7
+
8
+ __all__ = [
9
+ "build_config",
10
+ "build_config_from_namespace",
11
+ "build_config_from_sources",
12
+ "config_from_mapping",
13
+ "config_from_source",
14
+ "config_to_dict",
15
+ "namespace_to_overrides",
16
+ ]
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Mapping
4
+
5
+
6
+ def listify(value: Any) -> list[Any]:
7
+ if value is None:
8
+ return []
9
+ if isinstance(value, list):
10
+ result: list[Any] = []
11
+ for item in value:
12
+ if isinstance(item, str) and "," in item:
13
+ result.extend(part.strip() for part in item.split(",") if part.strip())
14
+ else:
15
+ result.append(item)
16
+ return result
17
+ if isinstance(value, str):
18
+ return [part.strip() for part in value.split(",") if part.strip()]
19
+ return [value]
20
+
21
+
22
+ def mapping_get(source: Mapping[str, Any], *path: str) -> Any:
23
+ cursor: Any = source
24
+ for segment in path:
25
+ if not isinstance(cursor, Mapping):
26
+ return None
27
+ cursor = cursor.get(segment)
28
+ return cursor
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from argparse import Namespace
4
+ from typing import Any
5
+
6
+ from tigrcorn_core.constants import DEFAULT_HOST, DEFAULT_PORT
7
+
8
+ from .helpers import listify
9
+
10
+
11
+ def parse_bind(value: str, *, kind: str) -> dict[str, Any]:
12
+ if value.startswith("fd://"):
13
+ return {"kind": kind, "fd": int(value.removeprefix("fd://"))}
14
+ if value.startswith("unix:"):
15
+ return {"kind": "unix", "path": value.split(":", 1)[1]}
16
+ if value.startswith("udp://"):
17
+ kind = "udp"
18
+ value = value.removeprefix("udp://")
19
+ elif value.startswith("tcp://"):
20
+ kind = "tcp"
21
+ value = value.removeprefix("tcp://")
22
+ elif value.startswith("quic://"):
23
+ kind = "udp"
24
+ value = value.removeprefix("quic://")
25
+ if value.startswith("[") and "]:" in value:
26
+ host, port = value.rsplit(":", 1)
27
+ host = host[1:-1]
28
+ elif ":" in value:
29
+ host, port = value.rsplit(":", 1)
30
+ else:
31
+ host, port = DEFAULT_HOST, value
32
+ return {"kind": kind, "host": host, "port": int(port)}
33
+
34
+
35
+ def listener_overrides_from_namespace(ns: Namespace) -> list[dict[str, Any]] | None:
36
+ listeners: list[dict[str, Any]] = []
37
+ for item in list(ns.bind or []):
38
+ listeners.append(parse_bind(item, kind="tcp"))
39
+ for item in list(ns.quic_bind or []):
40
+ listener = parse_bind(item, kind="udp")
41
+ listener["quic_bind"] = item
42
+ listeners.append(listener)
43
+ for item in list(ns.insecure_bind or []):
44
+ listener = parse_bind(item, kind="tcp")
45
+ listener["insecure_bind"] = item
46
+ listeners.append(listener)
47
+ for item in list(ns.fd or []):
48
+ listeners.append({"kind": ns.transport or "tcp", "fd": int(item)})
49
+ for item in list(ns.endpoint or []):
50
+ listeners.append({"kind": ns.transport or "tcp", "endpoint": item})
51
+
52
+ if not listeners:
53
+ if ns.uds:
54
+ kind = "pipe" if ns.transport == "pipe" else "unix"
55
+ listeners.append({"kind": kind, "path": ns.uds})
56
+ else:
57
+ listeners.append({"kind": ns.transport or "tcp", "host": ns.host or DEFAULT_HOST, "port": ns.port or DEFAULT_PORT})
58
+
59
+ for listener in listeners:
60
+ _apply_common_listener_fields(ns, listener)
61
+ return listeners
62
+
63
+
64
+ def _apply_common_listener_fields(ns: Namespace, listener: dict[str, Any]) -> None:
65
+ for attr in ("backlog", "reuse_port", "reuse_address"):
66
+ value = getattr(ns, attr, None)
67
+ if value is not None:
68
+ listener[attr] = value
69
+ if ns.pipe_mode is not None and listener.get("kind") == "pipe":
70
+ listener["pipe_mode"] = ns.pipe_mode
71
+ if ns.user is not None and listener.get("kind") == "unix":
72
+ listener["user"] = ns.user
73
+ if ns.group is not None and listener.get("kind") == "unix":
74
+ listener["group"] = ns.group
75
+ if ns.umask is not None and listener.get("kind") == "unix":
76
+ listener["umask"] = ns.umask
77
+ if ns.http_versions:
78
+ listener["http_versions"] = list(ns.http_versions)
79
+ if ns.protocols:
80
+ listener["protocols"] = list(ns.protocols)
81
+ if ns.disable_websocket is not None:
82
+ listener["websocket"] = not ns.disable_websocket
83
+ _apply_listener_tls_fields(ns, listener)
84
+ _apply_listener_quic_fields(ns, listener)
85
+
86
+
87
+ def _apply_listener_tls_fields(ns: Namespace, listener: dict[str, Any]) -> None:
88
+ if listener.get("insecure_bind"):
89
+ return
90
+ mapping = {
91
+ "ssl_certfile": "ssl_certfile",
92
+ "ssl_keyfile": "ssl_keyfile",
93
+ "ssl_keyfile_password": "ssl_keyfile_password",
94
+ "ssl_ca_certs": "ssl_ca_certs",
95
+ "ssl_require_client_cert": "ssl_require_client_cert",
96
+ "ssl_ocsp_mode": "ocsp_mode",
97
+ "ssl_ocsp_soft_fail": "ocsp_soft_fail",
98
+ "ssl_ocsp_cache_size": "ocsp_cache_size",
99
+ "ssl_ocsp_max_age": "ocsp_max_age",
100
+ "ssl_crl_mode": "crl_mode",
101
+ "ssl_crl": "ssl_crl",
102
+ }
103
+ for source, dest in mapping.items():
104
+ value = getattr(ns, source, None)
105
+ if value is not None:
106
+ listener[dest] = value
107
+ if ns.ssl_alpn:
108
+ listener["alpn_protocols"] = listify(ns.ssl_alpn)
109
+ if getattr(ns, "ssl_revocation_fetch", None) is not None:
110
+ value = ns.ssl_revocation_fetch
111
+ listener["revocation_fetch"] = value == "on" if isinstance(value, str) else bool(value)
112
+
113
+
114
+ def _apply_listener_quic_fields(ns: Namespace, listener: dict[str, Any]) -> None:
115
+ if listener.get("kind") != "udp":
116
+ return
117
+ if ns.quic_require_retry is not None:
118
+ listener["quic_require_retry"] = ns.quic_require_retry
119
+ if ns.quic_max_datagram_size is not None:
120
+ listener["max_datagram_size"] = ns.quic_max_datagram_size
121
+ if ns.quic_secret is not None:
122
+ listener["quic_secret"] = ns.quic_secret.encode("utf-8") if isinstance(ns.quic_secret, str) else ns.quic_secret
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ from pathlib import Path
5
+ from typing import Any, Mapping
6
+
7
+ from tigrcorn_config.defaults import default_config
8
+ from tigrcorn_config.files import load_config_source
9
+ from tigrcorn_config.merge import merge_config_dicts
10
+ from tigrcorn_config.model import ListenerConfig, ServerConfig
11
+ from tigrcorn_config.normalize import normalize_config
12
+ from tigrcorn_config.profiles import resolve_effective_profile_mapping, resolve_requested_profile
13
+ from tigrcorn_config.validate import validate_config
14
+
15
+
16
+ def _dataclass_to_dict(value: Any) -> Any:
17
+ if dataclasses.is_dataclass(value):
18
+ return {field.name: _dataclass_to_dict(getattr(value, field.name)) for field in dataclasses.fields(value)}
19
+ if isinstance(value, list):
20
+ return [_dataclass_to_dict(item) for item in value]
21
+ if isinstance(value, tuple):
22
+ return [_dataclass_to_dict(item) for item in value]
23
+ return value
24
+
25
+
26
+ def config_to_dict(config: ServerConfig) -> dict[str, Any]:
27
+ return _dataclass_to_dict(config)
28
+
29
+
30
+ def _apply_mapping(target: Any, data: Mapping[str, Any]) -> None:
31
+ for key, value in data.items():
32
+ if not hasattr(target, key):
33
+ continue
34
+ current = getattr(target, key)
35
+ if isinstance(current, list) and key == "listeners" and isinstance(value, list):
36
+ listeners: list[ListenerConfig] = []
37
+ for entry in value:
38
+ if isinstance(entry, Mapping):
39
+ listener = ListenerConfig()
40
+ _apply_mapping(listener, entry)
41
+ listeners.append(listener)
42
+ setattr(target, key, listeners)
43
+ elif dataclasses.is_dataclass(current) and isinstance(value, Mapping):
44
+ _apply_mapping(current, value)
45
+ else:
46
+ setattr(target, key, value)
47
+
48
+
49
+ def config_from_mapping(data: Mapping[str, Any]) -> ServerConfig:
50
+ profile_name = resolve_requested_profile(data)
51
+ effective_mapping = merge_config_dicts(resolve_effective_profile_mapping(profile_name), data)
52
+ config = default_config()
53
+ _apply_mapping(config, effective_mapping)
54
+ normalize_config(config)
55
+ validate_config(config)
56
+ return config
57
+
58
+
59
+ def config_from_source(source: str | Path | Mapping[str, Any] | Any | None) -> ServerConfig:
60
+ return config_from_mapping(load_config_source(source))
@@ -0,0 +1,215 @@
1
+ from __future__ import annotations
2
+
3
+ from argparse import Namespace
4
+ from typing import Any
5
+
6
+ from .helpers import listify
7
+ from .listeners import listener_overrides_from_namespace
8
+
9
+
10
+ def namespace_to_overrides(ns: Namespace) -> dict[str, Any]:
11
+ overrides: dict[str, Any] = {}
12
+ blocks = {
13
+ "app": {},
14
+ "process": {},
15
+ "tls": {},
16
+ "proxy": {},
17
+ "http": {},
18
+ "websocket": {},
19
+ "static": {},
20
+ "quic": {},
21
+ "webtransport": {},
22
+ "logging": {},
23
+ "metrics": {},
24
+ "scheduler": {},
25
+ }
26
+
27
+ _populate_app_block(ns, blocks["app"])
28
+ logging_explicit_fields = _populate_mapped_blocks(ns, blocks)
29
+ _populate_special_blocks(ns, blocks, logging_explicit_fields)
30
+
31
+ listeners = listener_overrides_from_namespace(ns)
32
+ if listeners:
33
+ overrides["listeners"] = listeners
34
+ if logging_explicit_fields:
35
+ blocks["logging"]["explicit_fields"] = sorted(set(logging_explicit_fields))
36
+
37
+ for name, block in blocks.items():
38
+ if block:
39
+ overrides[name] = block
40
+ return overrides
41
+
42
+
43
+ def _populate_app_block(ns: Namespace, app_block: dict[str, Any]) -> None:
44
+ if ns.app is not None:
45
+ app_block["target"] = ns.app
46
+ for key, dest in (
47
+ ("factory", "factory"),
48
+ ("app_interface", "interface"),
49
+ ("app_dir", "app_dir"),
50
+ ("lifespan", "lifespan"),
51
+ ("reload", "reload"),
52
+ ("config", "config_file"),
53
+ ("env_prefix", "env_prefix"),
54
+ ("env_file", "env_file"),
55
+ ):
56
+ value = getattr(ns, key, None)
57
+ if value is not None:
58
+ app_block[dest] = value
59
+ if ns.reload_dir:
60
+ app_block["reload_dirs"] = list(ns.reload_dir)
61
+ if ns.reload_include:
62
+ app_block["reload_include"] = list(ns.reload_include)
63
+ if ns.reload_exclude:
64
+ app_block["reload_exclude"] = list(ns.reload_exclude)
65
+
66
+
67
+ def _populate_mapped_blocks(ns: Namespace, blocks: dict[str, dict[str, Any]]) -> list[str]:
68
+ logging_explicit_fields: list[str] = []
69
+ for key, (block_name, dest) in _NAMESPACE_MAPPING.items():
70
+ value = getattr(ns, key, None)
71
+ if value is not None:
72
+ block = blocks[block_name]
73
+ block[dest] = value
74
+ if block_name == "logging" and dest in _LOGGING_EXPLICIT_DESTS:
75
+ logging_explicit_fields.append(dest)
76
+ return logging_explicit_fields
77
+
78
+
79
+ def _populate_special_blocks(
80
+ ns: Namespace,
81
+ blocks: dict[str, dict[str, Any]],
82
+ logging_explicit_fields: list[str],
83
+ ) -> None:
84
+ if ns.ssl_alpn:
85
+ blocks["tls"]["alpn_protocols"] = listify(ns.ssl_alpn)
86
+ if ns.log_config is not None:
87
+ logging_explicit_fields.append("log_config")
88
+ if getattr(ns, "ssl_revocation_fetch", None) is not None:
89
+ value = ns.ssl_revocation_fetch
90
+ blocks["tls"]["revocation_fetch"] = value == "on" if isinstance(value, str) else bool(value)
91
+ if ns.forwarded_allow_ips:
92
+ blocks["proxy"]["forwarded_allow_ips"] = listify(ns.forwarded_allow_ips)
93
+ if ns.server_header is not None:
94
+ blocks["proxy"]["server_header"] = ns.server_header
95
+ blocks["proxy"]["include_server_header"] = True
96
+ if ns.no_server_header:
97
+ blocks["proxy"]["include_server_header"] = False
98
+ blocks["proxy"]["server_header"] = ""
99
+ if ns.date_header is not None:
100
+ blocks["proxy"]["include_date_header"] = ns.date_header
101
+ if ns.headers:
102
+ blocks["proxy"]["default_headers"] = list(ns.headers)
103
+ if ns.server_name:
104
+ blocks["proxy"]["server_names"] = listify(ns.server_name)
105
+ if ns.http_versions:
106
+ blocks["http"]["http_versions"] = list(ns.http_versions)
107
+ if getattr(ns, "alt_svc", None):
108
+ blocks["http"]["alt_svc_headers"] = listify(ns.alt_svc)
109
+ if ns.content_codings:
110
+ blocks["http"]["content_codings"] = listify(ns.content_codings)
111
+ if getattr(ns, "connect_allow", None):
112
+ blocks["http"]["connect_allow"] = listify(ns.connect_allow)
113
+ if ns.disable_h2c is not None:
114
+ blocks["http"]["enable_h2c"] = not ns.disable_h2c
115
+ if ns.disable_websocket is not None:
116
+ blocks["websocket"]["enabled"] = not ns.disable_websocket
117
+ if ns.quic_secret is not None:
118
+ blocks["quic"]["quic_secret"] = ns.quic_secret.encode("utf-8") if isinstance(ns.quic_secret, str) else ns.quic_secret
119
+ if getattr(ns, "webtransport_origin", None):
120
+ blocks["webtransport"]["origins"] = listify(ns.webtransport_origin)
121
+
122
+
123
+ _LOGGING_EXPLICIT_DESTS = {
124
+ "level",
125
+ "access_log",
126
+ "access_log_file",
127
+ "access_log_format",
128
+ "error_log_file",
129
+ "structured",
130
+ "use_colors",
131
+ "log_config",
132
+ }
133
+
134
+ _NAMESPACE_MAPPING: dict[str, tuple[str, str]] = {
135
+ "workers": ("process", "workers"),
136
+ "worker_class": ("process", "worker_class"),
137
+ "runtime": ("process", "runtime"),
138
+ "pid": ("process", "pid_file"),
139
+ "worker_healthcheck_timeout": ("process", "worker_healthcheck_timeout"),
140
+ "limit_max_requests": ("process", "limit_max_requests"),
141
+ "max_requests_jitter": ("process", "max_requests_jitter"),
142
+ "ssl_certfile": ("tls", "certfile"),
143
+ "ssl_keyfile": ("tls", "keyfile"),
144
+ "ssl_keyfile_password": ("tls", "keyfile_password"),
145
+ "ssl_ca_certs": ("tls", "ca_certs"),
146
+ "ssl_require_client_cert": ("tls", "require_client_cert"),
147
+ "ssl_ciphers": ("tls", "ciphers"),
148
+ "ssl_ocsp_mode": ("tls", "ocsp_mode"),
149
+ "ssl_ocsp_soft_fail": ("tls", "ocsp_soft_fail"),
150
+ "ssl_ocsp_cache_size": ("tls", "ocsp_cache_size"),
151
+ "ssl_ocsp_max_age": ("tls", "ocsp_max_age"),
152
+ "ssl_crl_mode": ("tls", "crl_mode"),
153
+ "ssl_crl": ("tls", "crl"),
154
+ "proxy_headers": ("proxy", "proxy_headers"),
155
+ "root_path": ("proxy", "root_path"),
156
+ "timeout_keep_alive": ("http", "keep_alive_timeout"),
157
+ "read_timeout": ("http", "read_timeout"),
158
+ "write_timeout": ("http", "write_timeout"),
159
+ "timeout_graceful_shutdown": ("http", "shutdown_timeout"),
160
+ "idle_timeout": ("http", "idle_timeout"),
161
+ "max_body_size": ("http", "max_body_size"),
162
+ "max_header_size": ("http", "max_header_size"),
163
+ "http1_max_incomplete_event_size": ("http", "http1_max_incomplete_event_size"),
164
+ "http1_buffer_size": ("http", "http1_buffer_size"),
165
+ "http1_header_read_timeout": ("http", "http1_header_read_timeout"),
166
+ "http1_keep_alive": ("http", "http1_keep_alive"),
167
+ "http2_max_concurrent_streams": ("http", "http2_max_concurrent_streams"),
168
+ "http2_max_headers_size": ("http", "http2_max_headers_size"),
169
+ "http2_max_frame_size": ("http", "http2_max_frame_size"),
170
+ "http2_adaptive_window": ("http", "http2_adaptive_window"),
171
+ "http2_initial_connection_window_size": ("http", "http2_initial_connection_window_size"),
172
+ "http2_initial_stream_window_size": ("http", "http2_initial_stream_window_size"),
173
+ "http2_keep_alive_interval": ("http", "http2_keep_alive_interval"),
174
+ "http2_keep_alive_timeout": ("http", "http2_keep_alive_timeout"),
175
+ "connect_policy": ("http", "connect_policy"),
176
+ "trailer_policy": ("http", "trailer_policy"),
177
+ "content_coding_policy": ("http", "content_coding_policy"),
178
+ "alt_svc_auto": ("http", "alt_svc_auto"),
179
+ "alt_svc_ma": ("http", "alt_svc_max_age"),
180
+ "alt_svc_persist": ("http", "alt_svc_persist"),
181
+ "websocket_max_message_size": ("websocket", "max_message_size"),
182
+ "websocket_max_queue": ("websocket", "max_queue"),
183
+ "websocket_ping_interval": ("websocket", "ping_interval"),
184
+ "websocket_ping_timeout": ("websocket", "ping_timeout"),
185
+ "websocket_compression": ("websocket", "compression"),
186
+ "static_path_route": ("static", "route"),
187
+ "static_path_mount": ("static", "mount"),
188
+ "static_path_dir_to_file": ("static", "dir_to_file"),
189
+ "static_path_index_file": ("static", "index_file"),
190
+ "static_path_expires": ("static", "expires"),
191
+ "quic_require_retry": ("quic", "require_retry"),
192
+ "quic_max_datagram_size": ("quic", "max_datagram_size"),
193
+ "quic_idle_timeout": ("quic", "idle_timeout"),
194
+ "quic_early_data_policy": ("quic", "early_data_policy"),
195
+ "webtransport_max_sessions": ("webtransport", "max_sessions"),
196
+ "webtransport_max_streams": ("webtransport", "max_streams"),
197
+ "webtransport_max_datagram_size": ("webtransport", "max_datagram_size"),
198
+ "webtransport_path": ("webtransport", "path"),
199
+ "log_level": ("logging", "level"),
200
+ "access_log": ("logging", "access_log"),
201
+ "access_log_file": ("logging", "access_log_file"),
202
+ "access_log_format": ("logging", "access_log_format"),
203
+ "error_log_file": ("logging", "error_log_file"),
204
+ "log_config": ("logging", "log_config"),
205
+ "structured_log": ("logging", "structured"),
206
+ "use_colors": ("logging", "use_colors"),
207
+ "metrics": ("metrics", "enabled"),
208
+ "metrics_bind": ("metrics", "bind"),
209
+ "statsd_host": ("metrics", "statsd_host"),
210
+ "otel_endpoint": ("metrics", "otel_endpoint"),
211
+ "limit_concurrency": ("scheduler", "limit_concurrency"),
212
+ "max_connections": ("scheduler", "max_connections"),
213
+ "max_tasks": ("scheduler", "max_tasks"),
214
+ "max_streams": ("scheduler", "max_streams"),
215
+ }