You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Callable types are not handled correctly in frozen dataclasses #12312
Using a frozen dataclass with an attribute typed as a TypeAlias for a Callable results in mypy treating the attribute as the return value. See reproduce for more easy to understand example.
To Reproduce
fromdataclassesimportdataclassfromtypingimportCallable, TypeAliasFunc: TypeAlias=Callable[..., None]
@dataclass(frozen=True, eq=False)classFoo:
bar: Funcdefexample() ->None:
passFoo(bar=example).bar()
results in
test.py:13:1: error: "None" not callable [misc]
Found 1 error in 1 file (checked 1 source file)
Expected Behavior
mypy should pass
Actual Behavior
Removing frozen=True causes mypy to pass this code.
Your Environment
Mypy version used: 0.931
Mypy command-line flags: N/A
Mypy configuration options from mypy.ini (and other config files):
python_version=3.10
platform=linux
plugins=pydantic.mypy, sqlalchemy.ext.mypy.plugin
show_column_numbers=True
show_error_codes=True
# show error messages from unrelated files
follow_imports=normal
# show errors about unsatisfied imports
ignore_missing_imports = False
# be strict
disallow_untyped_calls=True
warn_return_any=True
strict_optional=True
warn_no_return=True
warn_redundant_casts=True
warn_unused_ignores=True
# The following are off by default. Flip them on if you feel
# adventurous.
disallow_untyped_defs=True
check_untyped_defs=True
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index eed329bb5..ab2efacc8 100644
--- a/test-data/unit/check-dataclasses.test+++ b/test-data/unit/check-dataclasses.test@@ -1536,3 +1536,20 @@ A(a=1, b=2)
A(1)
A(a="foo") # E: Argument "a" to "A" has incompatible type "str"; expected "int"
[builtins fixtures/dataclasses.pyi]
++[case testDataclassesCallableTypeAlias]+# flags: --python-version 3.7+from dataclasses import dataclass+from typing import Any, Callable+from typing_extensions import TypeAlias+alias: TypeAlias = Callable[..., None]+@dataclass(frozen=True)+class A:+ a: alias++def func() -> None:+ pass++reveal_type(A.a) # N: Revealed type is "def (*Any, **Any)"+A(a=func).a()+[builtins fixtures/dataclasses.pyi]
data: /home/jordan/src/mypy/test-data/unit/check-dataclasses.test:1540:
/home/jordan/src/mypy/mypy/test/testcheck.py:140: in run_case
self.run_case_once(testcase)
/home/jordan/src/mypy/mypy/test/testcheck.py:227: in run_case_once
assert_string_arrays_equal(output, a, msg.format(testcase.file, testcase.line))
E AssertionError: Unexpected type checker output (/home/jordan/src/mypy/test-data/unit/check-dataclasses.test, line 1540)
--------------------------------------------------------- Captured stderr call ----------------------------------------------------------
Expected:
main:13: note: Revealed type is "def (*Any, **Any)"
Actual:
main:13: note: Revealed type is "def (*Any, **Any)"
main:14: error: "None" not callable (diff)
Upon further investigation, this issue is not specific to TypeAlias. Any dataclass with a Callable attribute will not work if frozen. See this failing test case:
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
index eed329bb5..cc8efb5b4 100644
--- a/test-data/unit/check-dataclasses.test+++ b/test-data/unit/check-dataclasses.test@@ -1536,3 +1536,18 @@ A(a=1, b=2)
A(1)
A(a="foo") # E: Argument "a" to "A" has incompatible type "str"; expected "int"
[builtins fixtures/dataclasses.pyi]
++[case testDataclassesCallableFrozen]+# flags: --python-version 3.7+from dataclasses import dataclass+from typing import Any, Callable+@dataclass(frozen=True)+class A:+ a: Callable[..., None]++def func() -> None:+ pass++reveal_type(A.a) # N: Revealed type is "def (*Any, **Any)"+A(a=func).a()+[builtins fixtures/dataclasses.pyi]
Here's a simple fix that i will make a PR for shortly:
diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py
index 091c627f5..254491853 100644
--- a/mypy/plugins/dataclasses.py+++ b/mypy/plugins/dataclasses.py@@ -211,8 +211,8 @@ class DataclassTransformer:
if decorator_arguments['frozen']:
self._freeze(attributes)
- else:- self._propertize_callables(attributes)++ self._propertize_callables(attributes)
if decorator_arguments['slots']:
self.add_slots(info, attributes, correct_version=py_version >= (3, 10))
diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test
It seems that the dataclass plugin was not calling self._propertize_callables() if the dataclass is frozen. I'll look more into the history around that code to see if there's a reason why it was written that way, but the above change passes the entire test suite.
changed the title [-]Frozen dataclass Callable attribute cannot be a TypeAlias[/-][+]Callable types are not handled correctly in frozen dataclasses[/+]on Mar 19, 2022
Activity
uSpike commentedon Mar 19, 2022
I have a test case that reproduces this:
uSpike commentedon Mar 19, 2022
Upon further investigation, this issue is not specific to
TypeAlias. Anydataclasswith aCallableattribute will not work if frozen. See this failing test case:Here's a simple fix that i will make a PR for shortly:
It seems that the dataclass plugin was not calling
self._propertize_callables()if the dataclass is frozen. I'll look more into the history around that code to see if there's a reason why it was written that way, but the above change passes the entire test suite.[-]Frozen dataclass Callable attribute cannot be a TypeAlias[/-][+]Callable types are not handled correctly in frozen dataclasses[/+]wrobell commentedon Mar 25, 2022
Looks like regression #10711?
uSpike commentedon Mar 25, 2022
I'm not sure it's really a regression since mypy seems to have never properly supported frozen dataclasses with Callable types.