Skip to content

Commit 9b704c8

Browse files
author
rodrigo.nogueira
committed
tests: strict type annotations for test_to_dict for 100% coveralls precision
1 parent 636e308 commit 9b704c8

1 file changed

Lines changed: 45 additions & 29 deletions

File tree

tests/test_to_dict.py

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,24 @@
11
"""Test to_dict functionality for all multidict types."""
22

3-
from typing import Any, Callable, Type
3+
from typing import Any, Callable, Type, Iterable
44
from multidict import MultiMapping
55

6+
7+
from typing import Protocol
8+
9+
10+
from typing import Protocol, Type
11+
from multidict import MultiDict, CIMultiDict, MultiDictProxy, CIMultiDictProxy
12+
13+
class MultidictModule(Protocol):
14+
MultiDict: Type[MultiDict[object]]
15+
CIMultiDict: Type[CIMultiDict[object]]
16+
MultiDictProxy: Type[MultiDictProxy[object]]
17+
CIMultiDictProxy: Type[CIMultiDictProxy[object]]
18+
19+
class DictFactory(Protocol):
20+
def __call__(self, arg: Iterable[tuple[str, object]] | None = None) -> MultiMapping[object]: ...
21+
622
import pytest
723

824
from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy
@@ -11,26 +27,26 @@
1127
class BaseToDictTests:
1228
"""Base tests for to_dict() method, inherited by all multidict type tests."""
1329

14-
def test_to_dict_simple(self, cls: Callable[..., MultiMapping[Any]]) -> None:
30+
def test_to_dict_simple(self, cls: DictFactory) -> None:
1531
"""Test basic conversion with unique keys."""
1632
d = cls([("a", 1), ("b", 2)])
1733
result = d.to_dict()
1834
assert result == {"a": [1], "b": [2]}
1935

20-
def test_to_dict_multi_values(self, cls: Callable[..., MultiMapping[Any]]) -> None:
36+
def test_to_dict_multi_values(self, cls: DictFactory) -> None:
2137
"""Test grouping multiple values under the same key."""
2238
d = cls([("a", 1), ("b", 2), ("a", 3)])
2339
result = d.to_dict()
2440
assert result == {"a": [1, 3], "b": [2]}
2541

26-
def test_to_dict_empty(self, cls: Callable[..., MultiMapping[Any]]) -> None:
42+
def test_to_dict_empty(self, cls: DictFactory) -> None:
2743
"""Test conversion of an empty multidict."""
2844
d = cls()
2945
result = d.to_dict()
3046
assert result == {}
3147

3248
def test_to_dict_returns_new_dict(
33-
self, cls: Callable[..., MultiMapping[Any]]
49+
self, cls: DictFactory
3450
) -> None:
3551
"""Test that each call returns a new dictionary instance."""
3652
d = cls([("a", 1)])
@@ -39,22 +55,22 @@ def test_to_dict_returns_new_dict(
3955
assert result1 == result2
4056
assert result1 is not result2
4157

42-
def test_to_dict_list_is_fresh(self, cls: Callable[..., MultiMapping[Any]]) -> None:
58+
def test_to_dict_list_is_fresh(self, cls: DictFactory) -> None:
4359
"""Test that value lists are independent between calls."""
4460
d = cls([("a", 1)])
4561
result1 = d.to_dict()
4662
result2 = d.to_dict()
4763
assert result1["a"] is not result2["a"]
4864

4965
def test_to_dict_order_preservation(
50-
self, cls: Callable[..., MultiMapping[Any]]
66+
self, cls: DictFactory
5167
) -> None:
5268
"""Test that value lists maintain insertion order."""
5369
d = cls([("x", 3), ("x", 1), ("x", 2)])
5470
result = d.to_dict()
5571
assert result["x"] == [3, 1, 2]
5672

57-
def test_to_dict_large_data(self, cls: Callable[..., MultiMapping[Any]]) -> None:
73+
def test_to_dict_large_data(self, cls: DictFactory) -> None:
5874
"""Test to_dict with a large number of entries for performance."""
5975
items = [(f"key{i % 100}", i) for i in range(10000)]
6076
d = cls(items)
@@ -63,7 +79,7 @@ def test_to_dict_large_data(self, cls: Callable[..., MultiMapping[Any]]) -> None
6379
assert all(len(v) == 100 for v in result.values())
6480

6581
def test_to_dict_mixed_value_types(
66-
self, cls: Callable[..., MultiMapping[Any]]
82+
self, cls: DictFactory
6783
) -> None:
6884
"""Test to_dict with mixed value types (str, int) to verify generic _V."""
6985
d = cls([("a", 1), ("a", "two"), ("b", 3.14)])
@@ -76,19 +92,19 @@ class TestMultiDictToDict(BaseToDictTests):
7692
"""Tests for MultiDict.to_dict()."""
7793

7894
@pytest.fixture
79-
def cls(self, multidict_module: Any) -> Type[MultiDict[Any]]:
80-
return multidict_module.MultiDict # type: ignore[no-any-return]
95+
def cls(self, multidict_module: MultidictModule) -> Type[MultiDict[object]]:
96+
return multidict_module.MultiDict
8197

8298

8399
class TestCIMultiDictToDict(BaseToDictTests):
84100
"""Tests for CIMultiDict.to_dict()."""
85101

86102
@pytest.fixture
87-
def cls(self, multidict_module: Any) -> Type[CIMultiDict[Any]]:
88-
return multidict_module.CIMultiDict # type: ignore[no-any-return]
103+
def cls(self, multidict_module: MultidictModule) -> Type[CIMultiDict[object]]:
104+
return multidict_module.CIMultiDict
89105

90106
def test_to_dict_case_insensitive_grouping(
91-
self, cls: Callable[..., MultiMapping[Any]]
107+
self, cls: DictFactory
92108
) -> None:
93109
"""Test that case variants are grouped under the same key."""
94110
d = cls([("A", 1), ("a", 2), ("B", 3)])
@@ -106,19 +122,19 @@ class TestMultiDictProxyToDict(BaseToDictTests):
106122
"""Tests for MultiDictProxy.to_dict()."""
107123

108124
@pytest.fixture
109-
def cls(self, multidict_module: Any) -> Any:
110-
def make_proxy(*args: Any, **kwargs: Any) -> MultiDictProxy[Any]:
111-
md = multidict_module.MultiDict(*args, **kwargs)
112-
return multidict_module.MultiDictProxy(md) # type: ignore[no-any-return]
125+
def cls(self, multidict_module: MultidictModule) -> DictFactory:
126+
def make_proxy(arg: Iterable[tuple[str, object]] | None = None) -> MultiMapping[object]:
127+
md: MultiDict[object] = multidict_module.MultiDict(arg) if arg else multidict_module.MultiDict()
128+
return multidict_module.MultiDictProxy(md)
113129

114130
return make_proxy
115131

116132
def test_to_dict_proxy_mutation_isolation(
117-
self, cls: Callable[..., MultiMapping[Any]], multidict_module: Any
133+
self, cls: DictFactory, multidict_module: MultidictModule
118134
) -> None:
119135
"""Test that modifying returned dict does not affect the proxy."""
120-
md = multidict_module.MultiDict([("a", 1)])
121-
proxy = multidict_module.MultiDictProxy(md)
136+
md: MultiDict[object] = multidict_module.MultiDict([("a", 1)])
137+
proxy: MultiMapping[object] = multidict_module.MultiDictProxy(md)
122138
result = proxy.to_dict()
123139
result["a"].append(999)
124140
assert proxy.getall("a") == [1]
@@ -128,15 +144,15 @@ class TestCIMultiDictProxyToDict(BaseToDictTests):
128144
"""Tests for CIMultiDictProxy.to_dict()."""
129145

130146
@pytest.fixture
131-
def cls(self, multidict_module: Any) -> Any:
132-
def make_proxy(*args: Any, **kwargs: Any) -> CIMultiDictProxy[Any]:
133-
md = multidict_module.CIMultiDict(*args, **kwargs)
134-
return multidict_module.CIMultiDictProxy(md) # type: ignore[no-any-return]
147+
def cls(self, multidict_module: MultidictModule) -> DictFactory:
148+
def make_proxy(arg: Iterable[tuple[str, object]] | None = None) -> MultiMapping[object]:
149+
md: CIMultiDict[object] = multidict_module.CIMultiDict(arg) if arg else multidict_module.CIMultiDict()
150+
return multidict_module.CIMultiDictProxy(md)
135151

136152
return make_proxy
137153

138154
def test_to_dict_case_insensitive_grouping(
139-
self, cls: Callable[..., MultiMapping[Any]]
155+
self, cls: DictFactory
140156
) -> None:
141157
"""Test that case variants are grouped under the same key."""
142158
d = cls([("A", 1), ("a", 2), ("B", 3)])
@@ -150,11 +166,11 @@ def test_to_dict_case_insensitive_grouping(
150166
assert result[key_b] == [3]
151167

152168
def test_to_dict_proxy_mutation_isolation(
153-
self, cls: Callable[..., MultiMapping[Any]], multidict_module: Any
169+
self, cls: DictFactory, multidict_module: MultidictModule
154170
) -> None:
155171
"""Test that modifying returned dict does not affect the proxy."""
156-
md = multidict_module.CIMultiDict([("a", 1)])
157-
proxy = multidict_module.CIMultiDictProxy(md)
172+
md: CIMultiDict[object] = multidict_module.CIMultiDict([("a", 1)])
173+
proxy: MultiMapping[object] = multidict_module.CIMultiDictProxy(md)
158174
result = proxy.to_dict()
159175
result["a"].append(999)
160176
assert proxy.getall("a") == [1]

0 commit comments

Comments
 (0)