Skip to content

Commit 604f181

Browse files
authored
Merge pull request #2326 from gitpython-developers/fix/iterable-list-index-protocol
fix/iterable list index protocol
2 parents e347d11 + 451a526 commit 604f181

2 files changed

Lines changed: 45 additions & 6 deletions

File tree

‎git/util.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,13 +1334,13 @@ def __getattr__(self, attr: str) -> T_IterableObj:
13341334
def __getitem__( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
13351335
self, index: Union[SupportsIndex, int, slice, str]
13361336
) -> T_IterableObj:
1337-
if isinstance(index, int):
1338-
return list.__getitem__(self, index)
1339-
elif isinstance(index, slice):
1337+
if isinstance(index, slice):
13401338
raise ValueError("Index should be an int or str")
1339+
elif not isinstance(index, str):
1340+
return list.__getitem__(self, index)
13411341
else:
13421342
try:
1343-
return self.__getattr__(cast(str, index))
1343+
return self.__getattr__(index)
13441344
except AttributeError as e:
13451345
raise IndexError(f"No item found with id {self._prefix}{index}") from e
13461346
# END handle getattr

‎test/test_util.py‎

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
import contextlib
77
import errno
8-
from datetime import datetime
98
import os
109
import pathlib
1110
import pickle
1211
import stat
1312
import sys
1413
import threading
1514
import time
15+
from datetime import datetime
1616
from unittest import SkipTest, mock
1717

1818
import ddt
@@ -43,8 +43,8 @@
4343
rmtree,
4444
)
4545
from test.cleanup import cleanup_directory
46-
from test.lib.fixtures import writable_fixture
4746
from test.lib import TestBase, requires_symlinks, with_rw_repo
47+
from test.lib.fixtures import writable_fixture
4848

4949

5050
@pytest.mark.parametrize(
@@ -312,6 +312,45 @@ def test_altz_to_utctz_str_accepts_float_seconds(offset, expected):
312312
assert utctz_to_altz(result) == utctz_to_altz(expected)
313313

314314

315+
@pytest.mark.parametrize("prefix", ["", "refs/"])
316+
@pytest.mark.parametrize("position", [0, -1])
317+
def test_iterable_list_accepts_index_protocol(prefix, position):
318+
class Index:
319+
def __index__(self):
320+
return position
321+
322+
values = IterableList("name", prefix)
323+
members = [_Member(prefix + name) for name in ("one", "two")]
324+
values.extend(members)
325+
assert values[Index()] is members[position]
326+
327+
328+
@pytest.mark.parametrize("position", [2, -3])
329+
def test_iterable_list_index_protocol_preserves_bounds_errors(position):
330+
class Index:
331+
def __index__(self):
332+
return position
333+
334+
values = IterableList("name")
335+
values.extend([_Member("one"), _Member("two")])
336+
with pytest.raises(IndexError):
337+
values[Index()]
338+
339+
340+
def test_iterable_list_index_protocol_preserves_named_lookup_and_slice_rejection():
341+
values = IterableList("name", "refs/")
342+
named = _Member("refs/0")
343+
values.extend([named, _Member("refs/one")])
344+
assert values["0"] is named
345+
assert values[False] is named
346+
with pytest.raises(IndexError):
347+
values["missing"]
348+
with pytest.raises(ValueError):
349+
values[:1]
350+
with pytest.raises(TypeError):
351+
values[0.0]
352+
353+
315354
@ddt.ddt
316355
class TestUtils(TestBase):
317356
"""Tests for most utilities in :mod:`git.util`."""

0 commit comments

Comments
 (0)