Repository: pydantic/pydantic
Pull request #11769: Make sure Pydantic dataclasses with slots and `validate_assignment` can be unpickled
Author: login: Viicos
Labels: name: relnotes-fix
description: Used for bugfixes.
Created: 2025-04-17T12:54:27Z | Merged: 2025-04-29T07:12:33Z



Fixes https://github.com/pydantic/pydantic/issues/11768.

<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->

## Change Summary

<!-- Please give a short summary of the changes. -->

## Related issue number

<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

## Checklist

* [ ] The pull request title is a good summary of the changes - it will be used in the changelog
* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI
* [ ] Documentation reflects the changes where applicable
* [ ] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**



Diff:
diff --git a/pydantic/_internal/_dataclasses.py b/pydantic/_internal/_dataclasses.py
index 954ab32f234..656ef781226 100644
--- a/pydantic/_internal/_dataclasses.py
+++ b/pydantic/_internal/_dataclasses.py
@@ -5,7 +5,7 @@
 import dataclasses
 import typing
 import warnings
-from functools import partial, wraps
+from functools import partial
 from typing import Any, ClassVar
 
 from pydantic_core import (
@@ -178,22 +178,12 @@ def __init__(__dataclass_self__: PydanticDataclass, *args: Any, **kwargs: Any) -
     # We are about to set all the remaining required properties expected for this cast;
     # __pydantic_decorators__ and __pydantic_fields__ should already be set
     cls = typing.cast('type[PydanticDataclass]', cls)
-    # debug(schema)
 
     cls.__pydantic_core_schema__ = schema
-    cls.__pydantic_validator__ = validator = create_schema_validator(
+    cls.__pydantic_validator__ = create_schema_validator(
         schema, cls, cls.__module__, cls.__qualname__, 'dataclass', core_config, config_wrapper.plugin_settings
     )
     cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config)
-
-    if config_wrapper.validate_assignment:
-
-        @wraps(cls.__setattr__)
-        def validated_setattr(instance: Any, field: str, value: str, /) -> None:
-            validator.validate_assignment(instance, field, value)
-
-        cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
-
     cls.__pydantic_complete__ = True
     return True
 
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
index f1ac051dab2..57008b7915b 100644
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -3,6 +3,7 @@
 from __future__ import annotations as _annotations
 
 import dataclasses
+import functools
 import sys
 import types
 from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, overload
@@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn expects all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
+
+                def _dataclass_setstate(self: Any, state: list[Any]) -> None:
+                    for field, value in zip(dataclasses.fields(self), state):
+                        object.__setattr__(self, field.name, value)
+
+                cls.__getstate__ = _dataclass_getstate  # pyright: ignore[reportAttributeAccessIssue]
+                cls.__setstate__ = _dataclass_setstate  # pyright: ignore[reportAttributeAccessIssue]
+
         # This is an undocumented attribute to distinguish stdlib/Pydantic dataclasses.
         # It should be set as early as possible:
         cls.__is_pydantic_dataclass__ = True
diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py
index f0a6960f652..c963ba733c5 100644
--- a/tests/test_dataclasses.py
+++ b/tests/test_dataclasses.py
@@ -2430,6 +2430,23 @@ class Model:
     assert dc.b == 'bar'
 
 
+# Must be defined at the module level to be picklable:
+@pydantic.dataclasses.dataclass(slots=True, config={'validate_assignment': True})
+class DataclassSlotsValidateAssignment:
+    a: int
+
+
+@pytest.mark.skipif(sys.version_info < (3, 10), reason='slots are only supported for dataclasses in Python >= 3.10')
+def test_dataclass_slots_validate_assignment():
+    """https://github.com/pydantic/pydantic/issues/11768"""
+
+    m = DataclassSlotsValidateAssignment(1)
+    m_pickle = pickle.loads(pickle.dumps(m))
+    assert m_pickle.a == 1
+    with pytest.raises(ValidationError):
+        m.a = 'not_an_int'
+
+
 @pytest.mark.parametrize(
     'dataclass_decorator',
     [


Reviews:
user: login: DouweM
body: @Viicos Take my comments with a grain of salt because you've looked into this much more deeply than I have, but I was confused about a few things which _may_ point at real issues/opportunities for improvement. But if not, let me know and I'll defer to your judgment and merge this :) 
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T21:06:05Z
user: login: Viicos
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T07:31:26Z
user: login: Viicos
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T07:37:47Z
user: login: Viicos
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-26T07:39:10Z
user: login: DouweM
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T17:56:53Z
user: login: Viicos
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T19:03:18Z
user: login: DouweM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T20:27:46Z

Review comments:
diff_hunk: @@ -2430,6 +2430,23 @@ class Model:
     assert dc.b == 'bar'
 
 
+# Must be defined at the module level to be pickable:
path: tests/test_dataclasses.py
user: login: DouweM
body: ```suggestion
# Must be defined at the module level to be picklable:
```
created_at: 2025-04-25T20:56:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
path: pydantic/dataclasses.py
user: login: DouweM
body: ```suggestion
                # in turn expects all the field values to be already present on the
```
created_at: 2025-04-25T20:58:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
path: pydantic/dataclasses.py
user: login: DouweM
body: Could we use a `dict` here instead of relying on `dataclasses.fields` being stable, which it may not be if the pickle and unpickle happen on different versions of the code?

I haven't looked into this deeply, but https://stackoverflow.com/questions/76143398/pickle-ignores-getstate-on-frozen-dataclasses-with-slots suggests it's fine.
created_at: 2025-04-25T21:02:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
path: pydantic/dataclasses.py
user: login: DouweM
body: Is it required to skip this when frozen is set on top of slots? https://github.com/python/cpython/pull/104041#issue-1690829773 suggests a bug was fixed hereabouts.
created_at: 2025-04-25T21:03:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
+
+                def _dataclass_setstate(self: Any, state: list[Any]) -> None:
+                    for field, value in zip(dataclasses.fields(self), state):
+                        object.__setattr__(self, field.name, value)
path: pydantic/dataclasses.py
user: login: DouweM
body: Should we run validation after this?
created_at: 2025-04-25T21:04:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
+
+                def _dataclass_setstate(self: Any, state: list[Any]) -> None:
+                    for field, value in zip(dataclasses.fields(self), state):
+                        object.__setattr__(self, field.name, value)
path: pydantic/dataclasses.py
user: login: Viicos
body: Pickling is used with the assumption that the data is trusted already (if you need validation to be performed, then you have bigger problems as you are most likely vulnerable to arbitrary code execution). See `BaseModel.__setstate__()` as well for reference where we don't apply validation.
created_at: 2025-04-26T07:31:26Z
author_association: MEMBER
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
path: pydantic/dataclasses.py
user: login: Viicos
body: When a frozen and slotted dataclass is created, the stdlib adds a `__set/getstate__()` implementation (and we actually reuse this implementation below). We could also unconditionally set our own `__set/getstate__()`, but this would also override any user-defined one (the CPython PR you linked is actually a fix for this on their end)?
created_at: 2025-04-26T07:37:47Z
author_association: MEMBER
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
path: pydantic/dataclasses.py
user: login: Viicos
body: `dataclasses.fields` is documented so should be stable across all versions, and I don't think it will ever change even as a breaking change.
created_at: 2025-04-26T07:39:10Z
author_association: MEMBER
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
path: pydantic/dataclasses.py
user: login: DouweM
body: @Viicos I was thinking about a case where a user-defined dataclass is modified and its `fields` change as a result. Then if we have a pickle from one version and unpickle on the next, the order (and length) of pickled values and dataclass fields may not match so the setstate below could fail. I haven't verified this though.
created_at: 2025-04-28T17:56:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -264,6 +265,35 @@ def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
             **kwargs,
         )
 
+        if config_wrapper.validate_assignment:
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: Any, field: str, value: str, /) -> None:
+                type(instance).__pydantic_validator__.validate_assignment(instance, field, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn excepts all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
path: pydantic/dataclasses.py
user: login: Viicos
body: I'm not entirely sure I follow, but I think it should be fine: the provided `__get/setstate__()` implementations are used to pickle the dataclass _instances_. If the class definition changes between pickling/unpickling, then it is expected to fail anyway
created_at: 2025-04-28T19:03:18Z
author_association: MEMBER

PR comments:
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-04-17T12:55:20Z
body: ## Deploying pydantic-docs with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>4cf3a25</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://28d9942a.pydantic-docs.pages.dev'>https://28d9942a.pydantic-docs.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://dc-slots-validate-assignment.pydantic-docs.pages.dev'>https://dc-slots-validate-assignment.pydantic-docs.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/58c2606138e6f7346345b5a882718168/pages/view/pydantic-docs/28d9942a-4d0f-4376-8af5-180d35d97688)

author_association: NONE
user: login: codspeed-hq[bot]
created_at: 2025-04-17T13:49:57Z
body: <!-- __CODSPEED_PERFORMANCE_REPORT_COMMENT__ -->
<!-- __CODSPEED_INSTRUMENTATION_PERFORMANCE_REPORT_COMMENT__ -->

## [CodSpeed Performance Report](https://codspeed.io/pydantic/pydantic/branches/dc-slots-validate-assignment)

### Merging #11769 will **not alter performance**

<sub>Comparing <code>dc-slots-validate-assignment</code> (4cf3a25) with <code>main</code> (bf06866)</sub>



### Summary

`✅ 46` untouched benchmarks  




author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-17T13:56:40Z
body: ## Coverage report


<img title="Coverage for the whole project is 95.67%. Previous coverage rate is not available, cannot report on evolution." src="https://img.shields.io/badge/Coverage-95%25-orange.svg"> <img title="100% of the statement lines added by this PR are covered" src="https://img.shields.io/badge/PR%20Coverage-100%25-brightgreen.svg"><details><summary>Click to see where and how coverage changed</summary><table><thead>
  <tr><th>File</th><th>Statements</th><th>Missing</th><th>Coverage</th><th>Coverage<br>(new stmts)</th><th>Lines missing</th></tr>
</thead>
<tbody><tr>
<td colspan="6">&nbsp;&nbsp;<b>pydantic</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efa">dataclasses.py</a></td>
<td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efa"><img title="This PR adds 112 statements to pydantic/dataclasses.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/112-%28%2B112%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efa"><img title="This PR adds 2 statements missing coverage to pydantic/dataclasses.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/2-%28%2B2%29-red.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efa"><img title="The coverage rate of pydantic/dataclasses.py is 98.07% (110/112). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/98%25-%28110/112%29-orange.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efa"><img title="In this PR, 14 new statements are added to pydantic/dataclasses.py, 14 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%2814/14%29-brightgreen.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>pydantic/_internal</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-95820a10464ee8f0a17ccb6d58a22333f0215387df497df714fb395c2de207d4">_dataclasses.py</a></td>
<td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-95820a10464ee8f0a17ccb6d58a22333f0215387df497df714fb395c2de207d4"><img title="This PR adds 63 statements to pydantic/_internal/_dataclasses.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/63-%28%2B63%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-95820a10464ee8f0a17ccb6d58a22333f0215387df497df714fb395c2de207d4"><img title="This PR adds 3 statements missing coverage to pydantic/_internal/_dataclasses.py. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/3-%28%2B3%29-red.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-95820a10464ee8f0a17ccb6d58a22333f0215387df497df714fb395c2de207d4"><img title="The coverage rate of pydantic/_internal/_dataclasses.py is 95.65% (60/63). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/95%25-%2860/63%29-orange.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-95820a10464ee8f0a17ccb6d58a22333f0215387df497df714fb395c2de207d4"><img title="In this PR, 2 new statements are added to pydantic/_internal/_dataclasses.py, 2 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%282/2%29-brightgreen.svg"></a></td><td></td></tbody>
<tfoot>
<tr>
<td><b>Project Total</b></td>
<td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR adds 9885 statements to the whole project. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/9.9k-%28%2B9885%29-007ec6.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR adds 320 statements missing coverage to the whole project. The file did not seem to exist on the base branch." src="https://img.shields.io/badge/320-%28%2B320%29-red.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="The coverage rate of the whole project is 95.67% (9565/9885). The file did not seem to exist on the base branch." src="https://img.shields.io/badge/95%25-%289.6k/9.9k%29-orange.svg"></a></td><td align="center"><a href="https://github.com/pydantic/pydantic/pull/11769/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="In this PR, 16 new statements are added to the whole project, 16 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%2816/16%29-brightgreen.svg"></a></td><td>&nbsp;</td>
</tr>
</tfoot>
</table>

<sub>

This report was generated by [python-coverage-comment-action](https://github.com/py-cov-action/python-coverage-comment-action)

</sub>
</details>



<!-- This comment was produced by python-coverage-comment-action -->
author_association: CONTRIBUTOR