From fa45526e9d87ddb71de7dc0fbde24a916f2051b2 Mon Sep 17 00:00:00 2001 From: fan wu <336798967+FanWu-ai@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:19:24 -0700 Subject: [PATCH] fix: resolve IterableList names independently of list attributes Normal attribute lookup makes string indexing return list methods for item names such as `count`, and reports absent `append` identifiers as present. Use `__getattr__` explicitly for identifier lookup and remove its fallback to list attributes. Ordinary attribute access still exposes list methods. Add twelve present/absent collision cases with and without prefixes. The full suite passes 1701 tests, 89 conditional skips, one existing xfail, and 64 subtests; pinned hooks, type checks, docs and packaging also pass. --- git/util.py | 6 +++--- test/test_util.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/git/util.py b/git/util.py index 61ddab861..c007c93ed 100644 --- a/git/util.py +++ b/git/util.py @@ -1382,7 +1382,7 @@ def __contains__(self, attr: object) -> bool: # Otherwise make a full name search. try: - getattr(self, cast(str, attr)) # Use cast to silence mypy. + self.__getattr__(cast(str, attr)) # Use cast to silence mypy. return True except (AttributeError, TypeError): return False @@ -1394,7 +1394,7 @@ def __getattr__(self, attr: str) -> T_IterableObj: if getattr(item, self._id_attr) == attr: return item # END for each item - return list.__getattribute__(self, attr) + raise AttributeError(f"No item found with id {attr}") def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] self, index: Union[SupportsIndex, int, slice, str] @@ -1405,7 +1405,7 @@ def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatible raise ValueError("Index should be an int or str") else: try: - return getattr(self, cast(str, index)) + return self.__getattr__(cast(str, index)) except AttributeError as e: raise IndexError(f"No item found with id {self._prefix}{index}") from e # END handle getattr diff --git a/test/test_util.py b/test/test_util.py index ca8e006f3..d759343f0 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -862,3 +862,23 @@ def test_mode_str_to_int_accepts_bytes(): assert mode_str_to_int(b"100644") == 0o100644 assert mode_str_to_int("644") == 0o644 assert mode_str_to_int(b"120000") == 0o120000 + + +@pytest.mark.parametrize("name", ["sort", "count", "append", "_prefix", "__class__", "ordinary"]) +@pytest.mark.parametrize("prefix", ["", "refs/"]) +def test_iterable_list_name_lookup_ignores_list_attributes(name, prefix): + values = IterableList("name", prefix) + member = _Member(prefix + name) + values.append(member) + + assert values[name] is member + assert name in values + assert member in values + assert values[0] is member + assert callable(values.append) + assert callable(values.sort) + + del values[name] + assert name not in values + with pytest.raises(IndexError, match="No item found with id"): + values[name]