Skip to content

Commit 9664961

Browse files
committed
fix: resolve ruff lint issues in copy.replace implementation
1 parent 507053e commit 9664961

3 files changed

Lines changed: 43 additions & 26 deletions

File tree

‎returns/maybe.py‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -354,15 +354,20 @@ def __replace__(self, **changes: Any) -> '_Nothing':
354354
anything other than ``None`` raises a TypeError.
355355
"""
356356
if '_inner_value' in changes and changes['_inner_value'] is not None:
357-
raise TypeError('Cannot replace _inner_value on Nothing with a non-None value')
357+
raise TypeError(
358+
'Cannot replace _inner_value on Nothing with a non-None value',
359+
)
358360
if 'inner_value' in changes and changes['inner_value'] is not None:
359-
raise TypeError('Cannot replace inner_value on Nothing with a non-None value')
361+
raise TypeError(
362+
'Cannot replace inner_value on Nothing with a non-None value',
363+
)
360364
changes.pop('_inner_value', None)
361365
changes.pop('inner_value', None)
362366
if changes:
363367
unexpected = next(iter(changes))
364368
raise TypeError(
365-
f'_Nothing.__replace__() got an unexpected keyword argument {unexpected!r}',
369+
f'_Nothing.__replace__() got an unexpected keyword '
370+
f'argument {unexpected!r}',
366371
)
367372
return self
368373

‎returns/primitives/container.py‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC
2-
from typing import Any, TypeVar
2+
from typing import Any, Self, TypeVar
33

44
from typing_extensions import TypedDict
55

@@ -8,7 +8,6 @@
88
from returns.primitives.types import Immutable
99

1010
_EqualType = TypeVar('_EqualType', bound=Equable)
11-
_ContainerType = TypeVar('_ContainerType', bound='BaseContainer')
1211

1312

1413
class _PickleState(TypedDict):
@@ -69,15 +68,16 @@ def __setstate__(self, state: _PickleState | Any) -> None:
6968
# backward compatibility with 0.19.0 and earlier
7069
object.__setattr__(self, '_inner_value', state)
7170

72-
def __replace__(self: _ContainerType, **changes: Any) -> _ContainerType:
71+
def __replace__(self, **changes: Any) -> Self:
7372
"""
7473
Implements Python 3.13 ``copy.replace`` protocol.
7574
7675
Allows creating a new container of the same type with replaced fields.
7776
"""
7877
if '_inner_value' in changes and 'inner_value' in changes:
7978
raise TypeError(
80-
f'{self.__class__.__name__}.__replace__() got multiple values for inner value',
79+
f'{self.__class__.__name__}.__replace__() got '
80+
f'multiple values for inner value',
8181
)
8282
inner_value = changes.pop(
8383
'_inner_value',
@@ -86,7 +86,8 @@ def __replace__(self: _ContainerType, **changes: Any) -> _ContainerType:
8686
if changes:
8787
unexpected = next(iter(changes))
8888
raise TypeError(
89-
f'{self.__class__.__name__}.__replace__() got an unexpected keyword argument {unexpected!r}',
89+
f'{self.__class__.__name__}.__replace__() got an '
90+
f'unexpected keyword argument {unexpected!r}',
9091
)
9192
return self.__class__(inner_value)
9293

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import copy
22
import sys
3-
from typing import Any
43

54
import pytest
6-
from hypothesis import example, given
5+
from hypothesis import given
76
from hypothesis import strategies as st
87

98
from returns.io import IO
10-
from returns.maybe import Maybe, Nothing, Some
9+
from returns.maybe import Nothing, Some
1110
from returns.primitives.container import BaseContainer
12-
from returns.result import Failure, Result, Success
11+
from returns.result import Failure, Success
1312

1413

1514
@given(st.integers(), st.integers())
@@ -19,25 +18,25 @@ def test_base_container_replace_inner_value(initial: int, new_val: int):
1918
replaced = container.__replace__(inner_value=new_val)
2019

2120
assert replaced != container or initial == new_val
22-
assert replaced._inner_value == new_val
23-
assert container._inner_value == initial
21+
assert replaced == BaseContainer(new_val)
22+
assert container == BaseContainer(initial)
2423
assert isinstance(replaced, BaseContainer)
2524

2625

2726
@given(st.text(), st.text())
28-
def test_base_container_replace_underscore_inner_value(initial: str, new_val: str):
27+
def test_replace_underscore_inner_value(initial: str, new_val: str):
2928
"""Ensures that __replace__ accepts _inner_value keyword argument."""
3029
container = BaseContainer(initial)
3130
replaced = container.__replace__(_inner_value=new_val)
3231

33-
assert replaced._inner_value == new_val
34-
assert container._inner_value == initial
32+
assert replaced == BaseContainer(new_val)
33+
assert container == BaseContainer(initial)
3534

3635

3736
def test_base_container_replace_no_args():
3837
"""Ensures that __replace__ without arguments returns an equal copy."""
3938
container = BaseContainer(42)
40-
replaced = container.__replace__()
39+
replaced = container.__replace__() # noqa: PLC2801 - testing the dunder
4140

4241
assert replaced == container
4342
assert replaced is not container
@@ -46,14 +45,17 @@ def test_base_container_replace_no_args():
4645
def test_base_container_replace_unexpected_kwarg():
4746
"""Ensures that unexpected kwargs raise TypeError."""
4847
container = BaseContainer(42)
49-
with pytest.raises(TypeError, match="unexpected keyword argument 'invalid'"):
48+
with pytest.raises(
49+
TypeError,
50+
match="unexpected keyword argument 'invalid'",
51+
):
5052
container.__replace__(invalid=True)
5153

5254

5355
def test_base_container_replace_multiple_values():
5456
"""Ensures providing both inner_value and _inner_value raises TypeError."""
5557
container = BaseContainer(42)
56-
with pytest.raises(TypeError, match="multiple values for inner value"):
58+
with pytest.raises(TypeError, match='multiple values for inner value'):
5759
container.__replace__(inner_value=1, _inner_value=2)
5860

5961

@@ -66,7 +68,7 @@ def test_base_container_replace_multiple_values():
6668
(IO(100), 200, IO(200)),
6769
],
6870
)
69-
def test_concrete_containers_replace(container: Any, new_value: Any, expected: Any):
71+
def test_concrete_containers_replace(container, new_value, expected):
7072
"""Ensures concrete containers support __replace__."""
7173
replaced = container.__replace__(inner_value=new_value)
7274
assert replaced == expected
@@ -75,21 +77,30 @@ def test_concrete_containers_replace(container: Any, new_value: Any, expected: A
7577

7678
def test_nothing_replace():
7779
"""Ensures Nothing supports __replace__ when value remains None."""
78-
assert Nothing.__replace__() is Nothing
80+
assert Nothing.__replace__() is Nothing # noqa: PLC2801 - testing the dunder
7981
assert Nothing.__replace__(inner_value=None) is Nothing
8082
assert Nothing.__replace__(_inner_value=None) is Nothing
8183

82-
with pytest.raises(TypeError, match="Cannot replace inner_value on Nothing"):
84+
with pytest.raises(
85+
TypeError,
86+
match='Cannot replace inner_value on Nothing',
87+
):
8388
Nothing.__replace__(inner_value=42)
8489

85-
with pytest.raises(TypeError, match="Cannot replace _inner_value on Nothing"):
90+
with pytest.raises(
91+
TypeError,
92+
match='Cannot replace _inner_value on Nothing',
93+
):
8694
Nothing.__replace__(_inner_value=42)
8795

8896

89-
@pytest.mark.skipif(sys.version_info < (3, 13), reason="copy.replace was added in Python 3.13")
97+
@pytest.mark.skipif(
98+
sys.version_info < (3, 13),
99+
reason='copy.replace was added in Python 3.13',
100+
)
90101
def test_copy_replace_standard_library():
91102
"""Ensures stdlib copy.replace works seamlessly on Python 3.13+."""
92-
replace = getattr(copy, "replace")
103+
replace = copy.replace
93104
s = Success(1)
94105
replaced = replace(s, inner_value=2)
95106
assert replaced == Success(2)

0 commit comments

Comments
 (0)