Skip to content

Commit 4169fca

Browse files
authored
Merge pull request #176 from ngoldbaum/force-parallel-threads
Add a force_parallel_threads mark
2 parents 9134a5a + 605ed8e commit 4169fca

4 files changed

Lines changed: 118 additions & 13 deletions

File tree

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,17 @@ those fixtures are shared between threads.
9393
as errors.
9494

9595

96-
- Four corresponding markers:
96+
- Five corresponding markers:
9797
- `pytest.mark.parallel_threads_limit(n)` to mark a single test to
9898
run in a maximum of `n` threads, even if the `--parallel-threads`
9999
command-line argument is set to a higher value. This is useful if a
100100
test uses resources that should be limited.
101-
- `pytest.mark.parallel_threads(n)` to mark a test to always run in `n`
101+
- `pytest.mark.force_parallel_threads(n)` to force a test to always run
102+
in `n` threads, overriding both the `--parallel-threads` command-line
103+
argument and any `parallel_threads_limit` marker. Use this when a
104+
test must always be multi-threaded with a specific thread count.
105+
- `pytest.mark.parallel_threads(n)` (deprecated when `n > 1` — use
106+
`force_parallel_threads` instead) to mark a test to always run in `n`
102107
threads. Note that this implies that the test will be multi-threaded
103108
just because pytest-run-parallel is installed, even if
104109
`--parallel-threads` is not passed at the command-line.
@@ -130,8 +135,9 @@ temporary directory in that fixture.
130135
When using the fixtures `thread_index` and `iteration_index`, they should be
131136
requested directly by tests, and will return 0 when requested by other fixtures.
132137

133-
**Note**: It's possible to specify `--parallel-threads=auto` or
134-
`pytest.mark.parallel_threads("auto")` which will let
138+
**Note**: It's possible to specify `--parallel-threads=auto`,
139+
`pytest.mark.force_parallel_threads("auto")`, or
140+
`pytest.mark.parallel_threads_limit("auto")` which will let
135141
`pytest-run-parallel` choose the number of logical CPU cores available
136142
to the testing process. If that cannot be determined, the number of
137143
physical CPU cores will be used. If that fails as well, it will fall
@@ -260,7 +266,7 @@ Note that using `pytest-xdist` and setting `iterations` to a number
260266
greater than one might cause tests to run even more times than intended.
261267

262268
The other mode of operation occurs at the individual test level, via the
263-
`pytest.mark.parallel_threads` and `pytest.mark.iterations` markers:
269+
`pytest.mark.force_parallel_threads` and `pytest.mark.iterations` markers:
264270

