relib 1.2.8__py3-none-any.whl → 1.2.10__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,46 +1,4 @@
1
- from .utils import (
2
- noop,
3
- non_none,
4
- as_any,
5
- list_split,
6
- drop_none,
7
- distinct,
8
- first,
9
- move_value,
10
- transpose_dict,
11
- make_combinations_by_dict,
12
- merge_dicts,
13
- intersect,
14
- ensure_tuple,
15
- key_of,
16
- omit,
17
- pick,
18
- dict_by,
19
- tuple_by,
20
- flatten,
21
- transpose,
22
- map_dict,
23
- deepen_dict,
24
- flatten_dict_inner,
25
- flatten_dict,
26
- group,
27
- reversed_enumerate,
28
- get_at,
29
- for_each,
30
- sized_partitions,
31
- num_partitions,
32
- df_from_array,
33
- StrFilter,
34
- str_filterer,
35
- )
36
- from .system import (
37
- read_json,
38
- write_json,
39
- clear_console,
40
- console_link,
41
- roll_tasks,
42
- as_async,
43
- async_limit,
44
- )
1
+ from .utils import *
2
+ from .system import *
45
3
  from .hashing import hash, hash_obj
46
4
  from .measure_duration import measure_duration
relib/system.py CHANGED
@@ -8,20 +8,31 @@ from pathlib import Path
8
8
  from typing import Any, Awaitable, Callable, Iterable, ParamSpec, TypeVar
9
9
  from .utils import noop
10
10
 
11
+ __all__ = [
12
+ "read_json",
13
+ "write_json",
14
+ "clear_console",
15
+ "console_link",
16
+ "roll_tasks",
17
+ "as_async",
18
+ "async_limit",
19
+ ]
20
+
11
21
  P = ParamSpec("P")
12
22
  R = TypeVar("R")
13
- default_num_workers = min(32, (os.cpu_count() or 1) + 4)
14
- _default_json: Any = {}
23
+ default_workers = min(32, (os.cpu_count() or 1) + 4)
24
+ default_sentinel = object()
15
25
 
16
- def read_json(path: Path, default=_default_json) -> Any:
17
- if default is not _default_json and not path.exists():
26
+ def read_json(path: Path, default=default_sentinel) -> Any:
27
+ if default is not default_sentinel and not path.exists():
18
28
  return default
19
29
  with path.open("r") as f:
20
30
  return json.load(f)
21
31
 
22
- def write_json(path: Path, obj: Any, indent: None | int = None) -> None:
32
+ def write_json(path: Path, obj: object, indent: None | int = None) -> None:
23
33
  with path.open("w") as f:
24
- return json.dump(obj, f, indent=indent)
34
+ separators = (",", ":") if indent is None else None
35
+ return json.dump(obj, f, indent=indent, separators=separators)
25
36
 
26
37
  def clear_console() -> None:
27
38
  os.system("cls" if os.name == "nt" else "clear")
