python-utils 3.8.2__tar.gz → 3.9.1__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.
- {python-utils-3.8.2/python_utils.egg-info → python_utils-3.9.1}/PKG-INFO +7 -3
- python_utils-3.9.1/_python_utils_tests/test_aio.py +68 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_decorators.py +15 -14
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_generators.py +5 -5
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_import.py +11 -11
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_logger.py +2 -2
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_python_utils.py +1 -1
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_time.py +42 -28
- python_utils-3.9.1/pyproject.toml +20 -0
- python_utils-3.9.1/python_utils/__about__.py +22 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/__init__.py +81 -32
- python_utils-3.9.1/python_utils/aio.py +117 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/containers.py +283 -33
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/converters.py +143 -63
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/decorators.py +47 -24
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/exceptions.py +20 -2
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/formatters.py +36 -15
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/generators.py +38 -6
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/import_.py +34 -14
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/logger.py +134 -17
- python_utils-3.9.1/python_utils/loguru.py +51 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/terminal.py +46 -20
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/time.py +98 -51
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/types.py +109 -92
- {python-utils-3.8.2 → python_utils-3.9.1/python_utils.egg-info}/PKG-INFO +7 -3
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils.egg-info/SOURCES.txt +0 -1
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils.egg-info/requires.txt +5 -1
- {python-utils-3.8.2 → python_utils-3.9.1}/setup.py +23 -8
- {python-utils-3.8.2 → python_utils-3.9.1}/tox.ini +9 -7
- python-utils-3.8.2/_python_utils_tests/test_aio.py +0 -42
- python-utils-3.8.2/pyproject.toml +0 -12
- python-utils-3.8.2/python_utils/__about__.py +0 -10
- python-utils-3.8.2/python_utils/aio.py +0 -54
- python-utils-3.8.2/python_utils/compat.py +0 -0
- python-utils-3.8.2/python_utils/loguru.py +0 -16
- {python-utils-3.8.2 → python_utils-3.9.1}/LICENSE +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/MANIFEST.in +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/README.rst +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/__init__.py +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/requirements.txt +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/_python_utils_tests/test_containers.py +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/coverage.rc +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/pytest.ini +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils/py.typed +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils.egg-info/dependency_links.txt +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/python_utils.egg-info/top_level.txt +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/requirements.txt +0 -0
- {python-utils-3.8.2 → python_utils-3.9.1}/setup.cfg +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-utils
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.9.1
|
|
4
4
|
Summary: Python Utils is a module with some convenient utilities not included with the standard Python install
|
|
5
5
|
Home-page: https://github.com/WoLpH/python-utils
|
|
6
6
|
Author: Rick van Hattem
|
|
7
7
|
Author-email: Wolph@wol.ph
|
|
8
8
|
License: BSD
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
-
Requires-Python:
|
|
10
|
+
Requires-Python: >=3.9.0
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Requires-Dist: typing_extensions>3.10.0.2
|
|
13
13
|
Provides-Extra: loguru
|
|
@@ -17,7 +17,8 @@ Requires-Dist: mock; extra == "docs"
|
|
|
17
17
|
Requires-Dist: sphinx; extra == "docs"
|
|
18
18
|
Requires-Dist: python-utils; extra == "docs"
|
|
19
19
|
Provides-Extra: tests
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: ruff; extra == "tests"
|
|
21
|
+
Requires-Dist: pyright; extra == "tests"
|
|
21
22
|
Requires-Dist: pytest; extra == "tests"
|
|
22
23
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
23
24
|
Requires-Dist: pytest-mypy; extra == "tests"
|
|
@@ -25,6 +26,9 @@ Requires-Dist: pytest-asyncio; extra == "tests"
|
|
|
25
26
|
Requires-Dist: sphinx; extra == "tests"
|
|
26
27
|
Requires-Dist: types-setuptools; extra == "tests"
|
|
27
28
|
Requires-Dist: loguru; extra == "tests"
|
|
29
|
+
Requires-Dist: loguru-mypy; extra == "tests"
|
|
30
|
+
Requires-Dist: mypy-ipython; extra == "tests"
|
|
31
|
+
Requires-Dist: blessings; extra == "tests"
|
|
28
32
|
|
|
29
33
|
Useful Python Utils
|
|
30
34
|
==============================================================================
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from python_utils import types
|
|
6
|
+
from python_utils.aio import acontainer, acount, adict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.asyncio
|
|
10
|
+
async def test_acount(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
11
|
+
sleeps: types.List[float] = []
|
|
12
|
+
|
|
13
|
+
async def mock_sleep(delay: float) -> None:
|
|
14
|
+
sleeps.append(delay)
|
|
15
|
+
|
|
16
|
+
monkeypatch.setattr(asyncio, 'sleep', mock_sleep)
|
|
17
|
+
|
|
18
|
+
async for _i in acount(delay=1, stop=3.5):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
assert len(sleeps) == 4
|
|
22
|
+
assert sum(sleeps) == 4
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.asyncio
|
|
26
|
+
async def test_acontainer() -> None:
|
|
27
|
+
async def async_gen() -> types.AsyncIterable[int]:
|
|
28
|
+
yield 1
|
|
29
|
+
yield 2
|
|
30
|
+
yield 3
|
|
31
|
+
|
|
32
|
+
async def empty_gen() -> types.AsyncIterable[int]:
|
|
33
|
+
if False:
|
|
34
|
+
yield 1
|
|
35
|
+
|
|
36
|
+
assert await acontainer(async_gen) == [1, 2, 3]
|
|
37
|
+
assert await acontainer(async_gen()) == [1, 2, 3]
|
|
38
|
+
assert await acontainer(async_gen, set) == {1, 2, 3}
|
|
39
|
+
assert await acontainer(async_gen(), set) == {1, 2, 3}
|
|
40
|
+
assert await acontainer(async_gen, list) == [1, 2, 3]
|
|
41
|
+
assert await acontainer(async_gen(), list) == [1, 2, 3]
|
|
42
|
+
assert await acontainer(async_gen, tuple) == (1, 2, 3)
|
|
43
|
+
assert await acontainer(async_gen(), tuple) == (1, 2, 3)
|
|
44
|
+
assert await acontainer(empty_gen) == []
|
|
45
|
+
assert await acontainer(empty_gen()) == []
|
|
46
|
+
assert await acontainer(empty_gen, set) == set()
|
|
47
|
+
assert await acontainer(empty_gen(), set) == set()
|
|
48
|
+
assert await acontainer(empty_gen, list) == list()
|
|
49
|
+
assert await acontainer(empty_gen(), list) == list()
|
|
50
|
+
assert await acontainer(empty_gen, tuple) == tuple()
|
|
51
|
+
assert await acontainer(empty_gen(), tuple) == tuple()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.mark.asyncio
|
|
55
|
+
async def test_adict() -> None:
|
|
56
|
+
async def async_gen() -> types.AsyncIterable[types.Tuple[int, int]]:
|
|
57
|
+
yield 1, 2
|
|
58
|
+
yield 3, 4
|
|
59
|
+
yield 5, 6
|
|
60
|
+
|
|
61
|
+
async def empty_gen() -> types.AsyncIterable[types.Tuple[int, int]]:
|
|
62
|
+
if False:
|
|
63
|
+
yield 1, 2
|
|
64
|
+
|
|
65
|
+
assert await adict(async_gen) == {1: 2, 3: 4, 5: 6}
|
|
66
|
+
assert await adict(async_gen()) == {1: 2, 3: 4, 5: 6}
|
|
67
|
+
assert await adict(empty_gen) == {}
|
|
68
|
+
assert await adict(empty_gen()) == {}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from unittest.mock import MagicMock
|
|
2
3
|
|
|
3
4
|
import pytest
|
|
4
5
|
|
|
5
6
|
from python_utils.decorators import sample, wraps_classmethod
|
|
6
7
|
|
|
8
|
+
T = typing.TypeVar('T')
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
@pytest.fixture
|
|
9
12
|
def random(monkeypatch: pytest.MonkeyPatch) -> MagicMock:
|
|
@@ -14,7 +17,7 @@ def random(monkeypatch: pytest.MonkeyPatch) -> MagicMock:
|
|
|
14
17
|
return mock
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def test_sample_called(random: MagicMock):
|
|
20
|
+
def test_sample_called(random: MagicMock) -> None:
|
|
18
21
|
demo_function = MagicMock()
|
|
19
22
|
decorated = sample(0.5)(demo_function)
|
|
20
23
|
random.return_value = 0.4
|
|
@@ -28,7 +31,7 @@ def test_sample_called(random: MagicMock):
|
|
|
28
31
|
assert demo_function.call_count == 3
|
|
29
32
|
|
|
30
33
|
|
|
31
|
-
def test_sample_not_called(random: MagicMock):
|
|
34
|
+
def test_sample_not_called(random: MagicMock) -> None:
|
|
32
35
|
demo_function = MagicMock()
|
|
33
36
|
decorated = sample(0.5)(demo_function)
|
|
34
37
|
random.return_value = 0.5
|
|
@@ -40,31 +43,29 @@ def test_sample_not_called(random: MagicMock):
|
|
|
40
43
|
|
|
41
44
|
class SomeClass:
|
|
42
45
|
@classmethod
|
|
43
|
-
def some_classmethod(cls, arg
|
|
44
|
-
return arg
|
|
46
|
+
def some_classmethod(cls, arg: T) -> T:
|
|
47
|
+
return arg
|
|
45
48
|
|
|
46
49
|
@classmethod
|
|
47
50
|
def some_annotated_classmethod(cls, arg: int) -> int:
|
|
48
51
|
return arg
|
|
49
52
|
|
|
50
53
|
|
|
51
|
-
def test_wraps_classmethod()
|
|
54
|
+
def test_wraps_classmethod() -> None:
|
|
52
55
|
some_class = SomeClass()
|
|
53
|
-
some_class.some_classmethod = MagicMock()
|
|
54
|
-
wrapped_method = wraps_classmethod(
|
|
55
|
-
|
|
56
|
-
)( # type: ignore
|
|
57
|
-
some_class.some_classmethod # type: ignore
|
|
56
|
+
some_class.some_classmethod = MagicMock() # type: ignore[method-assign]
|
|
57
|
+
wrapped_method = wraps_classmethod(SomeClass.some_classmethod)(
|
|
58
|
+
some_class.some_classmethod
|
|
58
59
|
)
|
|
59
60
|
wrapped_method(123)
|
|
60
|
-
some_class.some_classmethod.assert_called_with(123)
|
|
61
|
+
some_class.some_classmethod.assert_called_with(123)
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
def test_wraps_annotated_classmethod()
|
|
64
|
+
def test_wraps_annotated_classmethod() -> None:
|
|
64
65
|
some_class = SomeClass()
|
|
65
|
-
some_class.some_annotated_classmethod = MagicMock()
|
|
66
|
+
some_class.some_annotated_classmethod = MagicMock() # type: ignore[method-assign]
|
|
66
67
|
wrapped_method = wraps_classmethod(SomeClass.some_annotated_classmethod)(
|
|
67
68
|
some_class.some_annotated_classmethod
|
|
68
69
|
)
|
|
69
|
-
wrapped_method(123)
|
|
70
|
+
wrapped_method(123)
|
|
70
71
|
some_class.some_annotated_classmethod.assert_called_with(123)
|
|
@@ -7,7 +7,7 @@ from python_utils import types
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@pytest.mark.asyncio
|
|
10
|
-
async def test_abatcher():
|
|
10
|
+
async def test_abatcher() -> None:
|
|
11
11
|
async for batch in python_utils.abatcher(python_utils.acount(stop=9), 3):
|
|
12
12
|
assert len(batch) == 3
|
|
13
13
|
|
|
@@ -28,8 +28,8 @@ async def test_abatcher_timed() -> None:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@pytest.mark.asyncio
|
|
31
|
-
async def test_abatcher_timed_with_timeout():
|
|
32
|
-
async def generator():
|
|
31
|
+
async def test_abatcher_timed_with_timeout() -> None:
|
|
32
|
+
async def generator() -> types.AsyncIterator[int]:
|
|
33
33
|
# Test if the timeout is respected
|
|
34
34
|
yield 0
|
|
35
35
|
yield 1
|
|
@@ -57,12 +57,12 @@ async def test_abatcher_timed_with_timeout():
|
|
|
57
57
|
await batcher.__anext__()
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def test_batcher():
|
|
60
|
+
def test_batcher() -> None:
|
|
61
61
|
batch = []
|
|
62
62
|
for batch in python_utils.batcher(range(9), 3):
|
|
63
63
|
assert len(batch) == 3
|
|
64
64
|
|
|
65
65
|
for batch in python_utils.batcher(range(4), 3):
|
|
66
|
-
|
|
66
|
+
assert batch is not None
|
|
67
67
|
|
|
68
68
|
assert len(batch) == 1
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
from python_utils import import_, types
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def test_import_globals_relative_import():
|
|
4
|
+
def test_import_globals_relative_import() -> None:
|
|
5
5
|
for i in range(-1, 5):
|
|
6
6
|
relative_import(i)
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def relative_import(level: int):
|
|
9
|
+
def relative_import(level: int) -> None:
|
|
10
10
|
locals_: types.Dict[str, types.Any] = {}
|
|
11
11
|
globals_ = {'__name__': 'python_utils.import_'}
|
|
12
12
|
import_.import_global('.formatters', locals_=locals_, globals_=globals_)
|
|
13
13
|
assert 'camel_to_underscore' in globals_
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def test_import_globals_without_inspection():
|
|
17
|
-
locals_ = {}
|
|
18
|
-
globals_ = {'__name__': __name__}
|
|
16
|
+
def test_import_globals_without_inspection() -> None:
|
|
17
|
+
locals_: types.Dict[str, types.Any] = {}
|
|
18
|
+
globals_: types.Dict[str, types.Any] = {'__name__': __name__}
|
|
19
19
|
import_.import_global(
|
|
20
20
|
'python_utils.formatters', locals_=locals_, globals_=globals_
|
|
21
21
|
)
|
|
22
22
|
assert 'camel_to_underscore' in globals_
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def test_import_globals_single_method():
|
|
26
|
-
locals_ = {}
|
|
27
|
-
globals_ = {'__name__': __name__}
|
|
25
|
+
def test_import_globals_single_method() -> None:
|
|
26
|
+
locals_: types.Dict[str, types.Any] = {}
|
|
27
|
+
globals_: types.Dict[str, types.Any] = {'__name__': __name__}
|
|
28
28
|
import_.import_global(
|
|
29
29
|
'python_utils.formatters',
|
|
30
30
|
['camel_to_underscore'],
|
|
@@ -34,19 +34,19 @@ def test_import_globals_single_method():
|
|
|
34
34
|
assert 'camel_to_underscore' in globals_
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def test_import_globals_with_inspection():
|
|
37
|
+
def test_import_globals_with_inspection() -> None:
|
|
38
38
|
import_.import_global('python_utils.formatters')
|
|
39
39
|
assert 'camel_to_underscore' in globals()
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
def test_import_globals_missing_module():
|
|
42
|
+
def test_import_globals_missing_module() -> None:
|
|
43
43
|
import_.import_global(
|
|
44
44
|
'python_utils.spam', exceptions=ImportError, locals_=locals()
|
|
45
45
|
)
|
|
46
46
|
assert 'camel_to_underscore' in globals()
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def test_import_locals_missing_module():
|
|
49
|
+
def test_import_locals_missing_module() -> None:
|
|
50
50
|
import_.import_global(
|
|
51
51
|
'python_utils.spam', exceptions=ImportError, globals_=globals()
|
|
52
52
|
)
|
|
@@ -32,7 +32,7 @@ async def test_aio_timeout_generator(
|
|
|
32
32
|
maximum_interval: float,
|
|
33
33
|
iterable: types.AsyncIterable[types.Any],
|
|
34
34
|
result: int,
|
|
35
|
-
):
|
|
35
|
+
) -> None:
|
|
36
36
|
i = None
|
|
37
37
|
async for i in python_utils.aio_timeout_generator(
|
|
38
38
|
timeout, interval, iterable, maximum_interval=maximum_interval
|
|
@@ -45,16 +45,16 @@ async def test_aio_timeout_generator(
|
|
|
45
45
|
@pytest.mark.parametrize(
|
|
46
46
|
'timeout,interval,interval_multiplier,maximum_interval,iterable,result',
|
|
47
47
|
[
|
|
48
|
-
(0.
|
|
49
|
-
(0.
|
|
50
|
-
(0.
|
|
51
|
-
(0.
|
|
48
|
+
(0.1, 0.06, 0.5, 0.1, 'abc', 'c'),
|
|
49
|
+
(0.1, 0.07, 0.5, 0.1, itertools.count, 2),
|
|
50
|
+
(0.1, 0.07, 0.5, 0.1, itertools.count(), 2),
|
|
51
|
+
(0.1, 0.06, 1.0, None, 'abc', 'c'),
|
|
52
52
|
(
|
|
53
|
-
timedelta(seconds=0.
|
|
54
|
-
timedelta(seconds=0.
|
|
53
|
+
timedelta(seconds=0.1),
|
|
54
|
+
timedelta(seconds=0.06),
|
|
55
55
|
2.0,
|
|
56
|
-
timedelta(seconds=0.
|
|
57
|
-
itertools.count,
|
|
56
|
+
timedelta(seconds=0.1),
|
|
57
|
+
itertools.count,
|
|
58
58
|
2,
|
|
59
59
|
),
|
|
60
60
|
],
|
|
@@ -70,7 +70,7 @@ def test_timeout_generator(
|
|
|
70
70
|
types.Callable[..., types.Iterable[types.Any]],
|
|
71
71
|
],
|
|
72
72
|
result: int,
|
|
73
|
-
):
|
|
73
|
+
) -> None:
|
|
74
74
|
i = None
|
|
75
75
|
for i in python_utils.timeout_generator(
|
|
76
76
|
timeout=timeout,
|
|
@@ -79,40 +79,40 @@ def test_timeout_generator(
|
|
|
79
79
|
iterable=iterable,
|
|
80
80
|
maximum_interval=maximum_interval,
|
|
81
81
|
):
|
|
82
|
-
|
|
82
|
+
assert i is not None
|
|
83
83
|
|
|
84
84
|
assert i == result
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
@pytest.mark.asyncio
|
|
88
|
-
async def test_aio_generator_timeout_detector():
|
|
88
|
+
async def test_aio_generator_timeout_detector() -> None:
|
|
89
89
|
# Make pyright happy
|
|
90
90
|
i = None
|
|
91
91
|
|
|
92
|
-
async def generator():
|
|
92
|
+
async def generator() -> types.AsyncGenerator[int, None]:
|
|
93
93
|
for i in range(10):
|
|
94
|
-
await asyncio.sleep(i /
|
|
94
|
+
await asyncio.sleep(i / 20.0)
|
|
95
95
|
yield i
|
|
96
96
|
|
|
97
97
|
detector = python_utils.aio_generator_timeout_detector
|
|
98
98
|
# Test regular timeout with reraise
|
|
99
99
|
with pytest.raises(asyncio.TimeoutError):
|
|
100
|
-
async for i in detector(generator(), 0.
|
|
100
|
+
async for i in detector(generator(), 0.25):
|
|
101
101
|
pass
|
|
102
102
|
|
|
103
103
|
# Test regular timeout with clean exit
|
|
104
|
-
async for i in detector(generator(), 0.
|
|
104
|
+
async for i in detector(generator(), 0.25, on_timeout=None):
|
|
105
105
|
pass
|
|
106
106
|
|
|
107
107
|
assert i == 4
|
|
108
108
|
|
|
109
109
|
# Test total timeout with reraise
|
|
110
110
|
with pytest.raises(asyncio.TimeoutError):
|
|
111
|
-
async for i in detector(generator(), total_timeout=0.
|
|
111
|
+
async for i in detector(generator(), total_timeout=0.5):
|
|
112
112
|
pass
|
|
113
113
|
|
|
114
114
|
# Test total timeout with clean exit
|
|
115
|
-
async for i in detector(generator(), total_timeout=0.
|
|
115
|
+
async for i in detector(generator(), total_timeout=0.5, on_timeout=None):
|
|
116
116
|
pass
|
|
117
117
|
|
|
118
118
|
assert i == 4
|
|
@@ -123,26 +123,29 @@ async def test_aio_generator_timeout_detector():
|
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
@pytest.mark.asyncio
|
|
126
|
-
async def
|
|
127
|
-
# Make pyright happy
|
|
128
|
-
i = None
|
|
129
|
-
|
|
126
|
+
async def test_aio_generator_timeout_detector_decorator_reraise() -> None:
|
|
130
127
|
# Test regular timeout with reraise
|
|
131
128
|
@python_utils.aio_generator_timeout_detector_decorator(timeout=0.05)
|
|
132
|
-
async def generator_timeout():
|
|
129
|
+
async def generator_timeout() -> types.AsyncGenerator[int, None]:
|
|
133
130
|
for i in range(10):
|
|
134
131
|
await asyncio.sleep(i / 100.0)
|
|
135
132
|
yield i
|
|
136
133
|
|
|
137
134
|
with pytest.raises(asyncio.TimeoutError):
|
|
138
|
-
async for
|
|
135
|
+
async for _ in generator_timeout():
|
|
139
136
|
pass
|
|
140
137
|
|
|
138
|
+
|
|
139
|
+
@pytest.mark.asyncio
|
|
140
|
+
async def test_aio_generator_timeout_detector_decorator_clean_exit() -> None:
|
|
141
|
+
# Make pyright happy
|
|
142
|
+
i = None
|
|
143
|
+
|
|
141
144
|
# Test regular timeout with clean exit
|
|
142
145
|
@python_utils.aio_generator_timeout_detector_decorator(
|
|
143
146
|
timeout=0.05, on_timeout=None
|
|
144
147
|
)
|
|
145
|
-
async def generator_clean():
|
|
148
|
+
async def generator_clean() -> types.AsyncGenerator[int, None]:
|
|
146
149
|
for i in range(10):
|
|
147
150
|
await asyncio.sleep(i / 100.0)
|
|
148
151
|
yield i
|
|
@@ -152,22 +155,33 @@ async def test_aio_generator_timeout_detector_decorator():
|
|
|
152
155
|
|
|
153
156
|
assert i == 4
|
|
154
157
|
|
|
158
|
+
|
|
159
|
+
@pytest.mark.asyncio
|
|
160
|
+
async def test_aio_generator_timeout_detector_decorator_reraise_total() -> (
|
|
161
|
+
None
|
|
162
|
+
):
|
|
155
163
|
# Test total timeout with reraise
|
|
156
164
|
@python_utils.aio_generator_timeout_detector_decorator(total_timeout=0.1)
|
|
157
|
-
async def generator_reraise():
|
|
165
|
+
async def generator_reraise() -> types.AsyncGenerator[int, None]:
|
|
158
166
|
for i in range(10):
|
|
159
167
|
await asyncio.sleep(i / 100.0)
|
|
160
168
|
yield i
|
|
161
169
|
|
|
162
170
|
with pytest.raises(asyncio.TimeoutError):
|
|
163
|
-
async for
|
|
171
|
+
async for _ in generator_reraise():
|
|
164
172
|
pass
|
|
165
173
|
|
|
174
|
+
|
|
175
|
+
@pytest.mark.asyncio
|
|
176
|
+
async def test_aio_generator_timeout_detector_decorator_clean_total() -> None:
|
|
177
|
+
# Make pyright happy
|
|
178
|
+
i = None
|
|
179
|
+
|
|
166
180
|
# Test total timeout with clean exit
|
|
167
181
|
@python_utils.aio_generator_timeout_detector_decorator(
|
|
168
182
|
total_timeout=0.1, on_timeout=None
|
|
169
183
|
)
|
|
170
|
-
async def generator_clean_total():
|
|
184
|
+
async def generator_clean_total() -> types.AsyncGenerator[int, None]:
|
|
171
185
|
for i in range(10):
|
|
172
186
|
await asyncio.sleep(i / 100.0)
|
|
173
187
|
yield i
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[tool.black]
|
|
2
|
+
line-length = 79
|
|
3
|
+
target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
|
|
4
|
+
skip-string-normalization = true
|
|
5
|
+
|
|
6
|
+
[tool.pyright]
|
|
7
|
+
# include = ['python_utils']
|
|
8
|
+
include = ['python_utils', '_python_utils_tests', 'setup.py']
|
|
9
|
+
strict = ['python_utils', '_python_utils_tests', 'setup.py']
|
|
10
|
+
# The terminal file is very OS specific and dependent on imports so we're skipping it from type checking
|
|
11
|
+
ignore = ['python_utils/terminal.py']
|
|
12
|
+
pythonVersion = '3.9'
|
|
13
|
+
|
|
14
|
+
[tool.mypy]
|
|
15
|
+
strict = true
|
|
16
|
+
check_untyped_defs = true
|
|
17
|
+
files = ['python_utils', '_python_utils_tests', 'setup.py']
|
|
18
|
+
|
|
19
|
+
[[tool.mypy.overrides]]
|
|
20
|
+
module = '_python_utils_tests.*'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains metadata about the `python-utils` package.
|
|
3
|
+
|
|
4
|
+
Attributes:
|
|
5
|
+
__package_name__ (str): The name of the package.
|
|
6
|
+
__author__ (str): The author of the package.
|
|
7
|
+
__author_email__ (str): The email of the author.
|
|
8
|
+
__description__ (str): A brief description of the package.
|
|
9
|
+
__url__ (str): The URL of the package's repository.
|
|
10
|
+
__version__ (str): The current version of the package.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__package_name__: str = 'python-utils'
|
|
14
|
+
__author__: str = 'Rick van Hattem'
|
|
15
|
+
__author_email__: str = 'Wolph@wol.ph'
|
|
16
|
+
__description__: str = (
|
|
17
|
+
'Python Utils is a module with some convenient utilities not included '
|
|
18
|
+
'with the standard Python install'
|
|
19
|
+
)
|
|
20
|
+
__url__: str = 'https://github.com/WoLpH/python-utils'
|
|
21
|
+
# Omit type info due to automatic versioning script
|
|
22
|
+
__version__ = '3.9.1'
|
|
@@ -1,6 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module initializes the `python_utils` package by importing various
|
|
3
|
+
submodules and functions.
|
|
4
|
+
|
|
5
|
+
Submodules:
|
|
6
|
+
aio
|
|
7
|
+
converters
|
|
8
|
+
decorators
|
|
9
|
+
formatters
|
|
10
|
+
generators
|
|
11
|
+
import_
|
|
12
|
+
logger
|
|
13
|
+
terminal
|
|
14
|
+
time
|
|
15
|
+
types
|
|
16
|
+
|
|
17
|
+
Functions:
|
|
18
|
+
acount
|
|
19
|
+
remap
|
|
20
|
+
scale_1024
|
|
21
|
+
to_float
|
|
22
|
+
to_int
|
|
23
|
+
to_str
|
|
24
|
+
to_unicode
|
|
25
|
+
listify
|
|
26
|
+
set_attributes
|
|
27
|
+
raise_exception
|
|
28
|
+
reraise
|
|
29
|
+
camel_to_underscore
|
|
30
|
+
timesince
|
|
31
|
+
abatcher
|
|
32
|
+
batcher
|
|
33
|
+
import_global
|
|
34
|
+
get_terminal_size
|
|
35
|
+
aio_generator_timeout_detector
|
|
36
|
+
aio_generator_timeout_detector_decorator
|
|
37
|
+
aio_timeout_generator
|
|
38
|
+
delta_to_seconds
|
|
39
|
+
delta_to_seconds_or_none
|
|
40
|
+
format_time
|
|
41
|
+
timedelta_to_seconds
|
|
42
|
+
timeout_generator
|
|
43
|
+
|
|
44
|
+
Classes:
|
|
45
|
+
CastedDict
|
|
46
|
+
LazyCastedDict
|
|
47
|
+
UniqueList
|
|
48
|
+
Logged
|
|
49
|
+
LoggerBase
|
|
50
|
+
"""
|
|
51
|
+
|
|
1
52
|
from . import (
|
|
2
53
|
aio,
|
|
3
|
-
compat,
|
|
4
54
|
converters,
|
|
5
55
|
decorators,
|
|
6
56
|
formatters,
|
|
@@ -33,45 +83,44 @@ from .time import (
|
|
|
33
83
|
)
|
|
34
84
|
|
|
35
85
|
__all__ = [
|
|
86
|
+
'CastedDict',
|
|
87
|
+
'LazyCastedDict',
|
|
88
|
+
'Logged',
|
|
89
|
+
'LoggerBase',
|
|
90
|
+
'UniqueList',
|
|
91
|
+
'abatcher',
|
|
92
|
+
'acount',
|
|
36
93
|
'aio',
|
|
37
|
-
'
|
|
38
|
-
'
|
|
94
|
+
'aio_generator_timeout_detector',
|
|
95
|
+
'aio_generator_timeout_detector_decorator',
|
|
96
|
+
'aio_timeout_generator',
|
|
97
|
+
'batcher',
|
|
98
|
+
'camel_to_underscore',
|
|
39
99
|
'converters',
|
|
40
100
|
'decorators',
|
|
101
|
+
'delta_to_seconds',
|
|
102
|
+
'delta_to_seconds_or_none',
|
|
103
|
+
'format_time',
|
|
41
104
|
'formatters',
|
|
105
|
+
'generators',
|
|
106
|
+
'get_terminal_size',
|
|
42
107
|
'import_',
|
|
108
|
+
'import_global',
|
|
109
|
+
'listify',
|
|
43
110
|
'logger',
|
|
44
|
-
'
|
|
45
|
-
'time',
|
|
46
|
-
'types',
|
|
47
|
-
'to_int',
|
|
48
|
-
'to_float',
|
|
49
|
-
'to_unicode',
|
|
50
|
-
'to_str',
|
|
51
|
-
'scale_1024',
|
|
111
|
+
'raise_exception',
|
|
52
112
|
'remap',
|
|
113
|
+
'reraise',
|
|
114
|
+
'scale_1024',
|
|
53
115
|
'set_attributes',
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'timesince',
|
|
57
|
-
'import_global',
|
|
58
|
-
'get_terminal_size',
|
|
116
|
+
'terminal',
|
|
117
|
+
'time',
|
|
59
118
|
'timedelta_to_seconds',
|
|
60
|
-
'format_time',
|
|
61
119
|
'timeout_generator',
|
|
62
|
-
'
|
|
63
|
-
'
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
'
|
|
68
|
-
'delta_to_seconds',
|
|
69
|
-
'delta_to_seconds_or_none',
|
|
70
|
-
'reraise',
|
|
71
|
-
'raise_exception',
|
|
72
|
-
'Logged',
|
|
73
|
-
'LoggerBase',
|
|
74
|
-
'CastedDict',
|
|
75
|
-
'LazyCastedDict',
|
|
76
|
-
'UniqueList',
|
|
120
|
+
'timesince',
|
|
121
|
+
'to_float',
|
|
122
|
+
'to_int',
|
|
123
|
+
'to_str',
|
|
124
|
+
'to_unicode',
|
|
125
|
+
'types',
|
|
77
126
|
]
|