python-utils 3.8.1__tar.gz → 3.9.0__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 (47) hide show
  1. {python-utils-3.8.1/python_utils.egg-info → python_utils-3.9.0}/PKG-INFO +7 -3
  2. python_utils-3.9.0/_python_utils_tests/test_aio.py +68 -0
  3. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_decorators.py +15 -14
  4. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_generators.py +5 -5
  5. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_import.py +11 -11
  6. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_logger.py +2 -2
  7. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_python_utils.py +1 -1
  8. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_time.py +32 -18
  9. {python-utils-3.8.1 → python_utils-3.9.0}/pyproject.toml +10 -3
  10. python_utils-3.9.0/python_utils/__about__.py +22 -0
  11. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/__init__.py +51 -2
  12. python_utils-3.9.0/python_utils/aio.py +117 -0
  13. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/containers.py +283 -33
  14. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/converters.py +143 -63
  15. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/decorators.py +47 -24
  16. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/exceptions.py +20 -2
  17. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/formatters.py +31 -10
  18. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/generators.py +38 -6
  19. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/import_.py +34 -14
  20. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/logger.py +134 -17
  21. python_utils-3.9.0/python_utils/loguru.py +51 -0
  22. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/terminal.py +46 -20
  23. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/time.py +99 -52
  24. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/types.py +25 -8
  25. {python-utils-3.8.1 → python_utils-3.9.0/python_utils.egg-info}/PKG-INFO +7 -3
  26. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils.egg-info/SOURCES.txt +0 -1
  27. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils.egg-info/requires.txt +5 -1
  28. {python-utils-3.8.1 → python_utils-3.9.0}/setup.py +23 -8
  29. {python-utils-3.8.1 → python_utils-3.9.0}/tox.ini +8 -7
  30. python-utils-3.8.1/_python_utils_tests/test_aio.py +0 -42
  31. python-utils-3.8.1/python_utils/__about__.py +0 -10
  32. python-utils-3.8.1/python_utils/aio.py +0 -54
  33. python-utils-3.8.1/python_utils/compat.py +0 -0
  34. python-utils-3.8.1/python_utils/loguru.py +0 -16
  35. {python-utils-3.8.1 → python_utils-3.9.0}/LICENSE +0 -0
  36. {python-utils-3.8.1 → python_utils-3.9.0}/MANIFEST.in +0 -0
  37. {python-utils-3.8.1 → python_utils-3.9.0}/README.rst +0 -0
  38. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/__init__.py +0 -0
  39. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/requirements.txt +0 -0
  40. {python-utils-3.8.1 → python_utils-3.9.0}/_python_utils_tests/test_containers.py +0 -0
  41. {python-utils-3.8.1 → python_utils-3.9.0}/coverage.rc +0 -0
  42. {python-utils-3.8.1 → python_utils-3.9.0}/pytest.ini +0 -0
  43. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils/py.typed +0 -0
  44. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils.egg-info/dependency_links.txt +0 -0
  45. {python-utils-3.8.1 → python_utils-3.9.0}/python_utils.egg-info/top_level.txt +0 -0
  46. {python-utils-3.8.1 → python_utils-3.9.0}/requirements.txt +0 -0
  47. {python-utils-3.8.1 → python_utils-3.9.0}/setup.cfg +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-utils
3
- Version: 3.8.1
3
+ Version: 3.9.0
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: >3.8.0
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: flake8; extra == "tests"
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): # type: ignore
44
- return arg # type: ignore
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(): # type: ignore
54
+ def test_wraps_classmethod() -> None:
52
55
  some_class = SomeClass()
53
- some_class.some_classmethod = MagicMock()
54
- wrapped_method = wraps_classmethod( # type: ignore
55
- SomeClass.some_classmethod # type: ignore
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) # type: ignore
61
+ some_class.some_classmethod.assert_called_with(123)
61
62
 
62
63
 
63
- def test_wraps_annotated_classmethod(): # type: ignore
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) # type: ignore
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
- pass
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
  )
@@ -1,12 +1,12 @@
1
+ # mypy: disable-error-code=misc
1
2
  import pytest
2
3
 
3
4
  from python_utils.loguru import Logurud
4
5
 
5
-
6
6
  loguru = pytest.importorskip('loguru')
7
7
 
8
8
 
9
- def test_logurud():
9
+ def test_logurud() -> None:
10
10
  class MyClass(Logurud):
11
11
  pass
12
12
 
@@ -1,7 +1,7 @@
1
1
  from python_utils import __about__
2
2
 
3
3
 
4
- def test_definitions():
4
+ def test_definitions() -> None:
5
5
  # The setup.py requires this so we better make sure they exist :)
6
6
  assert __about__.__version__
7
7
  assert __about__.__author__
