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.
Files changed (44) hide show
  1. {python-utils-3.7.0/python_utils.egg-info → python-utils-3.8.1}/PKG-INFO +15 -2
  2. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_aio.py +6 -0
  3. python-utils-3.8.1/_python_utils_tests/test_containers.py +73 -0
  4. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_decorators.py +1 -1
  5. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_generators.py +1 -1
  6. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/__about__.py +1 -1
  7. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/aio.py +1 -1
  8. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/containers.py +40 -14
  9. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/decorators.py +4 -6
  10. {python-utils-3.7.0 → python-utils-3.8.1/python_utils.egg-info}/PKG-INFO +15 -2
  11. python-utils-3.7.0/_python_utils_tests/test_containers.py +0 -31
  12. {python-utils-3.7.0 → python-utils-3.8.1}/LICENSE +0 -0
  13. {python-utils-3.7.0 → python-utils-3.8.1}/MANIFEST.in +0 -0
  14. {python-utils-3.7.0 → python-utils-3.8.1}/README.rst +0 -0
  15. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/__init__.py +0 -0
  16. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/requirements.txt +0 -0
  17. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_import.py +0 -0
  18. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_logger.py +0 -0
  19. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_python_utils.py +0 -0
  20. {python-utils-3.7.0 → python-utils-3.8.1}/_python_utils_tests/test_time.py +0 -0
  21. {python-utils-3.7.0 → python-utils-3.8.1}/coverage.rc +0 -0
  22. {python-utils-3.7.0 → python-utils-3.8.1}/pyproject.toml +0 -0
  23. {python-utils-3.7.0 → python-utils-3.8.1}/pytest.ini +0 -0
  24. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/__init__.py +0 -0
  25. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/compat.py +0 -0
  26. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/converters.py +0 -0
  27. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/exceptions.py +0 -0
  28. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/formatters.py +0 -0
  29. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/generators.py +0 -0
  30. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/import_.py +0 -0
  31. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/logger.py +0 -0
  32. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/loguru.py +0 -0
  33. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/py.typed +0 -0
  34. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/terminal.py +0 -0
  35. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/time.py +0 -0
  36. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils/types.py +0 -0
  37. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/SOURCES.txt +0 -0
  38. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/dependency_links.txt +0 -0
  39. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/requires.txt +0 -0
  40. {python-utils-3.7.0 → python-utils-3.8.1}/python_utils.egg-info/top_level.txt +0 -0
  41. {python-utils-3.7.0 → python-utils-3.8.1}/requirements.txt +0 -0
  42. {python-utils-3.7.0 → python-utils-3.8.1}/setup.cfg +0 -0
  43. {python-utils-3.7.0 → python-utils-3.8.1}/setup.py +0 -0
  44. {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.7.0
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
- License-File: LICENSE
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 test_wraps_classmethod(): # type: ignore
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
@@ -7,4 +7,4 @@ __description__: str = (
7
7
  )
8
8
  __url__: str = 'https://github.com/WoLpH/python-utils'
9
9
  # Omit type info due to automatic versioning script
10
- __version__ = '3.7.0'
10
+ __version__ = '3.8.1'
@@ -48,7 +48,7 @@ async def acontainer(
48
48
 
49
49
  item: _T
50
50
  items: types.List[_T] = []
51
- async for item in iterable_:
51
+ async for item in iterable_: # pragma: no branch
52
52
  items.append(item)
53
53
 
54
54
  return container(items)
@@ -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: %s' % 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: %s' % 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
- @types.overload
261
+ @typing.overload
262
262
  def __setitem__(self, indices: types.SupportsIndex, values: HT) -> None:
263
263
  ...
264
264
 
265
- @types.overload
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
- class SlicableDeque(types.Generic[T], collections.deque): # type: ignore
314
- @types.overload
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
- @types.overload
319
- def __getitem__(self, index: slice) -> 'SlicableDeque[T]':
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, 'SlicableDeque[T]']:
326
+ ) -> types.Union[T, 'SliceableDeque[T]']:
325
327
  '''
326
328
  Return the item or slice at the given index.
327
329
 
328
- >>> d = SlicableDeque[int]([1, 2, 3, 4, 5])
330
+ >>> d = SliceableDeque[int]([1, 2, 3, 4, 5])
329
331
  >>> d[1:4]
330
- SlicableDeque([2, 3, 4])
332
+ SliceableDeque([2, 3, 4])
331
333
 
332
- >>> d = SlicableDeque[str](['a', 'b', 'c'])
334
+ >>> d = SliceableDeque[str](['a', 'b', 'c'])
333
335
  >>> d[-2:]
334
- SlicableDeque(['b', 'c'])
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
- try: # pragma: no cover
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.7.0
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
- License-File: LICENSE
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