Repository: pydantic/pydantic

Issue:
dataclass with methods cannot use init=False fields
### Initial Checks

- [X] I have searched GitHub for a duplicate issue and I'm sure this is something new
- [X] I have searched Google & StackOverflow for a solution and couldn't find anything
- [X] I have read and followed [the docs](https://pydantic-docs.helpmanual.io) and still think this is a bug
- [X] I am confident that the issue is with pydantic (not my code, or another library in the ecosystem like [FastAPI](https://fastapi.tiangolo.com) or [mypy](https://mypy.readthedocs.io/en/stable))


### Description

Since pydantic 1.10.2, it fails to parse a builtin dataclass `dataclass.field(init=False)` if it has at least one method.
Similarly to #4498, the cause seems [this change](https://github.com/pydantic/pydantic/pull/4484/files#diff-8ff2ca642bcd607c31ac848396deaf4353b8b4a835473cbca58a22376fa72efaR200) in #4484.

### Example Code

```Python
import dataclasses
import pydantic

@dataclasses.dataclass
class Ok:
    a: int
    b: str = "b"
    c: float = dataclasses.field(init=False)

    def __post_init__(self):
        self.c = float(self.a)

@dataclasses.dataclass
class NotOkSince1_10_2:
    a: int
    b: str = "b"
    c: float = dataclasses.field(init=False)

    def __post_init__(self):
        self.c = float(self.a)

    @pydantic.validator("b")
    def check_b(cls, v):
        if not v:
            raise ValueError("b should not be empty")
        return v

    # ... or with a non-validator method
    # def foo(self):
    #     print("foo")


print(pydantic.parse_obj_as(Ok, {"a": 3}))  # Ok(a=3, b='b', c=3.0)
print(pydantic.parse_obj_as(NotOkSince1_10_2, {"a": 3}))  # TypeError: non-default argument 'c' follows default argument
```


### Python, Pydantic & OS Version

```Text
pydantic version: 1.10.2
            pydantic compiled: True
                 install path: /Users/tos/.pyenv/versions/3.10.0/envs/p10/lib/python3.10/site-packages/pydantic
               python version: 3.10.0 (default, Oct 26 2021, 11:19:32) [Clang 11.0.0 (clang-1100.0.33.16)]
                     platform: macOS-11.7-x86_64-i386-64bit
     optional deps. installed: ['typing-extensions']
```


### Affected Components

- [ ] [Compatibility between releases](https://pydantic-docs.helpmanual.io/changelog/)
- [ ] [Data validation/parsing](https://pydantic-docs.helpmanual.io/usage/models/#basic-model-usage)
- [ ] [Data serialization](https://pydantic-docs.helpmanual.io/usage/exporting_models/) - `.dict()` and `.json()`
- [ ] [JSON Schema](https://pydantic-docs.helpmanual.io/usage/schema/)
- [X] [Dataclasses](https://pydantic-docs.helpmanual.io/usage/dataclasses/)
- [ ] [Model Config](https://pydantic-docs.helpmanual.io/usage/model_config/)
- [ ] [Field Types](https://pydantic-docs.helpmanual.io/usage/types/) - adding or changing a particular data type
- [ ] [Function validation decorator](https://pydantic-docs.helpmanual.io/usage/validation_decorator/)
- [ ] [Generic Models](https://pydantic-docs.helpmanual.io/usage/models/#generic-models)
- [ ] [Other Model behaviour](https://pydantic-docs.helpmanual.io/usage/models/) - `construct()`, pickling, private attributes, ORM mode
- [ ] [Plugins](https://pydantic-docs.helpmanual.io/) and integration with other tools - mypy, FastAPI, python-devtools, Hypothesis, VS Code, PyCharm, etc.


Discussion:
@PrettyWood, any idea on this?

My instinct is that we have to call this a limitation of pydantic v1.10 and fix in v2.

Resolving patch:
diff --git a/pydantic/dataclasses.py b/pydantic/dataclasses.py
--- a/pydantic/dataclasses.py
+++ b/pydantic/dataclasses.py
@@ -34,20 +34,7 @@ class M:
 import sys
 from contextlib import contextmanager
 from functools import wraps
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Callable,
-    ClassVar,
-    Dict,
-    Generator,
-    Optional,
-    Set,
-    Type,
-    TypeVar,
-    Union,
-    overload,
-)
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Generator, Optional, Type, TypeVar, Union, overload
 
 from typing_extensions import dataclass_transform
 
@@ -117,6 +104,7 @@ def dataclass(
         frozen: bool = False,
         config: Union[ConfigDict, Type[object], None] = None,
         validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
         kw_only: bool = ...,
     ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
         ...
@@ -134,6 +122,7 @@ def dataclass(
         frozen: bool = False,
         config: Union[ConfigDict, Type[object], None] = None,
         validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
         kw_only: bool = ...,
     ) -> 'DataclassClassOrWrapper':
         ...
@@ -152,6 +141,7 @@ def dataclass(
         frozen: bool = False,
         config: Union[ConfigDict, Type[object], None] = None,
         validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
     ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
         ...
 
@@ -168,6 +158,7 @@ def dataclass(
         frozen: bool = False,
         config: Union[ConfigDict, Type[object], None] = None,
         validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
     ) -> 'DataclassClassOrWrapper':
         ...
 
@@ -184,6 +175,7 @@ def dataclass(
     frozen: bool = False,
     config: Union[ConfigDict, Type[object], None] = None,
     validate_on_init: Optional[bool] = None,
+    use_proxy: Optional[bool] = None,
     kw_only: bool = False,
 ) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']:
     """
@@ -197,7 +189,15 @@ def dataclass(
     def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper':
         import dataclasses
 
-        if is_builtin_dataclass(cls) and _extra_dc_args(_cls) == _extra_dc_args(_cls.__bases__[0]):  # type: ignore
+        should_use_proxy = (
+            use_proxy
+            if use_proxy is not None
+            else (
+                is_builtin_dataclass(cls)
+                and (cls.__bases__[0] is object or set(dir(cls)) == set(dir(cls.__bases__[0])))
+            )
+        )
+        if should_use_proxy:
             dc_cls_doc = ''
             dc_cls = DataclassProxy(cls)
             default_validate_on_init = False
@@ -437,14 +437,6 @@ def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value:
     object.__setattr__(self, name, value)
 
 
-def _extra_dc_args(cls: Type[Any]) -> Set[str]:
-    return {
-        x
-        for x in dir(cls)
-        if x not in getattr(cls, '__dataclass_fields__', {}) and not (x.startswith('__') and x.endswith('__'))
-    }
-
-
 def is_builtin_dataclass(_cls: Type[Any]) -> bool:
     """
     Whether a class is a stdlib dataclass
@@ -482,4 +474,4 @@ def make_dataclass_validator(dc_cls: Type['Dataclass'], config: Type[BaseConfig]
     and yield the validators
     It retrieves the parameters of the dataclass and forwards them to the newly created dataclass
     """
-    yield from _get_validators(dataclass(dc_cls, config=config, validate_on_init=False))
+    yield from _get_validators(dataclass(dc_cls, config=config, use_proxy=True))


Test patch:
diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py
--- a/tests/test_dataclasses.py
+++ b/tests/test_dataclasses.py
@@ -1513,3 +1513,98 @@ class Foo:
     assert config.bar == 'cat'
     setattr(config, 'bar', 'dog')
     assert config.bar == 'dog'
+
+
+def test_frozen_dataclasses():
+    @dataclasses.dataclass(frozen=True)
+    class First:
+        a: int
+
+    @dataclasses.dataclass(frozen=True)
+    class Second(First):
+        @property
+        def b(self):
+            return self.a
+
+    class My(BaseModel):
+        my: Second
+
+    assert My(my=Second(a='1')).my.b == 1
+
+
+def test_empty_dataclass():
+    """should be able to inherit without adding a field"""
+
+    @dataclasses.dataclass
+    class UnvalidatedDataclass:
+        a: int = 0
+
+    @pydantic.dataclasses.dataclass
+    class ValidatedDerivedA(UnvalidatedDataclass):
+        ...
+
+    @pydantic.dataclasses.dataclass()
+    class ValidatedDerivedB(UnvalidatedDataclass):
+        b: int = 0
+
+    @pydantic.dataclasses.dataclass()
+    class ValidatedDerivedC(UnvalidatedDataclass):
+        ...
+
+
+def test_proxy_dataclass():
+    @dataclasses.dataclass
+    class Foo:
+        a: Optional[int] = dataclasses.field(default=42)
+        b: List = dataclasses.field(default_factory=list)
+
+        @dataclasses.dataclass
+        class Bar:
+            pass
+
+    @dataclasses.dataclass
+    class Model1:
+        foo: Foo
+
+    class Model2(BaseModel):
+        foo: Foo
+
+    m1 = Model1(foo=Foo())
+    m2 = Model2(foo=Foo())
+
+    assert m1.foo.a == m2.foo.a == 42
+    assert m1.foo.b == m2.foo.b == []
+    assert m1.foo.Bar() is not None
+    assert m2.foo.Bar() is not None
+
+
+def test_proxy_dataclass_2():
+    @dataclasses.dataclass
+    class M1:
+        a: int
+        b: str = 'b'
+        c: float = dataclasses.field(init=False)
+
+        def __post_init__(self):
+            self.c = float(self.a)
+
+    @dataclasses.dataclass
+    class M2:
+        a: int
+        b: str = 'b'
+        c: float = dataclasses.field(init=False)
+
+        def __post_init__(self):
+            self.c = float(self.a)
+
+        @pydantic.validator('b')
+        def check_b(cls, v):
+            if not v:
+                raise ValueError('b should not be empty')
+            return v
+
+    m1 = pydantic.parse_obj_as(M1, {'a': 3})
+    m2 = pydantic.parse_obj_as(M2, {'a': 3})
+    assert m1.a == m2.a == 3
+    assert m1.b == m2.b == 'b'
+    assert m1.c == m2.c == 3.0