pytest-isolate 0.0.3__tar.gz → 0.0.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_isolate-0.0.3 → pytest_isolate-0.0.4}/.github/workflows/python-package.yml +2 -2
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/PKG-INFO +15 -6
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/README.md +9 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/pyproject.toml +2 -3
- pytest_isolate-0.0.4/src/pytest_isolate/__about__.py +1 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/src/pytest_isolate/plugin.py +97 -29
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/tests/test_isolate.py +8 -2
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/tox.ini +4 -4
- pytest_isolate-0.0.3/src/pytest_isolate/__about__.py +0 -1
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/.github/workflows/python-publish.yml +0 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/.gitignore +0 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/LICENSE.txt +0 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/src/pytest_isolate/__init__.py +0 -0
- {pytest_isolate-0.0.3 → pytest_isolate-0.0.4}/tests/conftest.py +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-isolate
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: Run pytest tests in isolated subprocesses
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Run pytest tests in isolated subprocesses
|
|
5
5
|
Project-URL: Documentation, https://github.com/gilfree/pytest-isolate#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/gilfree/pytest-isolate/issues
|
|
7
7
|
Project-URL: Source, https://github.com/gilfree/pytest-isolate
|
|
8
8
|
Author-email: gilfree <gilfree@users.noreply.github.com>
|
|
9
|
-
License: MIT
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.txt
|
|
10
11
|
Classifier: Development Status :: 4 - Beta
|
|
11
12
|
Classifier: Programming Language :: Python
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
19
|
Requires-Python: >=3.7
|
|
@@ -39,8 +39,10 @@ This pytest plugin was generated with Cookiecutter along with `@hackebrot`'s `co
|
|
|
39
39
|
* Captures stdout & stderr of crashing processes
|
|
40
40
|
* Add Timeout to a forked test
|
|
41
41
|
* Limit memory used by test
|
|
42
|
+
* Limit CPU time used by test
|
|
42
43
|
* Plays nice with pytest-xdist
|
|
43
44
|
* Shows warnings, even with xdist!
|
|
45
|
+
* *Create visual timeline of test execution (isolated or not)*
|
|
44
46
|
|
|
45
47
|
## Requirements
|
|
46
48
|
|
|
@@ -64,6 +66,13 @@ Or:
|
|
|
64
66
|
|
|
65
67
|
To set a timeout to every test in addition to forking, and limit to 10 cpu seconds.
|
|
66
68
|
|
|
69
|
+
Or:
|
|
70
|
+
|
|
71
|
+
pytest --timeline
|
|
72
|
+
|
|
73
|
+
With possible combination of the above, to generate a timeline of test execution. The
|
|
74
|
+
timeline can be viewed in chrome://tracing.
|
|
75
|
+
|
|
67
76
|
> Note:
|
|
68
77
|
>
|
|
69
78
|
> Since this plugin uses `fork`, it will not work on operating systems without `fork` support (e.g. Windows).
|
|
@@ -15,8 +15,10 @@ This pytest plugin was generated with Cookiecutter along with `@hackebrot`'s `co
|
|
|
15
15
|
* Captures stdout & stderr of crashing processes
|
|
16
16
|
* Add Timeout to a forked test
|
|
17
17
|
* Limit memory used by test
|
|
18
|
+
* Limit CPU time used by test
|
|
18
19
|
* Plays nice with pytest-xdist
|
|
19
20
|
* Shows warnings, even with xdist!
|
|
21
|
+
* *Create visual timeline of test execution (isolated or not)*
|
|
20
22
|
|
|
21
23
|
## Requirements
|
|
22
24
|
|
|
@@ -40,6 +42,13 @@ Or:
|
|
|
40
42
|
|
|
41
43
|
To set a timeout to every test in addition to forking, and limit to 10 cpu seconds.
|
|
42
44
|
|
|
45
|
+
Or:
|
|
46
|
+
|
|
47
|
+
pytest --timeline
|
|
48
|
+
|
|
49
|
+
With possible combination of the above, to generate a timeline of test execution. The
|
|
50
|
+
timeline can be viewed in chrome://tracing.
|
|
51
|
+
|
|
43
52
|
> Note:
|
|
44
53
|
>
|
|
45
54
|
> Since this plugin uses `fork`, it will not work on operating systems without `fork` support (e.g. Windows).
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pytest-isolate"
|
|
7
|
-
description = 'Run pytest tests in isolated subprocesses'
|
|
7
|
+
description = 'Run pytest tests in isolated subprocesses '
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.7"
|
|
10
10
|
license = "MIT"
|
|
@@ -13,11 +13,10 @@ authors = [{ name = "gilfree", email = "gilfree@users.noreply.github.com" }]
|
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 4 - Beta",
|
|
15
15
|
"Programming Language :: Python",
|
|
16
|
-
"Programming Language :: Python :: 3.7",
|
|
17
|
-
"Programming Language :: Python :: 3.8",
|
|
18
16
|
"Programming Language :: Python :: 3.9",
|
|
19
17
|
"Programming Language :: Python :: 3.10",
|
|
20
18
|
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
21
20
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
22
21
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
23
22
|
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.4"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# This file is was originally based on pytest forked:
|
|
2
2
|
# see: https://github.com/pytest-dev/pytest-forked/blob/master/src/pytest_forked/__init__.py
|
|
3
|
-
# Since pytest forked is un unmaintained I decided to make my
|
|
3
|
+
# Since pytest forked is un unmaintained I decided to make my
|
|
4
4
|
# changes in a different project
|
|
5
5
|
# Please see README.md
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
import fcntl
|
|
9
9
|
import io
|
|
10
|
+
import json
|
|
10
11
|
import multiprocessing as mp
|
|
11
12
|
import os
|
|
12
13
|
import resource
|
|
@@ -28,7 +29,7 @@ except ImportError:
|
|
|
28
29
|
pass
|
|
29
30
|
|
|
30
31
|
try:
|
|
31
|
-
import setproctitle
|
|
32
|
+
import setproctitle # noqa F401
|
|
32
33
|
except ImportError:
|
|
33
34
|
pass
|
|
34
35
|
|
|
@@ -209,6 +210,23 @@ def pytest_addoption(parser):
|
|
|
209
210
|
"isolate_cpu_limit", "Default cpu limit for isolated tests", type="string"
|
|
210
211
|
)
|
|
211
212
|
|
|
213
|
+
parser.addoption(
|
|
214
|
+
"--timeline",
|
|
215
|
+
dest="timeline",
|
|
216
|
+
action="store_true",
|
|
217
|
+
default=False,
|
|
218
|
+
help="Report test timelines",
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
parser.addoption(
|
|
222
|
+
"--timeline-file",
|
|
223
|
+
dest="timeline_file",
|
|
224
|
+
default="./timeline.json",
|
|
225
|
+
help="Path to the timeline file",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
parser.addini("timeline_file", "timeline file", type="string")
|
|
229
|
+
|
|
212
230
|
|
|
213
231
|
@pytest.hookimpl(trylast=True)
|
|
214
232
|
def pytest_configure(config):
|
|
@@ -558,34 +576,84 @@ def get_isolation_options(item):
|
|
|
558
576
|
|
|
559
577
|
|
|
560
578
|
@pytest.hookimpl(tryfirst=True)
|
|
561
|
-
def pytest_terminal_summary(terminalreporter) -> None:
|
|
579
|
+
def pytest_terminal_summary(terminalreporter, exitstatus, config) -> None:
|
|
562
580
|
durations = terminalreporter.config.option.durations
|
|
563
581
|
durations_min = terminalreporter.config.option.durations_min
|
|
564
582
|
verbose = terminalreporter.config.getvalue("verbose")
|
|
565
|
-
if durations is None:
|
|
566
|
-
return
|
|
567
583
|
tr = terminalreporter
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
for
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
584
|
+
if durations is not None:
|
|
585
|
+
dlist = []
|
|
586
|
+
for replist in tr.stats.values():
|
|
587
|
+
for rep in replist:
|
|
588
|
+
if hasattr(rep, "cpu_usage"):
|
|
589
|
+
dlist.append(rep)
|
|
590
|
+
if not dlist:
|
|
591
|
+
return
|
|
592
|
+
dlist.sort(key=lambda x: x.cpu_usage, reverse=True) # type: ignore[no-any-return]
|
|
593
|
+
if not durations:
|
|
594
|
+
tr.write_sep("=", "highest cpu_usage")
|
|
595
|
+
else:
|
|
596
|
+
tr.write_sep("=", "highest %s cpu_usage" % durations)
|
|
597
|
+
dlist = dlist[:durations]
|
|
598
|
+
|
|
599
|
+
for i, rep in enumerate(dlist):
|
|
600
|
+
if verbose < 2 and rep.cpu_usage < durations_min:
|
|
601
|
+
tr.write_line("")
|
|
602
|
+
tr.write_line(
|
|
603
|
+
"(%s cpu_usage < %gs hidden. Use -vv to show these durations.)"
|
|
604
|
+
% (len(dlist) - i, durations_min)
|
|
605
|
+
)
|
|
606
|
+
break
|
|
607
|
+
if rep.when == "teardown":
|
|
608
|
+
tr.write_line(f"{rep.cpu_usage:02.2f}s {rep.nodeid}")
|
|
609
|
+
|
|
610
|
+
if config.getoption("timeline"):
|
|
611
|
+
events = []
|
|
612
|
+
for replist in tr.stats.values():
|
|
613
|
+
rep: pytest.TestReport
|
|
614
|
+
|
|
615
|
+
for rep in replist:
|
|
616
|
+
if not isinstance(rep, pytest.TestReport) or rep.when != "call":
|
|
617
|
+
continue
|
|
618
|
+
props = dict(getattr(rep, "user_properties", ()))
|
|
619
|
+
events.extend(
|
|
620
|
+
create_event(
|
|
621
|
+
props.get("worker_id", "master"),
|
|
622
|
+
rep.nodeid,
|
|
623
|
+
rep.outcome,
|
|
624
|
+
rep.start,
|
|
625
|
+
rep.stop,
|
|
626
|
+
)
|
|
627
|
+
)
|
|
628
|
+
filename = config.getoption("timeline_file")
|
|
629
|
+
os.makedirs(os.path.dirname(os.path.abspath(filename)), exist_ok=True)
|
|
630
|
+
json.dump(events, open(filename, "w"))
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
@pytest.hookimpl(tryfirst=False, hookwrapper=True)
|
|
634
|
+
def pytest_runtest_call(item: pytest.Item):
|
|
635
|
+
yield
|
|
636
|
+
item.user_properties.append(
|
|
637
|
+
("worker_id", os.getenv("PYTEST_XDIST_WORKER", "master"))
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def create_event(worker_name, test_name, category, start_time, end_time, **kwargs):
|
|
642
|
+
start_event = {
|
|
643
|
+
"name": test_name.rsplit("/")[-1],
|
|
644
|
+
"cat": category,
|
|
645
|
+
"ph": "B",
|
|
646
|
+
"pid": worker_name,
|
|
647
|
+
"tid": 0,
|
|
648
|
+
"ts": start_time * (1000**2),
|
|
649
|
+
"args": kwargs,
|
|
650
|
+
}
|
|
651
|
+
end_event = {
|
|
652
|
+
"name": test_name.rsplit("/")[-1],
|
|
653
|
+
"cat": "pipeline",
|
|
654
|
+
"ph": "E",
|
|
655
|
+
"pid": worker_name,
|
|
656
|
+
"tid": 0,
|
|
657
|
+
"ts": end_time * (1000**2),
|
|
658
|
+
}
|
|
659
|
+
return start_event, end_event
|
|
@@ -74,7 +74,13 @@ def test_warn():
|
|
|
74
74
|
@pytest.mark.isolate(cpu_limit=1)
|
|
75
75
|
def test_isolate_cpu():
|
|
76
76
|
import numpy as np
|
|
77
|
+
|
|
77
78
|
# hog CPU:
|
|
78
|
-
x=0
|
|
79
|
+
x = 0
|
|
79
80
|
for i in range(100):
|
|
80
|
-
x+=(np.random.rand(1000,1000)@np.random.rand(1000,1000)).sum()
|
|
81
|
+
x += (np.random.rand(1000, 1000) @ np.random.rand(1000, 1000)).sum()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_slow():
|
|
85
|
+
sleep(0.1)
|
|
86
|
+
pass
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[tox]
|
|
3
3
|
requires =
|
|
4
4
|
tox >=4
|
|
5
|
-
envlist = py{
|
|
5
|
+
envlist = py{39,312}
|
|
6
6
|
isolated_build = True
|
|
7
7
|
|
|
8
8
|
[testenv]
|
|
@@ -12,7 +12,7 @@ deps =
|
|
|
12
12
|
pytest-xdist
|
|
13
13
|
pytest-sugar
|
|
14
14
|
|
|
15
|
-
commands = pytest --isolate --import-mode importlib {posargs:tests}
|
|
15
|
+
commands = pytest --isolate --import-mode importlib {posargs:tests} --timeline
|
|
16
16
|
|
|
17
17
|
[testenv:lint]
|
|
18
18
|
skip_install = true
|
|
@@ -21,5 +21,5 @@ commands = ruff check {postargs:.}
|
|
|
21
21
|
|
|
22
22
|
[gh]
|
|
23
23
|
python =
|
|
24
|
-
3.
|
|
25
|
-
3.
|
|
24
|
+
3.9 = py39
|
|
25
|
+
3.12 = py312
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.0.3'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|