relib 1.2.3__py3-none-any.whl → 1.2.5__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/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .utils import (
2
+ noop,
2
3
  non_none,
3
4
  as_any,
4
5
  list_split,
@@ -32,6 +33,13 @@ from .utils import (
32
33
  StrFilter,
33
34
  str_filterer,
34
35
  )
35
- from .system import read_json, write_json, clear_console, console_link
36
+ from .system import (
37
+ read_json,
38
+ write_json,
39
+ clear_console,
40
+ console_link,
41
+ roll_tasks,
42
+ as_async,
43
+ )
36
44
  from .hashing import hash, hash_obj
37
45
  from .measure_duration import measure_duration
relib/system.py CHANGED
@@ -1,7 +1,16 @@
1
+ import asyncio
2
+ import contextvars
3
+ import functools
1
4
  import json
2
5
  import os
6
+ from concurrent.futures import ThreadPoolExecutor
3
7
  from pathlib import Path
4
- from typing import Any
8
+ from typing import Any, Awaitable, Callable, Iterable, ParamSpec, TypeVar
9
+ from .utils import noop
10
+
11
+ P = ParamSpec("P")
12
+ R = TypeVar("R")
13
+ default_num_workers = min(32, (os.cpu_count() or 1) + 4)
5
14
 
6
15
  def read_json(path: Path) -> Any:
7
16
  with path.open("r") as f:
@@ -14,5 +23,35 @@ def write_json(path: Path, obj: Any) -> None:
14
23
  def clear_console() -> None:
15
24
  os.system("cls" if os.name == "nt" else "clear")
16
25
 
17
- def console_link(text, url) -> str:
26
+ def console_link(text: str, url: str) -> str:
18
27
  return f"\033]8;;{url}\033\\{text}\033]8;;\033\\"
28
+
29
+ async def worker[T](task: Awaitable[T], semaphore: asyncio.Semaphore, update=noop) -> T:
30
+ async with semaphore:
31
+ result = await task
32
+ update()
33
+ return result
34
+
35
+ async def roll_tasks[T](tasks: Iterable[Awaitable[T]], workers: int, progress=False) -> list[T]:
36
+ semaphore = asyncio.Semaphore(workers)
37
+ if not progress:
38
+ return await asyncio.gather(*(worker(task, semaphore) for task in tasks))
39
+ from tqdm import tqdm
40
+ assert isinstance(tasks, (list, tuple, set, frozenset, dict))
41
+ with tqdm(total=len(tasks)) as pbar:
42
+ update = lambda: pbar.update(1)
43
+ return await asyncio.gather(*(worker(task, semaphore, update) for task in tasks))
44
+
45
+ def as_async(num_workers=default_num_workers) -> Callable[[Callable[P, R]], Callable[P, Awaitable[R]]]:
46
+ executor = ThreadPoolExecutor(max_workers=num_workers)
47
+
48
+ def on_fn(func: Callable[P, R]) -> Callable[P, Awaitable[R]]:
49
+ @functools.wraps(func)
50
+ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
51
+ loop = asyncio.get_running_loop()
52
+ ctx = contextvars.copy_context()
53
+ fn_call = functools.partial(ctx.run, func, *args, **kwargs)
54
+ return await loop.run_in_executor(executor, fn_call)
55
+ return wrapper
56
+
57
+ return on_fn
relib/utils.py CHANGED
@@ -1,6 +1,9 @@
1
1
  import re
2
- from typing import Iterable, Callable, Any, overload
3
2
  from itertools import chain
3
+ from typing import Any, Callable, Iterable, overload
4
+
5
+ def noop() -> None:
6
+ pass
4
7
 
5
8
  def non_none[T](obj: T | None) -> T:
6
9
  assert obj is not None
@@ -182,8 +185,8 @@ def df_from_array(
182
185
  dim_labels: list[tuple[str, list[str | int | float]]],
183
186
  indexed=False,
184
187
  ):
185
- import pandas as pd
186
188
  import numpy as np
189
+ import pandas as pd
187
190
  dim_sizes = np.array([len(labels) for _, labels in dim_labels])
188
191
  assert all(array.shape == tuple(dim_sizes) for array in value_cols.values())
189
192
  array_offsets = [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relib
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Project-URL: Repository, https://github.com/Reddan/relib.git
5
5
  Author: Hampus Hallman
6
6
  License: Copyright 2018-2025 Hampus Hallman
@@ -0,0 +1,9 @@
1
+ relib/__init__.py,sha256=E_ye3ZsGqW3xCibphEWtXhXjIJI58zVxX53VajgvG2Q,674
2
+ relib/hashing.py,sha256=DB_fnkj0ls01FgZbf4nPFHl4EBU8X_0OrmDvty4HlRE,6020
3
+ relib/measure_duration.py,sha256=LCTo_D_qReNprD3fhtJ0daeWycS6xQE_cwxeg2_h0xo,456
4
+ relib/system.py,sha256=YSovHakRl4VjERuoFgYKMZcuSpBVtuQHdjlc_p_Dfr4,1921
5
+ relib/utils.py,sha256=4fM0zsAdfrM2w9Q1YKcTfFG5kDzoNgD2amkgfTG39OY,6933
6
+ relib-1.2.5.dist-info/METADATA,sha256=xy2aVPv-Rhe5P93QDgxJzoJdvKvJoae5upeFwt5SgnE,1295
7
+ relib-1.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ relib-1.2.5.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
9
+ relib-1.2.5.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- relib/__init__.py,sha256=dLFft8umAfLeZfTiecZ2Cx_-C-nKoBepUk-aWivI5ZE,627
2
- relib/hashing.py,sha256=DB_fnkj0ls01FgZbf4nPFHl4EBU8X_0OrmDvty4HlRE,6020
3
- relib/measure_duration.py,sha256=LCTo_D_qReNprD3fhtJ0daeWycS6xQE_cwxeg2_h0xo,456
4
- relib/system.py,sha256=H-SJccCVLNTDhWTT5jo1NFUiQJzHv2Z1xvq0OVrnJcM,431
5
- relib/utils.py,sha256=6Y_77KeejooPa54Z57EwPbNVvnFncmGay-VBbv4gAQQ,6905
6
- relib-1.2.3.dist-info/METADATA,sha256=SrcjKfhYBKtDMGjdZqmwLsgm5I3hln8Q4ZW2FtFHz80,1295
7
- relib-1.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- relib-1.2.3.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
9
- relib-1.2.3.dist-info/RECORD,,
File without changes