From 1d1e7a88276a57ade356c9995207e395d56f5420 Mon Sep 17 00:00:00 2001 From: Simon Billinge Date: Sun, 8 Dec 2024 09:28:31 +0100 Subject: [PATCH] try just using CaseSenssitiveDict from requests to see if it works (#5744) * try just using CaseSenssitiveDict from requests to see if it works * put the dependency back in pyproject.toml and ignore typing on import * news * code now passing tests locally. I still need to write tests for the new Class * tests for CaseInsensitiveDict --- news/remove-caseinsdict.rst | 24 ++++++++++++ pyproject.toml | 4 +- tests/test_commands_cache.py | 75 ++++++++++++++++++++++++++++++++++++ xonsh/commands_cache.py | 46 +++++++++++++++++++++- 4 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 news/remove-caseinsdict.rst diff --git a/news/remove-caseinsdict.rst b/news/remove-caseinsdict.rst new file mode 100644 index 000000000..196e04f0f --- /dev/null +++ b/news/remove-caseinsdict.rst @@ -0,0 +1,24 @@ +**Added:** + +* + +**Changed:** + +* replaced `case_insensitive_dictionary` dependency with local + `CaseInsensitiveDict` class + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index fa6e2688b..632323698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,9 +15,7 @@ authors = [{ name = "Anthony Scopatz" }, { email = "scopatz@gmail.com" }] maintainers = [{ name = "Xonsh Community" }, { email = "xonsh@gil.forsyth.dev" }] license = { text = "BSD 2-Clause License" } requires-python = ">=3.9" -dependencies = [ - "case-insensitive-dictionary; platform_system=='Windows'", -] +dependencies = [] [tool.setuptools.dynamic] version = {attr = "xonsh.__version__"} diff --git a/tests/test_commands_cache.py b/tests/test_commands_cache.py index b8825ba1b..d90dec63c 100644 --- a/tests/test_commands_cache.py +++ b/tests/test_commands_cache.py @@ -8,6 +8,7 @@ import pytest from xonsh.commands_cache import ( SHELL_PREDICTOR_PARSER, + CaseInsensitiveDict, CommandsCache, _Commands, executables_in, @@ -306,3 +307,77 @@ def test_executables_in(xession): else: result = set(executables_in(test_path)) assert expected == result + + +def test_caseinsdict_constructor(): + actual = CaseInsensitiveDict({"key1": "val1", "Key2": "Val2"}) + assert isinstance(actual, CaseInsensitiveDict) + assert actual["key1"] == "val1" + assert actual["Key2"] == "Val2" + + +def test_caseinsdict_getitem(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert actual["Key1"] == "Val1" + assert actual["key1"] == "Val1" + + +def test_caseinsdict_setitem(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + actual["Key1"] = "Val2" + assert actual["Key1"] == "Val2" + assert actual["key1"] == "Val2" + actual["key1"] = "Val3" + assert actual["Key1"] == "Val3" + assert actual["key1"] == "Val3" + + +def test_caseinsdict_delitem(): + actual = CaseInsensitiveDict({"Key1": "Val1", "Key2": "Val2"}) + del actual["Key1"] + assert actual == CaseInsensitiveDict({"Key2": "Val2"}) + del actual["key2"] + assert actual == CaseInsensitiveDict({}) + + +def test_caseinsdict_contains(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert actual.__contains__("Key1") + assert actual.__contains__("key1") + assert not actual.__contains__("key2") + + +def test_caseinsdict_get(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert actual.get("Key1") == "Val1" + assert actual.get("key1") == "Val1" + assert actual.get("key2", "no val") == "no val" + assert actual.get("key1", "no val") == "Val1" + + +def test_caseinsdict_update(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + actual.update({"Key2": "Val2"}) + assert actual["key2"] == "Val2" + + +def test_caseinsdict_keys(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert next(actual.keys()) == "Key1" + + +def test_caseinsdict_items(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert next(actual.items()) == ("Key1", "Val1") + + +def test_caseinsdict_repr(): + actual = CaseInsensitiveDict({"Key1": "Val1"}) + assert actual.__repr__() == "CaseInsensitiveDict({'Key1': 'Val1'})" + + +def test_caseinsdict_copy(): + initial = CaseInsensitiveDict({"Key1": "Val1"}) + actual = initial.copy() + assert actual == initial + assert id(actual) != id(initial) diff --git a/xonsh/commands_cache.py b/xonsh/commands_cache.py index 0e8191584..bfd50f859 100644 --- a/xonsh/commands_cache.py +++ b/xonsh/commands_cache.py @@ -23,8 +23,52 @@ from xonsh.procs.executables import ( is_executable_in_windows, ) + +class CaseInsensitiveDict(dict[tp.Any, tp.Any]): + def __init__(self, *args, **kwargs): + super().__init__() + self._store = {} + self.update(*args, **kwargs) + + def __setitem__(self, key, value): + # Store the key in lowercase but preserve the original case for display + self._store[key.casefold()] = key + super().__setitem__(key.casefold(), value) + + def __getitem__(self, key): + return super().__getitem__(key.casefold()) + + def __delitem__(self, key): + del self._store[key.casefold()] + super().__delitem__(key.casefold()) + + def __contains__(self, key): + return key.casefold() in self._store + + def get(self, key, default=None): + return super().get(key.casefold(), default) + + def update(self, *args, **kwargs): + for k, v in dict(*args, **kwargs).items(): + self[k] = v + + def keys(self): + # Return the original keys with their original casing + return (self._store[k] for k in self._store) + + def items(self): + return ((self._store[k], self[k]) for k in self._store) + + def __repr__(self): + return f"{self.__class__.__name__}({dict(self.items())})" + + def copy(self): + return CaseInsensitiveDict(self.items()) + + +CacheDict: tp.Union[type[CaseInsensitiveDict], type[dict]] if ON_WINDOWS: - from case_insensitive_dict import CaseInsensitiveDict as CacheDict + CacheDict = CaseInsensitiveDict else: CacheDict = dict