relib 1.3.11__py3-none-any.whl → 1.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
relib/class_utils.py ADDED
@@ -0,0 +1,9 @@
1
+ from typing import Callable, Generic, TypeVar
2
+
3
+ Fn = TypeVar("Fn", bound=Callable)
4
+
5
+ class slicer(Generic[Fn]):
6
+ def __init__(self, fn: Fn):
7
+ self.fn = fn
8
+
9
+ __getitem__: Fn = lambda self, key: self.fn(key) # type: ignore
relib/iter_utils.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
  from contextlib import contextmanager
3
- from itertools import chain, islice
3
+ from itertools import chain, islice, tee
4
4
  from typing import Any, Generic, Iterable, Literal, Sequence, overload
5
+ from .class_utils import slicer
5
6
  from .dict_utils import dict_firsts
6
7
  from .types import T1, T2, T3, T4, T5, T, U
7
8
 
@@ -17,6 +18,7 @@ __all__ = [
17
18
  "range_of", "reversed_enumerate",
18
19
  "seekable", "sort_by",
19
20
  "transpose",
21
+ "unzip_iterable",
20
22
  ]
21
23
 
22
24
  def as_list(iterable: Iterable[T]) -> list[T]:
@@ -81,6 +83,7 @@ class seekable(Generic[T]):
81
83
  self.index = 0
82
84
  self.source = iter(iterable)
83
85
  self.sink: list[T] = []
86
+ self.abs = slicer(self.abs_getitem)
84
87
 
85
88
  def __iter__(self):
86
89
  return self
@@ -97,15 +100,16 @@ class seekable(Generic[T]):
97
100
  def __bool__(self):
98
101
  return bool(self[:1])
99
102
 
100
- def clear(self):
103
+ def clear(self) -> seekable[T]:
101
104
  self.sink[:self.index] = []
102
105
  self.index = 0
106
+ return self
103
107
 
104
108
  def seek(self, index: int) -> seekable[T]:
105
- remainder = index - self.index
106
- if remainder > 0:
109
+ index = max(0, index)
110
+ self.index = min(index, len(self.sink))
111
+ if (remainder := index - self.index) > 0:
107
112
  next(islice(self, remainder, remainder), None)
108
- self.index = max(0, min(index, len(self.sink)))
109
113
  return self
110
114
 
111
115
  def step(self, count: int) -> seekable[T]:
@@ -130,10 +134,21 @@ class seekable(Generic[T]):
130
134
  with self.freeze():
131
135
  if isinstance(key, int):
132
136
  return self[key:key + 1][0]
133
- parts = (key.start, key.stop, key.step)
134
- if not all(x is None or x >= 0 for x in parts):
135
- raise ValueError(f"Indices for seekable() must be positive int")
136
- return list(islice(self, *parts))
137
+ start, stop, step = key.start, key.stop, key.step
138
+ delta = min(self.index, -min(0, start or 0))
139
+ start = None if start is None else max(0, start + delta)
140
+ stop = None if stop is None else max(0, stop + delta)
141
+ self.step(-delta)
142
+ return list(islice(self, start, stop, step))
143
+
144
+ @overload
145
+ def abs_getitem(self, key: int) -> T: ...
146
+ @overload
147
+ def abs_getitem(self, key: slice[int | None]) -> list[T]: ...
148
+ def abs_getitem(self, key: int | slice[int | None]):
149
+ with self.freeze():
150
+ self.seek(0)
151
+ return self[key]
137
152
 
138
153
  def consume(self) -> Iterable[T]:
139
154
  for value in self:
