python-utils 3.7.0__tar.gz → 3.8.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.7.0/python_utils.egg-info → python-utils-3.8.1}/PKG-INFO +15 -2
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_aio.py +6 -0
- python-utils-3.8.1/_python_utils_tests/test_containers.py +73 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_decorators.py +1 -1
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_generators.py +1 -1
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/__about__.py +1 -1
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/aio.py +1 -1
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/containers.py +40 -14
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/decorators.py +4 -6
- {python-utils-3.7.0 → python-utils-3.8.1/python_utils.egg-info}/PKG-INFO +15 -2
- python-utils-3.7.0/_python_utils_tests/test_containers.py +0 -31
- {python-utils-3.7.0 → python-utils-3.8.1}/LICENSE +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/MANIFEST.in +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/README.rst +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/__init__.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/requirements.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_import.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_logger.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_python_utils.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_time.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/coverage.rc +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/pyproject.toml +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/pytest.ini +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/__init__.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/compat.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/converters.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/exceptions.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/formatters.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/generators.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/import_.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/logger.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/loguru.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/py.typed +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/terminal.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/time.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/types.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/SOURCES.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/dependency_links.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/requires.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/top_level.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/requirements.txt +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/setup.cfg +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/setup.py +0 -0
- {python-utils-3.7.0 → python-utils-3.8.1}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-utils
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.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
|
|
@@ -8,10 +8,23 @@ Author-email: Wolph@wol.ph
|
|
|
8
8
|
License: BSD
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
10
|
Requires-Python: >3.8.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typing_extensions>3.10.0.2
|
|
11
13
|
Provides-Extra: loguru
|
|
14
|
+
Requires-Dist: loguru; extra == "loguru"
|
|
12
15
|
Provides-Extra: docs
|
|
16
|
+
Requires-Dist: mock; extra == "docs"
|
|
17
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
18
|
+
Requires-Dist: python-utils; extra == "docs"
|
|
13
19
|
Provides-Extra: tests
|
|
14
|
-
|
|
20
|
+
Requires-Dist: flake8; extra == "tests"
|
|
21
|
+
Requires-Dist: pytest; extra == "tests"
|
|
22
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
23
|
+
Requires-Dist: pytest-mypy; extra == "tests"
|
|
24
|
+
Requires-Dist: pytest-asyncio; extra == "tests"
|
|
25
|
+
Requires-Dist: sphinx; extra == "tests"
|
|
26
|
+
Requires-Dist: types-setuptools; extra == "tests"
|
|
27
|
+
Requires-Dist: loguru; extra == "tests"
|
|
15
28
|
|
|
16
29
|
Useful Python Utils
|
|
17
30
|
==============================================================================
|
|
@@ -30,7 +30,13 @@ async def test_acontainer():
|
|
|
30
30
|
yield 2
|
|
31
31
|
yield 3
|
|
32
32
|
|
|
33
|
+
async def empty_gen():
|
|
34
|
+
if False:
|
|
35
|
+
yield 1
|
|
36
|
+
|
|
33
37
|
assert await acontainer(async_gen) == [1, 2, 3]
|
|
34
38
|
assert await acontainer(async_gen()) == [1, 2, 3]
|
|
35
39
|
assert await acontainer(async_gen, set) == {1, 2, 3}
|
|
36
40
|
assert await acontainer(async_gen(), set) == {1, 2, 3}
|
|
41
|
+
assert await acontainer(empty_gen) == []
|
|
42
|
+
assert await acontainer(empty_gen()) == []
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from python_utils import containers
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_unique_list_ignore() -> None:
|
|
7
|
+
a: containers.UniqueList[int] = containers.UniqueList()
|
|
8
|
+
a.append(1)
|
|
9
|
+
a.append(1)
|
|
10
|
+
assert a == [1]
|
|
11
|
+
|
|
12
|
+
a = containers.UniqueList(*range(20))
|
|
13
|
+
with pytest.raises(RuntimeError):
|
|
14
|
+
a[10:20:2] = [1, 2, 3, 4, 5]
|
|
15
|
+
|
|
16
|
+
a[3] = 5
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_unique_list_raise() -> None:
|
|
20
|
+
a: containers.UniqueList[int] = containers.UniqueList(
|
|
21
|
+
*range(20), on_duplicate='raise'
|
|
22
|
+
)
|
|
23
|
+
with pytest.raises(ValueError):
|
|
24
|
+
a[10:20:2] = [1, 2, 3, 4, 5]
|
|
25
|
+
|
|
26
|
+
a[10:20:2] = [21, 22, 23, 24, 25]
|
|
27
|
+
with pytest.raises(ValueError):
|
|
28
|
+
a[3] = 5
|
|
29
|
+
|
|
30
|
+
del a[10]
|
|
31
|
+
del a[5:15]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_sliceable_deque() -> None:
|
|
35
|
+
d: containers.SliceableDeque[int] = containers.SliceableDeque(range(10))
|
|
36
|
+
assert d[0] == 0
|
|
37
|
+
assert d[-1] == 9
|
|
38
|
+
assert d[1:3] == [1, 2]
|
|
39
|
+
assert d[1:3:2] == [1]
|
|
40
|
+
assert d[1:3:-1] == []
|
|
41
|
+
assert d[3:1] == []
|
|
42
|
+
assert d[3:1:-1] == [3, 2]
|
|
43
|
+
assert d[3:1:-2] == [3]
|
|
44
|
+
with pytest.raises(ValueError):
|
|
45
|
+
assert d[1:3:0]
|
|
46
|
+
assert d[1:3:1] == [1, 2]
|
|
47
|
+
assert d[1:3:2] == [1]
|
|
48
|
+
assert d[1:3:-1] == []
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_sliceable_deque_pop() -> None:
|
|
52
|
+
d: containers.SliceableDeque[int] = containers.SliceableDeque(range(10))
|
|
53
|
+
|
|
54
|
+
assert d.pop() == 9 == 9
|
|
55
|
+
assert d.pop(0) == 0
|
|
56
|
+
|
|
57
|
+
with pytest.raises(IndexError):
|
|
58
|
+
assert d.pop(100)
|
|
59
|
+
|
|
60
|
+
with pytest.raises(IndexError):
|
|
61
|
+
assert d.pop(2)
|
|
62
|
+
|
|
63
|
+
with pytest.raises(IndexError):
|
|
64
|
+
assert d.pop(-2)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_sliceable_deque_eq() -> None:
|
|
68
|
+
d: containers.SliceableDeque[int] = containers.SliceableDeque([1, 2, 3])
|
|
69
|
+
assert d == [1, 2, 3]
|
|
70
|
+
assert d == (1, 2, 3)
|
|
71
|
+
assert d == {1, 2, 3}
|
|
72
|
+
assert d == d
|
|
73
|
+
assert d == containers.SliceableDeque([1, 2, 3])
|
|
@@ -60,7 +60,7 @@ def test_wraps_classmethod(): # type: ignore
|
|
|
60
60
|
some_class.some_classmethod.assert_called_with(123) # type: ignore
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def test_wraps_annotated_classmethod(): # type: ignore
|
|
64
64
|
some_class = SomeClass()
|
|
65
65
|
some_class.some_annotated_classmethod = MagicMock()
|
|
66
66
|
wrapped_method = wraps_classmethod(SomeClass.some_annotated_classmethod)(
|
|
@@ -16,7 +16,7 @@ async def test_abatcher():
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@pytest.mark.asyncio
|
|
19
|
-
async def test_abatcher_timed():
|
|
19
|
+
async def test_abatcher_timed() -> None:
|
|
20
20
|
batches: types.List[types.List[int]] = []
|
|
21
21
|
async for batch in python_utils.abatcher(
|
|
22
22
|
python_utils.acount(stop=10, delay=0.08), interval=0.1
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pyright: reportIncompatibleMethodOverride=false
|
|
2
2
|
import abc
|
|
3
|
-
import typing
|
|
4
3
|
import collections
|
|
4
|
+
import typing
|
|
5
5
|
|
|
6
6
|
from . import types
|
|
7
7
|
|
|
@@ -238,7 +238,7 @@ class UniqueList(types.List[HT]):
|
|
|
238
238
|
def insert(self, index: types.SupportsIndex, value: HT) -> None:
|
|
239
239
|
if value in self._set:
|
|
240
240
|
if self.on_duplicate == 'raise':
|
|
241
|
-
raise ValueError('Duplicate value:
|
|
241
|
+
raise ValueError(f'Duplicate value: {value}')
|
|
242
242
|
else:
|
|
243
243
|
return
|
|
244
244
|
|
|
@@ -248,7 +248,7 @@ class UniqueList(types.List[HT]):
|
|
|
248
248
|
def append(self, value: HT) -> None:
|
|
249
249
|
if value in self._set:
|
|
250
250
|
if self.on_duplicate == 'raise':
|
|
251
|
-
raise ValueError('Duplicate value:
|
|
251
|
+
raise ValueError(f'Duplicate value: {value}')
|
|
252
252
|
else:
|
|
253
253
|
return
|
|
254
254
|
|
|
@@ -258,11 +258,11 @@ class UniqueList(types.List[HT]):
|
|
|
258
258
|
def __contains__(self, item: HT) -> bool: # type: ignore
|
|
259
259
|
return item in self._set
|
|
260
260
|
|
|
261
|
-
@
|
|
261
|
+
@typing.overload
|
|
262
262
|
def __setitem__(self, indices: types.SupportsIndex, values: HT) -> None:
|
|
263
263
|
...
|
|
264
264
|
|
|
265
|
-
@
|
|
265
|
+
@typing.overload
|
|
266
266
|
def __setitem__(self, indices: slice, values: types.Iterable[HT]) -> None:
|
|
267
267
|
...
|
|
268
268
|
|
|
@@ -310,28 +310,30 @@ class UniqueList(types.List[HT]):
|
|
|
310
310
|
super().__delitem__(index)
|
|
311
311
|
|
|
312
312
|
|
|
313
|
-
|
|
314
|
-
|
|
313
|
+
# Type hinting `collections.deque` does not work consistently between Python
|
|
314
|
+
# runtime, mypy and pyright currently so we have to ignore the errors
|
|
315
|
+
class SliceableDeque(types.Generic[T], collections.deque): # type: ignore
|
|
316
|
+
@typing.overload
|
|
315
317
|
def __getitem__(self, index: types.SupportsIndex) -> T:
|
|
316
318
|
...
|
|
317
319
|
|
|
318
|
-
@
|
|
319
|
-
def __getitem__(self, index: slice) -> '
|
|
320
|
+
@typing.overload
|
|
321
|
+
def __getitem__(self, index: slice) -> 'SliceableDeque[T]':
|
|
320
322
|
...
|
|
321
323
|
|
|
322
324
|
def __getitem__(
|
|
323
325
|
self, index: types.Union[types.SupportsIndex, slice]
|
|
324
|
-
) -> types.Union[T, '
|
|
326
|
+
) -> types.Union[T, 'SliceableDeque[T]']:
|
|
325
327
|
'''
|
|
326
328
|
Return the item or slice at the given index.
|
|
327
329
|
|
|
328
|
-
>>> d =
|
|
330
|
+
>>> d = SliceableDeque[int]([1, 2, 3, 4, 5])
|
|
329
331
|
>>> d[1:4]
|
|
330
|
-
|
|
332
|
+
SliceableDeque([2, 3, 4])
|
|
331
333
|
|
|
332
|
-
>>> d =
|
|
334
|
+
>>> d = SliceableDeque[str](['a', 'b', 'c'])
|
|
333
335
|
>>> d[-2:]
|
|
334
|
-
|
|
336
|
+
SliceableDeque(['b', 'c'])
|
|
335
337
|
|
|
336
338
|
'''
|
|
337
339
|
if isinstance(index, slice):
|
|
@@ -340,6 +342,30 @@ class SlicableDeque(types.Generic[T], collections.deque): # type: ignore
|
|
|
340
342
|
else:
|
|
341
343
|
return types.cast(T, super().__getitem__(index))
|
|
342
344
|
|
|
345
|
+
def __eq__(self, other: types.Any) -> bool:
|
|
346
|
+
# Allow for comparison with a list or tuple
|
|
347
|
+
if isinstance(other, list):
|
|
348
|
+
return list(self) == other
|
|
349
|
+
elif isinstance(other, tuple):
|
|
350
|
+
return tuple(self) == other
|
|
351
|
+
elif isinstance(other, set):
|
|
352
|
+
return set(self) == other
|
|
353
|
+
else:
|
|
354
|
+
return super().__eq__(other)
|
|
355
|
+
|
|
356
|
+
def pop(self, index: int = -1) -> T:
|
|
357
|
+
# We need to allow for an index but a deque only allows the removal of
|
|
358
|
+
# the first or last item.
|
|
359
|
+
if index == 0:
|
|
360
|
+
return typing.cast(T, super().popleft())
|
|
361
|
+
elif index in {-1, len(self) - 1}:
|
|
362
|
+
return typing.cast(T, super().pop())
|
|
363
|
+
else:
|
|
364
|
+
raise IndexError(
|
|
365
|
+
'Only index 0 and the last index (`N-1` or `-1`) '
|
|
366
|
+
'are supported'
|
|
367
|
+
)
|
|
368
|
+
|
|
343
369
|
|
|
344
370
|
if __name__ == '__main__':
|
|
345
371
|
import doctest
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import functools
|
|
2
3
|
import logging
|
|
3
4
|
import random
|
|
@@ -175,7 +176,9 @@ def wraps_classmethod(
|
|
|
175
176
|
def _wraps_classmethod(
|
|
176
177
|
wrapper: types.Callable[types.Concatenate[types.Any, _P], _T],
|
|
177
178
|
) -> types.Callable[types.Concatenate[types.Type[_S], _P], _T]:
|
|
178
|
-
|
|
179
|
+
# For some reason `functools.update_wrapper` fails on some test
|
|
180
|
+
# runs but not while running actual code
|
|
181
|
+
with contextlib.suppress(AttributeError):
|
|
179
182
|
wrapper = functools.update_wrapper(
|
|
180
183
|
wrapper,
|
|
181
184
|
wrapped,
|
|
@@ -185,11 +188,6 @@ def wraps_classmethod(
|
|
|
185
188
|
if a != '__annotations__'
|
|
186
189
|
),
|
|
187
190
|
)
|
|
188
|
-
except AttributeError:
|
|
189
|
-
# For some reason `functools.update_wrapper` fails on some test
|
|
190
|
-
# runs but not while running actual code
|
|
191
|
-
pass
|
|
192
|
-
|
|
193
191
|
if annotations := getattr(wrapped, '__annotations__', {}):
|
|
194
192
|
annotations.pop('self', None)
|
|
195
193
|
wrapper.__annotations__ = annotations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-utils
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.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
|
|
@@ -8,10 +8,23 @@ Author-email: Wolph@wol.ph
|
|
|
8
8
|
License: BSD
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
10
|
Requires-Python: >3.8.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: typing_extensions>3.10.0.2
|
|
11
13
|
Provides-Extra: loguru
|
|
14
|
+
Requires-Dist: loguru; extra == "loguru"
|
|
12
15
|
Provides-Extra: docs
|
|
16
|
+
Requires-Dist: mock; extra == "docs"
|
|
17
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
18
|
+
Requires-Dist: python-utils; extra == "docs"
|
|
13
19
|
Provides-Extra: tests
|
|
14
|
-
|
|
20
|
+
Requires-Dist: flake8; extra == "tests"
|
|
21
|
+
Requires-Dist: pytest; extra == "tests"
|
|
22
|
+
Requires-Dist: pytest-cov; extra == "tests"
|
|
23
|
+
Requires-Dist: pytest-mypy; extra == "tests"
|
|
24
|
+
Requires-Dist: pytest-asyncio; extra == "tests"
|
|
25
|
+
Requires-Dist: sphinx; extra == "tests"
|
|
26
|
+
Requires-Dist: types-setuptools; extra == "tests"
|
|
27
|
+
Requires-Dist: loguru; extra == "tests"
|
|
15
28
|
|
|
16
29
|
Useful Python Utils
|
|
17
30
|
==============================================================================
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
|
|
3
|
-
from python_utils import containers
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_unique_list_ignore() -> None:
|
|
7
|
-
a: containers.UniqueList[int] = containers.UniqueList()
|
|
8
|
-
a.append(1)
|
|
9
|
-
a.append(1)
|
|
10
|
-
assert a == [1]
|
|
11
|
-
|
|
12
|
-
a = containers.UniqueList(*range(20))
|
|
13
|
-
with pytest.raises(RuntimeError):
|
|
14
|
-
a[10:20:2] = [1, 2, 3, 4, 5]
|
|
15
|
-
|
|
16
|
-
a[3] = 5
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def test_unique_list_raise() -> None:
|
|
20
|
-
a: containers.UniqueList[int] = containers.UniqueList(
|
|
21
|
-
*range(20), on_duplicate='raise'
|
|
22
|
-
)
|
|
23
|
-
with pytest.raises(ValueError):
|
|
24
|
-
a[10:20:2] = [1, 2, 3, 4, 5]
|
|
25
|
-
|
|
26
|
-
a[10:20:2] = [21, 22, 23, 24, 25]
|
|
27
|
-
with pytest.raises(ValueError):
|
|
28
|
-
a[3] = 5
|
|
29
|
-
|
|
30
|
-
del a[10]
|
|
31
|
-
del a[5:15]
|
|
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
|