]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Wrap dataclass exceptions clarifying origin
authorFederico Caselli <cfederico87@gmail.com>
Wed, 29 Mar 2023 22:25:39 +0000 (00:25 +0200)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 31 Mar 2023 13:57:09 +0000 (09:57 -0400)
Exceptions such as ``TypeError`` and ``ValueError`` raised by Python
dataclasses when making use of the :class:`_orm.MappedAsDataclass` mixin
class or :meth:`_orm.registry.mapped_as_dataclass` decorator are now
wrapped within an :class:`.InvalidRequestError` wrapper along with
informative context about the error message, referring to the Python
dataclasses documentation as the authoritative source of background
information on the cause of the exception.

Fixes: #9563
Change-Id: I25652485b91c4ee8cf112b91aae8f9041061a8bd

doc/build/changelog/unreleased_20/9563.rst [new file with mode: 0644]
doc/build/errors.rst
lib/sqlalchemy/orm/decl_base.py
test/orm/declarative/test_dc_transforms.py

diff --git a/doc/build/changelog/unreleased_20/9563.rst b/doc/build/changelog/unreleased_20/9563.rst
new file mode 100644 (file)
index 0000000..501cb5c
--- /dev/null
@@ -0,0 +1,16 @@
+.. change::
+    :tags: usecase, orm
+    :tickets: 9563
+
+    Exceptions such as ``TypeError`` and ``ValueError`` raised by Python
+    dataclasses when making use of the :class:`_orm.MappedAsDataclass` mixin
+    class or :meth:`_orm.registry.mapped_as_dataclass` decorator are now
+    wrapped within an :class:`.InvalidRequestError` wrapper along with
+    informative context about the error message, referring to the Python
+    dataclasses documentation as the authoritative source of background
+    information on the cause of the exception.
+
+    .. seealso::
+
+        :ref:`error_dcte`
+
index 307a27414c80f6d3f5569b6a19c0a7b9581929d7..4c8cb53d734be28fd929a712e0f9b61786890435 100644 (file)
@@ -1443,6 +1443,39 @@ mixin classes which have SQLAlchemy mapped attributes within a dataclass
 hierarchy have to themselves be dataclasses.
 
 
+.. _error_dcte:
+
+Python dataclasses error encountered when creating dataclass for <classname>
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using the :class:`_orm.MappedAsDataclass` mixin class or
+:meth:`_orm.registry.mapped_as_dataclass` decorator, SQLAlchemy makes use
+of the actual `Python dataclasses <dataclasses>`_ module that's in the Python standard library
+in order to apply dataclass behaviors to the target class.   This API has
+its own error scenarios, most of which involve the construction of an
+``__init__()`` method on the user defined class; the order of attributes
+declared on the class, as well as `on superclasses <dc_superclass>`_, determines
+how the ``__init__()`` method will be constructed and there are specific
+rules in how the attributes are organized as well as how they should make
+use of parameters such as ``init=False``, ``kw_only=True``, etc.   **SQLAlchemy
+does not control or implement these rules**.  Therefore, for errors of this nature,
+consult the `Python dataclasses <dataclasses>`_ documentation, with special
+attention to the rules applied to `inheritance <_dc_superclass>`_.
+
+.. seealso::
+
+  :ref:`orm_declarative_native_dataclasses` - SQLAlchemy dataclasses documentation
+
+  `Python dataclasses <dataclasses>`_ - on the python.org website
+
+  `inheritance <_dc_superclass>`_ - on the python.org website
+
+.. _dataclasses: https://docs.python.org/3/library/dataclasses.html
+
+.. _dc_superclass: https://docs.python.org/3/library/dataclasses.html#inheritance
+
+
+
 
 
 AsyncIO Exceptions
index 828501d8cb788b173ec8b6336682db63f6647cbf..bd62c3c1b448f6160e82bb7fc7c430dba718f962 100644 (file)
@@ -1210,6 +1210,14 @@ class _ClassScanMapperConfig(_MapperConfig):
                     if v is not _NoArg.NO_ARG and k != "dataclass_callable"
                 },
             )
+        except (TypeError, ValueError) as ex:
+            raise exc.InvalidRequestError(
+                f"Python dataclasses error encountered when creating "
+                f"dataclass for {klass.__name__!r}: "
+                f"{ex!r}. Please refer to Python dataclasses "
+                "documentation for additional information.",
+                code="dcte",
+            ) from ex
         finally:
             # restore original annotations outside of the dataclasses
             # process; for mixins and __abstract__ superclasses, SQLAlchemy
index a8a5e04bbf63a960e48df745c55bbe62319b632e..031aad5d52513754399dc437c3184735473b5d24 100644 (file)
@@ -741,6 +741,21 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase):
             class Foo(Mixin):
                 bar_value: Mapped[float] = mapped_column(default=78)
 
+    def test_dataclass_exception_wrapped(self, dc_decl_base):
+        with expect_raises_message(
+            exc.InvalidRequestError,
+            r"Python dataclasses error encountered when creating dataclass "
+            r"for \'Foo\': .*Please refer to Python dataclasses.*",
+        ) as ec:
+
+            class Foo(dc_decl_base):
+                id: Mapped[int] = mapped_column(primary_key=True, init=False)
+                foo_value: Mapped[float] = mapped_column(default=78)
+                foo_no_value: Mapped[float] = mapped_column()
+                __tablename__ = "foo"
+
+        is_true(isinstance(ec.error.__cause__, TypeError))
+
 
 class RelationshipDefaultFactoryTest(fixtures.TestBase):
     def test_list(self, dc_decl_base: Type[MappedAsDataclass]):