Skip to content

Custom dataclasses with __dataclass_{fields,params}__ fields are not considered DataclassInstance #1987

@jeertmans

Description

@jeertmans

Summary

Hi!

First of all, thank you very much for this tool!

I use equinox.Module to create Python objects that are both dataclasses and PyTrees (to be compatible with JAX). However, it looks like running ty check emits a lot of errors that should not occur, at least from what I understood: a Python class is a DataclassInstance if it has both __dataclass_fields__ and __dataclass_params__ attributes. Another issue is that ty will raise an error about missing required arguments, whereas they are not missing.

I looked at other issues on this repo and couldn't find a similar one, but maybe I missed it.

FYI, I tried to used the playground but I don't see how we can include external dependencies, and the equinox module is too complex to be included manually.

MWE (check.py)

# /// script
# requires-python = ">=3.11"
# dependencies = [
#     "equinox",
# ]
# ///
from dataclasses import dataclass, replace, asdict, field

import equinox as eqx


@dataclass
class MyBuiltinDataClass(eqx.Module):
    a: int
    b: float
    c: str = field(default="default_value")


class MyEqxDataClass(eqx.Module):
    a: int
    b: float
    c: str = eqx.field(default="default_value")


m = MyBuiltinDataClass(a=1, b=2.0)

print(m)
print(replace(m, b=3.5))
print(asdict(m))
print(hasattr(m, "__dataclass_fields__"))  # True
print(hasattr(m, "__dataclass_params__"))  # True

m = MyEqxDataClass(a=1, b=2.0)

print(m)
print(replace(m, b=3.5))
print(asdict(m))
print(hasattr(m, "__dataclass_fields__"))  # True
print(hasattr(m, "__dataclass_params__"))  # True

uv run python check.py's output

MyBuiltinDataClass(a=1, b=2.0, c='default_value')
MyBuiltinDataClass(a=1, b=3.5, c='default_value')
{'a': 1, 'b': 2.0, 'c': 'default_value'}
True
True
MyEqxDataClass(a=1, b=2.0)
MyEqxDataClass(a=1, b=3.5)
{'a': 1, 'b': 2.0, 'c': 'default_value'}
True
True

ty check check.py's output

error[missing-argument]: No argument provided for required parameter `c`
  --> check.py:26:5
   |
24 | print(hasattr(m, "__dataclass_params__"))  # True
25 |
26 | m = MyEqxDataClass(a=1, b=2.0)
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
27 |
28 | print(m)
   |
info: rule `missing-argument` is enabled by default

error[invalid-argument-type]: Argument to function `replace` is incorrect
  --> check.py:29:15
   |
28 | print(m)
29 | print(replace(m, b=3.5))
   |               ^ Argument type `MyEqxDataClass` does not satisfy upper bound `DataclassInstance` of type variable `_DataclassT`
30 | print(asdict(m))
31 | print(hasattr(m, "__dataclass_fields__"))  # True
   |
info: Type variable defined here
  --> stdlib/dataclasses.pyi:32:1
   |
30 |     __all__ += ["KW_ONLY"]
31 |
32 | _DataclassT = TypeVar("_DataclassT", bound=DataclassInstance)
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33 |
34 | @type_check_only
   |
info: rule `invalid-argument-type` is enabled by default

error[invalid-argument-type]: Argument to function `asdict` is incorrect
  --> check.py:30:14
   |
28 | print(m)
29 | print(replace(m, b=3.5))
30 | print(asdict(m))
   |              ^ Expected `DataclassInstance`, found `MyEqxDataClass`
31 | print(hasattr(m, "__dataclass_fields__"))  # True
32 | print(hasattr(m, "__dataclass_params__"))  # True
   |
info: Matching overload defined here
  --> stdlib/dataclasses.pyi:67:5
   |
66 | @overload
67 | def asdict(obj: DataclassInstance) -> dict[str, Any]:
   |     ^^^^^^ ---------------------- Parameter declared here
68 |     """Return the fields of a dataclass instance as a new dictionary mapping
69 |     field names to field values.
   |
info: Non-matching overloads for function `asdict`:
info:   (obj: DataclassInstance, *, dict_factory: (list[tuple[str, Any]], /) -> _T@asdict) -> _T@asdict
info: rule `invalid-argument-type` is enabled by default

Found 3 diagnostics

Version

ty 0.0.2

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdataclassesIssues relating to dataclasses and dataclass_transformlibraryDedicated support for popular third-party libraries

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions