summaryrefslogtreecommitdiffstats
path: root/Lib/_collections_abc.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/_collections_abc.py')
-rw-r--r--Lib/_collections_abc.py203
1 files changed, 197 insertions, 6 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index 33b59ab..f89bb6f 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -9,7 +9,8 @@ Unit tests are in test_collections.
from abc import ABCMeta, abstractmethod
import sys
-__all__ = ["Hashable", "Iterable", "Iterator",
+__all__ = ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator",
+ "Hashable", "Iterable", "Iterator", "Generator",
"Sized", "Container", "Callable",
"Set", "MutableSet",
"Mapping", "MutableMapping",
@@ -50,6 +51,13 @@ dict_values = type({}.values())
dict_items = type({}.items())
## misc ##
mappingproxy = type(type.__dict__)
+generator = type((lambda: (yield))())
+## coroutine ##
+async def _coro(): pass
+_coro = _coro()
+coroutine = type(_coro)
+_coro.close() # Prevent ResourceWarning
+del _coro
### ONE-TRICK PONIES ###
@@ -73,6 +81,113 @@ class Hashable(metaclass=ABCMeta):
return NotImplemented
+class Awaitable(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def __await__(self):
+ yield
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Awaitable:
+ for B in C.__mro__:
+ if "__await__" in B.__dict__:
+ if B.__dict__["__await__"]:
+ return True
+ break
+ return NotImplemented
+
+
+class Coroutine(Awaitable):
+
+ __slots__ = ()
+
+ @abstractmethod
+ def send(self, value):
+ """Send a value into the coroutine.
+ Return next yielded value or raise StopIteration.
+ """
+ raise StopIteration
+
+ @abstractmethod
+ def throw(self, typ, val=None, tb=None):
+ """Raise an exception in the coroutine.
+ Return next yielded value or raise StopIteration.
+ """
+ if val is None:
+ if tb is None:
+ raise typ
+ val = typ()
+ if tb is not None:
+ val = val.with_traceback(tb)
+ raise val
+
+ def close(self):
+ """Raise GeneratorExit inside coroutine.
+ """
+ try:
+ self.throw(GeneratorExit)
+ except (GeneratorExit, StopIteration):
+ pass
+ else:
+ raise RuntimeError("coroutine ignored GeneratorExit")
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Coroutine:
+ mro = C.__mro__
+ for method in ('__await__', 'send', 'throw', 'close'):
+ for base in mro:
+ if method in base.__dict__:
+ break
+ else:
+ return NotImplemented
+ return True
+ return NotImplemented
+
+
+Coroutine.register(coroutine)
+
+
+class AsyncIterable(metaclass=ABCMeta):
+
+ __slots__ = ()
+
+ @abstractmethod
+ async def __aiter__(self):
+ return AsyncIterator()
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is AsyncIterable:
+ if any("__aiter__" in B.__dict__ for B in C.__mro__):
+ return True
+ return NotImplemented
+
+
+class AsyncIterator(AsyncIterable):
+
+ __slots__ = ()
+
+ @abstractmethod
+ async def __anext__(self):
+ """Return the next item or raise StopAsyncIteration when exhausted."""
+ raise StopAsyncIteration
+
+ async def __aiter__(self):
+ return self
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is AsyncIterator:
+ if (any("__anext__" in B.__dict__ for B in C.__mro__) and
+ any("__aiter__" in B.__dict__ for B in C.__mro__)):
+ return True
+ return NotImplemented
+
+
class Iterable(metaclass=ABCMeta):
__slots__ = ()
@@ -124,6 +239,64 @@ Iterator.register(str_iterator)
Iterator.register(tuple_iterator)
Iterator.register(zip_iterator)
+
+class Generator(Iterator):
+
+ __slots__ = ()
+
+ def __next__(self):
+ """Return the next item from the generator.
+ When exhausted, raise StopIteration.
+ """
+ return self.send(None)
+
+ @abstractmethod
+ def send(self, value):
+ """Send a value into the generator.
+ Return next yielded value or raise StopIteration.
+ """
+ raise StopIteration
+
+ @abstractmethod
+ def throw(self, typ, val=None, tb=None):
+ """Raise an exception in the generator.
+ Return next yielded value or raise StopIteration.
+ """
+ if val is None:
+ if tb is None:
+ raise typ
+ val = typ()
+ if tb is not None:
+ val = val.with_traceback(tb)
+ raise val
+
+ def close(self):
+ """Raise GeneratorExit inside generator.
+ """
+ try:
+ self.throw(GeneratorExit)
+ except (GeneratorExit, StopIteration):
+ pass
+ else:
+ raise RuntimeError("generator ignored GeneratorExit")
+
+ @classmethod
+ def __subclasshook__(cls, C):
+ if cls is Generator:
+ mro = C.__mro__
+ for method in ('__iter__', '__next__', 'send', 'throw', 'close'):
+ for base in mro:
+ if method in base.__dict__:
+ break
+ else:
+ return NotImplemented
+ return True
+ return NotImplemented
+
+
+Generator.register(generator)
+
+
class Sized(metaclass=ABCMeta):
__slots__ = ()
@@ -453,6 +626,8 @@ Mapping.register(mappingproxy)
class MappingView(Sized):
+ __slots__ = '_mapping',
+
def __init__(self, mapping):
self._mapping = mapping
@@ -465,6 +640,8 @@ class MappingView(Sized):
class KeysView(MappingView, Set):
+ __slots__ = ()
+
@classmethod
def _from_iterable(self, it):
return set(it)
@@ -480,6 +657,8 @@ KeysView.register(dict_keys)
class ItemsView(MappingView, Set):
+ __slots__ = ()
+
@classmethod
def _from_iterable(self, it):
return set(it)
@@ -502,6 +681,8 @@ ItemsView.register(dict_items)
class ValuesView(MappingView):
+ __slots__ = ()
+
def __contains__(self, value):
for key in self._mapping:
if value == self._mapping[key]:
@@ -647,13 +828,23 @@ class Sequence(Sized, Iterable, Container):
for i in reversed(range(len(self))):
yield self[i]
- def index(self, value):
- '''S.index(value) -> integer -- return first index of value.
+ def index(self, value, start=0, stop=None):
+ '''S.index(value, [start, [stop]]) -> integer -- return first index of value.
Raises ValueError if the value is not present.
'''
- for i, v in enumerate(self):
- if v == value:
- return i
+ if start is not None and start < 0:
+ start = max(len(self) + start, 0)
+ if stop is not None and stop < 0:
+ stop += len(self)
+
+ i = start
+ while stop is None or i < stop:
+ try:
+ if self[i] == value:
+ return i
+ except IndexError:
+ break
+ i += 1
raise ValueError
def count(self, value):