]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/smb: add error handling & conversion hook to resourcelib
authorJohn Mulligan <jmulligan@redhat.com>
Thu, 2 May 2024 20:28:46 +0000 (16:28 -0400)
committerJohn Mulligan <jmulligan@redhat.com>
Mon, 17 Jun 2024 15:16:59 +0000 (11:16 -0400)
Add a method to supply the Resource instances with a callback that
can handle errors that occur during object construction from simplified
data. If the callback is not set, the exceptions are handled as usual.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
src/pybind/mgr/smb/resourcelib.py

index 6d3a7ff63c762c4d8282c289b2e61d3edb7dc191..63dfaa837c0e23a00ca552246949686ca09e9436 100644 (file)
@@ -83,6 +83,7 @@ from typing import (
     Callable,
     Dict,
     Hashable,
+    Iterator,
     List,
     Optional,
     Tuple,
@@ -91,6 +92,7 @@ from typing import (
 import dataclasses
 import logging
 import sys
+from contextlib import contextmanager
 from itertools import chain
 
 from .proto import Self, Simplified
@@ -304,6 +306,7 @@ class Resource:
         self.resource_cls = cls
         self.fields: Dict[str, Field] = {}
         self._on_condition: Optional[Callable[..., bool]] = None
+        self._on_construction_error: Optional[Callable[..., Exception]] = None
 
         for fld in dataclasses.fields(self.resource_cls):
             self.fields[fld.name] = Field.create(fld)
@@ -317,6 +320,12 @@ class Resource:
         """Set a condition function."""
         self._on_condition = cond
 
+    def on_construction_error(self, cond: Callable[..., Exception]) -> None:
+        """Set a function to handle/convert exceptions that occur while
+        constructing objects from simplified data.
+        """
+        self._on_construction_error = cond
+
     def type_name(self) -> str:
         """Return the name of the type managed by this resource."""
         return self.resource_cls.__name__
@@ -330,16 +339,29 @@ class Resource:
         """Given a dict-based unstructured data object return the structured
         object-based equivalent.
         """
-        kw = {}
-        for fld in self.fields.values():
-            value = self._object_field_from_simplified(fld, data)
-            if value is not _unset:
-                kw[fld.name] = value
-        obj = self.resource_cls(**kw)
-        validate = getattr(obj, 'validate', None)
-        if validate:
-            validate()
-        return obj
+        with self._structuring_error_hook(self.resource_cls, data):
+            kw = {}
+            for fld in self.fields.values():
+                value = self._object_field_from_simplified(fld, data)
+                if value is not _unset:
+                    kw[fld.name] = value
+            obj = self.resource_cls(**kw)
+            validate = getattr(obj, 'validate', None)
+            if validate:
+                validate()
+            return obj
+
+    @contextmanager
+    @_xt
+    def _structuring_error_hook(
+        self, resource_cls: Any, data: Simplified
+    ) -> Iterator[None]:
+        try:
+            yield
+        except Exception as err:
+            if self._on_construction_error:
+                raise self._on_construction_error(err, data) from err
+            raise
 
     @_xt
     def _object_field_from_simplified(