Skip to content

Commit 29e12e9

Browse files
committed
Make traversable and serilizable into protocols
1 parent 44f0578 commit 29e12e9

File tree

1 file changed

+54
-24
lines changed

1 file changed

+54
-24
lines changed

Diff for: git/objects/util.py

+54-24
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
"""Module for general utility functions"""
77

8+
from abc import abstractmethod
9+
import warnings
810
from git.util import (
911
IterableList,
1012
IterableObj,
@@ -23,7 +25,7 @@
2325
from typing import (Any, Callable, Deque, Iterator, NamedTuple, overload, Sequence,
2426
TYPE_CHECKING, Tuple, Type, TypeVar, Union, cast)
2527

26-
from git.types import Has_id_attribute, Literal
28+
from git.types import Has_id_attribute, Literal, Protocol, runtime_checkable
2729

2830
if TYPE_CHECKING:
2931
from io import BytesIO, StringIO
@@ -289,7 +291,8 @@ def __getattr__(self, attr: str) -> Any:
289291
return getattr(self._stream, attr)
290292

291293

292-
class Traversable(object):
294+
@runtime_checkable
295+
class Traversable(Protocol):
293296

294297
"""Simple interface to perform depth-first or breadth-first traversals
295298
into one direction.
@@ -301,6 +304,7 @@ class Traversable(object):
301304
__slots__ = ()
302305

303306
@classmethod
307+
@abstractmethod
304308
def _get_intermediate_items(cls, item) -> Sequence['Traversable']:
305309
"""
306310
Returns:
@@ -313,7 +317,18 @@ class Tree:: (cls, Tree) -> Tuple[Tree, ...]
313317
"""
314318
raise NotImplementedError("To be implemented in subclass")
315319

316-
def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']]:
320+
@abstractmethod
321+
def list_traverse(self, *args: Any, **kwargs: Any) -> Any:
322+
""" """
323+
warnings.warn("list_traverse() method should only be called from subclasses."
324+
"Calling from Traversable abstract class will raise NotImplementedError in 3.1.20"
325+
"Builtin sublclasses are 'Submodule', 'Tree' and 'Commit",
326+
DeprecationWarning,
327+
stacklevel=2)
328+
return self._list_traverse(*args, **kwargs)
329+
330+
def _list_traverse(self, as_edge=False, *args: Any, **kwargs: Any
331+
) -> IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']]:
317332
"""
318333
:return: IterableList with the results of the traversal as produced by
319334
traverse()
@@ -329,22 +344,34 @@ def list_traverse(self, *args: Any, **kwargs: Any) -> IterableList[Union['Commit
329344
id = "" # shouldn't reach here, unless Traversable subclass created with no _id_attribute_
330345
# could add _id_attribute_ to Traversable, or make all Traversable also Iterable?
331346

332-
out: IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']] = IterableList(id)
333-
# overloads in subclasses (mypy does't allow typing self: subclass)
334-
# Union[IterableList['Commit'], IterableList['Submodule'], IterableList[Union['Submodule', 'Tree', 'Blob']]]
335-
336-
# NOTE: if is_edge=True, self.traverse returns a Tuple, so should be prevented or flattened?
337-
kwargs['as_edge'] = False
338-
out.extend(self.traverse(*args, **kwargs)) # type: ignore
339-
return out
340-
341-
def traverse(self,
342-
predicate: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: True,
343-
prune: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: False,
344-
depth: int = -1, branch_first: bool = True, visit_once: bool = True,
345-
ignore_self: int = 1, as_edge: bool = False
346-
) -> Union[Iterator[Union['Traversable', 'Blob']],
347-
Iterator[TraversedTup]]:
347+
if not as_edge:
348+
out: IterableList[Union['Commit', 'Submodule', 'Tree', 'Blob']] = IterableList(id)
349+
out.extend(self.traverse(as_edge=as_edge, *args, **kwargs)) # type: ignore
350+
return out
351+
# overloads in subclasses (mypy does't allow typing self: subclass)
352+
# Union[IterableList['Commit'], IterableList['Submodule'], IterableList[Union['Submodule', 'Tree', 'Blob']]]
353+
else:
354+
# Raise deprecationwarning, doesn't make sense to use this
355+
out_list: IterableList = IterableList(self.traverse(*args, **kwargs))
356+
return out_list
357+
358+
@ abstractmethod
359+
def traverse(self, *args: Any, **kwargs) -> Any:
360+
""" """
361+
warnings.warn("traverse() method should only be called from subclasses."
362+
"Calling from Traversable abstract class will raise NotImplementedError in 3.1.20"
363+
"Builtin sublclasses are 'Submodule', 'Tree' and 'Commit",
364+
DeprecationWarning,
365+
stacklevel=2)
366+
return self._traverse(*args, **kwargs)
367+
368+
def _traverse(self,
369+
predicate: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: True,
370+
prune: Callable[[Union['Traversable', 'Blob', TraversedTup], int], bool] = lambda i, d: False,
371+
depth: int = -1, branch_first: bool = True, visit_once: bool = True,
372+
ignore_self: int = 1, as_edge: bool = False
373+
) -> Union[Iterator[Union['Traversable', 'Blob']],
374+
Iterator[TraversedTup]]:
348375
""":return: iterator yielding of items found when traversing self
349376
:param predicate: f(i,d) returns False if item i at depth d should not be included in the result
350377
@@ -435,32 +462,35 @@ def addToStack(stack: Deque[TraverseNT],
435462
# END for each item on work stack
436463

437464

438-
class Serializable(object):
465+
@ runtime_checkable
466+
class Serializable(Protocol):
439467

440468
"""Defines methods to serialize and deserialize objects from and into a data stream"""
441469
__slots__ = ()
442470

471+
# @abstractmethod
443472
def _serialize(self, stream: 'BytesIO') -> 'Serializable':
444473
"""Serialize the data of this object into the given data stream
445474
:note: a serialized object would ``_deserialize`` into the same object
446475
:param stream: a file-like object
447476
:return: self"""
448477
raise NotImplementedError("To be implemented in subclass")
449478

479+
# @abstractmethod
450480
def _deserialize(self, stream: 'BytesIO') -> 'Serializable':
451481
"""Deserialize all information regarding this object from the stream
452482
:param stream: a file-like object
453483
:return: self"""
454484
raise NotImplementedError("To be implemented in subclass")
455485

456486

457-
class TraversableIterableObj(Traversable, IterableObj):
487+
class TraversableIterableObj(IterableObj, Traversable):
458488
__slots__ = ()
459489

460490
TIobj_tuple = Tuple[Union[T_TIobj, None], T_TIobj]
461491

462-
def list_traverse(self: T_TIobj, *args: Any, **kwargs: Any) -> IterableList[T_TIobj]: # type: ignore[override]
463-
return super(TraversableIterableObj, self).list_traverse(* args, **kwargs)
492+
def list_traverse(self: T_TIobj, *args: Any, **kwargs: Any) -> IterableList[T_TIobj]:
493+
return super(TraversableIterableObj, self)._list_traverse(* args, **kwargs)
464494

465495
@ overload # type: ignore
466496
def traverse(self: T_TIobj,
@@ -522,6 +552,6 @@ def is_commit_traversed(inp: Tuple) -> TypeGuard[Tuple[Iterator[Tuple['Commit',
522552
"""
523553
return cast(Union[Iterator[T_TIobj],
524554
Iterator[Tuple[Union[None, T_TIobj], T_TIobj]]],
525-
super(TraversableIterableObj, self).traverse(
555+
super(TraversableIterableObj, self)._traverse(
526556
predicate, prune, depth, branch_first, visit_once, ignore_self, as_edge # type: ignore
527557
))

0 commit comments

Comments
 (0)