Skip to content

Commit 66d3e3b

Browse files
fix(data-classes): keep CaseInsensitiveDict on copy and merge (#8537)
* fix(data-classes): keep CaseInsensitiveDict on copy and merge * test(data-classes): cover non-mapping operands in CaseInsensitiveDict merge --------- Co-authored-by: Leandro Damascena <lcdama@amazon.pt>
1 parent 5524980 commit 66d3e3b

2 files changed

Lines changed: 61 additions & 0 deletions

File tree

‎aws_lambda_powertools/utilities/data_classes/common.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,27 @@ def update(self, data=None, **kwargs):
6363
super().update((k.lower(), v) for k, v in data)
6464
super().update((k.lower(), v) for k, v in kwargs.items())
6565

66+
def copy(self):
67+
return CaseInsensitiveDict(self)
68+
69+
def __or__(self, other):
70+
if not isinstance(other, Mapping):
71+
return NotImplemented
72+
new = self.copy()
73+
new.update(other)
74+
return new
75+
76+
def __ror__(self, other):
77+
if not isinstance(other, Mapping):
78+
return NotImplemented
79+
new = CaseInsensitiveDict(other)
80+
new.update(self)
81+
return new
82+
83+
def __ior__(self, other):
84+
self.update(other)
85+
return self
86+
6687
def __contains__(self, k):
6788
return super().__contains__(k.lower())
6889

‎tests/unit/data_classes/required_dependencies/test_common.py‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import pytest
4+
35
from aws_lambda_powertools.utilities.data_classes.common import CaseInsensitiveDict
46

57

@@ -17,3 +19,41 @@ def test_case_insensitive_dict_update_with_kwargs():
1719
assert headers["AB"] == "1"
1820
assert headers["user_agent"] == "test"
1921
assert len(headers) == 3
22+
23+
24+
def test_case_insensitive_dict_copy_keeps_case_insensitive_lookup():
25+
headers = CaseInsensitiveDict({"Content-Type": "application/json"})
26+
copied = headers.copy()
27+
28+
assert isinstance(copied, CaseInsensitiveDict)
29+
assert copied.get("Content-Type") == "application/json"
30+
31+
copied["X-Trace-Id"] = "abc"
32+
assert "x-trace-id" not in headers
33+
34+
35+
def test_case_insensitive_dict_merge_operators():
36+
headers = CaseInsensitiveDict({"Content-Type": "application/json"})
37+
38+
merged = headers | {"X-Trace-Id": "abc"}
39+
assert isinstance(merged, CaseInsensitiveDict)
40+
assert merged["x-trace-id"] == "abc"
41+
assert merged["CONTENT-TYPE"] == "application/json"
42+
43+
merged = {"X-Trace-Id": "abc"} | headers
44+
assert isinstance(merged, CaseInsensitiveDict)
45+
assert merged["X-TRACE-ID"] == "abc"
46+
47+
headers |= {"X-Trace-Id": "abc"}
48+
assert headers["x-trace-id"] == "abc"
49+
assert list(headers) == ["content-type", "x-trace-id"]
50+
51+
52+
def test_case_insensitive_dict_merge_rejects_non_mapping():
53+
headers = CaseInsensitiveDict({"Content-Type": "application/json"})
54+
55+
with pytest.raises(TypeError):
56+
headers | [("X-Trace-Id", "abc")]
57+
58+
with pytest.raises(TypeError):
59+
[("X-Trace-Id", "abc")] | headers

0 commit comments

Comments
 (0)