ADR-013 Phase 0: plugin lifecycle groundwork (validate, apply-profile, dependency fixes)
This commit is contained in:
@@ -242,24 +242,35 @@ class PluginLoader:
|
||||
"""Sort plugins so dependencies come first.
|
||||
|
||||
Reads dependencies from manifest.json directly; does not
|
||||
instantiate plugin classes during sort.
|
||||
instantiate plugin classes during sort. Detects a dependency cycle
|
||||
(a back edge in the DFS) and raises PluginDependencyError rather than
|
||||
looping or silently dropping a plugin.
|
||||
"""
|
||||
sorted_list = []
|
||||
visited = set()
|
||||
visited = set() # fully processed (post-order emitted)
|
||||
visiting = set() # on the current DFS stack; a revisit here is a cycle
|
||||
|
||||
def visit(name):
|
||||
if name in visited:
|
||||
return
|
||||
visited.add(name)
|
||||
|
||||
if name in visiting:
|
||||
raise PluginDependencyError(
|
||||
f'Circular plugin dependency involving "{name}"',
|
||||
plugin_name=name,
|
||||
)
|
||||
visiting.add(name)
|
||||
# Only the manifest read is tolerant; a cycle raised by a nested
|
||||
# visit must propagate, so the dep recursion sits outside the guard
|
||||
# (PluginDependencyError is itself a PluginError).
|
||||
try:
|
||||
manifest = self.load_manifest(name)
|
||||
for dep in manifest.get('dependencies', []):
|
||||
if dep in plugin_names:
|
||||
visit(dep)
|
||||
deps = self.load_manifest(name).get('dependencies', [])
|
||||
except PluginError:
|
||||
pass
|
||||
|
||||
deps = []
|
||||
for dep in deps:
|
||||
if dep in plugin_names:
|
||||
visit(dep)
|
||||
visiting.discard(name)
|
||||
visited.add(name)
|
||||
sorted_list.append(name)
|
||||
|
||||
for name in plugin_names:
|
||||
|
||||
Reference in New Issue
Block a user