cli2 5.0.0rc9__tar.gz → 5.0.0rc12__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.
- {cli2-5.0.0rc9/cli2.egg-info → cli2-5.0.0rc12}/PKG-INFO +1 -4
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/__init__.py +1 -1
- cli2-5.0.0rc12/cli2/asyncio.py +130 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/display.py +1 -6
- {cli2-5.0.0rc9 → cli2-5.0.0rc12/cli2.egg-info}/PKG-INFO +1 -4
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2.egg-info/requires.txt +0 -4
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/setup.py +1 -5
- cli2-5.0.0rc12/tests/test_asyncio.py +68 -0
- cli2-5.0.0rc9/cli2/asyncio.py +0 -42
- cli2-5.0.0rc9/tests/test_asyncio.py +0 -18
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/MANIFEST.in +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/README.rst +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/classifiers.txt +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/cli.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/cli2.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/colors.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/configuration.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/decorators.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/__init__.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/conf.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/example.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/example_obj.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/nesting.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/obj.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/obj2.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/examples/test.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/lock.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/log.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/mask.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/node.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/sphinx.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/table.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2/test.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2.egg-info/SOURCES.txt +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2.egg-info/dependency_links.txt +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2.egg-info/entry_points.txt +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/cli2.egg-info/top_level.txt +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/setup.cfg +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_ansible.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_ansible_variables.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_cli.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_client.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_command.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_configuration.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_decorators.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_display.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_entry_point.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_group.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_inject.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_lock.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_mask.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_node.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_restful.py +0 -0
- {cli2-5.0.0rc9 → cli2-5.0.0rc12}/tests/test_table.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: cli2
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.0rc12
|
|
4
4
|
Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
|
|
5
5
|
Home-page: https://yourlabs.io/oss/cli2
|
|
6
6
|
Author: James Pic
|
|
@@ -13,9 +13,6 @@ Requires-Dist: docstring_parser
|
|
|
13
13
|
Requires-Dist: pyyaml
|
|
14
14
|
Requires-Dist: pygments
|
|
15
15
|
Requires-Dist: structlog
|
|
16
|
-
Provides-Extra: client
|
|
17
|
-
Requires-Dist: httpx; extra == "client"
|
|
18
|
-
Requires-Dist: truststore; extra == "client"
|
|
19
16
|
Provides-Extra: test
|
|
20
17
|
Requires-Dist: freezegun; extra == "test"
|
|
21
18
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import inspect
|
|
3
|
+
from . import display
|
|
4
|
+
from .log import log
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def async_iter(obj):
|
|
8
|
+
""" Check if an object is an async iterable. """
|
|
9
|
+
return inspect.isasyncgen(obj) or hasattr(obj, '__aiter__')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def async_resolve(result, output=False):
|
|
13
|
+
"""
|
|
14
|
+
Recursively resolve awaitables and async iterables.
|
|
15
|
+
|
|
16
|
+
:param result: The awaitable or async iterable to resolve
|
|
17
|
+
:param output: If True, print results as they are resolved. If False,
|
|
18
|
+
collect results.
|
|
19
|
+
|
|
20
|
+
:return: The resolved value(s). If output is True, returns None. If output
|
|
21
|
+
is False, returns a list of resolved values from async iterables.
|
|
22
|
+
"""
|
|
23
|
+
while inspect.iscoroutine(result):
|
|
24
|
+
result = await result
|
|
25
|
+
if async_iter(result):
|
|
26
|
+
results = []
|
|
27
|
+
async for _ in result:
|
|
28
|
+
if output:
|
|
29
|
+
if (
|
|
30
|
+
not inspect.iscoroutine(_)
|
|
31
|
+
and not inspect.isasyncgen(_)
|
|
32
|
+
):
|
|
33
|
+
display.print(_)
|
|
34
|
+
else:
|
|
35
|
+
await async_resolve(_, output=output)
|
|
36
|
+
else:
|
|
37
|
+
results.append(await async_resolve(_))
|
|
38
|
+
return None if output else results
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def async_run(coroutine):
|
|
43
|
+
"""Run an async coroutine in the current event loop or create a new one.
|
|
44
|
+
|
|
45
|
+
If an event loop is already running, creates a task in that loop.
|
|
46
|
+
If no event loop is running, creates a new one and runs the coroutine.
|
|
47
|
+
|
|
48
|
+
:param coroutine: The coroutine to run (return value of an async function)
|
|
49
|
+
:return: The result of the coroutine execution
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
loop = asyncio.get_running_loop()
|
|
53
|
+
except RuntimeError:
|
|
54
|
+
return asyncio.run(coroutine)
|
|
55
|
+
else:
|
|
56
|
+
return loop.create_task(coroutine)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Queue(asyncio.Queue):
|
|
60
|
+
"""
|
|
61
|
+
An async queue with worker pool for concurrent task processing.
|
|
62
|
+
|
|
63
|
+
Extends asyncio.Queue to manage a pool of worker tasks that process items
|
|
64
|
+
from the queue concurrently.
|
|
65
|
+
|
|
66
|
+
.. code-block:: python
|
|
67
|
+
|
|
68
|
+
# will run 2 at the time
|
|
69
|
+
queue = cli2.Queue(num_workers=2)
|
|
70
|
+
# call like asyncio.run
|
|
71
|
+
await queue.run(foo(), bar(), other())
|
|
72
|
+
|
|
73
|
+
.. py:attribute:: num_workers
|
|
74
|
+
|
|
75
|
+
Number of concurrent workers (default: 12)
|
|
76
|
+
|
|
77
|
+
.. py:attribute:: results
|
|
78
|
+
|
|
79
|
+
List of results from completed tasks, order of results not garanteed
|
|
80
|
+
due to concurrency.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, *args, num_workers=12, **kwargs):
|
|
84
|
+
"""Initialize the queue with worker pool.
|
|
85
|
+
|
|
86
|
+
:param num_workers: Number of concurrent workers (default: 12)
|
|
87
|
+
:paarm *args: Positional arguments for asyncio.Queue
|
|
88
|
+
:param **kwargs: Keyword arguments for asyncio.Queue
|
|
89
|
+
"""
|
|
90
|
+
self.num_workers = num_workers or 12
|
|
91
|
+
self.results = []
|
|
92
|
+
super().__init__(*args, **kwargs)
|
|
93
|
+
|
|
94
|
+
async def run(self, *tasks):
|
|
95
|
+
"""
|
|
96
|
+
Run tasks through the worker pool.
|
|
97
|
+
|
|
98
|
+
:param tasks: Coroutines
|
|
99
|
+
"""
|
|
100
|
+
self.results = []
|
|
101
|
+
|
|
102
|
+
for task in tasks:
|
|
103
|
+
await self.put(task)
|
|
104
|
+
|
|
105
|
+
workers = [
|
|
106
|
+
asyncio.create_task(self.worker())
|
|
107
|
+
for i in range(self.num_workers)
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
await self.join()
|
|
111
|
+
for worker in workers:
|
|
112
|
+
worker.cancel()
|
|
113
|
+
|
|
114
|
+
async def worker(self):
|
|
115
|
+
"""Worker task that processes items from the queue.
|
|
116
|
+
|
|
117
|
+
Continuously gets tasks from the queue, executes them, and stores
|
|
118
|
+
results.
|
|
119
|
+
Handles exceptions by logging them.
|
|
120
|
+
"""
|
|
121
|
+
while True:
|
|
122
|
+
task = await self.get()
|
|
123
|
+
try:
|
|
124
|
+
result = await task
|
|
125
|
+
except: # noqa
|
|
126
|
+
log.exception()
|
|
127
|
+
else:
|
|
128
|
+
self.results.append(result)
|
|
129
|
+
finally:
|
|
130
|
+
self.task_done()
|
|
@@ -7,16 +7,11 @@ Generic pretty display utils.
|
|
|
7
7
|
force it with :envvar:`FORCE_COLOR`, ie. gitlab-ci etc
|
|
8
8
|
"""
|
|
9
9
|
import difflib
|
|
10
|
+
import json
|
|
10
11
|
import os
|
|
11
12
|
import sys
|
|
12
13
|
import yaml
|
|
13
14
|
|
|
14
|
-
try:
|
|
15
|
-
import jsonlight as json
|
|
16
|
-
except ImportError:
|
|
17
|
-
import json
|
|
18
|
-
|
|
19
|
-
|
|
20
15
|
_print = print
|
|
21
16
|
|
|
22
17
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: cli2
|
|
3
|
-
Version: 5.0.
|
|
3
|
+
Version: 5.0.0rc12
|
|
4
4
|
Summary: image:: https://yourlabs.io/oss/cli2/badges/master/pipeline.svg
|
|
5
5
|
Home-page: https://yourlabs.io/oss/cli2
|
|
6
6
|
Author: James Pic
|
|
@@ -13,9 +13,6 @@ Requires-Dist: docstring_parser
|
|
|
13
13
|
Requires-Dist: pyyaml
|
|
14
14
|
Requires-Dist: pygments
|
|
15
15
|
Requires-Dist: structlog
|
|
16
|
-
Provides-Extra: client
|
|
17
|
-
Requires-Dist: httpx; extra == "client"
|
|
18
|
-
Requires-Dist: truststore; extra == "client"
|
|
19
16
|
Provides-Extra: test
|
|
20
17
|
Requires-Dist: freezegun; extra == "test"
|
|
21
18
|
Requires-Dist: pytest; extra == "test"
|
|
@@ -44,7 +44,7 @@ from setuptools import setup
|
|
|
44
44
|
|
|
45
45
|
setup(
|
|
46
46
|
name='cli2',
|
|
47
|
-
version='5.0.
|
|
47
|
+
version='5.0.0rc12',
|
|
48
48
|
setup_requires='setupmeta',
|
|
49
49
|
packages=['cli2'],
|
|
50
50
|
install_requires=[
|
|
@@ -54,10 +54,6 @@ setup(
|
|
|
54
54
|
'structlog',
|
|
55
55
|
],
|
|
56
56
|
extras_require=dict(
|
|
57
|
-
client=[
|
|
58
|
-
'httpx',
|
|
59
|
-
'truststore',
|
|
60
|
-
],
|
|
61
57
|
test=[
|
|
62
58
|
'freezegun',
|
|
63
59
|
'pytest',
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import cli2
|
|
3
|
+
import pytest
|
|
4
|
+
from unittest import mock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_async_run_noloop():
|
|
8
|
+
async def task():
|
|
9
|
+
await asyncio.sleep(.01)
|
|
10
|
+
|
|
11
|
+
cli2.async_run(task())
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.asyncio
|
|
15
|
+
async def test_async_run_loop():
|
|
16
|
+
async def task():
|
|
17
|
+
await asyncio.sleep(.01)
|
|
18
|
+
|
|
19
|
+
cli2.async_run(task())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.asyncio
|
|
23
|
+
async def test_queue_basic():
|
|
24
|
+
async def task(i):
|
|
25
|
+
return i
|
|
26
|
+
|
|
27
|
+
queue = cli2.Queue()
|
|
28
|
+
await queue.run(task(1), task(2), task(3))
|
|
29
|
+
|
|
30
|
+
assert sorted(queue.results) == [1, 2, 3]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.mark.asyncio
|
|
34
|
+
async def test_queue_concurrency():
|
|
35
|
+
start = asyncio.get_event_loop().time()
|
|
36
|
+
|
|
37
|
+
async def worker(i):
|
|
38
|
+
await asyncio.sleep(0.1)
|
|
39
|
+
return i
|
|
40
|
+
|
|
41
|
+
queue = cli2.Queue(num_workers=3)
|
|
42
|
+
await queue.run(worker(1), worker(2), worker(3))
|
|
43
|
+
|
|
44
|
+
duration = asyncio.get_event_loop().time() - start
|
|
45
|
+
assert duration < 0.15, 'Should run in parallel'
|
|
46
|
+
assert sorted(queue.results) == [1, 2, 3]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.asyncio
|
|
50
|
+
async def test_queue_error_handling(monkeypatch):
|
|
51
|
+
import cli2.asyncio
|
|
52
|
+
logger = mock.Mock()
|
|
53
|
+
monkeypatch.setattr(cli2.asyncio, 'log', logger)
|
|
54
|
+
|
|
55
|
+
async def worker(i):
|
|
56
|
+
if i == 2:
|
|
57
|
+
raise ValueError('test error')
|
|
58
|
+
await asyncio.sleep(0.01)
|
|
59
|
+
return i
|
|
60
|
+
|
|
61
|
+
queue = cli2.Queue()
|
|
62
|
+
await queue.run(worker(1), worker(2), worker(3))
|
|
63
|
+
|
|
64
|
+
# Error should not stop other tasks
|
|
65
|
+
assert queue.results == [1, 3]
|
|
66
|
+
|
|
67
|
+
# Exceptions should be printed though, not swallowed
|
|
68
|
+
logger.exception.assert_called_once()
|
cli2-5.0.0rc9/cli2/asyncio.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import inspect
|
|
3
|
-
from . import display
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def async_iter(obj):
|
|
7
|
-
return inspect.isasyncgen(obj) or hasattr(obj, '__aiter__')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
async def async_resolve(result, output=False):
|
|
11
|
-
""" Recursively resolve awaitables. """
|
|
12
|
-
while inspect.iscoroutine(result):
|
|
13
|
-
result = await result
|
|
14
|
-
if async_iter(result):
|
|
15
|
-
results = []
|
|
16
|
-
async for _ in result:
|
|
17
|
-
if output:
|
|
18
|
-
if (
|
|
19
|
-
not inspect.iscoroutine(_)
|
|
20
|
-
and not inspect.isasyncgen(_)
|
|
21
|
-
):
|
|
22
|
-
display.print(_)
|
|
23
|
-
else:
|
|
24
|
-
await async_resolve(_, output=output)
|
|
25
|
-
else:
|
|
26
|
-
results.append(await async_resolve(_))
|
|
27
|
-
return None if output else results
|
|
28
|
-
return result
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def async_run(coroutine):
|
|
32
|
-
"""
|
|
33
|
-
Run an async coroutineutine, in running loop or new loop.
|
|
34
|
-
|
|
35
|
-
:param coroutine: The return value of an async function call.
|
|
36
|
-
"""
|
|
37
|
-
try:
|
|
38
|
-
loop = asyncio.get_running_loop()
|
|
39
|
-
except RuntimeError:
|
|
40
|
-
return asyncio.run(coroutine)
|
|
41
|
-
else:
|
|
42
|
-
return loop.create_task(coroutine)
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import cli2
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_async_run_noloop():
|
|
7
|
-
async def task():
|
|
8
|
-
await asyncio.sleep(.01)
|
|
9
|
-
|
|
10
|
-
cli2.async_run(task())
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@pytest.mark.asyncio
|
|
14
|
-
async def test_async_run_loop():
|
|
15
|
-
async def task():
|
|
16
|
-
await asyncio.sleep(.01)
|
|
17
|
-
|
|
18
|
-
cli2.async_run(task())
|
|
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
|
|
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
|