Skip to content

Commit 16601bb

Browse files
committed
Remove our crummy multiprocessing hooks and work on some tests with the builtin mp support in coverage.
1 parent a5b0742 commit 16601bb

File tree

3 files changed

+49
-74
lines changed

3 files changed

+49
-74
lines changed

src/pytest-cov.pth

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
import os, sys;exec('if \'COV_CORE_SOURCE\' in os.environ:\n try:\n from pytest_cov.embed import init\n init()\n except Exception as exc:\n sys.stderr.write(\n "pytest-cov: Failed to setup subprocess coverage. "\n "Environ: {0!r} "\n "Exception: {1!r}\\n".format(\n dict((k, v) for k, v in os.environ.items() if k.startswith(\'COV_CORE\')),\n exc\n )\n )\n')
1+
import os, sys;exec('if \'COV_CORE_SOURCE\' in os.environ:\n try:\n from pytest_cov.embed import init\n init()\n except Exception as exc:\n sys.stderr.write(\n "pytest-cov: Failed to setup subprocess coverage. "\n "Environ: {0!r} "\n "Exception: {1!r}\\n".format(\n dict((k, v) for k, v in os.environ.items() if k.startswith(\'COV_CORE\')),\n exc\n )\n )\n')

src/pytest_cov/embed.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,6 @@
2020
_active_cov = None
2121

2222

23-
def multiprocessing_start(_):
24-
global _active_cov
25-
cov = init()
26-
if cov:
27-
_active_cov = cov
28-
multiprocessing.util.Finalize(None, cleanup, exitpriority=1000)
29-
30-
31-
try:
32-
import multiprocessing.util
33-
except ImportError:
34-
pass
35-
else:
36-
multiprocessing.util.register_after_fork(multiprocessing_start, multiprocessing_start)
37-
38-
3923
def init():
4024
# Only continue if ancestor process has set everything needed in
4125
# the env.
@@ -105,8 +89,6 @@ def cleanup():
10589
_signal_cleanup_handler(*pending_signal)
10690

10791

108-
multiprocessing_finish = cleanup # in case someone dared to use this internal
109-
11092
_previous_handlers = {}
11193
_pending_signal = None
11294
_cleanup_in_progress = False

tests/test_pytest_cov.py

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -157,15 +157,6 @@ def test_foo(cov):
157157
pytest.param('-n 1', marks=pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"'))
158158
], ids=['nodist', 'xdist'])
159159

160-
skipif_multiprocessing_is_broken = pytest.mark.skipif(
161-
'sys.version_info[:2] >= (3, 8)',
162-
reason="deadlocks on Python 3.8+, see: https://bugs.python.org/issue38227"
163-
)
164-
method_params = pytest.mark.parametrize('method', [
165-
pytest.param('fork', marks=skipif_multiprocessing_is_broken),
166-
pytest.param('spawn', marks=skipif_multiprocessing_is_broken),
167-
])
168-
169160

170161
@pytest.fixture(scope='session', autouse=True)
171162
def adjust_sys_path():
@@ -203,7 +194,7 @@ def prop(request):
203194
)
204195

205196

206-
def test_central(testdir, prop):
197+
def test_central(pytester, testdir, prop):
207198
script = testdir.makepyfile(prop.code)
208199
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
209200

@@ -470,7 +461,7 @@ def test_cov_min_no_report(testdir):
470461
])
471462

472463

473-
def test_central_nonspecific(testdir, prop):
464+
def test_central_nonspecific(pytester, testdir, prop):
474465
script = testdir.makepyfile(prop.code)
475466
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
476467
result = testdir.runpytest('-v',
@@ -505,7 +496,7 @@ def test_cov_min_from_coveragerc(testdir):
505496
assert result.ret != 0
506497

507498

508-
def test_central_coveragerc(testdir, prop):
499+
def test_central_coveragerc(pytester, testdir, prop):
509500
script = testdir.makepyfile(prop.code)
510501
testdir.tmpdir.join('.coveragerc').write(COVERAGERC_SOURCE + prop.conf)
511502

@@ -523,7 +514,7 @@ def test_central_coveragerc(testdir, prop):
523514

524515

525516
@xdist_params
526-
def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop):
517+
def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop):
527518
mod1 = testdir.mkdir('src').join('mod.py')
528519
mod1.write(SCRIPT)
529520
mod2 = testdir.mkdir('aliased').join('mod.py')
@@ -557,7 +548,7 @@ def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop):
557548

558549