265271
```python
266272
# test_file.py
@@ -270,15 +276,15 @@ import pytest
270276
def my_fixture():
271277
...
272278

273-
@pytest.mark.parallel_threads(2)
279+
@pytest.mark.force_parallel_threads(2)
274280
@pytest.mark.iterations(10)
275281
def test_something_1():
276282
# This test will be run in parallel using two concurrent threads
277283
# and 10 times in each thread
278284
...
279285

280286
@pytest.mark.parametrize('arg', [1, 2, 3])
281-
@pytest.mark.parallel_threads(3)
287+
@pytest.mark.force_parallel_threads(3)
282288
def test_fixture(my_fixture, arg):
283289
# pytest markers and fixtures are supported as well
284290
...

src/pytest_run_parallel/plugin.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -436,16 +436,22 @@ def pytest_configure(config):
436436
config.addinivalue_line(
437437
"markers",
438438
"parallel_threads(n): run the given test function in parallel "
439-
"using `n` threads. Note that if n is greater than 1, the test "
440-
"run with this many threads even if the --parallel-threads "
441-
"command-line argument is not passed. Use parallel_threads_limit "
442-
"instead if you want to avoid this pitfall.",
439+
"using `n` threads. Deprecated if `n` is greater than 1. Use either "
440+
"the parallel_threads_limit or force_parallel_threads marker to make "
441+
"your intention clearer.",
443442
)
444443
config.addinivalue_line(
445444
"markers",
446445
"parallel_threads_limit(n): run the given test function in parallel "
447446
"using a maximum of `n` threads.",
448447
)
448+
config.addinivalue_line(
449+
"markers",
450+
"force_parallel_threads(n): force the given test function to run in "
451+
"parallel using `n` threads, overriding the --parallel-threads "
452+
"command-line argument and any parallel_threads_limit marker. Use "
453+
"this to ensure a test always runs with a specific number of threads.",
454+
)
449455
config.addinivalue_line(
450456
"markers",
451457
"iterations(n): run the given test function `n` times in each thread",

src/pytest_run_parallel/utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ def auto_or_int(val):
2222

2323
def get_num_workers(item):
2424
n_workers = get_configured_num_workers(item.config)
25-
# TODO: deprecate in favor of parallel_threads_limit
2625
marker_used = False
2726
marker = item.get_closest_marker("parallel_threads")
2827
if marker is not None:
2928
marker_used = True
3029
n_workers = auto_or_int(marker.args[0])
3130
if n_workers > 1:
3231
warnings.warn(
33-
"Using the parallel_threads marker with a value greater than 1 is deprecated. Use parallel_threads_limit instead.",
32+
"Using the parallel_threads marker with a value greater than 1 is deprecated. "
33+
"Use parallel_threads_limit if you want to set a thread limit or "
34+
"force_parallel_threads if you want to force a test to be multithreaded.",
3435
DeprecationWarning,
3536
stacklevel=2,
3637
)
@@ -42,6 +43,12 @@ def get_num_workers(item):
4243
if n_workers > val:
4344
n_workers = val
4445

46+
force_marker = item.get_closest_marker("force_parallel_threads")
47+
if force_marker is not None:
48+
n_workers = auto_or_int(force_marker.args[0])
49+
if n_workers == 1:
50+
marker_used = True
51+
4552
return n_workers, marker_used
4653

4754

tests/test_run_parallel.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,92 @@ def test_parallel_threads_two():
846846
)
847847

848848

849+
def test_force_parallel_threads_marker(pytester):
850+
pytester.makepyfile("""
851+
import pytest
852+
853+
def test_default_threads(num_parallel_threads):
854+
assert num_parallel_threads == 1
855+
856+
@pytest.mark.force_parallel_threads(4)
857+
def test_forced_four(num_parallel_threads):
858+
assert num_parallel_threads == 4
859+
860+
@pytest.mark.force_parallel_threads(1)
861+
def test_forced_one(num_parallel_threads):
862+
assert num_parallel_threads == 1
863+
""")
864+
865+
result = pytester.runpytest("-v")
866+
assert result.ret == 0
867+
result.stdout.fnmatch_lines(
868+
[
869+
"*::test_default_threads PASSED*",
870+
"*::test_forced_four PARALLEL PASSED*",
871+
"*::test_forced_one PASSED*",
872+
]
873+
)
874+
875+
876+
def test_force_parallel_threads_overrides_cli(pytester):
877+
pytester.makepyfile("""
878+
import pytest
879+
880+
def test_uses_cli(num_parallel_threads):
881+
assert num_parallel_threads == 10
882+
883+
@pytest.mark.force_parallel_threads(3)
884+
def test_forced_overrides_cli(num_parallel_threads):
885+
assert num_parallel_threads == 3
886+
""")
887+
888+
result = pytester.runpytest("--parallel-threads=10", "-v")
889+
assert result.ret == 0
890+
result.stdout.fnmatch_lines(
891+
[
892+
"*::test_uses_cli PARALLEL PASSED*",
893+
"*::test_forced_overrides_cli PARALLEL PASSED*",
894+
]
895+
)
896+
897+
898+
def test_force_parallel_threads_overrides_limit(pytester):
899+
pytester.makepyfile("""
900+
import pytest
901+
902+
@pytest.mark.force_parallel_threads(5)
903+
@pytest.mark.parallel_threads_limit(2)
904+
def test_force_beats_limit(num_parallel_threads):
905+
assert num_parallel_threads == 5
906+
""")
907+
908+
result = pytester.runpytest("--parallel-threads=10", "-v")
909+
assert result.ret == 0
910+
result.stdout.fnmatch_lines(
911+
[
912+
"*::test_force_beats_limit PARALLEL PASSED*",
913+
]
914+
)
915+
916+
917+
def test_force_parallel_threads_no_deprecation_warning(pytester):
918+
pytester.makepyfile("""
919+
import pytest
920+
921+
@pytest.mark.force_parallel_threads(3)
922+
def test_no_warning():
923+
assert True
924+
""")
925+
926+
result = pytester.runpytest("-v", "-W", "error::DeprecationWarning")
927+
assert result.ret == 0
928+
result.stdout.fnmatch_lines(
929+
[
930+
"*::test_no_warning PARALLEL PASSED*",
931+
]
932+
)
933+
934+
849935
def test_forever_without_selected_tests(pytester):
850936
pytester.makepyfile("")
851937
result = pytester.runpytest("--forever", "-v")

0 commit comments

Comments
 (0)