csrd-versioning 0.3.32__tar.gz → 0.3.34__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.
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/PKG-INFO +1 -1
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/pyproject.toml +1 -1
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_config.py +3 -1
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_orchestration.py +49 -4
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/__init__.py +12 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/_discovery.py +58 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/_registry.py +92 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/_types.py +49 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/hosts/__init__.py +6 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/hosts/_actuator.py +57 -0
- csrd_versioning-0.3.34/src/csrd/versioning/extensions/hosts/_swagger.py +45 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/.gitignore +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/README.md +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_constants.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_core.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_dependencies.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_dependency_wiring.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_dispatch.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_docs.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_fastapi_types.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_helpers.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_redoc.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_settings.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_swagger_ui_version.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_types.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/README.md +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/actuator.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/base.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/env/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/env/plugin.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/env/providers.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/env/registry.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/health/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/health/auto.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/health/indicators.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/health/plugin.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/info.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/tools/README.md +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/tools/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/tools/generate_service_info_from_git.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/exception_handlers.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/py.typed +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/_base.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/file_upload/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/file_upload/_body_factory.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/file_upload/_schema_patcher.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/file_upload/file_upload_plugin.css +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/file_upload/file_upload_plugin.js +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/__init__.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/favicon.png +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.css +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.html +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.js +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_actuator.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_core.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_dependency_wiring.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_dispatch.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_docs.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_exception_handlers.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_helpers.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_orchestration.py +0 -0
- {csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/tests/test_settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csrd-versioning
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.34
|
|
4
4
|
Summary: API versioning, dispatch, docs, and actuator for FastAPI
|
|
5
5
|
Project-URL: Repository, https://github.com/csrd-api/fastapi-common
|
|
6
6
|
Project-URL: Documentation, https://github.com/csrd-api/fastapi-common/tree/main/packages/versioning
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
1
|
+
from collections.abc import Callable, Sequence
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
@@ -12,6 +12,7 @@ from ._fastapi_types import (
|
|
|
12
12
|
)
|
|
13
13
|
from ._types import VersionedAppState, VersionKey
|
|
14
14
|
from .actuator.plugins import ActuatorPlugin
|
|
15
|
+
from .extensions import HostPlugin
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@dataclass(slots=True)
|
|
@@ -34,6 +35,7 @@ class VersionedApiConfig:
|
|
|
34
35
|
include_actuator_endpoints: bool = True
|
|
35
36
|
actuator_plugins: list[ActuatorPlugin] = field(default_factory=list)
|
|
36
37
|
swagger_plugins: list[Any] = field(default_factory=list)
|
|
38
|
+
extensions: Sequence[HostPlugin] = field(default_factory=list)
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
@dataclass(slots=True)
|
|
@@ -45,6 +45,42 @@ logger = logging.getLogger(__name__)
|
|
|
45
45
|
_VERSIONING_CONFIGURED_KEY = "_versioning_configured"
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
def _resolve_extensions(
|
|
49
|
+
config: VersionedApiConfig,
|
|
50
|
+
) -> tuple[Any, ...] | tuple[tuple[Any, ...], tuple[Any, ...]]:
|
|
51
|
+
"""Resolve inner-layer plugins for built-in hosts (actuator, swagger_ui).
|
|
52
|
+
|
|
53
|
+
Plugin Architecture:
|
|
54
|
+
- Layer 1 (Outer): Host plugins (actuator, swagger_ui, custom)
|
|
55
|
+
- Layer 2 (Inner): Plugins managed by each host
|
|
56
|
+
|
|
57
|
+
This function resolves ONLY the inner-layer for built-in hosts.
|
|
58
|
+
Custom outer-layer hosts manage their own inner registries internally.
|
|
59
|
+
|
|
60
|
+
For built-in hosts:
|
|
61
|
+
- actuator_plugins: built-in defaults + user overrides (by-name merge)
|
|
62
|
+
- swagger_plugins: built-in defaults + user overrides (by-name merge)
|
|
63
|
+
- extensions: outer-layer plugins (custom hosts) — not touched here
|
|
64
|
+
"""
|
|
65
|
+
# Inner-layer: actuator
|
|
66
|
+
# User can override built-in plugins by name via config.actuator_plugins
|
|
67
|
+
actuator_plugins: Any = list(config.actuator_plugins)
|
|
68
|
+
|
|
69
|
+
# Inner-layer: swagger_ui
|
|
70
|
+
# User can override built-in plugins by name via config.swagger_plugins
|
|
71
|
+
swagger_plugins: Any = list(config.swagger_plugins)
|
|
72
|
+
|
|
73
|
+
# Outer-layer: custom hosts in config.extensions
|
|
74
|
+
# Each custom host is self-contained and manages its own inner registries
|
|
75
|
+
# No merging needed here — they're passed through as-is
|
|
76
|
+
|
|
77
|
+
# TODO: Future: when built-in hosts themselves become HostPlugins,
|
|
78
|
+
# we could manage their inner layers via a single registry.
|
|
79
|
+
# For now, keep the layers separate to prevent mixing concerns.
|
|
80
|
+
|
|
81
|
+
return actuator_plugins, swagger_plugins
|
|
82
|
+
|
|
83
|
+
|
|
48
84
|
def default_exception_handlers_provider() -> Mapping[int | type[Exception], ExceptionHandler]:
|
|
49
85
|
"""Return the default exception-handler map used by versioning integrations."""
|
|
50
86
|
from csrd.versioning.exception_handlers import EXCEPTION_HANDLERS
|
|
@@ -126,6 +162,17 @@ def configure_versioned_api(
|
|
|
126
162
|
resolved_handler_map=resolved_handler_map,
|
|
127
163
|
)
|
|
128
164
|
|
|
165
|
+
# Resolve inner-layer plugins for built-in hosts
|
|
166
|
+
actuator_plugins, swagger_plugins = _resolve_extensions(config)
|
|
167
|
+
|
|
168
|
+
# Log custom outer-layer hosts (they manage their own inner plugins)
|
|
169
|
+
if config.extensions:
|
|
170
|
+
logger.debug(
|
|
171
|
+
"Registered %d custom host plugin(s): %s",
|
|
172
|
+
len(config.extensions),
|
|
173
|
+
[getattr(p, "host", "?") for p in config.extensions],
|
|
174
|
+
)
|
|
175
|
+
|
|
129
176
|
docs._register_custom_docs(
|
|
130
177
|
app=app,
|
|
131
178
|
version_mapping=version_mapping,
|
|
@@ -138,13 +185,11 @@ def configure_versioned_api(
|
|
|
138
185
|
include_info_endpoints=config.include_info_endpoints,
|
|
139
186
|
build_tag=getattr(versioning_settings, "build_tag", None),
|
|
140
187
|
include_root_favicon_alias=config.include_root_favicon_alias,
|
|
141
|
-
swagger_plugins=list(
|
|
188
|
+
swagger_plugins=list(swagger_plugins) if swagger_plugins else None,
|
|
142
189
|
)
|
|
143
190
|
|
|
144
191
|
if config.include_actuator_endpoints:
|
|
145
|
-
register_actuator_router(
|
|
146
|
-
app, plugins=list(config.actuator_plugins) if config.actuator_plugins else None
|
|
147
|
-
)
|
|
192
|
+
register_actuator_router(app, plugins=list(actuator_plugins) if actuator_plugins else None)
|
|
148
193
|
|
|
149
194
|
dependency_wiring._mount_and_wire_versions(
|
|
150
195
|
app=app,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Unified extension host system for actuator, swagger, and custom hosts."""
|
|
2
|
+
|
|
3
|
+
from ._discovery import discover_plugins
|
|
4
|
+
from ._registry import ExtensionRegistry
|
|
5
|
+
from ._types import ExtensionContext, HostPlugin
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
"ExtensionContext",
|
|
9
|
+
"ExtensionRegistry",
|
|
10
|
+
"HostPlugin",
|
|
11
|
+
"discover_plugins",
|
|
12
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Optional plugin discovery via entry points.
|
|
2
|
+
|
|
3
|
+
Supports loading plugins from installed packages without explicit registration.
|
|
4
|
+
Keep as opt-in to maintain control over startup behavior.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ._types import HostPlugin
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def discover_plugins(group: str) -> tuple["HostPlugin", ...]:
|
|
17
|
+
"""Load plugins from entry points in a group.
|
|
18
|
+
|
|
19
|
+
Entry point names should be fully-qualified plugin classes or factory functions.
|
|
20
|
+
Returns discovered plugins ready for registry registration.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
group: Entry point group name, e.g., 'csrd.extensions.actuator'
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tuple of discovered plugins.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
import importlib.metadata as metadata
|
|
30
|
+
except ImportError:
|
|
31
|
+
logger.warning("importlib.metadata not available; plugin discovery disabled")
|
|
32
|
+
return ()
|
|
33
|
+
|
|
34
|
+
discovered = []
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
eps = metadata.entry_points()
|
|
38
|
+
# Support both dict-like (3.9) and SelectableGroups (3.10+) interfaces
|
|
39
|
+
group_eps = eps.get(group) if isinstance(eps, dict) else eps.select(group=group)
|
|
40
|
+
|
|
41
|
+
for ep in group_eps:
|
|
42
|
+
try:
|
|
43
|
+
plugin_class_or_factory = ep.load()
|
|
44
|
+
# Try to instantiate if it looks like a class, else call as factory
|
|
45
|
+
if isinstance(plugin_class_or_factory, type):
|
|
46
|
+
plugin = plugin_class_or_factory()
|
|
47
|
+
else:
|
|
48
|
+
plugin = plugin_class_or_factory()
|
|
49
|
+
|
|
50
|
+
logger.debug("Discovered plugin from entry point %s: %s", ep.name, plugin.name)
|
|
51
|
+
discovered.append(plugin)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logger.warning("Failed to load plugin from entry point %s: %s", ep.name, e)
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.warning("Failed to read entry points for group %s: %s", group, e)
|
|
57
|
+
|
|
58
|
+
return tuple(discovered)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Optional registry for deterministic plugin merge/order/override.
|
|
2
|
+
|
|
3
|
+
This module handles:
|
|
4
|
+
- merging defaults + user plugins by name
|
|
5
|
+
- ordering by (order, name)
|
|
6
|
+
- enabling/disabling
|
|
7
|
+
- discovery contribution
|
|
8
|
+
|
|
9
|
+
Hosts may use this or manage plugins directly.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ._types import HostPlugin
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ExtensionRegistry:
|
|
23
|
+
"""Registry for plugins targeting one or more hosts.
|
|
24
|
+
|
|
25
|
+
Provides by-name override semantics: defaults load first,
|
|
26
|
+
user plugins override by name, discovery plugins feed in last
|
|
27
|
+
and also follow override rules.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self) -> None:
|
|
31
|
+
"""Initialize empty registry."""
|
|
32
|
+
self._plugins: dict[tuple[str, str], HostPlugin] = {}
|
|
33
|
+
"""Map (host, name) -> HostPlugin for fast lookup and override."""
|
|
34
|
+
|
|
35
|
+
def register_defaults(self, defaults: Sequence["HostPlugin"]) -> None:
|
|
36
|
+
"""Register default plugins.
|
|
37
|
+
|
|
38
|
+
These load first and can be overridden by explicit or discovered plugins.
|
|
39
|
+
"""
|
|
40
|
+
for plugin in defaults:
|
|
41
|
+
key = (plugin.host, plugin.name)
|
|
42
|
+
self._plugins[key] = plugin
|
|
43
|
+
logger.debug("Registered default plugin %s for host %s", plugin.name, plugin.host)
|
|
44
|
+
|
|
45
|
+
def register_user(self, plugins: Sequence["HostPlugin"]) -> None:
|
|
46
|
+
"""Register user-provided plugins.
|
|
47
|
+
|
|
48
|
+
User plugins override defaults and discovered plugins with same (host, name).
|
|
49
|
+
"""
|
|
50
|
+
for plugin in plugins:
|
|
51
|
+
key = (plugin.host, plugin.name)
|
|
52
|
+
if key in self._plugins:
|
|
53
|
+
logger.debug(
|
|
54
|
+
"User plugin %s for host %s overrides default",
|
|
55
|
+
plugin.name,
|
|
56
|
+
plugin.host,
|
|
57
|
+
)
|
|
58
|
+
self._plugins[key] = plugin
|
|
59
|
+
|
|
60
|
+
def register_discovered(self, plugins: Sequence["HostPlugin"]) -> None:
|
|
61
|
+
"""Register discovered plugins (e.g., from entry points).
|
|
62
|
+
|
|
63
|
+
Discovered plugins can be overridden by user plugins.
|
|
64
|
+
"""
|
|
65
|
+
for plugin in plugins:
|
|
66
|
+
key = (plugin.host, plugin.name)
|
|
67
|
+
if key in self._plugins:
|
|
68
|
+
logger.debug(
|
|
69
|
+
"Discovered plugin %s for host %s already registered; skipping",
|
|
70
|
+
plugin.name,
|
|
71
|
+
plugin.host,
|
|
72
|
+
)
|
|
73
|
+
continue
|
|
74
|
+
self._plugins[key] = plugin
|
|
75
|
+
logger.debug("Registered discovered plugin %s for host %s", plugin.name, plugin.host)
|
|
76
|
+
|
|
77
|
+
def resolve_for_host(self, host: str) -> tuple["HostPlugin", ...]:
|
|
78
|
+
"""Return plugins for a host, ordered and filtered by enabled status.
|
|
79
|
+
|
|
80
|
+
Returns tuple sorted by (order, name) for deterministic behavior.
|
|
81
|
+
"""
|
|
82
|
+
host_plugins = [
|
|
83
|
+
plugin
|
|
84
|
+
for (h, _), plugin in self._plugins.items()
|
|
85
|
+
if h == host and plugin.enabled_by_default
|
|
86
|
+
]
|
|
87
|
+
return tuple(sorted(host_plugins, key=lambda p: (p.order, p.name)))
|
|
88
|
+
|
|
89
|
+
def resolve_all(self) -> tuple["HostPlugin", ...]:
|
|
90
|
+
"""Return all registered plugins, ordered by (order, name)."""
|
|
91
|
+
all_plugins = [p for p in self._plugins.values() if p.enabled_by_default]
|
|
92
|
+
return tuple(sorted(all_plugins, key=lambda p: (p.order, p.name)))
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Core types for the unified plugin extension system.
|
|
2
|
+
|
|
3
|
+
This module is a dependency leaf: it imports no host-specific modules.
|
|
4
|
+
All host modules (`actuator`, `swagger_ui`, custom) import from here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Protocol
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class ExtensionContext:
|
|
13
|
+
"""Read-only metadata passed to plugin contributions.
|
|
14
|
+
|
|
15
|
+
Plugins should not assume any context fields exist beyond their host's
|
|
16
|
+
documented interface.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
host: str
|
|
20
|
+
"""Host name: 'actuator', 'swagger_ui', or custom."""
|
|
21
|
+
|
|
22
|
+
# Additional context fields added by specific hosts at contribution time
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class HostPlugin(Protocol):
|
|
26
|
+
"""Host-agnostic plugin protocol for all extension hosts.
|
|
27
|
+
|
|
28
|
+
Plugins implement this to contribute to any host (actuator, swagger_ui, custom).
|
|
29
|
+
The plugin registers itself and returns host-specific contributions.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
name: str
|
|
33
|
+
"""Unique plugin identifier. User plugins with same name override defaults."""
|
|
34
|
+
|
|
35
|
+
host: str
|
|
36
|
+
"""Target host: 'actuator', 'swagger_ui', or custom host name."""
|
|
37
|
+
|
|
38
|
+
order: int
|
|
39
|
+
"""Registration order within host (lower = earlier). Default 100."""
|
|
40
|
+
|
|
41
|
+
enabled_by_default: bool
|
|
42
|
+
"""Whether this plugin is enabled unless explicitly disabled. Default True."""
|
|
43
|
+
|
|
44
|
+
def contribute(self, ctx: ExtensionContext) -> Any:
|
|
45
|
+
"""Return host-specific contribution (e.g., SwaggerPluginContribution for swagger_ui).
|
|
46
|
+
|
|
47
|
+
Called at host initialization time. Returns should be idempotent.
|
|
48
|
+
May raise exceptions; host will log warnings and skip the plugin.
|
|
49
|
+
"""
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Actuator host adapter.
|
|
2
|
+
|
|
3
|
+
Manages actuator plugins using the unified extension system.
|
|
4
|
+
Provides backward compatibility with existing ActuatorPlugin interface.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from collections.abc import Mapping, Sequence
|
|
9
|
+
|
|
10
|
+
from csrd.versioning.actuator.plugins.base import ActuatorLink
|
|
11
|
+
from csrd.versioning.actuator.plugins.base import ActuatorPlugin as LegacyActuatorPlugin
|
|
12
|
+
|
|
13
|
+
from .._types import ExtensionContext
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ActuatorHostPlugin:
|
|
19
|
+
"""Adapter wrapping a legacy ActuatorPlugin for the unified extension system."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, legacy_plugin: LegacyActuatorPlugin) -> None:
|
|
22
|
+
"""Wrap an existing ActuatorPlugin."""
|
|
23
|
+
self._legacy_plugin = legacy_plugin
|
|
24
|
+
self.name: str = legacy_plugin.name
|
|
25
|
+
self.host: str = "actuator"
|
|
26
|
+
self.order: int = 100
|
|
27
|
+
self.enabled_by_default: bool = True
|
|
28
|
+
|
|
29
|
+
def contribute(self, ctx: ExtensionContext) -> Mapping[str, ActuatorLink]:
|
|
30
|
+
"""Delegate to legacy plugin's register() method."""
|
|
31
|
+
# For backward compatibility, we call register() instead of contribute()
|
|
32
|
+
# This should be migrated when legacy plugin interface is removed
|
|
33
|
+
return {}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ActuatorHostAdapter:
|
|
37
|
+
"""Manages plugin resolution and execution for the actuator host."""
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def resolve_plugins(
|
|
41
|
+
defaults: Sequence[LegacyActuatorPlugin],
|
|
42
|
+
user_plugins: Sequence[LegacyActuatorPlugin] | None = None,
|
|
43
|
+
) -> tuple[LegacyActuatorPlugin, ...]:
|
|
44
|
+
"""Resolve plugins using current by-name override semantics.
|
|
45
|
+
|
|
46
|
+
This is the bridge function that maintains the existing
|
|
47
|
+
_resolve_plugins behavior while ready for future extension system integration.
|
|
48
|
+
"""
|
|
49
|
+
resolved: dict[str, LegacyActuatorPlugin] = {plugin.name: plugin for plugin in defaults}
|
|
50
|
+
|
|
51
|
+
if user_plugins is not None:
|
|
52
|
+
for plugin in user_plugins:
|
|
53
|
+
if plugin.name in resolved:
|
|
54
|
+
logger.debug("Actuator plugin '%s' overridden by consumer", plugin.name)
|
|
55
|
+
resolved[plugin.name] = plugin
|
|
56
|
+
|
|
57
|
+
return tuple(resolved.values())
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Swagger UI host adapter.
|
|
2
|
+
|
|
3
|
+
Manages swagger plugins using the unified extension system.
|
|
4
|
+
Provides backward compatibility with existing SwaggerPlugin interface.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from collections.abc import Sequence
|
|
9
|
+
|
|
10
|
+
from csrd.versioning.swagger_plugins._base import SwaggerPlugin as LegacySwaggerPlugin
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SwaggerHostAdapter:
|
|
16
|
+
"""Manages plugin resolution and execution for the swagger_ui host."""
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def resolve_plugins(
|
|
20
|
+
defaults: Sequence[LegacySwaggerPlugin],
|
|
21
|
+
global_plugins: Sequence[LegacySwaggerPlugin] | None = None,
|
|
22
|
+
per_version_plugins: Sequence[LegacySwaggerPlugin] | None = None,
|
|
23
|
+
) -> tuple[LegacySwaggerPlugin, ...]:
|
|
24
|
+
"""Resolve plugins using current by-name override semantics.
|
|
25
|
+
|
|
26
|
+
This is the bridge function that maintains the existing
|
|
27
|
+
_resolve_swagger_plugins behavior while ready for future extension system integration.
|
|
28
|
+
"""
|
|
29
|
+
if global_plugins is not None and len(global_plugins) == 0:
|
|
30
|
+
return ()
|
|
31
|
+
|
|
32
|
+
resolved: dict[str, LegacySwaggerPlugin] = {p.name: p for p in defaults}
|
|
33
|
+
|
|
34
|
+
if global_plugins:
|
|
35
|
+
for p in global_plugins:
|
|
36
|
+
resolved[p.name] = p
|
|
37
|
+
|
|
38
|
+
if per_version_plugins is not None and len(per_version_plugins) == 0:
|
|
39
|
+
return ()
|
|
40
|
+
|
|
41
|
+
if per_version_plugins:
|
|
42
|
+
for p in per_version_plugins:
|
|
43
|
+
resolved[p.name] = p
|
|
44
|
+
|
|
45
|
+
return tuple(resolved.values())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/_swagger_ui_version.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/__init__.py
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/base.py
RENAMED
|
File without changes
|
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/env/plugin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/plugins/info.py
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/tools/README.md
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/actuator/tools/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/__init__.py
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/swagger_plugins/_base.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
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.css
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.html
RENAMED
|
File without changes
|
{csrd_versioning-0.3.32 → csrd_versioning-0.3.34}/src/csrd/versioning/templates/swagger_ui.js
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
|