559550
@xdist_params
560-
def test_borken_cwd(testdir, monkeypatch, opts):
551+
def test_borken_cwd(pytester, testdir, monkeypatch, opts):
561552
testdir.makepyfile(mod='''
562553
def foobar(a, b):
563554
return a + b
@@ -596,7 +587,7 @@ def test_foobar(bad):
596587
assert result.ret == 0
597588

598589

599-
def test_subprocess_with_path_aliasing(testdir, monkeypatch):
590+
def test_subprocess_with_path_aliasing(pytester, testdir, monkeypatch):
600591
src = testdir.mkdir('src')
601592
src.join('parent_script.py').write(SCRIPT_PARENT)
602593
src.join('child_script.py').write(SCRIPT_CHILD)
@@ -632,7 +623,7 @@ def test_subprocess_with_path_aliasing(testdir, monkeypatch):
632623
assert result.ret == 0
633624

634625

635-
def test_show_missing_coveragerc(testdir, prop):
626+
def test_show_missing_coveragerc(pytester, testdir, prop):
636627
script = testdir.makepyfile(prop.code)
637628
testdir.tmpdir.join('.coveragerc').write("""
638629
[run]
@@ -675,7 +666,7 @@ def test_fail():
675666
result.stdout.fnmatch_lines(['*1 failed*'])
676667

677668

678-
def test_no_cov(testdir, monkeypatch):
669+
def test_no_cov(pytester, testdir, monkeypatch):
679670
script = testdir.makepyfile(SCRIPT)
680671
testdir.makeini("""
681672
[pytest]
@@ -742,7 +733,7 @@ def test_foo(foo):
742733

743734

744735
@pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')
745-
def test_dist_collocated(testdir, prop):
736+
def test_dist_collocated(pytester, testdir, prop):
746737
script = testdir.makepyfile(prop.code)
747738
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
748739
result = testdir.runpytest('-v',
@@ -762,7 +753,7 @@ def test_dist_collocated(testdir, prop):
762753

763754

764755
@pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')
765-
def test_dist_not_collocated(testdir, prop):
756+
def test_dist_not_collocated(pytester, testdir, prop):
766757
script = testdir.makepyfile(prop.code)
767758
dir1 = testdir.mkdir('dir1')
768759
dir2 = testdir.mkdir('dir2')
@@ -795,7 +786,7 @@ def test_dist_not_collocated(testdir, prop):
795786

796787

797788
@pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')
798-
def test_dist_not_collocated_coveragerc_source(testdir, prop):
789+
def test_dist_not_collocated_coveragerc_source(pytester, testdir, prop):
799790
script = testdir.makepyfile(prop.code)
800791
dir1 = testdir.mkdir('dir1')
801792
dir2 = testdir.mkdir('dir2')
@@ -870,7 +861,7 @@ def test_central_subprocess_change_cwd(testdir):
870861
assert result.ret == 0
871862

872863

873-
def test_central_subprocess_change_cwd_with_pythonpath(testdir, monkeypatch):
864+
def test_central_subprocess_change_cwd_with_pythonpath(pytester, testdir, monkeypatch):
874865
stuff = testdir.mkdir('stuff')
875866
parent_script = stuff.join('parent_script.py')
876867
parent_script.write(SCRIPT_PARENT_CHANGE_CWD_IMPORT_CHILD)
@@ -941,7 +932,7 @@ def test_dist_subprocess_collocated(testdir):
941932

942933

943934
@pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')
944-
def test_dist_subprocess_not_collocated(testdir, tmpdir):
935+
def test_dist_subprocess_not_collocated(pytester, testdir, tmpdir):
945936
scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT,
946937
child_script=SCRIPT_CHILD)
947938
parent_script = scripts.dirpath().join('parent_script.py')
@@ -1071,10 +1062,11 @@ def test_funcarg_not_active(testdir):
10711062
@pytest.mark.skipif("sys.version_info[0] < 3", reason="no context manager api on Python 2")
10721063
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
10731064
@pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy")
1074-
@method_params
1075-
def test_multiprocessing_pool(testdir, method):
1065+
@pytest.mark.parametrize('method', ['fork', 'spawn'])
1066+
@pytest.mark.xfail
1067+
def test_multiprocessing_pool(pytester, testdir, method):
10761068
pytest.importorskip('multiprocessing.util')
1077-
1069+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
10781070
script = testdir.makepyfile('''
10791071
import multiprocessing
10801072
@@ -1084,9 +1076,6 @@ def target_fn(a):
10841076
10851077
def test_run_target():
10861078
multiprocessing.set_start_method({!r})
1087-
from pytest_cov.embed import cleanup_on_sigterm
1088-
cleanup_on_sigterm()
1089-
10901079
for i in range(33):
10911080
with multiprocessing.Pool(3) as p:
10921081
p.map(target_fn, [i * 3 + j for j in range(3)])
@@ -1102,21 +1091,21 @@ def test_run_target():
11021091

11031092
assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str()
11041093
assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str()
1105-
assert not testdir.tmpdir.listdir(".coverage.*")
11061094
result.stdout.fnmatch_lines([
11071095
'*- coverage: platform *, python * -*',
11081096
'test_multiprocessing_pool* 100%*',
11091097
'*1 passed*'
11101098
])
1099+
assert not testdir.tmpdir.listdir(".coverage.*")
11111100
assert result.ret == 0
11121101

11131102

11141103
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
11151104
@pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy")
1116-
@method_params
1117-
def test_multiprocessing_pool_terminate(testdir, method):
1105+
@pytest.mark.parametrize('method', ['fork', 'spawn'])
1106+
def test_multiprocessing_pool_terminate(pytester, testdir, method):
11181107
pytest.importorskip('multiprocessing.util')
1119-
1108+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
11201109
script = testdir.makepyfile('''
11211110
import multiprocessing
11221111
@@ -1126,8 +1115,6 @@ def target_fn(a):
11261115
11271116
def test_run_target():
11281117
multiprocessing.set_start_method({!r})
1129-
from pytest_cov.embed import cleanup_on_sigterm
1130-
cleanup_on_sigterm()
11311118
11321119
for i in range(33):
11331120
p = multiprocessing.Pool(3)
@@ -1147,21 +1134,21 @@ def test_run_target():
11471134

11481135
assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str()
11491136
assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str()
1150-
assert not testdir.tmpdir.listdir(".coverage.*")
11511137
result.stdout.fnmatch_lines([
11521138
'*- coverage: platform *, python * -*',
11531139
'test_multiprocessing_pool* 100%*',
11541140
'*1 passed*'
11551141
])
1142+
assert not testdir.tmpdir.listdir(".coverage.*")
11561143
assert result.ret == 0
11571144

11581145

11591146
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
11601147
@pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason="broken on PyPy3")
1161-
@method_params
1162-
def test_multiprocessing_pool_close(testdir, method):
1148+
@pytest.mark.parametrize('method', ['fork', 'spawn'])
1149+
def test_multiprocessing_pool_close(pytester, testdir, method):
11631150
pytest.importorskip('multiprocessing.util')
1164-
1151+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
11651152
script = testdir.makepyfile('''
11661153
import multiprocessing
11671154
@@ -1188,20 +1175,20 @@ def test_run_target():
11881175
script)
11891176
assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str()
11901177
assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str()
1191-
assert not testdir.tmpdir.listdir(".coverage.*")
11921178
result.stdout.fnmatch_lines([
11931179
'*- coverage: platform *, python * -*',
11941180
'test_multiprocessing_pool* 100%*',
11951181
'*1 passed*'
11961182
])
1183+
# assert not testdir.tmpdir.listdir(".coverage.*")
11971184
assert result.ret == 0
11981185

11991186

12001187
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
1201-
@method_params
1202-
def test_multiprocessing_process(testdir, method):
1188+
@pytest.mark.parametrize('method', ['fork', 'spawn'])
1189+
def test_multiprocessing_process(pytester, testdir, method):
12031190
pytest.importorskip('multiprocessing.util')
1204-
1191+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
12051192
script = testdir.makepyfile('''
12061193
import multiprocessing
12071194
@@ -1230,9 +1217,9 @@ def test_run_target():
12301217

12311218

12321219
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
1233-
def test_multiprocessing_process_no_source(testdir):
1220+
def test_multiprocessing_process_no_source(pytester, testdir):
12341221
pytest.importorskip('multiprocessing.util')
1235-
1222+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
12361223
script = testdir.makepyfile('''
12371224
import multiprocessing
12381225
@@ -1260,9 +1247,9 @@ def test_run_target():
12601247

12611248

12621249
@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows")
1263-
def test_multiprocessing_process_with_terminate(testdir):
1250+
def test_multiprocessing_process_with_terminate(pytester, testdir):
12641251
pytest.importorskip('multiprocessing.util')
1265-
1252+
pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC)
12661253
script = testdir.makepyfile('''
12671254
import multiprocessing
12681255
import time
@@ -1349,7 +1336,7 @@ def test_run():
13491336
('cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'),
13501337
('cleanup()', '73% 19-22'),
13511338
])
1352-
def test_cleanup_on_sigterm_sig_break(testdir, setup):
1339+
def test_cleanup_on_sigterm_sig_break(pytester, testdir, setup):
13531340
# worth a read: https://stefan.sofa-rockers.org/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/
13541341
script = testdir.makepyfile('''
13551342
import os, signal, subprocess, sys, time
@@ -1395,7 +1382,7 @@ def test_run():
13951382
('cleanup_on_sigterm()', '88% 18-19'),
13961383
('cleanup()', '75% 16-19'),
13971384
])
1398-
def test_cleanup_on_sigterm_sig_dfl(testdir, setup):
1385+
def test_cleanup_on_sigterm_sig_dfl(pytester, testdir, setup):
13991386
script = testdir.makepyfile('''
14001387
import os, signal, subprocess, sys, time
14011388
@@ -1551,17 +1538,17 @@ def test_cover_conftest(testdir):
15511538

15521539

15531540
@pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')
1554-
def test_cover_looponfail(testdir, monkeypatch):
1541+
def test_cover_looponfail(pytester, testdir, monkeypatch):
15551542
testdir.makepyfile(mod=MODULE)
15561543
testdir.makeconftest(CONFTEST)
15571544
script = testdir.makepyfile(BASIC_TEST)
15581545

15591546
def mock_run(*args, **kwargs):
15601547
return _TestProcess(*map(str, args))
15611548

1562-
monkeypatch.setattr(testdir, 'run', mock_run)
1549+
monkeypatch.setattr(pytester, testdir, 'run', mock_run)
15631550
assert testdir.run is mock_run
1564-
if hasattr(testdir, '_pytester'):
1551+
if hasattr(pytester, testdir, '_pytester'):
15651552
monkeypatch.setattr(testdir._pytester, 'run', mock_run)
15661553
assert testdir._pytester.run is mock_run
15671554
with testdir.runpytest('-v',
@@ -1637,7 +1624,13 @@ def test_basic(no_cover):
16371624
# Regexes for lines to exclude from consideration
16381625
exclude_lines =
16391626
raise NotImplementedError
1627+
'''
16401628

1629+
MP_COVERAGERC = '''
1630+
[run]
1631+
concurrency = multiprocessing
1632+
parallel = true
1633+
sigterm = True
16411634
'''
16421635

16431636
EXCLUDED_TEST = '''
@@ -1704,7 +1697,7 @@ def test_basic():
17041697
@pytest.mark.parametrize('report_option', [
17051698
'term-missing:skip-covered',
17061699
'term:skip-covered'])
1707-
def test_skip_covered_cli(testdir, report_option):
1700+
def test_skip_covered_cli(pytester, testdir, report_option):
17081701
testdir.makefile('', coveragerc=SKIP_COVERED_COVERAGERC)
17091702
script = testdir.makepyfile(SKIP_COVERED_TEST)
17101703
result = testdir.runpytest('-v',
@@ -1910,7 +1903,7 @@ def test_external_data_file_negative(testdir):
19101903

19111904

19121905
@xdist_params
1913-
def test_append_coverage(testdir, opts, prop):
1906+
def test_append_coverage(pytester, testdir, opts, prop):
19141907
script = testdir.makepyfile(test_1=prop.code)
19151908
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
19161909
result = testdir.runpytest('-v',
@@ -1933,7 +1926,7 @@ def test_append_coverage(testdir, opts, prop):
19331926

19341927

19351928
@xdist_params
1936-
def test_do_not_append_coverage(testdir, opts, prop):
1929+
def test_do_not_append_coverage(pytester, testdir, opts, prop):
19371930
script = testdir.makepyfile(test_1=prop.code)
19381931
testdir.tmpdir.join('.coveragerc').write(prop.fullconf)
19391932
result = testdir.runpytest('-v',
@@ -2115,7 +2108,7 @@ def find_labels(text, pattern):
21152108

21162109
@pytest.mark.skipif("coverage.version_info < (5, 0)")
21172110
@xdist_params
2118-
def test_contexts(testdir, opts):
2111+
def test_contexts(pytester, testdir, opts):
21192112
with open(os.path.join(os.path.dirname(__file__), "contextful.py")) as f:
21202113
contextful_tests = f.read()
21212114
script = testdir.makepyfile(contextful_tests)

0 commit comments

Comments
 (0)