summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Caselli <cfederico87@gmail.com>2023-03-30 00:25:39 +0200
committerMike Bayer <mike_mp@zzzcomputing.com>2023-03-31 09:57:09 -0400
commit96670516b917424e7dd4c6cee5e07147c583f6c9 (patch)
tree50e53530f33859228b989d03228f6da14449c5b1
parentdb69e680da3aa01572c19cdedb6448e74a01c790 (diff)
downloadsqlalchemy-96670516b917424e7dd4c6cee5e07147c583f6c9.tar.gz
Wrap dataclass exceptions clarifying origin
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
-rw-r--r--doc/build/changelog/unreleased_20/9563.rst16
-rw-r--r--doc/build/errors.rst33
-rw-r--r--lib/sqlalchemy/orm/decl_base.py8
-rw-r--r--test/orm/declarative/test_dc_transforms.py15
4 files changed, 72 insertions, 0 deletions
diff --git a/doc/build/changelog/unreleased_20/9563.rst b/doc/build/changelog/unreleased_20/9563.rst
new file mode 100644
index 000000000..501cb5c10
--- /dev/null
+++ b/doc/build/changelog/unreleased_20/9563.rst
@@ -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`
+
diff --git a/doc/build/errors.rst b/doc/build/errors.rst
index 307a27414..4c8cb53d7 100644
--- a/doc/build/errors.rst
+++ b/doc/build/errors.rst
@@ -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
diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py
index 828501d8c..bd62c3c1b 100644
--- a/lib/sqlalchemy/orm/decl_base.py
+++ b/lib/sqlalchemy/orm/decl_base.py
@@ -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
diff --git a/test/orm/declarative/test_dc_transforms.py b/test/orm/declarative/test_dc_transforms.py
index a8a5e04bb..031aad5d5 100644
--- a/test/orm/declarative/test_dc_transforms.py
+++ b/test/orm/declarative/test_dc_transforms.py
@@ -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]):