python-utils 4.0.1__tar.gz → 4.1.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-4.0.1 → python_utils-4.1.1}/PKG-INFO +22 -9
- {python_utils-4.0.1 → python_utils-4.1.1}/README.md +20 -8
- python_utils-4.1.1/_python_utils_tests/clock.py +56 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_aio.py +37 -0
- python_utils-4.1.1/_python_utils_tests/test_containers.py +1211 -0
- python_utils-4.1.1/_python_utils_tests/test_converters.py +39 -0
- python_utils-4.1.1/_python_utils_tests/test_decorators.py +178 -0
- python_utils-4.1.1/_python_utils_tests/test_formatters.py +118 -0
- python_utils-4.1.1/_python_utils_tests/test_generators.py +312 -0
- python_utils-4.1.1/_python_utils_tests/test_import.py +205 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_lazy_imports.py +5 -1
- python_utils-4.1.1/_python_utils_tests/test_logged.py +224 -0
- python_utils-4.1.1/_python_utils_tests/test_logger.py +144 -0
- python_utils-4.1.1/_python_utils_tests/test_time.py +587 -0
- python_utils-4.1.1/conftest.py +8 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/pyproject.toml +3 -1
- {python_utils-4.0.1 → python_utils-4.1.1}/pyproject.toml.orig +3 -2
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/aio.py +5 -2
- python_utils-4.1.1/python_utils/containers.py +1133 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/converters.py +70 -15
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/decorators.py +29 -3
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/formatters.py +34 -12
- python_utils-4.1.1/python_utils/generators.py +174 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/import_.py +49 -8
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/logger.py +89 -30
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/loguru.py +34 -1
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/time.py +55 -26
- {python_utils-4.0.1 → python_utils-4.1.1}/tox.ini +12 -0
- python_utils-4.0.1/_python_utils_tests/test_containers.py +0 -80
- python_utils-4.0.1/_python_utils_tests/test_decorators.py +0 -82
- python_utils-4.0.1/_python_utils_tests/test_generators.py +0 -76
- python_utils-4.0.1/_python_utils_tests/test_import.py +0 -62
- python_utils-4.0.1/_python_utils_tests/test_logger.py +0 -24
- python_utils-4.0.1/_python_utils_tests/test_time.py +0 -208
- python_utils-4.0.1/python_utils/containers.py +0 -637
- python_utils-4.0.1/python_utils/generators.py +0 -126
- {python_utils-4.0.1 → python_utils-4.1.1}/LICENSE +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/__init__.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/requirements.txt +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_aliases.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_import_footprint.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_python_utils.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/_python_utils_tests/test_types.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/__about__.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/__init__.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/_aliases.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/exceptions.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/py.typed +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/terminal.py +0 -0
- {python_utils-4.0.1 → python_utils-4.1.1}/python_utils/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-utils
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Python Utils is a module with some convenient utilities not included with the standard Python install
|
|
5
5
|
Keywords: utils,utilities,helpers,typing,typed,async,lazy-imports
|
|
6
6
|
Author: Rick van Hattem
|
|
@@ -35,6 +35,7 @@ Project-URL: Homepage, https://github.com/WoLpH/python-utils
|
|
|
35
35
|
Project-URL: Documentation, https://python-utils.readthedocs.io/
|
|
36
36
|
Project-URL: Repository, https://github.com/WoLpH/python-utils
|
|
37
37
|
Project-URL: Changelog, https://github.com/WoLpH/python-utils/releases
|
|
38
|
+
Project-URL: Funding, https://github.com/sponsors/wolph
|
|
38
39
|
Provides-Extra: docs
|
|
39
40
|
Provides-Extra: loguru
|
|
40
41
|
Provides-Extra: tests
|
|
@@ -48,14 +49,14 @@ Description-Content-Type: text/markdown
|
|
|
48
49
|
|
|
49
50
|
**The fast, fully-typed stdlib helpers you keep rewriting — in one tiny, dependency-light package.**
|
|
50
51
|
|
|
51
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
52
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
53
|
-
[](https://github.com/WoLpH/python-utils/blob/develop/LICENSE)
|
|
58
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
53
|
+
[](https://pypi.python.org/pypi/python-utils)
|
|
54
|
+
[](https://github.com/wolph/python-utils/actions/workflows/ci.yml?query=branch%3Amaster)
|
|
55
|
+
[](https://coveralls.io/github/wolph/python-utils?branch=master)
|
|
56
|
+
[Typed: mypy, pyright and pyrefly](https://github.com/WoLpH/python-utils)
|
|
57
|
+
[](https://github.com/astral-sh/ruff)
|
|
58
|
+
[](https://github.com/WoLpH/python-utils/blob/develop/LICENSE)
|
|
59
|
+
[](https://pepy.tech/projects/python-utils)
|
|
59
60
|
|
|
60
61
|
[**Documentation**](https://python-utils.readthedocs.io/en/latest/) ·
|
|
61
62
|
[**PyPI**](https://pypi.python.org/pypi/python-utils) ·
|
|
@@ -382,6 +383,18 @@ Contributions are very welcome! We keep a strict **100% coverage** bar and run
|
|
|
382
383
|
[CONTRIBUTING.md](https://github.com/WoLpH/python-utils/blob/develop/CONTRIBUTING.md)
|
|
383
384
|
to get set up.
|
|
384
385
|
|
|
386
|
+
## Support
|
|
387
|
+
|
|
388
|
+
python-utils is maintained by [Rick van Hattem](https://github.com/wolph) in his own time. It is a small library that four other packages depend on, which means every change is a compatibility question.
|
|
389
|
+
|
|
390
|
+
If it saved you an afternoon, a tip covers an hour of issue triage:
|
|
391
|
+
[Ko-fi](https://ko-fi.com/wolph_gh) or [GitHub Sponsors](https://github.com/sponsors/wolph).
|
|
392
|
+
|
|
393
|
+
If your company funds its dependencies, this package is on
|
|
394
|
+
[thanks.dev](https://thanks.dev/u/gh/wolph).
|
|
395
|
+
|
|
396
|
+
[](https://ko-fi.com/wolph_gh)
|
|
397
|
+
|
|
385
398
|
## 📄 License
|
|
386
399
|
|
|
387
400
|
BSD-3-Clause — see [LICENSE](https://github.com/WoLpH/python-utils/blob/develop/LICENSE).
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
**The fast, fully-typed stdlib helpers you keep rewriting — in one tiny, dependency-light package.**
|
|
8
8
|
|
|
9
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
10
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
11
|
-
[](https://github.com/WoLpH/python-utils/blob/develop/LICENSE)
|
|
16
|
-
[](https://pypi.python.org/pypi/python-utils)
|
|
10
|
+
[](https://pypi.python.org/pypi/python-utils)
|
|
11
|
+
[](https://github.com/wolph/python-utils/actions/workflows/ci.yml?query=branch%3Amaster)
|
|
12
|
+
[](https://coveralls.io/github/wolph/python-utils?branch=master)
|
|
13
|
+
[Typed: mypy, pyright and pyrefly](https://github.com/WoLpH/python-utils)
|
|
14
|
+
[](https://github.com/astral-sh/ruff)
|
|
15
|
+
[](https://github.com/WoLpH/python-utils/blob/develop/LICENSE)
|
|
16
|
+
[](https://pepy.tech/projects/python-utils)
|
|
17
17
|
|
|
18
18
|
[**Documentation**](https://python-utils.readthedocs.io/en/latest/) ·
|
|
19
19
|
[**PyPI**](https://pypi.python.org/pypi/python-utils) ·
|
|
@@ -340,6 +340,18 @@ Contributions are very welcome! We keep a strict **100% coverage** bar and run
|
|
|
340
340
|
[CONTRIBUTING.md](https://github.com/WoLpH/python-utils/blob/develop/CONTRIBUTING.md)
|
|
341
341
|
to get set up.
|
|
342
342
|
|
|
343
|
+
## Support
|
|
344
|
+
|
|
345
|
+
python-utils is maintained by [Rick van Hattem](https://github.com/wolph) in his own time. It is a small library that four other packages depend on, which means every change is a compatibility question.
|
|
346
|
+
|
|
347
|
+
If it saved you an afternoon, a tip covers an hour of issue triage:
|
|
348
|
+
[Ko-fi](https://ko-fi.com/wolph_gh) or [GitHub Sponsors](https://github.com/sponsors/wolph).
|
|
349
|
+
|
|
350
|
+
If your company funds its dependencies, this package is on
|
|
351
|
+
[thanks.dev](https://thanks.dev/u/gh/wolph).
|
|
352
|
+
|
|
353
|
+
[](https://ko-fi.com/wolph_gh)
|
|
354
|
+
|
|
343
355
|
## 📄 License
|
|
344
356
|
|
|
345
357
|
BSD-3-Clause — see [LICENSE](https://github.com/WoLpH/python-utils/blob/develop/LICENSE).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""A fake clock for the tests and doctests of ``python_utils.time``."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import python_utils.time
|
|
8
|
+
|
|
9
|
+
#: The doctest that sleeps on the clock, by the name pytest gives it.
|
|
10
|
+
TIMEOUT_GENERATOR_DOCTEST: str = 'python_utils.time.timeout_generator'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FakeClock:
|
|
14
|
+
"""
|
|
15
|
+
A clock that only moves when something sleeps on it.
|
|
16
|
+
|
|
17
|
+
``time.sleep`` promises to sleep at least as long as requested. A busy
|
|
18
|
+
machine sleeps tens of milliseconds longer, and that changes how many
|
|
19
|
+
items ``timeout_generator`` yields before its timeout. On this clock the
|
|
20
|
+
number of items depends on the arguments alone.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
now (float): The current time in seconds.
|
|
24
|
+
sleeps (list[float]): Every requested sleep, in order.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
"""Start at zero without any recorded sleeps."""
|
|
29
|
+
self.now: float = 0.0
|
|
30
|
+
self.sleeps: list[float] = []
|
|
31
|
+
|
|
32
|
+
def perf_counter(self) -> float:
|
|
33
|
+
"""Return the current time, like ``time.perf_counter``."""
|
|
34
|
+
return self.now
|
|
35
|
+
|
|
36
|
+
def sleep(self, seconds: float) -> None:
|
|
37
|
+
"""Record the sleep and move the clock forward, without waiting."""
|
|
38
|
+
self.sleeps.append(seconds)
|
|
39
|
+
self.now += seconds
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def fake_clock(monkeypatch: pytest.MonkeyPatch) -> FakeClock:
|
|
44
|
+
"""Replace the ``time`` module inside ``python_utils.time``."""
|
|
45
|
+
clock: FakeClock = FakeClock()
|
|
46
|
+
monkeypatch.setattr(python_utils.time, 'time', clock)
|
|
47
|
+
return clock
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.fixture(autouse=True)
|
|
51
|
+
def fake_clock_in_doctest(request: pytest.FixtureRequest) -> None:
|
|
52
|
+
"""Run the ``timeout_generator`` doctest on the fake clock."""
|
|
53
|
+
# pytest leaves `FixtureRequest.node` without a type.
|
|
54
|
+
node: pytest.Item = typing.cast(pytest.Item, request.node)
|
|
55
|
+
if node.name == TIMEOUT_GENERATOR_DOCTEST:
|
|
56
|
+
request.getfixturevalue('fake_clock')
|
|
@@ -25,6 +25,43 @@ async def test_acount(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
25
25
|
assert sum(sleeps) == 4
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
@pytest.mark.asyncio
|
|
29
|
+
@pytest.mark.parametrize(
|
|
30
|
+
'start,step,stop,expected',
|
|
31
|
+
[
|
|
32
|
+
(10, -2, 0, [10, 8, 6, 4, 2]),
|
|
33
|
+
(0, -1, -3, [0, -1, -2]),
|
|
34
|
+
(1.5, -0.5, 0, [1.5, 1.0, 0.5]),
|
|
35
|
+
# Nothing to count when `start` is already past `stop`, in either
|
|
36
|
+
# direction. This is what `range` does as well.
|
|
37
|
+
(0, -1, 5, []),
|
|
38
|
+
(5, 1, 3, []),
|
|
39
|
+
(0, 2, 5, [0, 2, 4]),
|
|
40
|
+
],
|
|
41
|
+
)
|
|
42
|
+
async def test_acount_stop_follows_step_direction(
|
|
43
|
+
start: float, step: float, stop: float, expected: types.List[float]
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Count down to a lower ``stop`` when the step is negative."""
|
|
46
|
+
limit: int = len(expected) + 5
|
|
47
|
+
items: types.List[float] = []
|
|
48
|
+
item: float
|
|
49
|
+
# `acount` is annotated as a plain iterator, closing takes a generator.
|
|
50
|
+
counter: types.AsyncGenerator[float, None] = types.cast(
|
|
51
|
+
types.AsyncGenerator[float, None],
|
|
52
|
+
aio.acount(start=start, step=step, stop=stop),
|
|
53
|
+
)
|
|
54
|
+
async for item in counter:
|
|
55
|
+
items.append(item)
|
|
56
|
+
# A counter that misses its `stop` never ends by itself.
|
|
57
|
+
if len(items) == limit:
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
await counter.aclose()
|
|
61
|
+
|
|
62
|
+
assert items == expected
|
|
63
|
+
|
|
64
|
+
|
|
28
65
|
@pytest.mark.asyncio
|
|
29
66
|
async def test_acontainer() -> None:
|
|
30
67
|
"""Collect an async iterable into the requested container."""
|