Last active
July 7, 2020 21:20
-
-
Save fakuivan/d089b1d982fca17b8287fc56a59529de to your computer and use it in GitHub Desktop.
Wraps an iterator so that StopIteration is only raised once
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3.8 | |
from typing import TypeVar, Iterator | |
""" | |
https://docs.python.org/3/library/stdtypes.html#iterator-types | |
This is considered broken by the iterator protocol, however I think | |
that what's considered broken is to continue to _yield values_, where | |
with this we emphasize the fact that if ``StopIteration`` is raised | |
once, the iterator _should not be used_ further. Those are two | |
different things. | |
""" | |
class IteratorExhaustedError(Exception): | |
"""Exception raised when exhausted iterators are ``next``d""" | |
T = TypeVar("T") | |
class reuse_guard(Iterator[T]): | |
""" | |
Wraps an iterator so that StopIteration is only raised once, | |
after that, ``IteratorExhaustedError`` will be raised to detect | |
fixed-size iterator misuses | |
""" | |
def __init__(self, iterator: Iterator[T]): | |
self._iterated: bool = False | |
self._iterator = iterator | |
def __next__(self) -> T: | |
try: | |
return next(self._iterator) | |
except StopIteration as e: | |
if self._iterated: | |
raise IteratorExhaustedError( | |
"This iterator has already reached its end") | |
self._iterated = True | |
raise e | |
def __iter__(self) -> Iterator[T]: | |
return self |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment