Repository: tefra/xsdata
Pull request #553 (2021-07-11 06:06:34)

ClassType: allow custom exception messages


Patch:
diff --git a/xsdata/formats/dataclass/compat.py b/xsdata/formats/dataclass/compat.py
index 6c9999fe..dfef2f88 100644
--- a/xsdata/formats/dataclass/compat.py
+++ b/xsdata/formats/dataclass/compat.py
@@ -9,6 +9,7 @@ from typing import Set
 from typing import Tuple
 from typing import Type
 
+from xsdata.exceptions import XmlContextError
 from xsdata.formats.dataclass.models.generics import AnyElement
 from xsdata.formats.dataclass.models.generics import DerivedElement
 from xsdata.utils.hooks import load_entry_points
@@ -40,7 +41,15 @@ class ClassType(abc.ABC):
 
     @abc.abstractmethod
     def is_model(self, obj: Any) -> bool:
-        """Return whether the given type is binding model."""
+        """Return whether the given value is binding model."""
+
+    @abc.abstractmethod
+    def verify_model(self, obj: Any):
+        """
+        Verify the given value is a binding model.
+
+        :raises xsdata.exceptions.XmlContextError: if not supported
+        """
 
     @abc.abstractmethod
     def get_fields(self, obj: Any) -> Tuple[Any, ...]:
@@ -111,6 +120,10 @@ class Dataclasses(ClassType):
     def is_model(self, obj: Any) -> bool:
         return is_dataclass(obj)
 
+    def verify_model(self, obj: Any):
+        if not self.is_model(obj):
+            raise XmlContextError(f"Type '{obj}' is not a dataclass.")
+
     def get_fields(self, obj: Any) -> Tuple[Any, ...]:
         return fields(obj)
 
diff --git a/xsdata/formats/dataclass/models/builders.py b/xsdata/formats/dataclass/models/builders.py
index 7e1a10aa..d6de970c 100644
--- a/xsdata/formats/dataclass/models/builders.py
+++ b/xsdata/formats/dataclass/models/builders.py
@@ -45,8 +45,7 @@ class XmlMetaBuilder:
     def build(self, clazz: Type, parent_ns: Optional[str]) -> XmlMeta:
         """Build the binding metadata for a dataclass and its fields."""
 
-        if not self.class_type.is_model(clazz):
-            raise XmlContextError(f"Type '{clazz}' is not a dataclass.")
+        self.class_type.verify_model(clazz)
 
         # Fetch the dataclass meta settings and make sure we don't inherit
         # the parent class meta.


Test patch:
diff --git a/tests/formats/dataclass/test_compat.py b/tests/formats/dataclass/test_compat.py
index 26a917ed..a6138146 100644
--- a/tests/formats/dataclass/test_compat.py
+++ b/tests/formats/dataclass/test_compat.py
@@ -1,7 +1,8 @@
 from unittest import TestCase
 
+from tests.fixtures.models import TypeA
 from tests.fixtures.models import TypeC
-from xsdata.formats.dataclass.compat import ClassType
+from xsdata.exceptions import XmlContextError
 from xsdata.formats.dataclass.compat import Dataclasses
 
 
@@ -16,3 +17,20 @@ class ClassTypeTests(TestCase):
         self.assertEqual(-1, class_type.score_object(None))
         self.assertEqual(1.0, class_type.score_object("a"))
         self.assertEqual(1.5, class_type.score_object(2.9))
+
+
+class DataclassesTests(TestCase):
+    def test_is_model(self):
+        class_type = Dataclasses()
+        self.assertTrue(class_type.is_model(TypeA))
+        self.assertTrue(class_type.is_model(TypeA(1)))
+        self.assertFalse(class_type.is_model(1))
+
+    def test_verify_model(self):
+        class_type = Dataclasses()
+        class_type.verify_model(TypeA)
+
+        with self.assertRaises(XmlContextError) as cm:
+            class_type.verify_model(int)
+
+        self.assertEqual(f"Type '{int}' is not a dataclass.", str(cm.exception))