@@ -185,3 +200,15 @@ def transpose(tuples: Iterable[tuple], default_num_returns=0) -> tuple[list, ...
185
200
  if not output:
186
201
  return ([],) * default_num_returns
187
202
  return tuple(map(list, output))
203
+
204
+ @overload
205
+ def unzip_iterable(iterable: Iterable[tuple[T1, T2]], n: Literal[2]) -> tuple[Iterable[T1], Iterable[T2]]: ...
206
+ @overload
207
+ def unzip_iterable(iterable: Iterable[tuple[T1, T2, T3]], n: Literal[3]) -> tuple[Iterable[T1], Iterable[T2], Iterable[T3]]: ...
208
+ @overload
209
+ def unzip_iterable(iterable: Iterable[tuple[T1, T2, T3, T4]], n: Literal[4]) -> tuple[Iterable[T1], Iterable[T2], Iterable[T3], Iterable[T4]]: ...
210
+ @overload
211
+ def unzip_iterable(iterable: Iterable[tuple[T1, T2, T3, T4, T5]], n: Literal[5]) -> tuple[Iterable[T1], Iterable[T2], Iterable[T3], Iterable[T4], Iterable[T5]]: ...
212
+ def unzip_iterable(iterable: Iterable[tuple], n: int) -> tuple[Iterable, ...]:
213
+ iters = tee(iterable, n)
214
+ return tuple(map(lambda i, iter: (x[i] for x in iter), range(n), iters))
relib/runtime_tools.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  from concurrent.futures import ThreadPoolExecutor
6
6
  from functools import partial, wraps
7
7
  from time import time
8
- from typing import Callable, Coroutine, Iterable, ParamSpec, TypeVar
8
+ from typing import Awaitable, Callable, Coroutine, Iterable, ParamSpec, TypeVar
9
9
  from .iter_utils import as_list
10
10
  from .processing_utils import noop
11
11
  from .types import T
@@ -35,13 +35,13 @@ def clear_console() -> None:
35
35
  def console_link(text: str, url: str) -> str:
36
36
  return f"\033]8;;{url}\033\\{text}\033]8;;\033\\"
37
37
 
38
- async def worker(task: Coro[T], semaphore: asyncio.Semaphore, update=noop) -> T:
38
+ async def worker(task: Coro[T] | Awaitable[T], semaphore: asyncio.Semaphore, update=noop) -> T:
39
39
  async with semaphore:
40
40
  result = await task
41
41
  update()
42
42
  return result
43
43
 
44
- async def roll_tasks(tasks: Iterable[Coro[T]], workers=default_workers, progress=False) -> list[T]:
44
+ async def roll_tasks(tasks: Iterable[Coro[T] | Awaitable[T]], workers=default_workers, progress=False) -> list[T]:
45
45
  semaphore = asyncio.Semaphore(workers)
46
46
  if not progress:
47
47
  return await asyncio.gather(*[worker(task, semaphore) for task in tasks])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relib
3
- Version: 1.3.11
3
+ Version: 1.3.13
4
4
  Project-URL: Repository, https://github.com/Reddan/relib.git
5
5
  Author: Hampus Hallman
6
6
  License: Copyright 2018-2025 Hampus Hallman
@@ -1,12 +1,13 @@
1
1
  relib/__init__.py,sha256=WerjUaM_sNvudjXFudLRtXB7viZWEW1RSinkDjrh4nE,163
2
+ relib/class_utils.py,sha256=3z23lxhjFbn68IM_YnJPlvGG2pkybZuK3o_C-9b_LCc,225
2
3
  relib/dict_utils.py,sha256=ez8egTPvO05wVNWIyO-aJjtqaXUDYbzyw32M42q1D44,3677
3
4
  relib/io_utils.py,sha256=cUyUFhrMCUDfkINhYo32QPaVGz3chqDO3ElymSCoWEg,1086
4
- relib/iter_utils.py,sha256=iH8fu9FrNRurOsa--GqDbTzHdrV8eJOONvumMGr2pMA,6303
5
+ relib/iter_utils.py,sha256=O5NAHUWLPzCDJiAUe1I5mghIV5T4Q2OcSKvs6OClC_w,7562
5
6
  relib/processing_utils.py,sha256=kFb4izrR8vec9BFWeGSb-TXR54bbA4X3dxAfIBZItuI,1911
6
- relib/runtime_tools.py,sha256=6HpRXmAHHLq4bNRj6fSiaLQeHR2h3OdxFZxaB93fXhI,2979
7
+ relib/runtime_tools.py,sha256=jK4L4uE3eyGtmLNHEOSE651nrFdql00w7ctudGN6mb8,3020
7
8
  relib/type_utils.py,sha256=68yEhEIBCIKfsIT1u647avHVA_JjVvDYSdtLD3nCrJs,338
8
9
  relib/types.py,sha256=JzSGDl2L6tUWizGSsUpuyCKf-VKgtgfK_n21azSSMLM,288
9
- relib-1.3.11.dist-info/METADATA,sha256=YvXC6KVDCiYFMUUcbP8onzIcGzUzgyerJ4EBVLkDeBM,1296
10
- relib-1.3.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- relib-1.3.11.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
12
- relib-1.3.11.dist-info/RECORD,,
10
+ relib-1.3.13.dist-info/METADATA,sha256=KYD1MwW_CYoc38hMwsjoi5-DZgE7MiH20Qn1q-wvdVs,1296
11
+ relib-1.3.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ relib-1.3.13.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
13
+ relib-1.3.13.dist-info/RECORD,,
File without changes