pythonwrench 0.4.3__tar.gz → 0.4.4__tar.gz
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.
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/LICENSE +1 -1
- {pythonwrench-0.4.3/src/pythonwrench.egg-info → pythonwrench-0.4.4}/PKG-INFO +4 -5
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/README.md +1 -1
- pythonwrench-0.4.4/docs/requirements.txt +3 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/__init__.py +2 -1
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/collections/__init__.py +1 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/collections/collections.py +22 -1
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/concurrent.py +25 -10
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/disk_cache.py +13 -2
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/jsonl.py +9 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4/src/pythonwrench.egg-info}/PKG-INFO +4 -5
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench.egg-info/SOURCES.txt +1 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_disk_cache.py +1 -1
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/pyproject.toml +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/setup.cfg +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/setup.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/argparse.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/cast.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/checksum.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/entries.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/functools.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/logging.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/pickle.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/typing/__init__.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/typing/checks.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/typing/classes.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench.egg-info/dependency_links.txt +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench.egg-info/entry_points.txt +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench.egg-info/requires.txt +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/src/pythonwrench.egg-info/top_level.txt +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_abc.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_argparse.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_cast.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_checksum.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_collections.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_csv.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_difflib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_entries.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_enum.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_functools.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_importlib.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_inspect.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_json.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_logging.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_math.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_os.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_random.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_readme.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_semver.py +0 -0
- {pythonwrench-0.4.3 → pythonwrench-0.4.4}/tests/test_typing.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
6
6
|
Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
7
7
|
License: MIT License
|
|
8
8
|
|
|
9
|
-
Copyright (c)
|
|
9
|
+
Copyright (c) 2026 Labbeti
|
|
10
10
|
|
|
11
11
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
12
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -52,7 +52,6 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
License-File: LICENSE
|
|
53
53
|
Requires-Dist: typing-extensions>=4.10.0
|
|
54
54
|
Provides-Extra: dev
|
|
55
|
-
Dynamic: license-file
|
|
56
55
|
|
|
57
56
|
# pythonwrench
|
|
58
57
|
|
|
@@ -91,7 +90,7 @@ This library has been tested on all Python versions **3.8 - 3.14**, requires onl
|
|
|
91
90
|
|
|
92
91
|
### Typing
|
|
93
92
|
|
|
94
|
-
Check generic types with
|
|
93
|
+
Check generic types with `isinstance_generic` :
|
|
95
94
|
|
|
96
95
|
```python
|
|
97
96
|
>>> import pythonwrench as pw
|
|
@@ -9,7 +9,7 @@ __author_email__ = "labbeti.pub@gmail.com"
|
|
|
9
9
|
__license__ = "MIT"
|
|
10
10
|
__maintainer__ = "Étienne Labbé (Labbeti)"
|
|
11
11
|
__status__ = "Development"
|
|
12
|
-
__version__ = "0.4.
|
|
12
|
+
__version__ = "0.4.4"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Re-import for language servers
|
|
@@ -18,6 +18,7 @@ from . import argparse as argparse
|
|
|
18
18
|
from . import cast as cast
|
|
19
19
|
from . import checksum as checksum
|
|
20
20
|
from . import collections as collections
|
|
21
|
+
from . import concurrent as concurrent
|
|
21
22
|
from . import csv as csv
|
|
22
23
|
from . import dataclasses as dataclasses
|
|
23
24
|
from . import datetime as datetime
|
|
@@ -10,8 +10,10 @@ from typing import (
|
|
|
10
10
|
Callable,
|
|
11
11
|
Dict,
|
|
12
12
|
Generator,
|
|
13
|
+
Generic,
|
|
13
14
|
Hashable,
|
|
14
15
|
Iterable,
|
|
16
|
+
Iterator,
|
|
15
17
|
List,
|
|
16
18
|
Literal,
|
|
17
19
|
Mapping,
|
|
@@ -47,6 +49,21 @@ KeyMode = Literal["intersect", "same", "union"]
|
|
|
47
49
|
Order = Literal["left", "right"]
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
class SizedGenerator(Generic[T]):
|
|
53
|
+
"""Wraps a generator and size to provide a sized iterable object."""
|
|
54
|
+
|
|
55
|
+
def __init__(self, generator: Generator[T, None, None], size: int) -> None:
|
|
56
|
+
super().__init__()
|
|
57
|
+
self._generator = generator
|
|
58
|
+
self._size = size
|
|
59
|
+
|
|
60
|
+
def __iter__(self) -> Iterator[T]:
|
|
61
|
+
yield from self._generator
|
|
62
|
+
|
|
63
|
+
def __len__(self) -> int:
|
|
64
|
+
return self._size
|
|
65
|
+
|
|
66
|
+
|
|
50
67
|
def contained(
|
|
51
68
|
x: T,
|
|
52
69
|
include: Optional[Iterable[T]] = None,
|
|
@@ -497,7 +514,7 @@ def list_dict_to_dict_list(
|
|
|
497
514
|
"""Convert list of dicts to dict of lists.
|
|
498
515
|
|
|
499
516
|
Args:
|
|
500
|
-
lst: The list of dict to merge.
|
|
517
|
+
lst: The list of dict to merge. Cannot be a Generator.
|
|
501
518
|
key_mode: Can be "same" or "intersect". \
|
|
502
519
|
- If "same", all the dictionaries must contains the same keys otherwise a ValueError will be raised. \
|
|
503
520
|
- If "intersect", only the intersection of all keys will be used in output. \
|
|
@@ -507,6 +524,10 @@ def list_dict_to_dict_list(
|
|
|
507
524
|
default_val_fn: Function to return the default value according to a specific key. defaults to None.
|
|
508
525
|
list_fn: Optional function to build the values. defaults to identity.
|
|
509
526
|
"""
|
|
527
|
+
if isinstance(lst, Generator):
|
|
528
|
+
msg = f"Invalid argument type {type(lst)}. (expected any Iterable except Generator)"
|
|
529
|
+
raise TypeError(msg)
|
|
530
|
+
|
|
510
531
|
try:
|
|
511
532
|
item0 = next(iter(lst))
|
|
512
533
|
except StopIteration:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
5
|
from concurrent.futures import Future, ThreadPoolExecutor
|
|
6
|
-
from typing import Callable, Generic, List, Optional, TypeVar
|
|
6
|
+
from typing import Any, Callable, Dict, Generic, Iterable, List, Optional, TypeVar
|
|
7
7
|
|
|
8
8
|
from typing_extensions import ParamSpec
|
|
9
9
|
|
|
@@ -15,16 +15,32 @@ T = TypeVar("T")
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ThreadPoolExecutorHelper(Generic[P, T]):
|
|
18
|
-
|
|
18
|
+
# Note: use commas for typing because Future is not generic in older python versions
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
fn: Callable[P, T],
|
|
23
|
+
*,
|
|
24
|
+
executor_kwds: Optional[Dict[str, Any]] = None,
|
|
25
|
+
executor: Optional[ThreadPoolExecutor] = None,
|
|
26
|
+
futures: "Iterable[Future[T]]" = (),
|
|
27
|
+
**default_fn_kwds,
|
|
28
|
+
) -> None:
|
|
29
|
+
futures = list(futures)
|
|
30
|
+
|
|
19
31
|
super().__init__()
|
|
20
32
|
self.fn = fn
|
|
21
|
-
self.
|
|
22
|
-
self.executor
|
|
23
|
-
self.futures
|
|
33
|
+
self.executor_kwds = executor_kwds
|
|
34
|
+
self.executor = executor
|
|
35
|
+
self.futures = futures
|
|
36
|
+
self.default_kwargs = default_fn_kwds
|
|
24
37
|
|
|
25
|
-
def submit(self, *args: P.args, **kwargs: P.kwargs) -> Future[T]:
|
|
38
|
+
def submit(self, *args: P.args, **kwargs: P.kwargs) -> "Future[T]":
|
|
26
39
|
if self.executor is None:
|
|
27
|
-
|
|
40
|
+
executor_kwds = self.executor_kwds
|
|
41
|
+
if executor_kwds is None:
|
|
42
|
+
executor_kwds = {}
|
|
43
|
+
self.executor = ThreadPoolExecutor(**executor_kwds)
|
|
28
44
|
|
|
29
45
|
kwargs = self.default_kwargs | kwargs # type: ignore
|
|
30
46
|
future = self.executor.submit(self.fn, *args, **kwargs)
|
|
@@ -39,9 +55,8 @@ class ThreadPoolExecutorHelper(Generic[P, T]):
|
|
|
39
55
|
|
|
40
56
|
futures = tqdm.tqdm(futures, disable=not verbose)
|
|
41
57
|
except ImportError:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
58
|
+
msg = "Cannot display verbose bar because tqdm is not installed."
|
|
59
|
+
logger.warning(msg)
|
|
45
60
|
|
|
46
61
|
results = [future.result() for future in futures]
|
|
47
62
|
self.futures.clear()
|
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
import shutil
|
|
7
7
|
import time
|
|
8
|
+
import warnings
|
|
8
9
|
from functools import wraps
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import (
|
|
@@ -25,7 +26,7 @@ from typing_extensions import ParamSpec
|
|
|
25
26
|
|
|
26
27
|
from pythonwrench.checksum import checksum_any
|
|
27
28
|
from pythonwrench.datetime import get_now
|
|
28
|
-
from pythonwrench.inspect import get_fullname
|
|
29
|
+
from pythonwrench.inspect import get_argnames, get_fullname
|
|
29
30
|
|
|
30
31
|
T = TypeVar("T")
|
|
31
32
|
P = ParamSpec("P")
|
|
@@ -218,6 +219,10 @@ def _disk_cache_impl(
|
|
|
218
219
|
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
219
220
|
# for backward compatibility
|
|
220
221
|
if cache_fname_fmt is None:
|
|
222
|
+
warnings.warn(
|
|
223
|
+
f"Deprecated argument value {cache_fname_fmt=}. (use default instead)",
|
|
224
|
+
DeprecationWarning,
|
|
225
|
+
)
|
|
221
226
|
cache_fname_fmt = "{fn_name}_{csum}{suffix}"
|
|
222
227
|
|
|
223
228
|
if cache_saving_backend == "pickle":
|
|
@@ -270,13 +275,19 @@ def _disk_cache_impl(
|
|
|
270
275
|
)
|
|
271
276
|
load_start_msg = f"[{fn_name}] Loading cache..."
|
|
272
277
|
load_end_msg = f"[{fn_name}] Cache loaded."
|
|
278
|
+
argnames = get_argnames(fn)
|
|
273
279
|
|
|
274
280
|
@wraps(fn)
|
|
275
281
|
def _disk_cache_wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
276
282
|
checksum_args = fn, args, kwargs
|
|
277
283
|
csum = cache_checksum_fn(checksum_args)
|
|
284
|
+
inputs = dict(zip(argnames, args))
|
|
285
|
+
inputs.update(kwargs)
|
|
278
286
|
cache_fname = cache_fname_fmt.format(
|
|
279
|
-
fn_name=fn_name,
|
|
287
|
+
fn_name=fn_name,
|
|
288
|
+
csum=csum,
|
|
289
|
+
suffix=suffix,
|
|
290
|
+
**inputs,
|
|
280
291
|
)
|
|
281
292
|
cache_fpath = cache_fn_dpath.joinpath(cache_fname)
|
|
282
293
|
|
|
@@ -18,6 +18,15 @@ from pythonwrench.json import (
|
|
|
18
18
|
from pythonwrench.semver import Version
|
|
19
19
|
from pythonwrench.warnings import warn_once
|
|
20
20
|
|
|
21
|
+
__all__ = [
|
|
22
|
+
"dump_jsonl",
|
|
23
|
+
"dumps_jsonl",
|
|
24
|
+
"save_jsonl",
|
|
25
|
+
"load_jsonl",
|
|
26
|
+
"loads_jsonl",
|
|
27
|
+
"read_jsonl",
|
|
28
|
+
]
|
|
29
|
+
|
|
21
30
|
# -- Dump / Save / Serialize content to JSONL --
|
|
22
31
|
|
|
23
32
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Author-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
6
6
|
Maintainer-email: "Étienne Labbé (Labbeti)" <labbeti.pub@gmail.com>
|
|
7
7
|
License: MIT License
|
|
8
8
|
|
|
9
|
-
Copyright (c)
|
|
9
|
+
Copyright (c) 2026 Labbeti
|
|
10
10
|
|
|
11
11
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
12
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -52,7 +52,6 @@ Description-Content-Type: text/markdown
|
|
|
52
52
|
License-File: LICENSE
|
|
53
53
|
Requires-Dist: typing-extensions>=4.10.0
|
|
54
54
|
Provides-Extra: dev
|
|
55
|
-
Dynamic: license-file
|
|
56
55
|
|
|
57
56
|
# pythonwrench
|
|
58
57
|
|
|
@@ -91,7 +90,7 @@ This library has been tested on all Python versions **3.8 - 3.14**, requires onl
|
|
|
91
90
|
|
|
92
91
|
### Typing
|
|
93
92
|
|
|
94
|
-
Check generic types with
|
|
93
|
+
Check generic types with `isinstance_generic` :
|
|
95
94
|
|
|
96
95
|
```python
|
|
97
96
|
>>> import pythonwrench as pw
|
|
@@ -23,7 +23,7 @@ class TestDiskCache(TestCase):
|
|
|
23
23
|
|
|
24
24
|
def test_disk_cache_example_2(self) -> None:
|
|
25
25
|
@pw.disk_cache_decorator(
|
|
26
|
-
cache_fname_fmt="{fn_name}_{csum}.json",
|
|
26
|
+
cache_fname_fmt="{fn_name}_{csum}_x={x}.json",
|
|
27
27
|
cache_load_fn=pw.load_json,
|
|
28
28
|
cache_dump_fn=pw.dump_json,
|
|
29
29
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|