pytest-asyncio-concurrent 0.2.3__tar.gz → 0.2.4__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.
- {pytest_asyncio_concurrent-0.2.3/pytest_asyncio_concurrent.egg-info → pytest_asyncio_concurrent-0.2.4}/PKG-INFO +1 -1
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pyproject.toml +2 -2
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent/plugin.py +32 -72
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4/pytest_asyncio_concurrent.egg-info}/PKG-INFO +1 -1
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent.egg-info/SOURCES.txt +1 -0
- pytest_asyncio_concurrent-0.2.4/tests/test_logging.py +30 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/LICENSE +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/README.rst +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent/__init__.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent/fixture_async.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent/grouping.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent/hooks.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent.egg-info/dependency_links.txt +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent.egg-info/entry_points.txt +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent.egg-info/requires.txt +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/pytest_asyncio_concurrent.egg-info/top_level.txt +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/setup.cfg +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_async_fixture.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_compatibility.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_error.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_fixture_lifecycle.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_fixture_scoping.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_grouping.py +0 -0
- {pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_status.py +0 -0
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pytest-asyncio-concurrent"
|
|
9
9
|
description = "Pytest plugin to execute python async tests concurrently."
|
|
10
|
-
version = "0.2.
|
|
10
|
+
version = "0.2.4"
|
|
11
11
|
readme = "README.rst"
|
|
12
12
|
requires-python = ">=3.8"
|
|
13
13
|
authors = [
|
|
@@ -84,7 +84,7 @@ concurrency = [
|
|
|
84
84
|
show_missing = true
|
|
85
85
|
|
|
86
86
|
[tool.bumpversion]
|
|
87
|
-
current_version = "0.2.
|
|
87
|
+
current_version = "0.2.4"
|
|
88
88
|
parse = """(?x)
|
|
89
89
|
(?P<major>0|[1-9]\\d*)\\.
|
|
90
90
|
(?P<minor>0|[1-9]\\d*)\\.
|
|
@@ -17,7 +17,6 @@ from typing import (
|
|
|
17
17
|
Dict,
|
|
18
18
|
Sequence,
|
|
19
19
|
Union,
|
|
20
|
-
ContextManager,
|
|
21
20
|
)
|
|
22
21
|
|
|
23
22
|
import pluggy
|
|
@@ -169,11 +168,11 @@ def pytest_runtest_protocol_async_group(
|
|
|
169
168
|
|
|
170
169
|
Hooks order:
|
|
171
170
|
- pytest_runtest_logstart (batch)
|
|
172
|
-
- pytest_runtest_setup_async_group (
|
|
171
|
+
- pytest_runtest_setup_async_group (reporting under first tests)
|
|
173
172
|
- pytest_runtest_setup (batch) (and reporting)
|
|
174
173
|
- pytest_runtest_call_async (batch) (and reporting)
|
|
175
174
|
- pytest_runtest_teardown (batch) (and reporting)
|
|
176
|
-
- pytest_runtest_teardown_async_group (
|
|
175
|
+
- pytest_runtest_teardown_async_group (reporting under last tests)
|
|
177
176
|
- pytest_runtest_logfinish (batch)
|
|
178
177
|
"""
|
|
179
178
|
|
|
@@ -191,7 +190,6 @@ def pytest_runtest_protocol_async_group(
|
|
|
191
190
|
)
|
|
192
191
|
|
|
193
192
|
item_passed_setup: List[AsyncioConcurrentGroupMember] = []
|
|
194
|
-
coros: List[Coroutine] = []
|
|
195
193
|
loop = asyncio.get_event_loop()
|
|
196
194
|
|
|
197
195
|
for childFunc in group.children:
|
|
@@ -203,10 +201,12 @@ def pytest_runtest_protocol_async_group(
|
|
|
203
201
|
if report.passed:
|
|
204
202
|
item_passed_setup.append(childFunc)
|
|
205
203
|
|
|
206
|
-
for childFunc in item_passed_setup
|
|
207
|
-
|
|
204
|
+
coros = [_call_runtest_async(childFunc) for childFunc in item_passed_setup]
|
|
205
|
+
callinfos = loop.run_until_complete(asyncio.gather(*coros))
|
|
208
206
|
|
|
209
|
-
|
|
207
|
+
for childFunc, callinfo in zip(item_passed_setup, callinfos):
|
|
208
|
+
report = childFunc.ihook.pytest_runtest_makereport(item=childFunc, call=callinfo)
|
|
209
|
+
childFunc.ihook.pytest_runtest_logreport(report=report)
|
|
210
210
|
|
|
211
211
|
for childFunc in group.children:
|
|
212
212
|
_call_and_report(_teardown_child(childFunc, nextgroup=nextgroup), childFunc, "teardown")
|
|
@@ -218,17 +218,15 @@ def pytest_runtest_protocol_async_group(
|
|
|
218
218
|
return True
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
async def
|
|
221
|
+
async def _call_runtest_async(item: AsyncioConcurrentGroupMember) -> pytest.CallInfo:
|
|
222
222
|
mark = _get_asyncio_concurrent_mark(item)
|
|
223
223
|
assert mark
|
|
224
224
|
timeout = mark.kwargs.get("timeout")
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
return await _async_callinfo_from_call(
|
|
227
227
|
functools.partial(item.ihook.pytest_runtest_call_async, item=item), # type: ignore
|
|
228
228
|
timeout=timeout,
|
|
229
229
|
)
|
|
230
|
-
report = item.ihook.pytest_runtest_makereport(item=item, call=callinfo)
|
|
231
|
-
item.ihook.pytest_runtest_logreport(report=report)
|
|
232
230
|
|
|
233
231
|
|
|
234
232
|
def _setup_child(item: AsyncioConcurrentGroupMember) -> Callable[[], None]:
|
|
@@ -314,9 +312,10 @@ async def pytest_runtest_call_async(item: pytest.Function) -> object:
|
|
|
314
312
|
|
|
315
313
|
pytest.skip("Marking a sync function with @asyncio_concurrent is invalid.")
|
|
316
314
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
315
|
+
with hook_wrapper_entered(item.ihook.pytest_runtest_call, item=item):
|
|
316
|
+
testfunction = item.obj
|
|
317
|
+
testargs = {arg: item.funcargs[arg] for arg in item._fixtureinfo.argnames}
|
|
318
|
+
return await testfunction(**testargs)
|
|
320
319
|
|
|
321
320
|
|
|
322
321
|
@pytest.hookimpl(specname="pytest_runtest_setup_async_group")
|
|
@@ -374,47 +373,8 @@ def pytest_runtest_teardown_handle_async_function(
|
|
|
374
373
|
def pytest_runtest_protocol_async_group_warning(
|
|
375
374
|
group: "AsyncioConcurrentGroup", nextgroup: Optional["AsyncioConcurrentGroup"]
|
|
376
375
|
) -> Generator[None, object, object]:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
if with_pytest_runtest_protocol_warning:
|
|
382
|
-
with with_pytest_runtest_protocol_warning(item=group, nextitem=nextgroup):
|
|
383
|
-
result = yield
|
|
384
|
-
|
|
385
|
-
return result
|
|
386
|
-
|
|
387
|
-
return (yield)
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
@pytest.hookimpl(specname="pytest_runtest_call_async", wrapper=True, tryfirst=True)
|
|
391
|
-
def pytest_runtest_call_async_logging(item: pytest.Item) -> Generator[None, object, object]:
|
|
392
|
-
with_pytest_runtest_call_logging = _with_specific_hook_wrapped(
|
|
393
|
-
item.ihook.pytest_runtest_call, "logging-plugin"
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
if with_pytest_runtest_call_logging:
|
|
397
|
-
with with_pytest_runtest_call_logging(item=item):
|
|
398
|
-
result = yield
|
|
399
|
-
|
|
400
|
-
return result
|
|
401
|
-
|
|
402
|
-
return (yield)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
@pytest.hookimpl(specname="pytest_runtest_call_async", wrapper=True, tryfirst=True)
|
|
406
|
-
def pytest_runtest_call_async_capture(item: pytest.Item) -> Generator[None, object, object]:
|
|
407
|
-
with_pytest_runtest_call_capture = _with_specific_hook_wrapped(
|
|
408
|
-
item.ihook.pytest_runtest_call, "capturemanager"
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
if with_pytest_runtest_call_capture:
|
|
412
|
-
with with_pytest_runtest_call_capture(item=item):
|
|
413
|
-
result = yield
|
|
414
|
-
|
|
415
|
-
return result
|
|
416
|
-
|
|
417
|
-
return (yield)
|
|
376
|
+
with hook_wrapper_entered(group.ihook.pytest_runtest_protocol, item=group, nextitem=nextgroup):
|
|
377
|
+
return (yield)
|
|
418
378
|
|
|
419
379
|
|
|
420
380
|
# =========================== # helper #===========================#
|
|
@@ -488,23 +448,23 @@ def _call_and_report(
|
|
|
488
448
|
return report
|
|
489
449
|
|
|
490
450
|
|
|
491
|
-
|
|
451
|
+
@contextlib.contextmanager
|
|
452
|
+
def hook_wrapper_entered(
|
|
492
453
|
hook: pluggy.HookCaller,
|
|
493
|
-
|
|
494
|
-
) ->
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
454
|
+
**kwds: Any,
|
|
455
|
+
) -> Generator[None, None, Any]:
|
|
456
|
+
"""
|
|
457
|
+
# Capture and logging are hard to handle without duplicating huge amount of code,
|
|
458
|
+
# so reusing defined hooks wrapper here.
|
|
459
|
+
"""
|
|
460
|
+
with contextlib.ExitStack() as es:
|
|
461
|
+
for hookimpl in hook.get_hookimpls():
|
|
462
|
+
if not hookimpl.wrapper:
|
|
463
|
+
continue
|
|
464
|
+
es.enter_context(
|
|
465
|
+
contextlib.contextmanager(hookimpl.function)( # type: ignore
|
|
466
|
+
**{k: v for k, v in kwds.items() if k in hookimpl.argnames}
|
|
467
|
+
)
|
|
468
|
+
)
|
|
499
469
|
|
|
500
|
-
@contextlib.contextmanager
|
|
501
|
-
def cm(**kwargs: Dict) -> Generator:
|
|
502
|
-
gen = hookimpl.function(**{k: v for k, v in kwargs.items() if k in hookimpl.argnames})
|
|
503
|
-
next(gen) # type: ignore
|
|
504
470
|
yield
|
|
505
|
-
try:
|
|
506
|
-
next(gen) # type: ignore
|
|
507
|
-
except StopIteration:
|
|
508
|
-
pass
|
|
509
|
-
|
|
510
|
-
return cm
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_log(pytester: pytest.Pytester):
|
|
6
|
+
"""log should not break tests"""
|
|
7
|
+
|
|
8
|
+
pytester.makepyfile(
|
|
9
|
+
dedent(
|
|
10
|
+
"""\
|
|
11
|
+
import asyncio
|
|
12
|
+
import pytest
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
@pytest.mark.parametrize("p", [0, 1, 2, 3, 4])
|
|
18
|
+
@pytest.mark.asyncio_concurrent(group="log")
|
|
19
|
+
async def test_log(p):
|
|
20
|
+
await asyncio.sleep(p / 10)
|
|
21
|
+
logger.info("info log would be captured")
|
|
22
|
+
logger.debug("debug log would not be captured")
|
|
23
|
+
"""
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
result = pytester.runpytest("--log-cli-level=INFO")
|
|
28
|
+
result.assert_outcomes(passed=5, errors=0)
|
|
29
|
+
assert len([line for line in result.outlines if "info log would be captured" in line]) == 5
|
|
30
|
+
assert len([line for line in result.outlines if "debug log would not be captured" in line]) == 0
|
|
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
|
{pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_async_fixture.py
RENAMED
|
File without changes
|
{pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_compatibility.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_fixture_lifecycle.py
RENAMED
|
File without changes
|
{pytest_asyncio_concurrent-0.2.3 → pytest_asyncio_concurrent-0.2.4}/tests/test_fixture_scoping.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|