mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
Old helpers are completely broken for reST, Google and NumPy docstring formats. (cherry picked from commit 9ce7f986164ca7a61710eefab38934837a36f00b) IJ-CR-16877 GitOrigin-RevId: 34f27150854279ba98afb8bb23711e8a62fa58c0
271 lines
8.3 KiB
Python
271 lines
8.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (c) 2018 the Pockets team, see AUTHORS.
|
|
# Licensed under the BSD License, see LICENSE for details.
|
|
|
|
"""A pocket full of useful iterators!"""
|
|
|
|
from __future__ import absolute_import, print_function
|
|
|
|
import collections
|
|
|
|
import six
|
|
|
|
|
|
__all__ = ["itermod", "iterpeek", "modify_iter", "peek_iter"]
|
|
|
|
|
|
class iterpeek(object):
|
|
"""
|
|
An iterator object that supports peeking ahead.
|
|
|
|
>>> p = iterpeek(["a", "b", "c", "d", "e"])
|
|
>>> p.peek()
|
|
'a'
|
|
>>> p.next()
|
|
'a'
|
|
>>> p.peek(3)
|
|
['b', 'c', 'd']
|
|
|
|
Args:
|
|
o (iterable or callable): `o` is interpreted very differently
|
|
depending on the presence of `sentinel`.
|
|
|
|
If `sentinel` is not given, then `o` must be a collection object
|
|
which supports either the iteration protocol or the sequence
|
|
protocol.
|
|
|
|
If `sentinel` is given, then `o` must be a callable object.
|
|
|
|
sentinel (any value, optional): If given, the iterator will call `o`
|
|
with no arguments for each call to its `next` method; if the value
|
|
returned is equal to `sentinel`, :exc:`StopIteration` will be
|
|
raised, otherwise the value will be returned.
|
|
|
|
See Also:
|
|
`iterpeek` can operate as a drop in replacement for the built-in
|
|
`iter <https://docs.python.org/3/library/functions.html#iter>`_
|
|
function.
|
|
|
|
Attributes:
|
|
sentinel (any value): The value used to indicate the iterator is
|
|
exhausted. If `sentinel` was not given when the `iterpeek` was
|
|
instantiated, then it will be set to a new object
|
|
instance: ``object()``.
|
|
|
|
"""
|
|
|
|
def __init__(self, *args):
|
|
"""__init__(o, sentinel=None)"""
|
|
self._iterable = iter(*args)
|
|
self._cache = collections.deque()
|
|
self.sentinel = args[1] if len(args) > 1 else object()
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self, n=None):
|
|
# NOTE: Prevent 2to3 from transforming self.next() in next(self),
|
|
# which causes an infinite loop!
|
|
return getattr(self, "next")(n)
|
|
|
|
def _fillcache(self, n):
|
|
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
|
|
if not n:
|
|
n = 1
|
|
try:
|
|
while len(self._cache) < n:
|
|
self._cache.append(next(self._iterable))
|
|
except StopIteration:
|
|
while len(self._cache) < n:
|
|
self._cache.append(self.sentinel)
|
|
|
|
def has_next(self):
|
|
"""
|
|
Determine if iterator is exhausted.
|
|
|
|
Returns:
|
|
bool: True if iterator has more items, False otherwise.
|
|
|
|
Note:
|
|
Will never raise :exc:`StopIteration`.
|
|
|
|
"""
|
|
return self.peek() != self.sentinel
|
|
|
|
def next(self, n=None):
|
|
"""
|
|
Get the next item or `n` items of the iterator.
|
|
|
|
Args:
|
|
n (int, optional): The number of items to retrieve. Defaults to
|
|
None.
|
|
|
|
Returns:
|
|
item or list of items: The next item or `n` items of the iterator.
|
|
If `n` is None, the item itself is returned. If `n` is an int,
|
|
the items will be returned in a list. If `n` is 0, an empty
|
|
list is returned:
|
|
|
|
>>> p = iterpeek(["a", "b", "c", "d", "e"])
|
|
>>> p.next()
|
|
'a'
|
|
>>> p.next(0)
|
|
[]
|
|
>>> p.next(1)
|
|
['b']
|
|
>>> p.next(2)
|
|
['c', 'd']
|
|
|
|
Raises:
|
|
StopIteration: Raised if the iterator is exhausted, even if
|
|
`n` is 0.
|
|
|
|
"""
|
|
self._fillcache(n)
|
|
if not n:
|
|
if self._cache[0] == self.sentinel:
|
|
raise StopIteration
|
|
if n is None:
|
|
result = self._cache.popleft()
|
|
else:
|
|
result = []
|
|
else:
|
|
if self._cache[n - 1] == self.sentinel:
|
|
raise StopIteration
|
|
result = [self._cache.popleft() for i in range(n)]
|
|
return result
|
|
|
|
def peek(self, n=None):
|
|
"""
|
|
Preview the next item or `n` items of the iterator.
|
|
|
|
The iterator is not advanced when peek is called.
|
|
|
|
Args:
|
|
n (int, optional): The number of items to retrieve. Defaults to
|
|
None.
|
|
|
|
Returns:
|
|
item or list of items: The next item or `n` items of the iterator.
|
|
If `n` is None, the item itself is returned. If `n` is an int,
|
|
the items will be returned in a list. If `n` is 0, an empty
|
|
list is returned.
|
|
|
|
If the iterator is exhausted, `iterpeek.sentinel` is returned,
|
|
or placed as the last item in the returned list:
|
|
|
|
>>> p = iterpeek(["a", "b", "c"])
|
|
>>> p.sentinel = "END"
|
|
>>> p.peek()
|
|
'a'
|
|
>>> p.peek(0)
|
|
[]
|
|
>>> p.peek(1)
|
|
['a']
|
|
>>> p.peek(2)
|
|
['a', 'b']
|
|
>>> p.peek(4)
|
|
['a', 'b', 'c', 'END']
|
|
|
|
Note:
|
|
Will never raise :exc:`StopIteration`.
|
|
|
|
"""
|
|
self._fillcache(n)
|
|
if n is None:
|
|
result = self._cache[0]
|
|
else:
|
|
result = [self._cache[i] for i in range(n)]
|
|
return result
|
|
|
|
|
|
# Backwards compatibility
|
|
peek_iter = iterpeek
|
|
|
|
|
|
class itermod(iterpeek):
|
|
"""
|
|
An iterator object that supports modifying items as they are returned.
|
|
|
|
>>> a = [" A list ",
|
|
... " of strings ",
|
|
... " with ",
|
|
... " extra ",
|
|
... " whitespace. "]
|
|
>>> modifier = lambda s: s.strip().replace('with', 'without')
|
|
>>> for s in itermod(a, modifier=modifier):
|
|
... print('"%s"' % s)
|
|
"A list"
|
|
"of strings"
|
|
"without"
|
|
"extra"
|
|
"whitespace."
|
|
|
|
Args:
|
|
o (iterable or callable): `o` is interpreted very differently
|
|
depending on the presence of `sentinel`.
|
|
|
|
If `sentinel` is not given, then `o` must be a collection object
|
|
which supports either the iteration protocol or the sequence
|
|
protocol.
|
|
|
|
If `sentinel` is given, then `o` must be a callable object.
|
|
|
|
sentinel (any value, optional): If given, the iterator will call `o`
|
|
with no arguments for each call to its `next` method; if the value
|
|
returned is equal to `sentinel`, :exc:`StopIteration` will be
|
|
raised, otherwise the value will be returned.
|
|
|
|
modifier (callable, optional): The function that will be used to
|
|
modify each item returned by the iterator. `modifier` should take
|
|
a single argument and return a single value. Defaults
|
|
to ``lambda x: x``.
|
|
|
|
If `sentinel` is not given, `modifier` must be passed as a keyword
|
|
argument.
|
|
|
|
Attributes:
|
|
modifier (callable): `modifier` is called with each item in `o` as it
|
|
is iterated. The return value of `modifier` is returned in lieu of
|
|
the item.
|
|
|
|
Values returned by `peek` as well as `next` are affected by
|
|
`modifier`. However, `itermod.sentinel` is never passed through
|
|
`modifier`; it will always be returned from `peek` unmodified.
|
|
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
"""__init__(o, sentinel=None, modifier=lambda x: x)"""
|
|
if "modifier" in kwargs:
|
|
self.modifier = kwargs["modifier"]
|
|
elif len(args) > 2:
|
|
self.modifier = args[2]
|
|
args = args[:2]
|
|
else:
|
|
self.modifier = lambda x: x
|
|
if not six.callable(self.modifier):
|
|
raise TypeError("itermod(o, modifier): modifier must be callable")
|
|
super(itermod, self).__init__(*args)
|
|
|
|
def _fillcache(self, n):
|
|
"""
|
|
Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
|
|
|
|
Each item returned by the iterator is passed through the
|
|
`itermod.modified` function before being cached.
|
|
|
|
"""
|
|
if not n:
|
|
n = 1
|
|
try:
|
|
while len(self._cache) < n:
|
|
self._cache.append(self.modifier(next(self._iterable)))
|
|
except StopIteration:
|
|
while len(self._cache) < n:
|
|
self._cache.append(self.sentinel)
|
|
|
|
|
|
# Backwards compatibility
|
|
modify_iter = itermod
|