@@ -35,7 +46,7 @@ async def worker[T](task: Awaitable[T], semaphore: asyncio.Semaphore, update=noo
35
46
  update()
36
47
  return result
37
48
 
38
- async def roll_tasks[T](tasks: Iterable[Awaitable[T]], workers=default_num_workers, progress=False) -> list[T]:
49
+ async def roll_tasks[T](tasks: Iterable[Awaitable[T]], workers=default_workers, progress=False) -> list[T]:
39
50
  semaphore = asyncio.Semaphore(workers)
40
51
  if not progress:
41
52
  return await asyncio.gather(*[worker(task, semaphore) for task in tasks])
@@ -46,7 +57,7 @@ async def roll_tasks[T](tasks: Iterable[Awaitable[T]], workers=default_num_worke
46
57
  update = functools.partial(pbar.update, 1)
47
58
  return await asyncio.gather(*[worker(task, semaphore, update) for task in tasks])
48
59
 
49
- def as_async(workers=default_num_workers) -> Callable[[Callable[P, R]], Callable[P, Awaitable[R]]]:
60
+ def as_async(workers=default_workers) -> Callable[[Callable[P, R]], Callable[P, Awaitable[R]]]:
50
61
  executor = ThreadPoolExecutor(max_workers=workers)
51
62
 
52
63
  def on_fn(func: Callable[P, R]) -> Callable[P, Awaitable[R]]:
@@ -59,7 +70,7 @@ def as_async(workers=default_num_workers) -> Callable[[Callable[P, R]], Callable
59
70
  return wrapper
60
71
  return on_fn
61
72
 
62
- def async_limit(workers=default_num_workers) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]:
73
+ def async_limit(workers=default_workers) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]:
63
74
  semaphore = asyncio.Semaphore(workers)
64
75
 
65
76
  def on_fn(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[R]]:
relib/utils.py CHANGED
@@ -2,9 +2,56 @@ import re
2
2
  from itertools import chain
3
3
  from typing import Any, Callable, Iterable, overload
4
4
 
5
+ __all__ = [
6
+ "noop",
7
+ "clamp",
8
+ "non_none",
9
+ "as_any",
10
+ "list_split",
11
+ "drop_none",
12
+ "distinct",
13
+ "dict_firsts",
14
+ "distinct_by",
15
+ "sort_by",
16
+ "first",
17
+ "move_value",
18
+ "transpose_dict",
19
+ "make_combinations_by_dict",
20
+ "merge_dicts",
21
+ "intersect",
22
+ "ensure_tuple",
23
+ "key_of",
24
+ "omit",
25
+ "pick",
26
+ "dict_by",
27
+ "tuple_by",
28
+ "flatten",
29
+ "transpose",
30
+ "map_dict",
31
+ "deepen_dict",
32
+ "flatten_dict_inner",
33
+ "flatten_dict",
34
+ "group",
35
+ "reversed_enumerate",
36
+ "get_at",
37
+ "for_each",
38
+ "sized_partitions",
39
+ "num_partitions",
40
+ "df_from_array",
41
+ "StrFilter",
42
+ "str_filterer",
43
+ ]
44
+
5
45
  def noop() -> None:
6
46
  pass
7
47
 
48
+ @overload
49
+ def clamp(value: int, low: int, high: int) -> int: ...
50
+ @overload
51
+ def clamp(value: float, low: float, high: float) -> float: ...
52
+ def clamp(value: float, low: float, high: float) -> float:
53
+ return max(low, min(value, high))
54
+
8
55
  def non_none[T](obj: T | None) -> T:
9
56
  assert obj is not None
10
57
  return obj
@@ -12,28 +59,45 @@ def non_none[T](obj: T | None) -> T:
12
59
  def as_any(obj: Any) -> Any:
13
60
  return obj
14
61
 
15
- def list_split[T](l: list[T], sep: T) -> list[list[T]]:
16
- l = [sep, *l, sep]
17
- split_at = [i for i, x in enumerate(l) if x is sep]
62
+ def list_split[T](iterable: Iterable[T], sep: T) -> list[list[T]]:
63
+ values = [sep, *iterable, sep]
64
+ split_at = [i for i, x in enumerate(values) if x is sep]
18
65
  ranges = list(zip(split_at[0:-1], split_at[1:]))
19
66
  return [
20
- l[start + 1:end]
67
+ values[start + 1:end]
21
68
  for start, end in ranges
22
69
  ]
23
70
 
24
71
  def drop_none[T](iterable: Iterable[T | None]) -> list[T]:
25
72
  return [x for x in iterable if x is not None]
26
73
 
27
- def distinct[T](items: Iterable[T]) -> list[T]:
28
- return list(dict.fromkeys(items))
74
+ def distinct[T](iterable: Iterable[T]) -> list[T]:
75
+ return list(dict.fromkeys(iterable))
76
+
77
+ def dict_firsts[T, K](pairs: Iterable[tuple[K, T]]) -> dict[K, T]:
78
+ result: dict[K, T] = {}
79
+ for key, item in pairs:
80
+ if key not in result:
81
+ result[key] = item
82
+ return result
83
+
84
+ def distinct_by[T](pairs: Iterable[tuple[object, T]]) -> list[T]:
85
+ return list(dict_firsts(pairs).values())
86
+
87
+ def sort_by[T](pairs: Iterable[tuple[object, T]]) -> list[T]:
88
+ pair_list: list[Any] = list(pairs)
89
+ pair_list.sort(key=lambda p: p[0])
90
+ for i in range(len(pair_list)):
91
+ pair_list[i] = pair_list[i][1]
92
+ return pair_list
29
93
 
30
94
  def first[T](iterable: Iterable[T]) -> T | None:
31
95
  return next(iter(iterable), None)
32
96
 
33
97
  def move_value[T](iterable: Iterable[T], from_i: int, to_i: int) -> list[T]:
34
- l = list(iterable)
35
- l.insert(to_i, l.pop(from_i))
36
- return l
98
+ values = list(iterable)
99
+ values.insert(to_i, values.pop(from_i))
100
+ return values
37
101
 
38
102
  def transpose_dict(des):
39
103
  if isinstance(des, list):
@@ -53,7 +117,7 @@ def transpose_dict(des):
53
117
  raise ValueError("transpose_dict only accepts dict or list")
54
118
 
55
119
  def make_combinations_by_dict(des, keys=None, pairs=[]):
56
- keys = sorted(des.keys()) if keys == None else keys
120
+ keys = sorted(des.keys()) if keys is None else keys
57
121
  if len(keys) == 0:
58
122
  return [dict(pairs)]
59
123
  key = keys[0]
@@ -148,8 +212,8 @@ def group[T, K](pairs: Iterable[tuple[K, T]]) -> dict[K, list[T]]:
148
212
  values_by_key.setdefault(key, []).append(value)
149
213
  return values_by_key
150
214
 
151
- def reversed_enumerate[T](l: list[T] | tuple[T, ...]) -> Iterable[tuple[int, T]]:
152
- return zip(reversed(range(len(l))), reversed(l))
215
+ def reversed_enumerate[T](values: list[T] | tuple[T, ...]) -> Iterable[tuple[int, T]]:
216
+ return zip(reversed(range(len(values))), reversed(values))
153
217
 
154
218
  def get_at[T](d: dict, keys: Iterable[Any], default: T) -> T:
155
219
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relib
3
- Version: 1.2.8
3
+ Version: 1.2.10
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=4_nmex7mRhCwdtLF8k0XLbxxPs-UeN2sP-EEImm5JGs,126
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=3RWmSweTCQtB1wzsgpUqcAsMo6TIhVRq2oSt28Ul_1E,2733
5
+ relib/utils.py,sha256=A6GenRxjzI-1R80QD3G8Xq-E72LezIbItuHeBSaYnHg,8362
6
+ relib-1.2.10.dist-info/METADATA,sha256=q2wmDYkUk7f_D3WxRQloVtqjOpI6_Z3h5AFt9qXFEto,1296
7
+ relib-1.2.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ relib-1.2.10.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
9
+ relib-1.2.10.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- relib/__init__.py,sha256=pypVcr0ctaSdFZmwMnZvtioTbms3MlWfAcrnPVdZxog,689
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=RO8pd5eUmucbAxgfFi7UWHH5Cdp0aM6O1oe5cylrc4k,2527
5
- relib/utils.py,sha256=4fM0zsAdfrM2w9Q1YKcTfFG5kDzoNgD2amkgfTG39OY,6933
6
- relib-1.2.8.dist-info/METADATA,sha256=b8RvpgbcT7Y9zq6r-kwnnCZgql_eAdG41Y83-q2YIXg,1295
7
- relib-1.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
- relib-1.2.8.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
9
- relib-1.2.8.dist-info/RECORD,,
File without changes