Class structural pattern matching always returns `Any` as type
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
Bug Report
I just hit this issue and I am not sure if it's is a problem of "join-vs-union" or if it is an actual bug.
I am returning a return's ResultE from a function and then I am parsing the result using match. The type of the values matched in the class structural matching always resolve to Any, even though mypy recognizes the type of the return value to have a specific combination of types:
Revealed type is "returns.result.Result[builtins.bool, builtins.Exception]"
To Reproduce
In an environment with returns installed:
from typing import reveal_type
from returns.result import Failure, Success, safe
@safe
def safe_function(value: int) -> bool:
if (value % 2) == 0:
raise Exception("It's not odd!")
return True
def pattern_matcher() -> bool:
function_out = safe_function(12)
reveal_type(function_out)
match function_out:
case Success(result):
reveal_type(result)
return result
case Failure(err):
reveal_type(err)
print(err)
return False
Expected Behavior
Mypy validates the file correctly with the following output:
$ mypy match_test.py
match_test.py:16: note: Revealed type is "returns.result.Result[builtins.bool, builtins.Exception]"
match_test.py:19: note: Revealed type is "builtins.bool"
match_test.py:22: note: Revealed type is "builtins.Exception"
Success: no issues found in 1 source file
Actual Behavior
Mypy returns error with the following output:
$ mypy match_test.py
match_test.py:16: note: Revealed type is "returns.result.Result[builtins.bool, builtins.Exception]"
match_test.py:19: note: Revealed type is "Any"
match_test.py:20: error: Returning Any from function declared to return "bool" [no-any-return]
match_test.py:22: note: Revealed type is "Any"
Found 1 error in 1 file (checked 1 source file)
Extra test
I changed the match pattern to this: case Success(bool(result)): and mypy did not return any error:
from typing import reveal_type
from returns.result import Failure, Success, safe
@safe
def safe_function(value: int) -> bool:
if (value % 2) == 0:
raise Exception("It's not odd!")
return True
def pattern_matcher() -> bool:
function_out = safe_function(12)
reveal_type(function_out)
match function_out:
case Success(bool(result)):
reveal_type(result)
return result
case Failure(err):
reveal_type(err)
print(err)
return False
Output:
$ mypy match_test.py
match_test.py:16: note: Revealed type is "returns.result.Result[builtins.bool, builtins.Exception]"
match_test.py:19: note: Revealed type is "builtins.bool"
match_test.py:22: note: Revealed type is "Any"
Success: no issues found in 1 source file
Your Environment
- Mypy version used:
mypy 1.11.0 (compiled: yes) - Mypy configuration options from
mypy.ini(and other config files):[tool.mypy] python_version = "3.12" mypy_path = "$MYPY_CONFIG_FILE_DIR/src" enable_incomplete_feature = ["NewGenericSyntax"] ignore_missing_imports = true no_implicit_optional = true strict_optional = true strict = true plugins = ["returns.contrib.mypy.returns_plugin"] disallow_untyped_defs = true - Python version used:
Python 3.12.4 - Returns version used:
0.23.0
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Run the supplied match_test.py reproducer with mypy 1.11.0 and compare the revealed types for Success(result) with Success(bool(result)). Trace the class structural pattern-matching type inference involved in those cases and add regression coverage. Done means the generic payload types are preserved, revealing bool and Exception instead of Any, without introducing errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100