@@ -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
@@ -46,15 +46,15 @@ async def test_aio_timeout_generator(
46
46
  'timeout,interval,interval_multiplier,maximum_interval,iterable,result',
47
47
  [
48
48
  (0.01, 0.006, 0.5, 0.01, 'abc', 'c'),
49
- (0.01, 0.006, 0.5, 0.01, itertools.count, 2), # type: ignore
50
- (0.01, 0.006, 0.5, 0.01, itertools.count(), 2),
49
+ (0.01, 0.007, 0.5, 0.01, itertools.count, 2),
50
+ (0.01, 0.007, 0.5, 0.01, itertools.count(), 2),
51
51
  (0.01, 0.006, 1.0, None, 'abc', 'c'),
52
52
  (
53
53
  timedelta(seconds=0.01),
54
54
  timedelta(seconds=0.006),
55
55
  2.0,
56
56
  timedelta(seconds=0.01),
57
- itertools.count, # type: ignore
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,17 +79,17 @@ def test_timeout_generator(
79
79
  iterable=iterable,
80
80
  maximum_interval=maximum_interval,
81
81
  ):
82
- pass
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
94
  await asyncio.sleep(i / 100.0)
95
95
  yield i
@@ -123,26 +123,29 @@ async def test_aio_generator_timeout_detector():
123
123
 
124
124
 
125
125
  @pytest.mark.asyncio
126
- async def test_aio_generator_timeout_detector_decorator():
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 i in generator_timeout():
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 i in generator_reraise():
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
@@ -5,8 +5,15 @@ skip-string-normalization = true
5
5
 
6
6
  [tool.pyright]
7
7
  # include = ['python_utils']
8
- include = ['python_utils', '_python_utils_tests']
9
- strict = ['python_utils', '_python_utils_tests']
8
+ include = ['python_utils', '_python_utils_tests', 'setup.py']
9
+ strict = ['python_utils', '_python_utils_tests', 'setup.py']
10
10
  # The terminal file is very OS specific and dependent on imports so we're skipping it from type checking
11
11
  ignore = ['python_utils/terminal.py']
12
- pythonVersion = '3.8'
12
+ pythonVersion = '3.9'
13
+
14
+ [tool.mypy]
15
+ strict = true
16
+ check_untyped_defs = true
17
+
18
+ [[tool.mypy.overrides]]
19
+ 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.0'
@@ -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,
@@ -35,7 +85,6 @@ from .time import (
35
85
  __all__ = [
36
86
  'aio',
37
87
  'generators',
38
- 'compat',
39
88
  'converters',
40
89
  'decorators',
41
90
  'formatters',
@@ -0,0 +1,117 @@
1
+ """Asyncio equivalents to regular Python functions."""
2
+
3
+ import asyncio
4
+ import itertools
5
+ import typing
6
+
7
+ from . import types
8
+
9
+ _N = types.TypeVar('_N', int, float)
10
+ _T = types.TypeVar('_T')
11
+ _K = types.TypeVar('_K')
12
+ _V = types.TypeVar('_V')
13
+
14
+
15
+ async def acount(
16
+ start: _N = 0,
17
+ step: _N = 1,
18
+ delay: float = 0,
19
+ stop: types.Optional[_N] = None,
20
+ ) -> types.AsyncIterator[_N]:
21
+ """Asyncio version of itertools.count()."""
22
+ for item in itertools.count(start, step): # pragma: no branch
23
+ if stop is not None and item >= stop:
24
+ break
25
+
26
+ yield item
27
+ await asyncio.sleep(delay)
28
+
29
+
30
+ @typing.overload
31
+ async def acontainer(
32
+ iterable: types.Union[
33
+ types.AsyncIterable[_T],
34
+ types.Callable[..., types.AsyncIterable[_T]],
35
+ ],
36
+ container: types.Type[types.Tuple[_T, ...]],
37
+ ) -> types.Tuple[_T, ...]: ...
38
+
39
+
40
+ @typing.overload
41
+ async def acontainer(
42
+ iterable: types.Union[
43
+ types.AsyncIterable[_T],
44
+ types.Callable[..., types.AsyncIterable[_T]],
45
+ ],
46
+ container: types.Type[types.List[_T]] = list,
47
+ ) -> types.List[_T]: ...
48
+
49
+
50
+ @typing.overload
51
+ async def acontainer(
52
+ iterable: types.Union[
53
+ types.AsyncIterable[_T],
54
+ types.Callable[..., types.AsyncIterable[_T]],
55
+ ],
56
+ container: types.Type[types.Set[_T]],
57
+ ) -> types.Set[_T]: ...
58
+
59
+
60
+ async def acontainer(
61
+ iterable: types.Union[
62
+ types.AsyncIterable[_T],
63
+ types.Callable[..., types.AsyncIterable[_T]],
64
+ ],
65
+ container: types.Callable[
66
+ [types.Iterable[_T]], types.Collection[_T]
67
+ ] = list,
68
+ ) -> types.Collection[_T]:
69
+ """
70
+ Asyncio version of list()/set()/tuple()/etc() using an async for loop.
71
+
72
+ So instead of doing `[item async for item in iterable]` you can do
73
+ `await acontainer(iterable)`.
74
+
75
+ """
76
+ iterable_: types.AsyncIterable[_T]
77
+ if callable(iterable):
78
+ iterable_ = iterable()
79
+ else:
80
+ iterable_ = iterable
81
+
82
+ item: _T
83
+ items: types.List[_T] = []
84
+ async for item in iterable_: # pragma: no branch
85
+ items.append(item)
86
+
87
+ return container(items)
88
+
89
+
90
+ async def adict(
91
+ iterable: types.Union[
92
+ types.AsyncIterable[types.Tuple[_K, _V]],
93
+ types.Callable[..., types.AsyncIterable[types.Tuple[_K, _V]]],
94
+ ],
95
+ container: types.Callable[
96
+ [types.Iterable[types.Tuple[_K, _V]]], types.Mapping[_K, _V]
97
+ ] = dict,
98
+ ) -> types.Mapping[_K, _V]:
99
+ """
100
+ Asyncio version of dict() using an async for loop.
101
+
102
+ So instead of doing `{key: value async for key, value in iterable}` you
103
+ can do `await adict(iterable)`.
104
+
105
+ """
106
+ iterable_: types.AsyncIterable[types.Tuple[_K, _V]]
107
+ if callable(iterable):
108
+ iterable_ = iterable()
109
+ else:
110
+ iterable_ = iterable
111
+
112
+ item: types.Tuple[_K, _V]
113
+ items: types.List[types.Tuple[_K, _V]] = []
114
+ async for item in iterable_: # pragma: no branch
115
+ items.append(item)
116
+
117
+ return container(items)