Skip to content

Commit 4a40f83

Browse files
Adrian AcalaAdrian Acala
Adrian Acala
authored and
Adrian Acala
committed
Update mypy configuration and add tests for partial function
- Introduced a new test suite for the `partial` function in `typesafety/test_curry/test_partial/test_partial.py` to ensure its correct behavior with various argument types and signatures. - Updated `mypy.ini` to specify Python version and include the necessary plugins for type checking. - Fixed compatibility issues with `curry.partial` and mypy 1.6.1+. - Added a new entry in `CHANGELOG.md` for unreleased bugfixes.
1 parent 5bac449 commit 4a40f83

File tree

5 files changed

+98
-12
lines changed

5 files changed

+98
-12
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ incremental in minor, bugfixes only are patches.
66
See [0Ver](https://0ver.org/).
77

88

9+
## Unreleased
10+
11+
### Bugfixes
12+
13+
- Fixes the `curry.partial` compatibility with mypy 1.6.1+
14+
15+
916
## 0.25.0
1017

1118
### Features

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ lint.per-file-ignores."tests/test_examples/test_maybe/test_maybe_pattern_matchin
185185
]
186186
lint.per-file-ignores."tests/test_examples/test_result/test_result_pattern_matching.py" = [ "D103" ]
187187
lint.per-file-ignores."tests/test_pattern_matching.py" = [ "S101" ]
188+
lint.per-file-ignores."typesafety/test_curry/test_partial/test_partial.py" = [ "S101" ]
188189
lint.external = [ "WPS" ]
189190
lint.flake8-quotes.inline-quotes = "single"
190191
lint.mccabe.max-complexity = 6

returns/contrib/mypy/_typeops/inference.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,25 +73,26 @@ def _infer_constraints(
7373
"""Creates mapping of ``typevar`` to real type that we already know."""
7474
checker = self._ctx.api.expr_checker # type: ignore
7575
kinds = [arg.kind for arg in applied_args]
76-
exprs = [arg.expression(self._ctx.context) for arg in applied_args]
77-
7876
formal_to_actual = map_actuals_to_formals(
7977
kinds,
8078
[arg.name for arg in applied_args],
8179
self._fallback.arg_kinds,
8280
self._fallback.arg_names,
83-
lambda index: checker.accept(exprs[index]),
84-
)
85-
constraints = infer_constraints_for_callable(
86-
self._fallback,
87-
arg_types=[arg.type for arg in applied_args],
88-
arg_kinds=kinds,
89-
arg_names=[arg.name for arg in applied_args],
90-
formal_to_actual=formal_to_actual,
91-
context=checker.argument_infer_context(),
81+
lambda index: checker.accept(
82+
applied_args[index].expression(self._ctx.context),
83+
),
9284
)
85+
9386
return {
94-
constraint.type_var: constraint.target for constraint in constraints
87+
constraint.type_var: constraint.target
88+
for constraint in infer_constraints_for_callable(
89+
self._fallback,
90+
arg_types=[arg.type for arg in applied_args],
91+
arg_kinds=kinds,
92+
arg_names=[arg.name for arg in applied_args],
93+
formal_to_actual=formal_to_actual,
94+
context=checker.argument_infer_context(),
95+
)
9596
}
9697

9798

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[mypy]
2+
python_version = 3.11
3+
plugins = returns.contrib.mypy.returns_plugin
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import pytest
2+
from typing_extensions import reveal_type
3+
4+
from returns.curry import partial
5+
6+
TEST_STR = 'a'
7+
8+
9+
def test_partial_fn( # noqa: WPS221
10+
first_arg: int,
11+
optional_arg: str | None,
12+
) -> tuple[int, str | None]:
13+
"""Return arguments as a tuple."""
14+
return first_arg, optional_arg
15+
16+
17+
def test_partial():
18+
"""Ensures that ``partial`` works correctly."""
19+
bound = partial(test_partial_fn, 1)
20+
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (*, optional_arg: Union[builtins.str, None] =) -> Tuple[builtins.int, Union[builtins.str, None]]'
21+
22+
assert bound() == (1, None)
23+
assert bound(optional_arg=TEST_STR) == (1, TEST_STR)
24+
25+
26+
def test_partial_with_decorator():
27+
"""Ensures that ``partial`` works correctly with decorators."""
28+
29+
@partial(first=1)
30+
def _decorated(first: int, second: str) -> float: # noqa: WPS430
31+
return first / float(second)
32+
33+
reveal_type(_decorated) # noqa: WPS421 # R: Revealed type is 'def (second: builtins.str) -> builtins.float'
34+
35+
assert _decorated(second='2') == pytest.approx(0.5)
36+
37+
38+
def test_partial_keyword():
39+
"""Ensures that ``partial`` works correctly with keyword args."""
40+
bound = partial(test_partial_fn, optional_arg=TEST_STR)
41+
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (first_arg: builtins.int) -> Tuple[builtins.int, builtins.str]'
42+
43+
assert bound(1) == (1, TEST_STR)
44+
45+
46+
def test_partial_keyword_only():
47+
"""Ensures that ``partial`` works with keyword only args."""
48+
49+
def _target(*, arg: int) -> int: # noqa: WPS430
50+
return arg
51+
52+
bound = partial(_target, arg=1)
53+
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def () -> builtins.int'
54+
55+
assert bound() == 1
56+
57+
58+
def test_partial_keyword_mixed():
59+
"""Ensures that ``partial`` works with keyword only args."""
60+
61+
def _target(arg1: int, *, arg2: int) -> int: # noqa: WPS430
62+
return arg1 + arg2
63+
64+
bound = partial(_target, arg2=1)
65+
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (arg1: builtins.int) -> builtins.int'
66+
67+
assert bound(1) == 2
68+
69+
70+
def test_partial_wrong_signature():
71+
"""Ensures that ``partial`` returns ``Any`` for wrong signature."""
72+
reveal_type(partial(len, 1)) # noqa: WPS421 # R: Revealed type is 'Any'
73+
with pytest.raises(TypeError):
74+
partial(len, 1)()

0 commit comments

Comments
 (0)