Skip to content

Commit 5aaaa02

Browse files
authored
Merge pull request #1780 from EliahKagan/iteritems
Better document IterableObj.iter_items and improve some subclasses
2 parents d986a59 + dfee31f commit 5aaaa02

File tree

4 files changed

+66
-36
lines changed

4 files changed

+66
-36
lines changed

git/objects/submodule/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1401,7 +1401,7 @@ def iter_items(
14011401
pc = repo.commit(parent_commit) # Parent commit instance
14021402
parser = cls._config_parser(repo, pc, read_only=True)
14031403
except (IOError, BadName):
1404-
return iter([])
1404+
return
14051405
# END handle empty iterator
14061406

14071407
for sms in parser.sections():

git/remote.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def to_progress_instance(
130130
return progress
131131

132132

133-
class PushInfo(IterableObj, object):
133+
class PushInfo(IterableObj):
134134
"""
135135
Carries information about the result of a push operation of a single head::
136136
@@ -300,7 +300,7 @@ def raise_if_error(self) -> None:
300300
raise self.error
301301

302302

303-
class FetchInfo(IterableObj, object):
303+
class FetchInfo(IterableObj):
304304
"""
305305
Carries information about the results of a fetch operation of a single head::
306306

git/util.py

+51-33
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,8 @@ def __delitem__(self, index: Union[SupportsIndex, int, slice, str]) -> None:
11831183

11841184

11851185
class IterableClassWatcher(type):
1186-
"""Metaclass that watches."""
1186+
"""Metaclass that issues :class:`DeprecationWarning` when :class:`git.util.Iterable`
1187+
is subclassed."""
11871188

11881189
def __init__(cls, name: str, bases: Tuple, clsdict: Dict) -> None:
11891190
for base in bases:
@@ -1199,39 +1200,49 @@ def __init__(cls, name: str, bases: Tuple, clsdict: Dict) -> None:
11991200

12001201

12011202
class Iterable(metaclass=IterableClassWatcher):
1202-
"""Defines an interface for iterable items, so there is a uniform way to retrieve
1203-
and iterate items within the git repository."""
1203+
"""Deprecated, use :class:`IterableObj` instead.
1204+
1205+
Defines an interface for iterable items, so there is a uniform way to retrieve
1206+
and iterate items within the git repository.
1207+
"""
12041208

12051209
__slots__ = ()
12061210

12071211
_id_attribute_ = "attribute that most suitably identifies your instance"
12081212

12091213
@classmethod
1210-
def list_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Any:
1214+
def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Any:
1215+
# return typed to be compatible with subtypes e.g. Remote
1216+
"""Deprecated, use :class:`IterableObj` instead.
1217+
1218+
Find (all) items of this type.
1219+
1220+
Subclasses can specify ``args`` and ``kwargs`` differently, and may use them for
1221+
filtering. However, when the method is called with no additional positional or
1222+
keyword arguments, subclasses are obliged to to yield all items.
1223+
1224+
:return: Iterator yielding Items
12111225
"""
1212-
Deprecated, use IterableObj instead.
1226+
raise NotImplementedError("To be implemented by Subclass")
1227+
1228+
@classmethod
1229+
def list_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Any:
1230+
"""Deprecated, use :class:`IterableObj` instead.
1231+
1232+
Find (all) items of this type and collect them into a list.
12131233
1214-
Find all items of this type - subclasses can specify args and kwargs differently.
1215-
If no args are given, subclasses are obliged to return all items if no additional
1216-
arguments arg given.
1234+
For more information about the arguments, see :meth:`list_items`.
12171235
1218-
:note: Favor the iter_items method as it will
1236+
:note: Favor the :meth:`iter_items` method as it will avoid eagerly collecting
1237+
all items. When there are many items, that can slow performance and increase
1238+
memory usage.
12191239
12201240
:return: list(Item,...) list of item instances
12211241
"""
12221242
out_list: Any = IterableList(cls._id_attribute_)
12231243
out_list.extend(cls.iter_items(repo, *args, **kwargs))
12241244
return out_list
12251245

1226-
@classmethod
1227-
def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Any:
1228-
# return typed to be compatible with subtypes e.g. Remote
1229-
"""For more information about the arguments, see list_items.
1230-
1231-
:return: Iterator yielding Items
1232-
"""
1233-
raise NotImplementedError("To be implemented by Subclass")
1234-
12351246

12361247
@runtime_checkable
12371248
class IterableObj(Protocol):
@@ -1246,30 +1257,37 @@ class IterableObj(Protocol):
12461257
_id_attribute_: str
12471258

12481259
@classmethod
1249-
def list_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> IterableList[T_IterableObj]:
1260+
@abstractmethod
1261+
def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Iterator[T_IterableObj]:
1262+
# Return-typed to be compatible with subtypes e.g. Remote.
1263+
"""Find (all) items of this type.
1264+
1265+
Subclasses can specify ``args`` and ``kwargs`` differently, and may use them for
1266+
filtering. However, when the method is called with no additional positional or
1267+
keyword arguments, subclasses are obliged to to yield all items.
1268+
1269+
For more information about the arguments, see list_items.
1270+
1271+
:return: Iterator yielding Items
12501272
"""
1251-
Find all items of this type - subclasses can specify args and kwargs differently.
1252-
If no args are given, subclasses are obliged to return all items if no additional
1253-
arguments arg given.
1273+
raise NotImplementedError("To be implemented by Subclass")
1274+
1275+
@classmethod
1276+
def list_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> IterableList[T_IterableObj]:
1277+
"""Find (all) items of this type and collect them into a list.
1278+
1279+
For more information about the arguments, see :meth:`list_items`.
12541280
1255-
:note: Favor the iter_items method as it will
1281+
:note: Favor the :meth:`iter_items` method as it will avoid eagerly collecting
1282+
all items. When there are many items, that can slow performance and increase
1283+
memory usage.
12561284
12571285
:return: list(Item,...) list of item instances
12581286
"""
12591287
out_list: IterableList = IterableList(cls._id_attribute_)
12601288
out_list.extend(cls.iter_items(repo, *args, **kwargs))
12611289
return out_list
12621290

1263-
@classmethod
1264-
@abstractmethod
1265-
def iter_items(cls, repo: "Repo", *args: Any, **kwargs: Any) -> Iterator[T_IterableObj]: # Iterator[T_IterableObj]:
1266-
# Return-typed to be compatible with subtypes e.g. Remote.
1267-
"""For more information about the arguments, see list_items.
1268-
1269-
:return: Iterator yielding Items
1270-
"""
1271-
raise NotImplementedError("To be implemented by Subclass")
1272-
12731291

12741292
# } END classes
12751293

test/test_submodule.py

+12
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,18 @@ def test_root_module(self, rwrepo):
688688
# gitdb: has either 1 or 2 submodules depending on the version.
689689
assert len(nsm.children()) >= 1 and nsmc.module_exists()
690690

691+
def test_iter_items_from_nonexistent_hash(self):
692+
it = Submodule.iter_items(self.rorepo, "b4ecbfaa90c8be6ed6d9fb4e57cc824663ae15b4")
693+
with self.assertRaisesRegex(ValueError, r"\bcould not be resolved\b"):
694+
next(it)
695+
696+
def test_iter_items_from_invalid_hash(self):
697+
"""Check legacy behavaior on BadName (also applies to IOError, i.e. OSError)."""
698+
it = Submodule.iter_items(self.rorepo, "xyz")
699+
with self.assertRaises(StopIteration) as ctx:
700+
next(it)
701+
self.assertIsNone(ctx.exception.value)
702+
691703
@with_rw_repo(k_no_subm_tag, bare=False)
692704
def test_first_submodule(self, rwrepo):
693705
assert len(list(rwrepo.iter_submodules())) == 0

0 commit comments

Comments
 (0)