relib 1.3.3__py3-none-any.whl → 1.3.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/dict_utils.py CHANGED
@@ -2,8 +2,8 @@ from typing import Any, Callable, Iterable, overload
2
2
  from .type_utils import as_any
3
3
 
4
4
  __all__ = [
5
- "deepen_dict", "dict_by", "dict_firsts",
6
- "flatten_dict_inner", "flatten_dict",
5
+ "deep_dict_pairs", "deepen_dict", "dict_by", "dict_firsts",
6
+ "flatten_dict",
7
7
  "get_at", "group",
8
8
  "key_of",
9
9
  "map_dict", "merge_dicts",
@@ -62,15 +62,15 @@ def group[T, K](pairs: Iterable[tuple[K, T]]) -> dict[K, list[T]]:
62
62
  values_by_key.setdefault(key, []).append(value)
63
63
  return values_by_key
64
64
 
65
- def flatten_dict_inner(d, prefix=()):
65
+ def deep_dict_pairs(d, prefix=()):
66
66
  for key, value in d.items():
67
67
  if not isinstance(value, dict) or value == {}:
68
68
  yield prefix + (key,), value
69
69
  else:
70
- yield from flatten_dict_inner(value, prefix + (key,))
70
+ yield from deep_dict_pairs(value, prefix + (key,))
71
71
 
72
72
  def flatten_dict(deep_dict: dict, prefix=()) -> dict:
73
- return dict(flatten_dict_inner(deep_dict, prefix))
73
+ return dict(deep_dict_pairs(deep_dict, prefix))
74
74
 
75
75
  @overload
76
76
  def deepen_dict[K1, U](d: dict[tuple[K1], U]) -> dict[K1, U]: ...
relib/io_utils.py CHANGED
@@ -1,8 +1,10 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from typing import Any
3
+ from typing import Any, Iterable
4
4
 
5
5
  __all__ = [
6
+ "clear_directory",
7
+ "empty_dirs",
6
8
  "read_json",
7
9
  "write_json",
8
10
  ]
@@ -19,3 +21,21 @@ def write_json(path: Path, obj: object, indent: None | int = None) -> None:
19
21
  with path.open("w") as f:
20
22
  separators = (",", ":") if indent is None else None
21
23
  return json.dump(obj, f, indent=indent, separators=separators)
24
+
25
+ def empty_dirs(path: Path) -> Iterable[Path]:
26
+ nonempty_count = 0
27
+ for child in path.iterdir():
28
+ nonempty_count += 1
29
+ if child.is_dir():
30
+ for grand_child in empty_dirs(child):
31
+ yield grand_child
32
+ nonempty_count -= child == grand_child
33
+ if nonempty_count == 0:
34
+ yield path
35
+
36
+ def clear_directory(path: Path):
37
+ if path.is_dir():
38
+ for file in path.glob("**/.DS_Store"):
39
+ file.unlink()
40
+ for directory in empty_dirs(path):
41
+ directory.rmdir()
relib/iter_utils.py CHANGED
@@ -1,9 +1,11 @@
1
1
  from contextlib import contextmanager
2
2
  from itertools import chain, islice
3
- from typing import Any, Iterable, Literal, Self, overload
3
+ from typing import Any, Iterable, Literal, Self, Sequence, overload
4
4
  from .dict_utils import dict_firsts
5
5
 
6
6
  __all__ = [
7
+ "as_list",
8
+ "at",
7
9
  "chunked",
8
10
  "distinct_by", "distinct", "drop_none",
9
11
  "first", "flatten",
@@ -16,6 +18,15 @@ __all__ = [
16
18
  "transpose",
17
19
  ]
18
20
 
21
+ def as_list[T](iterable: Iterable[T]) -> list[T]:
22
+ return iterable if isinstance(iterable, list) else list(iterable)
23
+
24
+ def at[T, U](values: Sequence[T], index: int, default: U = None) -> T | U:
25
+ try:
26
+ return values[index]
27
+ except IndexError:
28
+ return default
29
+
19
30
  def first[T](iterable: Iterable[T]) -> T | None:
20
31
  return next(iter(iterable), None)
21
32
 
@@ -28,8 +39,8 @@ def distinct[T](iterable: Iterable[T]) -> list[T]:
28
39
  def distinct_by[T](pairs: Iterable[tuple[object, T]]) -> list[T]:
29
40
  return list(dict_firsts(pairs).values())
30
41
 
31
- def sort_by[T](pairs: Iterable[tuple[Any, T]]) -> list[T]:
32
- pairs = sorted(pairs, key=lambda p: p[0])
42
+ def sort_by[T](pairs: Iterable[tuple[Any, T]], reverse=False) -> list[T]:
43
+ pairs = sorted(pairs, key=lambda p: p[0], reverse=reverse)
33
44
  return [v for _, v in pairs]
34
45
 
35
46
  def move_value[T](iterable: Iterable[T], from_i: int, to_i: int) -> list[T]:
@@ -37,7 +48,7 @@ def move_value[T](iterable: Iterable[T], from_i: int, to_i: int) -> list[T]:
37
48
  values.insert(to_i, values.pop(from_i))
38
49
  return values
39
50
 
40
- def reversed_enumerate[T](values: list[T] | tuple[T, ...]) -> Iterable[tuple[int, T]]:
51
+ def reversed_enumerate[T](values: Sequence[T] | tuple[T, ...]) -> Iterable[tuple[int, T]]:
41
52
  return zip(range(len(values))[::-1], reversed(values))
42
53
 
43
54
  def intersect[T](*iterables: Iterable[T]) -> list[T]:
@@ -116,7 +127,7 @@ def chunked[T](values: Iterable[T], *, num_chunks: int, chunk_size=None) -> list
116
127
  @overload
117
128
  def chunked[T](values: Iterable[T], *, num_chunks=None, chunk_size: int) -> list[list[T]]: ...
118
129
  def chunked(values, *, num_chunks=None, chunk_size=None):
119
- values = values if isinstance(values, list) else list(values)
130
+ values = as_list(values)
120
131
  if isinstance(num_chunks, int):
121
132
  chunk_size = (len(values) / num_chunks).__ceil__()
122
133
  elif isinstance(chunk_size, int):
relib/runtime_tools.py CHANGED
@@ -6,6 +6,7 @@ from concurrent.futures import ThreadPoolExecutor
6
6
  from functools import partial, wraps
7
7
  from time import time
8
8
  from typing import Callable, Coroutine, Iterable, ParamSpec, TypeVar
9
+ from .iter_utils import as_list
9
10
  from .processing_utils import noop
10
11
 
11
12
  __all__ = [
@@ -25,7 +26,7 @@ default_executor = ThreadPoolExecutor(max_workers=default_workers)
25
26
 
26
27
  def raise_if_interrupt():
27
28
  if sys.exc_info()[0] in (KeyboardInterrupt, SystemExit):
28
- raise
29
+ raise
29
30
 
30
31
  def clear_console() -> None:
31
32
  os.system("cls" if os.name == "nt" else "clear")
@@ -45,7 +46,7 @@ async def roll_tasks[T](tasks: Iterable[Coro[T]], workers=default_workers, progr
45
46
  return await asyncio.gather(*[worker(task, semaphore) for task in tasks])
46
47
 
47
48
  from tqdm import tqdm
48
- tasks = tasks if isinstance(tasks, list) else list(tasks)
49
+ tasks = as_list(tasks)
49
50
  with tqdm(total=len(tasks)) as pbar:
50
51
  update = partial(pbar.update, 1)
51
52
  return await asyncio.gather(*[worker(task, semaphore, update) for task in tasks])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: relib
3
- Version: 1.3.3
3
+ Version: 1.3.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,11 @@
1
+ relib/__init__.py,sha256=WerjUaM_sNvudjXFudLRtXB7viZWEW1RSinkDjrh4nE,163
2
+ relib/dict_utils.py,sha256=zuUkPI1uiElWUgt3ETdajG7S2grP5mX1qM0t2Cc1zy4,2968
3
+ relib/io_utils.py,sha256=cUyUFhrMCUDfkINhYo32QPaVGz3chqDO3ElymSCoWEg,1086
4
+ relib/iter_utils.py,sha256=xQOyNwYCc9fyKScyd56vx4CVCzZBDDROKVfnJuaInCM,5774
5
+ relib/processing_utils.py,sha256=eMzjlxsEmfvtKafDITBWSp9D5RwegSWsUsvj1FpmBM0,1893
6
+ relib/runtime_tools.py,sha256=50zfIXZwXXU6tGKM-1iS3FEax82vthgp-KNmvmIGlqE,2964
7
+ relib/type_utils.py,sha256=oY96cAAux1JwhXgWFFyqEv_f-wwyPc_Hm6I9Yeisu_M,323
8
+ relib-1.3.5.dist-info/METADATA,sha256=adnaXzA56Yff2cXRK79hAopNMnPs_rfwBBKs95l3neY,1295
9
+ relib-1.3.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ relib-1.3.5.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
11
+ relib-1.3.5.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- relib/__init__.py,sha256=WerjUaM_sNvudjXFudLRtXB7viZWEW1RSinkDjrh4nE,163
2
- relib/dict_utils.py,sha256=jqW6bYSaQMt2AC2KFzDJKyl88idyMttWxXDu3t-fA5I,2980
3
- relib/io_utils.py,sha256=EtnIGQmLXjoHUPFteB5yPXDD3wGLvH4O3CahlCebXDQ,555
4
- relib/iter_utils.py,sha256=r9tus9F_obwXsHE8Jk8H9_ZPdOgtBI6WnpDxI6La-to,5477
5
- relib/processing_utils.py,sha256=eMzjlxsEmfvtKafDITBWSp9D5RwegSWsUsvj1FpmBM0,1893
6
- relib/runtime_tools.py,sha256=9N8gn3WCfqyS6D3Qs4l2nDja_NxDVVF-7hHIVrXA7Fc,2968
7
- relib/type_utils.py,sha256=oY96cAAux1JwhXgWFFyqEv_f-wwyPc_Hm6I9Yeisu_M,323
8
- relib-1.3.3.dist-info/METADATA,sha256=3z1-dTnOlkjYiy54JQ2VQOs0zy5_kyP4Bk33l1YuAoQ,1295
9
- relib-1.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- relib-1.3.3.dist-info/licenses/LICENSE,sha256=9xVsdtv_-uSyY9Xl9yujwAPm4-mjcCLeVy-ljwXEWbo,1059
11
- relib-1.3.3.dist-info/RECORD,,
File without changes