Source code for niwaki.exceptions._design
"""Design-DSL exception hierarchy.
All errors raised while building or pushing a design tree derive from
:class:`DesignError`, itself a :class:`~niwaki.exceptions.NiwakiError`.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from niwaki.exceptions._base import NiwakiError
if TYPE_CHECKING:
from niwaki.design._push import PushReport
[docs]
class DesignError(NiwakiError):
"""Base class for all design-DSL errors."""
[docs]
class UnknownMakerError(DesignError, AttributeError):
"""A maker name resolved at no level of the cursor's ancestor path.
Also an :class:`AttributeError` so that ``hasattr`` and attribute
protocols keep working on cursors.
"""
[docs]
class DuplicateDeclarationError(DesignError):
"""The same object (class + naming) was declared twice in a design."""
[docs]
class UnresolvedReferenceError(DesignError):
"""A ``bind``/``provide``/``consume`` target is not declared in the design.
Raised during closed-world validation at push time. The message includes
the declared instances of the target class and a did-you-mean suggestion.
"""
[docs]
class AmbiguousBindError(DesignError):
"""No unambiguous Rs class exists for a ``bind`` edge.
Neither ``REFERENCE_MAP[owner][target]`` nor the inverse
``REFERENCE_MAP[target][owner]`` resolves to a relationship class. Use
``.mo(RsClass, ...)`` to create the relationship explicitly.
"""
[docs]
class StagedPushError(DesignError):
"""A ``push(mode="staged")`` partially succeeded.
Carries the partial :class:`~niwaki.design.PushReport` (the DNs actually
written, in execution order) and the failures as plain
``(dn, exception)`` pairs — no engine internals leak into the public
surface.
Args:
report: Partial push report — ``report.dns`` are the DNs written,
including every independent branch that succeeded around a failure.
failures: ``(dn, exception)`` for every operation that failed.
not_run: DNs never attempted because an *ancestor* object failed —
pushing them without their parent would only 404. A failure
isolates its own subtree; sibling branches are still written.
Example::
from niwaki.exceptions import StagedPushError
try:
config.push(aci, mode="staged")
except StagedPushError as exc:
print(f"written : {exc.report.dns}")
print(f"failed : {[dn for dn, _ in exc.failures]}")
print(f"skipped : {exc.not_run}")
"""
def __init__(
self,
report: PushReport,
failures: list[tuple[str, Exception]],
not_run: list[str],
) -> None:
self.report = report
self.failures = failures
self.not_run = not_run
total = len(report.dns) + len(failures) + len(not_run)
super().__init__(
f"staged push failed: {len(failures)}/{total} operation(s) did not "
f"succeed ({len(not_run)} never attempted)"
)