Skip to content

Update CHANGELOG and fix mypy compatibility for curry.partial #2094

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ incremental in minor, bugfixes only are patches.
See [0Ver](https://0ver.org/).


## Unreleased

### Bugfixes

- Fixes the `curry.partial` compatibility with mypy 1.6.1+


## 0.25.0

### Features
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ lint.per-file-ignores."tests/test_examples/test_maybe/test_maybe_pattern_matchin
]
lint.per-file-ignores."tests/test_examples/test_result/test_result_pattern_matching.py" = [ "D103" ]
lint.per-file-ignores."tests/test_pattern_matching.py" = [ "S101" ]
lint.per-file-ignores."typesafety/test_curry/test_partial/test_partial.py" = [ "S101" ]
lint.external = [ "WPS" ]
lint.flake8-quotes.inline-quotes = "single"
lint.mccabe.max-complexity = 6
Expand Down
25 changes: 13 additions & 12 deletions returns/contrib/mypy/_typeops/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,26 @@ def _infer_constraints(
"""Creates mapping of ``typevar`` to real type that we already know."""
checker = self._ctx.api.expr_checker # type: ignore
kinds = [arg.kind for arg in applied_args]
exprs = [arg.expression(self._ctx.context) for arg in applied_args]

formal_to_actual = map_actuals_to_formals(
kinds,
[arg.name for arg in applied_args],
self._fallback.arg_kinds,
self._fallback.arg_names,
lambda index: checker.accept(exprs[index]),
)
constraints = infer_constraints_for_callable(
self._fallback,
arg_types=[arg.type for arg in applied_args],
arg_kinds=kinds,
arg_names=[arg.name for arg in applied_args],
formal_to_actual=formal_to_actual,
context=checker.argument_infer_context(),
lambda index: checker.accept(
applied_args[index].expression(self._ctx.context),
),
)

return {
constraint.type_var: constraint.target for constraint in constraints
constraint.type_var: constraint.target
for constraint in infer_constraints_for_callable(
self._fallback,
arg_types=[arg.type for arg in applied_args],
arg_kinds=kinds,
arg_names=[arg.name for arg in applied_args],
formal_to_actual=formal_to_actual,
context=checker.argument_infer_context(),
)
}


Expand Down
3 changes: 3 additions & 0 deletions typesafety/test_curry/test_partial/mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[mypy]
python_version = 3.11
plugins = returns.contrib.mypy.returns_plugin
74 changes: 74 additions & 0 deletions typesafety/test_curry/test_partial/test_partial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import pytest
from typing_extensions import reveal_type

from returns.curry import partial

TEST_STR = 'a'


def test_partial_fn( # noqa: WPS221
first_arg: int,
optional_arg: str | None,
) -> tuple[int, str | None]:
"""Return arguments as a tuple."""
return first_arg, optional_arg


def test_partial():
"""Ensures that ``partial`` works correctly."""
bound = partial(test_partial_fn, 1)
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (*, optional_arg: Union[builtins.str, None] =) -> Tuple[builtins.int, Union[builtins.str, None]]'

assert bound() == (1, None)
assert bound(optional_arg=TEST_STR) == (1, TEST_STR)


def test_partial_with_decorator():
"""Ensures that ``partial`` works correctly with decorators."""

@partial(first=1)
def _decorated(first: int, second: str) -> float: # noqa: WPS430
return first / float(second)

reveal_type(_decorated) # noqa: WPS421 # R: Revealed type is 'def (second: builtins.str) -> builtins.float'

assert _decorated(second='2') == pytest.approx(0.5)


def test_partial_keyword():
"""Ensures that ``partial`` works correctly with keyword args."""
bound = partial(test_partial_fn, optional_arg=TEST_STR)
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (first_arg: builtins.int) -> Tuple[builtins.int, builtins.str]'

assert bound(1) == (1, TEST_STR)


def test_partial_keyword_only():
"""Ensures that ``partial`` works with keyword only args."""

def _target(*, arg: int) -> int: # noqa: WPS430
return arg

bound = partial(_target, arg=1)
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def () -> builtins.int'

assert bound() == 1


def test_partial_keyword_mixed():
"""Ensures that ``partial`` works with keyword only args."""

def _target(arg1: int, *, arg2: int) -> int: # noqa: WPS430
return arg1 + arg2

bound = partial(_target, arg2=1)
reveal_type(bound) # noqa: WPS421 # R: Revealed type is 'def (arg1: builtins.int) -> builtins.int'

assert bound(1) == 2


def test_partial_wrong_signature():
"""Ensures that ``partial`` returns ``Any`` for wrong signature."""
reveal_type(partial(len, 1)) # noqa: WPS421 # R: Revealed type is 'Any'
with pytest.raises(TypeError):
partial(len, 1)()