From 483336e79e88d53901f6eb7ff09af2ef38e92b92 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Fri, 28 May 2021 22:19:13 +0530 Subject: [PATCH] [utils] Add `LazyList` --- yt_dlp/utils.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py index ec8f007d5b..02a12307a7 100644 --- a/yt_dlp/utils.py +++ b/yt_dlp/utils.py @@ -3945,6 +3945,56 @@ def detect_exe_version(output, version_re=None, unrecognized='present'): return unrecognized +class LazyList(collections.Sequence): + ''' Lazy immutable list from an iterable + Note that slices of a LazyList are lists and not LazyList''' + + def __init__(self, iterable): + self.__iterable = iter(iterable) + self.__cache = [] + + def __iter__(self): + for item in self.__cache: + yield item + for item in self.__iterable: + self.__cache.append(item) + yield item + + def exhaust(self): + ''' Evaluate the entire iterable ''' + self.__cache.extend(self.__iterable) + + def __getitem__(self, idx): + if isinstance(idx, slice): + step = idx.step or 1 + start = idx.start if idx.start is not None else 1 if step > 0 else -1 + stop = idx.stop if idx.stop is not None else -1 if step > 0 else 0 + elif isinstance(idx, int): + start = stop = idx + else: + raise TypeError('indices must be integers or slices') + if start < 0 or stop < 0: + # We need to consume the entire iterable to be able to slice from the end + # Obviously, never use this with infinite iterables + self.exhaust() + else: + n = max(start, stop) - len(self.__cache) + 1 + if n > 0: + self.__cache.extend(itertools.islice(self.__iterable, n)) + return self.__cache[idx] + + def __bool__(self): + try: + self[0] + except IndexError: + return False + return True + + def __len__(self): + self.exhaust() + return len(self.__cache) + + class PagedList(object): def __len__(self): # This is only useful for tests