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.
@@ -16,8 +16,8 @@ jobs:
16
16
  os:
17
17
  - Ubuntu
18
18
  py:
19
- - "3.11"
20
- - "3.7"
19
+ - "3.12"
20
+ - "3.9"
21
21
  steps:
22
22
  - name: Setup python for test ${{ matrix.py }}
23
23
  uses: actions/setup-python@v2
@@ -1,19 +1,19 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: pytest-isolate
3
- Version: 0.0.3
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 # noqa F401
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
- dlist = []
569
- for replist in tr.stats.values():
570
- for rep in replist:
571
- if hasattr(rep, "cpu_usage"):
572
- dlist.append(rep)
573
- if not dlist:
574
- return
575
- dlist.sort(key=lambda x: x.cpu_usage, reverse=True) # type: ignore[no-any-return]
576
- if not durations:
577
- tr.write_sep("=", "highest cpu_usage")
578
- else:
579
- tr.write_sep("=", "highest %s cpu_usage" % durations)
580
- dlist = dlist[:durations]
581
-
582
- for i, rep in enumerate(dlist):
583
- if verbose < 2 and rep.cpu_usage < durations_min:
584
- tr.write_line("")
585
- tr.write_line(
586
- "(%s cpu_usage < %gs hidden. Use -vv to show these durations.)"
587
- % (len(dlist) - i, durations_min)
588
- )
589
- break
590
- if rep.when == "teardown":
591
- tr.write_line(f"{rep.cpu_usage:02.2f}s {rep.nodeid}")
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{37,311}
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.7 = py37
25
- 3.11 = py311
24
+ 3.9 = py39
25
+ 3.12 = py312
@@ -1 +0,0 @@
1
- __version__ = '0.0.3'