Skip to content

Commit fa45526

Browse files
committed
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.
1 parent 804bc80 commit fa45526

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

‎git/util.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,7 @@ def __contains__(self, attr: object) -> bool:
13821382

13831383
# Otherwise make a full name search.
13841384
try:
1385-
getattr(self, cast(str, attr)) # Use cast to silence mypy.
1385+
self.__getattr__(cast(str, attr)) # Use cast to silence mypy.
13861386
return True
13871387
except (AttributeError, TypeError):
13881388
return False
@@ -1394,7 +1394,7 @@ def __getattr__(self, attr: str) -> T_IterableObj:
13941394
if getattr(item, self._id_attr) == attr:
13951395
return item
13961396
# END for each item
1397-
return list.__getattribute__(self, attr)
1397+
raise AttributeError(f"No item found with id {attr}")
13981398

13991399
def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
14001400
self, index: Union[SupportsIndex, int, slice, str]
@@ -1405,7 +1405,7 @@ def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatible
14051405
raise ValueError("Index should be an int or str")
14061406
else:
14071407
try:
1408-
return getattr(self, cast(str, index))
1408+
return self.__getattr__(cast(str, index))
14091409
except AttributeError as e:
14101410
raise IndexError(f"No item found with id {self._prefix}{index}") from e
14111411
# END handle getattr

‎test/test_util.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,3 +862,23 @@ def test_mode_str_to_int_accepts_bytes():
862862
assert mode_str_to_int(b"100644") == 0o100644
863863
assert mode_str_to_int("644") == 0o644
864864
assert mode_str_to_int(b"120000") == 0o120000
865+
866+
867+
@pytest.mark.parametrize("name", ["sort", "count", "append", "_prefix", "__class__", "ordinary"])
868+
@pytest.mark.parametrize("prefix", ["", "refs/"])
869+
def test_iterable_list_name_lookup_ignores_list_attributes(name, prefix):
870+
values = IterableList("name", prefix)
871+
member = _Member(prefix + name)
872+
values.append(member)
873+
874+
assert values[name] is member
875+
assert name in values
876+
assert member in values
877+
assert values[0] is member
878+
assert callable(values.append)
879+
assert callable(values.sort)
880+
881+
del values[name]
882+
assert name not in values
883+
with pytest.raises(IndexError, match="No item found with id"):
884+
values[name]

0 commit comments

Comments
 (0)