From 0d9380040ce11b466ac0941290e146d8f0df9c40 Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 11:32:28 +0000
Subject: [PATCH 1/8] feature (v0.1.4): enhance KeyError message
---
README.md | 14 +-
pyproject.toml | 2 +-
.../case_insensitive_dict.py | 12 +-
src/tests/test_case_insensitive_dict.py | 155 +++++++++++++-----
4 files changed, 121 insertions(+), 62 deletions(-)
diff --git a/README.md b/README.md
index f6ce3be..d5c054d 100644
--- a/README.md
+++ b/README.md
@@ -28,18 +28,6 @@ $ pip install -U case-insensitive-dictionary
CaseInsensitiveDict:
-```py
->>> from case_insensitive_dict import CaseInsensitiveDict
-
->>> case_insensitive_dict = CaseInsensitiveDict[str, str](data={"Aa": "b"})
->>> case_insensitive_dict.get("aa")
-'b'
->>> case_insensitive_dict.get("Aa")
-'b'
-```
-
-also supports generic keys:
-
```py
>>> from typing import Union
@@ -53,7 +41,7 @@ also supports generic keys:
```
-and json encoding/decoding:
+which also supports json encoding/decoding:
```py
>>> import json
diff --git a/pyproject.toml b/pyproject.toml
index 8d2328c..bc2dce2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "case-insensitive-dictionary"
-version = "0.1.3"
+version = "0.1.4"
description = "Case Insensitive Dictionary"
authors = ["rikhilrai"]
license = "MIT"
diff --git a/src/case_insensitive_dict/case_insensitive_dict.py b/src/case_insensitive_dict/case_insensitive_dict.py
index 1c762fe..62d228a 100644
--- a/src/case_insensitive_dict/case_insensitive_dict.py
+++ b/src/case_insensitive_dict/case_insensitive_dict.py
@@ -12,6 +12,7 @@
from typing import Tuple
from typing import TypeVar
+T = TypeVar('T') # pylint: disable=invalid-name
KT = TypeVar('KT') # pylint: disable=invalid-name
VT = TypeVar('VT') # pylint: disable=invalid-name
@@ -30,6 +31,9 @@ def __init__(self, data: Optional[Mapping[KT, VT]] = None) -> None:
data = {}
self.update(data)
+ def __repr__(self) -> str:
+ return f'{self.__class__.__name__}({dict(self.items())!r})'
+
@staticmethod
def _convert_key(key: KT) -> KT:
if isinstance(key, str):
@@ -40,7 +44,10 @@ def __setitem__(self, key: KT, value: VT) -> None:
self._data[self._convert_key(key=key)] = (key, value)
def __getitem__(self, key: KT) -> VT:
- return self._data[self._convert_key(key=key)][1]
+ try:
+ return self._data[self._convert_key(key=key)][1]
+ except KeyError:
+ raise KeyError(f"Key: {key!r} not found.") from None
def __delitem__(self, key: KT) -> None:
del self._data[self._convert_key(key=key)]
@@ -63,9 +70,6 @@ def __eq__(self, other: Any) -> bool:
def copy(self) -> CaseInsensitiveDict[KT, VT]:
return CaseInsensitiveDict(data=dict(self._data.values()))
- def __repr__(self) -> str:
- return f'{self.__class__.__name__}({dict(self.items())!r})'
-
class CaseInsensitiveDictJSONEncoder(JSONEncoder):
def default(self, o: CaseInsensitiveDict[KT, VT]) -> Mapping[KT, VT]:
diff --git a/src/tests/test_case_insensitive_dict.py b/src/tests/test_case_insensitive_dict.py
index 5d46436..ed7dbb0 100644
--- a/src/tests/test_case_insensitive_dict.py
+++ b/src/tests/test_case_insensitive_dict.py
@@ -19,52 +19,52 @@ class CaseInsensitiveDictTestCase:
class TestInit(CaseInsensitiveDictTestCase):
# check that the store is structured as expected
def test_store_written(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data={"a": "b"})
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
assert case_insensitive_dict._data == {"a": ("a", "b")}
# check that the key in the store is lowered
def test_store_written_case_insensitive(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data={"A": "b"})
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
assert case_insensitive_dict._data == {"a": ("A", "b")}
# check instantiated with an empty dict
def test_store_written_empty(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data={})
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({})
assert isinstance(case_insensitive_dict._data, dict)
assert not case_insensitive_dict._data
# check instantiated with none
def test_store_written_none(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data=None)
+ case_insensitive_dict = CaseInsensitiveDict[str, str](None)
assert isinstance(case_insensitive_dict._data, dict)
assert not case_insensitive_dict._data
# check instantiated with none value
def test_store_written_with_optional_value(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, Optional[str]](data={"A": None})
+ case_insensitive_dict = CaseInsensitiveDict[str, Optional[str]]({"A": None})
assert case_insensitive_dict._data == {"a": ("A", None)}
- # check instantiation with non-str key
- def test_init_with_non_str_key(self) -> None:
- case_insensitive_dict_int = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check instantiation with non-str keys
+ def test_init_with_non_str_keys(self) -> None:
+ case_insensitive_dict_int = CaseInsensitiveDict[int, str]({1: "b"})
assert case_insensitive_dict_int._data == {1: (1, "b")}
- case_insensitive_dict_bool = CaseInsensitiveDict[bool, str](data={True: "b"})
+ case_insensitive_dict_bool = CaseInsensitiveDict[bool, str]({True: "b"})
assert case_insensitive_dict_bool._data == {True: (True, "b")}
class TestTyping(CaseInsensitiveDictTestCase):
# check valid typing
def test_valid_types(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data={"a": "b"})
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
# keys
case_insensitive_dict['a'] # pylint: disable=pointless-statement
case_insensitive_dict.get('a')
# values
case_insensitive_dict['b'] = 'a'
- # check valid typing
- def test_valid_types_non_str_key(self) -> None:
- case_insensitive_dict_int = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check valid typings
+ def test_valid_types_non_str_keys(self) -> None:
+ case_insensitive_dict_int = CaseInsensitiveDict[int, str]({1: "b"})
# keys
case_insensitive_dict_int[1] # pylint: disable=pointless-statement
case_insensitive_dict_int.get(1)
@@ -73,7 +73,7 @@ def test_valid_types_non_str_key(self) -> None:
# check valid with union
def test_valid_types_union(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[Union[str, int], Union[str, int, bool]](data={"a": "b", "b": 1, 1: "c"})
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], Union[str, int, bool]]({"a": "b", "b": 1, 1: "c"})
# keys
case_insensitive_dict[1] # pylint: disable=pointless-statement
case_insensitive_dict.get(1)
@@ -89,7 +89,7 @@ def test_valid_types_union(self) -> None:
# check invalid types
def test_invalid_type(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, int](data={"a": "3"}) # type: ignore[dict-item]
+ case_insensitive_dict = CaseInsensitiveDict[str, int]({"a": "3"}) # type: ignore[dict-item]
case_insensitive_dict[1] = 2 # type: ignore[index]
case_insensitive_dict['b'] = "2" # type: ignore[assignment]
@@ -97,15 +97,15 @@ def test_invalid_type(self) -> None:
class TestContains(CaseInsensitiveDictTestCase):
# check that key in CaseInsensitiveDict check works as expected
def test_contains(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str](data={"A": "b"})
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
assert 'A' in case_insensitive_dict
assert 'a' in case_insensitive_dict
- # check contains with non-str key
- def test_contains_with_non_str_key(self) -> None:
- case_insensitive_dict_int = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check contains with non-str keys
+ def test_contains_with_non_str_keys(self) -> None:
+ case_insensitive_dict_int = CaseInsensitiveDict[int, str]({1: "b"})
assert 1 in case_insensitive_dict_int
- case_insensitive_dict_bool = CaseInsensitiveDict[bool, str](data={True: "b"})
+ case_insensitive_dict_bool = CaseInsensitiveDict[bool, str]({True: "b"})
assert True in case_insensitive_dict_bool
@@ -123,9 +123,9 @@ def test_value(self) -> None:
case_insensitive_dict["A"] = "c"
assert case_insensitive_dict._data == {"a": ("A", "c")}
- # check set item with non-str key
- def test_set_item_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check set item with non-str keys
+ def test_set_item_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[int, str]({1: "b"})
assert case_insensitive_dict._data == {1: (1, "b")}
case_insensitive_dict[1] = "c"
assert case_insensitive_dict._data == {1: (1, "c")}
@@ -142,24 +142,29 @@ def test_value_returned(self) -> None:
def test_key_missing(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]()
assert case_insensitive_dict.get("b") is None
- with pytest.raises(KeyError):
+ with pytest.raises(KeyError, match=r"Key: 'b' not found."):
assert case_insensitive_dict["b"]
+ # check behaviour when key is missing and default passed
+ def test_key_missing_with_default(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]()
+ assert case_insensitive_dict.get("b", 1) == 1
+
# check value returned using get
def test_value_returned_using_get(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
assert case_insensitive_dict.get("A") == "b"
assert case_insensitive_dict.get("a") == "b"
- # check get item with non-str key
- def test_get_item_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check get item with non-str keys
+ def test_get_item_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[int, str]({1: "b"})
assert case_insensitive_dict.get(1) == "b"
assert case_insensitive_dict[1] == "b"
# check instantiated with none value
def test_store_written_with_optional_value(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, Optional[str]](data={"A": None})
+ case_insensitive_dict = CaseInsensitiveDict[str, Optional[str]]({"A": None})
assert case_insensitive_dict.get("a") is None
assert case_insensitive_dict["a"] is None
assert "a" in case_insensitive_dict
@@ -173,9 +178,9 @@ def test_value_removed(self) -> None:
del case_insensitive_dict["A"]
assert "a" not in case_insensitive_dict
- # check del item with non-str key
- def test_del_item_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[int, str](data={1: "b"})
+ # check del item with non-str keys
+ def test_del_item_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[int, str]({1: "b"})
assert case_insensitive_dict[1] == "b"
del case_insensitive_dict[1]
assert 1 not in case_insensitive_dict
@@ -187,9 +192,9 @@ def test_iter(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
assert list(case_insensitive_dict) == ["a"]
- # check iter with non-str key
- def test_iter_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str](data={1: "b", "a": "c"})
+ # check iter with non-str keys
+ def test_iter_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
assert list(case_insensitive_dict) == [1, "a"]
@@ -217,9 +222,9 @@ def test_lower_empty(self) -> None:
assert isinstance(case_insensitive_dict.lower_items(), GeneratorType)
assert not list(case_insensitive_dict.lower_items())
- # check lower items with non-str key
- def test_lower_items_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str](data={1: "b", "a": "c"})
+ # check lower items with non-str keys
+ def test_lower_items_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
assert list(case_insensitive_dict.lower_items()) == [(1, "b"), ("a", "c")]
@@ -244,9 +249,9 @@ def test_not_equality(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]()
assert case_insensitive_dict != 1
- # check equality with non-str key
- def test_equality_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str](data={1: "b", "a": "c"})
+ # check equality with non-str keys
+ def test_equality_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
assert case_insensitive_dict == {1: "b", "a": "c"}
@@ -262,9 +267,9 @@ def test_copy_ids(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
assert id(case_insensitive_dict) != id(case_insensitive_dict.copy())
- # check copy with non-str key
- def test_copy_with_non_str_key(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str](data={1: "b", "a": "c"})
+ # check copy with non-str keys
+ def test_copy_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
assert case_insensitive_dict.copy() == case_insensitive_dict
assert case_insensitive_dict == case_insensitive_dict.copy()
@@ -273,7 +278,7 @@ class TestJson(CaseInsensitiveDictTestCase):
# check to_json
def test_to_json(self) -> None:
data: Dict[Union[bool, str, int], Union[str, int, bool]] = {"A": "a", "b": 1, "c": False, 2: "a", True: 2}
- case_insensitive_dict = CaseInsensitiveDict[Union[bool, str, int], Union[str, int, bool]](data=data)
+ case_insensitive_dict = CaseInsensitiveDict[Union[bool, str, int], Union[str, int, bool]](data)
json_string = json.dumps(obj=case_insensitive_dict, cls=CaseInsensitiveDictJSONEncoder)
assert json_string == '{"A": "a", "b": 1, "c": false, "2": "a", "true": 2}'
assert json_string == json.dumps(data)
@@ -285,3 +290,65 @@ def test_from_json(self) -> None:
expected_case_insensitive_dict = CaseInsensitiveDict[Union[bool, str, int], Union[str, int, bool]]({"A": "a", "b": 1, "c": False, '2': "a", 'true': 2})
assert case_insensitive_dict == expected_case_insensitive_dict
assert case_insensitive_dict == json.loads(json_string)
+
+
+class TestStrAndRepr(CaseInsensitiveDictTestCase):
+ # check string and representation
+ def test_str_and_repr(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ assert case_insensitive_dict.__str__() == "CaseInsensitiveDict({'A': 'b'})"
+ assert case_insensitive_dict.__repr__() == "CaseInsensitiveDict({'A': 'b'})"
+
+
+class TestDictMethods(CaseInsensitiveDictTestCase):
+ # check dict
+ def test_dict(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ assert dict(case_insensitive_dict) == {'A': 'b'}
+
+ # check dict with non-str keys
+ def test_dict_with_non_str_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
+ assert dict(case_insensitive_dict) == {1: "b", "a": "c"}
+
+ # check keys
+ def test_keys(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ assert list(case_insensitive_dict.keys()) == ["A"]
+
+ # check falsey dict
+ def test_falsey(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]()
+ assert not case_insensitive_dict
+ assert bool(case_insensitive_dict) is False
+
+ # check truthy dict
+ def test_truthy(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
+ assert case_insensitive_dict
+ assert bool(case_insensitive_dict) is True
+
+ # check clear
+ def test_clear(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ case_insensitive_dict.clear()
+ assert not case_insensitive_dict
+
+ # check pop
+ def test_pop(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ response = case_insensitive_dict.pop("a")
+ assert response == "b"
+ assert not case_insensitive_dict
+
+ # check pop key not in dictionary
+ def test_pop_key_not_in_dictionary(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ with pytest.raises(KeyError):
+ case_insensitive_dict.pop("b")
+
+ # check pop key not in dictionary with default
+ def test_pop_key_not_in_dictionary_with_default(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ response = case_insensitive_dict.pop("b", "a")
+ assert response == 'a'
From 249525c4776c39be4abdae0b32d69b0da7dbd8bb Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 11:33:06 +0000
Subject: [PATCH 2/8] .
---
src/case_insensitive_dict/case_insensitive_dict.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/case_insensitive_dict/case_insensitive_dict.py b/src/case_insensitive_dict/case_insensitive_dict.py
index 62d228a..870043d 100644
--- a/src/case_insensitive_dict/case_insensitive_dict.py
+++ b/src/case_insensitive_dict/case_insensitive_dict.py
@@ -12,7 +12,6 @@
from typing import Tuple
from typing import TypeVar
-T = TypeVar('T') # pylint: disable=invalid-name
KT = TypeVar('KT') # pylint: disable=invalid-name
VT = TypeVar('VT') # pylint: disable=invalid-name
From bc3a83b9409da6e3736f486e7ad8986377bac0af Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:25:20 +0000
Subject: [PATCH 3/8] .
---
.../case_insensitive_dict.py | 15 +++
src/tests/test_case_insensitive_dict.py | 91 ++++++++++++++++---
2 files changed, 91 insertions(+), 15 deletions(-)
diff --git a/src/case_insensitive_dict/case_insensitive_dict.py b/src/case_insensitive_dict/case_insensitive_dict.py
index 870043d..a9685f2 100644
--- a/src/case_insensitive_dict/case_insensitive_dict.py
+++ b/src/case_insensitive_dict/case_insensitive_dict.py
@@ -6,11 +6,14 @@
from typing import Any
from typing import Dict
from typing import Generic
+from typing import Iterable
from typing import Iterator
from typing import Mapping
from typing import Optional
from typing import Tuple
from typing import TypeVar
+from typing import Union
+from typing import overload
KT = TypeVar('KT') # pylint: disable=invalid-name
VT = TypeVar('VT') # pylint: disable=invalid-name
@@ -23,7 +26,15 @@
class CaseInsensitiveDict(MutableMapping, Generic[KT, VT]):
+ @overload
def __init__(self, data: Optional[Mapping[KT, VT]] = None) -> None:
+ ...
+
+ @overload
+ def __init__(self, data: Optional[Iterable[Tuple[KT, VT]]] = None) -> None:
+ ...
+
+ def __init__(self, data: Optional[Union[Mapping[KT, VT], Iterable[Tuple[KT, VT]]]] = None) -> None:
# Mapping from lowercased key to tuple of (actual key, value)
self._data: Dict[KT, Tuple[KT, VT]] = {}
if data is None:
@@ -69,6 +80,10 @@ def __eq__(self, other: Any) -> bool:
def copy(self) -> CaseInsensitiveDict[KT, VT]:
return CaseInsensitiveDict(data=dict(self._data.values()))
+ @classmethod
+ def fromkeys(cls, iterable: Iterable[KT], value: VT) -> CaseInsensitiveDict[KT, VT]:
+ return cls([(key, value) for key in iterable])
+
class CaseInsensitiveDictJSONEncoder(JSONEncoder):
def default(self, o: CaseInsensitiveDict[KT, VT]) -> Mapping[KT, VT]:
diff --git a/src/tests/test_case_insensitive_dict.py b/src/tests/test_case_insensitive_dict.py
index ed7dbb0..6aeab95 100644
--- a/src/tests/test_case_insensitive_dict.py
+++ b/src/tests/test_case_insensitive_dict.py
@@ -51,6 +51,17 @@ def test_init_with_non_str_keys(self) -> None:
case_insensitive_dict_bool = CaseInsensitiveDict[bool, str]({True: "b"})
assert case_insensitive_dict_bool._data == {True: (True, "b")}
+ # check picks the last key/value if instantiated with conflicting cases
+ def test_store_written_with_conflicting_cases(self) -> None:
+ case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b", "A": "c"})
+ assert case_insensitive_dict._data == {"a": ("A", "c")}
+
+ # check instantiated with list of tuples
+ def test_store_written_with_list_of_tuples(self) -> None:
+ data = [("A", "b")]
+ case_insensitive_dict = CaseInsensitiveDict[str, str](data)
+ assert case_insensitive_dict._data == {"a": ("A", "b")}
+
class TestTyping(CaseInsensitiveDictTestCase):
# check valid typing
@@ -300,55 +311,105 @@ def test_str_and_repr(self) -> None:
assert case_insensitive_dict.__repr__() == "CaseInsensitiveDict({'A': 'b'})"
+class TestFromKeys(CaseInsensitiveDictTestCase):
+ # check fromkeys
+ def test_fromkeys(self) -> None:
+ dictionary = dict.fromkeys(["A", "b"], "c")
+ assert dictionary == {"A": "c", "b": "c"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str].fromkeys(["A", "b"], "c")
+ assert case_insensitive_dict == dictionary
+
+
class TestDictMethods(CaseInsensitiveDictTestCase):
# check dict
def test_dict(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
- assert dict(case_insensitive_dict) == {'A': 'b'}
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert dict(case_insensitive_dict) == dict(dictionary)
# check dict with non-str keys
def test_dict_with_non_str_keys(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[Union[str, int], str]({1: "b", "a": "c"})
- assert dict(case_insensitive_dict) == {1: "b", "a": "c"}
-
- # check keys
- def test_keys(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
- assert list(case_insensitive_dict.keys()) == ["A"]
+ assert dict(case_insensitive_dict) == dict({1: "b", "a": "c"})
# check falsey dict
def test_falsey(self) -> None:
case_insensitive_dict = CaseInsensitiveDict[str, str]()
assert not case_insensitive_dict
assert bool(case_insensitive_dict) is False
+ assert not {}
+ assert bool({}) is False
# check truthy dict
def test_truthy(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"a": "b"})
+ dictionary = {"a": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
assert case_insensitive_dict
assert bool(case_insensitive_dict) is True
+ assert dictionary
+ assert bool(dictionary) is True
# check clear
def test_clear(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
case_insensitive_dict.clear()
assert not case_insensitive_dict
+ dictionary.clear()
+ assert not dictionary
+
+ # check reference to instantiated value not maintained
+ def test_reference(self) -> None:
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert id(case_insensitive_dict._data) != id(dictionary)
+ case_insensitive_dict.pop("a")
+ assert dictionary == {"A": "b"}
# check pop
def test_pop(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
- response = case_insensitive_dict.pop("a")
- assert response == "b"
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert dictionary.pop("A") == case_insensitive_dict.pop("a") == "b"
assert not case_insensitive_dict
+ assert not dictionary
# check pop key not in dictionary
def test_pop_key_not_in_dictionary(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
with pytest.raises(KeyError):
case_insensitive_dict.pop("b")
+ with pytest.raises(KeyError):
+ dictionary.pop("b")
# check pop key not in dictionary with default
def test_pop_key_not_in_dictionary_with_default(self) -> None:
- case_insensitive_dict = CaseInsensitiveDict[str, str]({"A": "b"})
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert dictionary.pop("A") == case_insensitive_dict.pop("a") == "b"
response = case_insensitive_dict.pop("b", "a")
assert response == 'a'
+
+ # check popitem
+ def test_pop_item(self) -> None:
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert dictionary.popitem() == case_insensitive_dict.popitem() == ("A", "b")
+ with pytest.raises(KeyError):
+ case_insensitive_dict.popitem()
+ with pytest.raises(KeyError):
+ dictionary.popitem()
+
+ # check keys
+ def test_keys(self) -> None:
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert dictionary.keys() == case_insensitive_dict.keys()
+ assert list(dictionary.keys()) == list(case_insensitive_dict.keys()) == ["A"]
+
+ # check values
+ def test_values(self) -> None:
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert list(dictionary.values()) == list(case_insensitive_dict.values()) == ["b"]
From 1fda765dbb2c0b3bcb6d4062cd476030cd695be7 Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:38:50 +0000
Subject: [PATCH 4/8] .
---
README.md | 14 ++++++++++++++
src/tests/test_case_insensitive_dict.py | 6 ++++++
2 files changed, 20 insertions(+)
diff --git a/README.md b/README.md
index d5c054d..dfa766a 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,20 @@ Install and update using [pip](https://pypi.org/project/case-insensitive-diction
$ pip install -U case-insensitive-dictionary
```
+## API Reference
+
+| Method | Description |
+| :------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
+| clear() | Removes all elements from the dictionary. |
+| copy() | Returns a copy of the dictionary. |
+| get(key, default) | Returns the value (case-insensitively), of the item specified with the key.
Falls back to the default value if the specified key does not exist. |
+| fromkeys(iterable, value) | Returns a dictionary with the specified keys and the specified value. |
+| keys() | Returns the dictionary's keys. |
+| values() | Returns the dictionary's values. |
+| items() | Returns the key-value pairs. |
+| pop(key) | Remove the specified item (case-insensitively).
The value of the removed item is the return value. |
+| popitem() | Remove the last item that was inserted into the dictionary.
For Python version <3.7, popitem() removes a random item. |
+
## Example
CaseInsensitiveDict:
diff --git a/src/tests/test_case_insensitive_dict.py b/src/tests/test_case_insensitive_dict.py
index 6aeab95..38ccac7 100644
--- a/src/tests/test_case_insensitive_dict.py
+++ b/src/tests/test_case_insensitive_dict.py
@@ -413,3 +413,9 @@ def test_values(self) -> None:
dictionary = {"A": "b"}
case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
assert list(dictionary.values()) == list(case_insensitive_dict.values()) == ["b"]
+
+ # check items
+ def test_items(self) -> None:
+ dictionary = {"A": "b"}
+ case_insensitive_dict = CaseInsensitiveDict[str, str](dictionary)
+ assert list(dictionary.items()) == list(case_insensitive_dict.items()) == [("A", "b")]
From 04c6bc27d84b7a86d5ba4cfeb7f9f05e9dd2de9b Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:39:31 +0000
Subject: [PATCH 5/8] .
---
.coveragerc | 1 +
1 file changed, 1 insertion(+)
diff --git a/.coveragerc b/.coveragerc
index 0add24e..d64169d 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -15,6 +15,7 @@ exclude_lines =
@(abc\.)?abstractmethod
if TYPE_CHECKING
if sys\.version_info
+ @overload
fail_under = 95
From a296338deb824833c3f9722f8e2862e8eec0ec6f Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:40:03 +0000
Subject: [PATCH 6/8] .
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index bc2dce2..7b1c063 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "case-insensitive-dictionary"
-version = "0.1.4"
+version = "0.2.0"
description = "Case Insensitive Dictionary"
authors = ["rikhilrai"]
license = "MIT"
From 1f4f13ea569afd2fd013d6a15c357713fe4fea3c Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:48:12 +0000
Subject: [PATCH 7/8] .
---
src/case_insensitive_dict/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/case_insensitive_dict/__init__.py b/src/case_insensitive_dict/__init__.py
index d6a24cf..0c34020 100644
--- a/src/case_insensitive_dict/__init__.py
+++ b/src/case_insensitive_dict/__init__.py
@@ -8,7 +8,7 @@
from importlib_metadata import PackageNotFoundError # type: ignore[no-redef,misc]
try:
- __version__: str = version(__name__)
+ __version__: str = version('case-insensitive-dictionary')
except PackageNotFoundError:
__version__ = "unknown"
From 75f5c4b811bd0eda237ba754c5f228076c402ce1 Mon Sep 17 00:00:00 2001
From: Rikhil <23627977+rikhilrai@users.noreply.github.com>
Date: Wed, 2 Feb 2022 12:49:45 +0000
Subject: [PATCH 8/8] .
---
pyproject.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 7b1c063..c620a33 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]
name = "case-insensitive-dictionary"
version = "0.2.0"
-description = "Case Insensitive Dictionary"
+description = "Typed Python Case Insensitive Dictionary"
authors = ["rikhilrai"]
license = "MIT"
readme = "README.md"