From: John Mulligan Date: Thu, 2 May 2024 20:28:46 +0000 (-0400) Subject: mgr/smb: add error handling & conversion hook to resourcelib X-Git-Tag: testing/wip-pdonnell-testing-20240622.145006-debug~31^2~10 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=ec6e8b7e84b3b38da1590b2a199a4c298c128576;p=ceph-ci.git mgr/smb: add error handling & conversion hook to resourcelib 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 --- diff --git a/src/pybind/mgr/smb/resourcelib.py b/src/pybind/mgr/smb/resourcelib.py index 6d3a7ff63c7..63dfaa837c0 100644 --- a/src/pybind/mgr/smb/resourcelib.py +++ b/src/pybind/mgr/smb/resourcelib.py @@ -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(