Compare commits

...

23 Commits

Author SHA1 Message Date
Bruno Oliveira
92a76bb013 Release 3.1.0
Some checks failed
deploy / deploy (push) Failing after 1s
2022-12-01 17:37:59 -03:00
Bruno Oliveira
6226965dac Merge pull request #851 from nicoddemus/789-default-dist-mode
Let users configure dist mode in the configuration file
2022-12-01 17:16:09 -03:00
Bruno Oliveira
7a0bc4c1c9 Let users configure dist mode in the configuration file
Fix #789
2022-11-30 13:57:48 -03:00
pre-commit-ci[bot]
c6bcd204ea [pre-commit.ci] pre-commit autoupdate (#849)
updates:
- [github.com/PyCQA/autoflake: v1.7.7 → v2.0.0](https://github.com/PyCQA/autoflake/compare/v1.7.7...v2.0.0)
- [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0)
- [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-29 17:10:17 -03:00
Bread Genie
99c80c37a4 Fix typo psutils -> psutil (#848) 2022-11-24 09:50:25 -03:00
pre-commit-ci[bot]
e14895a96e [pre-commit.ci] pre-commit autoupdate (#846)
updates:
- [github.com/pre-commit/mirrors-mypy: v0.990 → v0.991](https://github.com/pre-commit/mirrors-mypy/compare/v0.990...v0.991)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2022-11-21 21:37:52 -03:00
Bruno Oliveira
bb27210cf9 Merge pull request #844 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2022-11-14 23:35:32 -03:00
Bruno Oliveira
4a33933e30 Use ternary operator to remove mypy error
Otherwise we would get this error:

```
error: Function "Callable[[Any, bool, Optional[float]], None]" could always be true in boolean context  [truthy-function]
```
2022-11-14 23:13:38 -03:00
pre-commit-ci[bot]
41620d2f46 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.2.0 → v3.2.2](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.2.2)
- [github.com/pre-commit/mirrors-mypy: v0.982 → v0.990](https://github.com/pre-commit/mirrors-mypy/compare/v0.982...v0.990)
2022-11-15 00:17:50 +00:00
Bruno Oliveira
6b6f133850 Merge pull request #842 from nicoddemus/drop-py36-add-py311
Drop Python 3.6 and add Python 3.11
2022-11-05 14:36:30 -03:00
Bruno Oliveira
0316baf3ec Add CHANGELOG for py36 drop and py311 support 2022-11-05 12:24:00 -03:00
Bruno Oliveira
aee850a121 Add official support for Python 3.11 2022-11-05 12:24:00 -03:00
Bruno Oliveira
66993ecada Split workflows into test and deploy 2022-11-05 12:24:00 -03:00
Bruno Oliveira
2ffb426178 Drop Python 3.6 and use Python 3.10 for secondary builds 2022-11-05 12:05:30 -03:00
Bruno Oliveira
d7d396a7f9 Run pyupgrade --py37-plus 2022-11-05 12:01:07 -03:00
Thomas Grainger
59457cc1bd Update pre-commit
Also:

* Configure flake8 to ignore formatting-related problems (let black worry about them).
* Fix reference to Python 3.10 in CI,

Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2022-11-05 11:58:57 -03:00
Bruno Oliveira
e232b7886a Merge pull request #838 from pytest-dev/hroncok-patch-1
Remove deprecated stuff from the package description
2022-10-28 13:52:42 -03:00
Miro Hrončok
873c1fdd5e Remove deprecated stuff from the package description
Fixes https://github.com/pytest-dev/pytest-xdist/issues/835
2022-10-28 16:29:45 +02:00
Bruno Oliveira
7949c14f67 Merge pull request #830 from nicoddemus/release-3.0.0
Release 3.0.2
2022-10-25 12:58:44 -03:00
Bruno Oliveira
eed37d4771 Update CHANGELOG
Some checks failed
build / build (ubuntu-latest, 3.10-dev, py310-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.6, py36-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.7, py37-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-psutil) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-pytestmain) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-setproctitle) (push) Failing after 1s
build / build (ubuntu-latest, 3.9, py39-pytestlatest) (push) Failing after 1s
build / build (windows-latest, 3.10-dev, py310-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.6, py36-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.7, py37-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-psutil) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestmain) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-setproctitle) (push) Has been cancelled
build / build (windows-latest, 3.9, py39-pytestlatest) (push) Has been cancelled
build / deploy (push) Has been cancelled
2022-10-25 12:36:11 -03:00
Bruno Oliveira
3e9284b6e0 Merge remote-tracking branch 'upstream/master' into release-3.0.0 2022-10-25 12:35:32 -03:00
Bruno Oliveira
ccdab727a2 Merge pull request #831 from pytest-dev/fix-setup
fix and update packaging
2022-10-25 12:32:31 -03:00
Ronny Pfannschmidt
0c981d3547 fix and update packaging
* drop empty setup.py which "breaks" on incomplete build envs
* use the build package to build the dist
* update the gh workflow to use it

fixup noise from pre-commit
2022-10-25 17:07:47 +02:00
20 changed files with 199 additions and 152 deletions

33
.github/workflows/deploy.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: deploy
on:
push:
tags:
- "v*"
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# Needed to fetch tags, which are required by setuptools-scm.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: Install build
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: |
python -m build
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.pypi_token }}

View File

@@ -1,87 +0,0 @@
name: build
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
tox_env:
- "py36-pytestlatest"
- "py37-pytestlatest"
- "py38-pytestlatest"
- "py39-pytestlatest"
- "py310-pytestlatest"
- "py38-pytestmain"
- "py38-psutil"
- "py38-setproctitle"
os: [ubuntu-latest, windows-latest]
include:
- tox_env: "py36-pytestlatest"
python: "3.6"
- tox_env: "py37-pytestlatest"
python: "3.7"
- tox_env: "py38-pytestlatest"
python: "3.8"
- tox_env: "py39-pytestlatest"
python: "3.9"
- tox_env: "py310-pytestlatest"
python: "3.10-dev"
- tox_env: "py38-pytestmain"
python: "3.8"
- tox_env: "py38-psutil"
python: "3.8"
- tox_env: "py38-setproctitle"
python: "3.8"
steps:
- uses: actions/checkout@v3
with:
# Needed to fetch tags, which are required by setuptools-scm.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install tox
run: |
python -m pip install --upgrade pip
pip install tox
- name: Test
run: |
tox -e ${{ matrix.tox_env }}
deploy:
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
with:
# Needed to fetch tags, which are required by setuptools-scm.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.7"
- name: Install wheel
run: |
python -m pip install --upgrade pip
pip install wheel
- name: Build package
run: |
python setup.py sdist bdist_wheel
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_token }}

67
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: test
on:
push:
branches:
- "*"
pull_request:
branches:
- "*"
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
tox_env:
- "py37-pytestlatest"
- "py38-pytestlatest"
- "py39-pytestlatest"
- "py310-pytestlatest"
- "py310-pytestmain"
- "py311-pytestlatest"
- "py311-pytestmain"
- "py310-psutil"
- "py310-setproctitle"
os: [ubuntu-latest, windows-latest]
include:
- tox_env: "py37-pytestlatest"
python: "3.7"
- tox_env: "py38-pytestlatest"
python: "3.8"
- tox_env: "py39-pytestlatest"
python: "3.9"
- tox_env: "py310-pytestlatest"
python: "3.10"
- tox_env: "py310-pytestmain"
python: "3.10"
- tox_env: "py311-pytestlatest"
python: "3.11"
- tox_env: "py311-pytestmain"
python: "3.11"
- tox_env: "py310-psutil"
python: "3.10"
- tox_env: "py310-setproctitle"
python: "3.10"
steps:
- uses: actions/checkout@v3
with:
# Needed to fetch tags, which are required by setuptools-scm.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- name: Install tox
run: |
python -m pip install --upgrade pip
pip install tox
- name: Test
run: |
tox -e ${{ matrix.tox_env }}

View File

@@ -1,11 +1,11 @@
repos: repos:
- repo: https://github.com/PyCQA/autoflake - repo: https://github.com/PyCQA/autoflake
rev: v1.7.6 rev: v2.0.0
hooks: hooks:
- id: autoflake - id: autoflake
args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"] args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"]
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.3.0 rev: 22.10.0
hooks: hooks:
- id: black - id: black
args: [--safe, --quiet, --target-version, py35] args: [--safe, --quiet, --target-version, py35]
@@ -15,18 +15,18 @@ repos:
- id: blacken-docs - id: blacken-docs
additional_dependencies: [black==20.8b1] additional_dependencies: [black==20.8b1]
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0 rev: v4.4.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-yaml - id: check-yaml
- id: debug-statements - id: debug-statements
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 4.0.1 rev: 6.0.0
hooks: hooks:
- id: flake8 - id: flake8
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.32.1 rev: v3.2.2
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus] args: [--py3-plus]
@@ -39,7 +39,7 @@ repos:
language: python language: python
additional_dependencies: [pygments, restructuredtext_lint] additional_dependencies: [pygments, restructuredtext_lint]
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.960 rev: v0.991
hooks: hooks:
- id: mypy - id: mypy
files: ^(src/|testing/) files: ^(src/|testing/)

View File

@@ -1,13 +1,26 @@
pytest-xdist 3.0.1 (2022-10-25) pytest-xdist 3.1.0 (2022-12-01)
=============================== ===============================
Trivial Changes Features
--------------- --------
- `#830 <https://github.com/pytest-dev/pytest-xdist/issues/830>`_: Fixed failed deploy of ``3.0.0`` release. - `#789 <https://github.com/pytest-dev/pytest-xdist/issues/789>`_: Users can now set a default distribution mode in their configuration file:
.. code-block:: ini
[pytest]
addopts = --dist loadscope
- `#842 <https://github.com/pytest-dev/pytest-xdist/issues/842>`_: Python 3.11 is now officially supported.
pytest-xdist 3.0.0 (2022-10-24) Removals
--------
- `#842 <https://github.com/pytest-dev/pytest-xdist/issues/842>`_: Python 3.6 is no longer supported.
pytest-xdist 3.0.2 (2022-10-25)
=============================== ===============================
Bug Fixes Bug Fixes

View File

@@ -13,7 +13,7 @@ pytest-xdist
:alt: Python versions :alt: Python versions
:target: https://pypi.python.org/pypi/pytest-xdist :target: https://pypi.python.org/pypi/pytest-xdist
.. image:: https://github.com/pytest-dev/pytest-xdist/workflows/build/badge.svg .. image:: https://github.com/pytest-dev/pytest-xdist/workflows/test/badge.svg
:target: https://github.com/pytest-dev/pytest-xdist/actions :target: https://github.com/pytest-dev/pytest-xdist/actions
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg .. image:: https://img.shields.io/badge/code%20style-black-000000.svg

View File

@@ -14,7 +14,7 @@ With ``-n auto``, pytest-xdist will use as many processes as your computer
has CPU cores. has CPU cores.
Use ``-n logical`` to use the number of *logical* CPU cores rather than Use ``-n logical`` to use the number of *logical* CPU cores rather than
physical ones. This currently requires the ``psutils`` package to be installed; physical ones. This currently requires the ``psutil`` package to be installed;
if it is not, pytest-xdist will fall back to ``-n auto`` behavior. if it is not, pytest-xdist will fall back to ``-n auto`` behavior.
Pass a number, e.g. ``-n 8``, to specify the number of processes explicitly. Pass a number, e.g. ``-n 8``, to specify the number of processes explicitly.

View File

@@ -1,6 +1,5 @@
[build-system] [build-system]
requires = [ requires = [
# sync with setup.py until we discard non-pep-517/518
"setuptools>=45.0", "setuptools>=45.0",
"setuptools-scm[toml]>=6.2.3", "setuptools-scm[toml]>=6.2.3",
"wheel", "wheel",

View File

@@ -1,6 +1,6 @@
[metadata] [metadata]
name = pytest-xdist name = pytest-xdist
description = pytest xdist plugin for distributed testing and loop-on-failing modes description = pytest xdist plugin for distributed testing, most importantly across multiple CPUs
long_description = file: README.rst long_description = file: README.rst
license = MIT license = MIT
author = holger krekel and contributors author = holger krekel and contributors
@@ -24,11 +24,11 @@ classifiers =
Programming Language :: Python Programming Language :: Python
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
license_file = LICENSE license_file = LICENSE
project_urls = project_urls =
Documentation=https://pytest-xdist.readthedocs.io/en/latest Documentation=https://pytest-xdist.readthedocs.io/en/latest
@@ -40,11 +40,10 @@ project_urls =
packages = find: packages = find:
package_dir = =src package_dir = =src
zip_safe = False zip_safe = False
python_requires = >=3.6 python_requires = >=3.7
install_requires = install_requires =
execnet>=1.1 execnet>=1.1
pytest>=6.2.0 pytest>=6.2.0
setup_requires = # left empty, enforce using isolated build system
[options.packages.find] [options.packages.find]
where = src where = src
@@ -61,6 +60,8 @@ psutil = psutil>=3.0
setproctitle = setproctitle setproctitle = setproctitle
[flake8] [flake8]
# Ignore any errors related to formatting, let black worry/fix them.
ignore = E501, W503, E203
max-line-length = 100 max-line-length = 100
[mypy] [mypy]

View File

@@ -1,4 +0,0 @@
from setuptools import setup
if __name__ == "__main__":
setup()

View File

@@ -169,7 +169,7 @@ class DSession:
""" """
self.config.hook.pytest_testnodedown(node=node, error=None) self.config.hook.pytest_testnodedown(node=node, error=None)
if node.workeroutput["exitstatus"] == 2: # keyboard-interrupt if node.workeroutput["exitstatus"] == 2: # keyboard-interrupt
self.shouldstop = "{} received keyboard-interrupt".format(node) self.shouldstop = f"{node} received keyboard-interrupt"
self.worker_errordown(node, "keyboard-interrupt") self.worker_errordown(node, "keyboard-interrupt")
return return
if node in self.sched.nodes: if node in self.sched.nodes:
@@ -230,7 +230,7 @@ class DSession:
@pytest.hookimpl @pytest.hookimpl
def pytest_terminal_summary(self, terminalreporter): def pytest_terminal_summary(self, terminalreporter):
if self.config.option.verbose >= 0 and self._summary_report: if self.config.option.verbose >= 0 and self._summary_report:
terminalreporter.write_sep("=", "xdist: {}".format(self._summary_report)) terminalreporter.write_sep("=", f"xdist: {self._summary_report}")
def worker_collectionfinish(self, node, ids): def worker_collectionfinish(self, node, ids):
"""worker has finished test collection. """worker has finished test collection.
@@ -345,7 +345,7 @@ class DSession:
# XXX count no of failures and retry N times # XXX count no of failures and retry N times
runner = self.config.pluginmanager.getplugin("runner") runner = self.config.pluginmanager.getplugin("runner")
fspath = nodeid.split("::")[0] fspath = nodeid.split("::")[0]
msg = "worker {!r} crashed while running {!r}".format(worker.gateway.id, nodeid) msg = f"worker {worker.gateway.id!r} crashed while running {nodeid!r}"
rep = runner.TestReport( rep = runner.TestReport(
nodeid, (fspath, None, fspath), (), "failed", msg, "???" nodeid, (fspath, None, fspath), (), "failed", msg, "???"
) )
@@ -381,9 +381,7 @@ class TerminalDistReporter:
def getstatus(self): def getstatus(self):
if self.config.option.verbose >= 0: if self.config.option.verbose >= 0:
parts = [ parts = [f"{spec.id} {self._status[spec.id]}" for spec in self._specs]
"{} {}".format(spec.id, self._status[spec.id]) for spec in self._specs
]
return " / ".join(parts) return " / ".join(parts)
else: else:
return "bringing up nodes..." return "bringing up nodes..."
@@ -431,7 +429,7 @@ class TerminalDistReporter:
def pytest_testnodedown(self, node, error): def pytest_testnodedown(self, node, error):
if not error: if not error:
return return
self.write_line("[{}] node down: {}".format(node.gateway.id, error)) self.write_line(f"[{node.gateway.id}] node down: {error}")
def get_default_max_worker_restart(config): def get_default_max_worker_restart(config):

View File

@@ -146,7 +146,7 @@ def repr_pytest_looponfailinfo(failreports, rootdirs):
tr.line(report, red=True) tr.line(report, red=True)
tr.sep("#", "waiting for changes", bold=True) tr.sep("#", "waiting for changes", bold=True)
for rootdir in rootdirs: for rootdir in rootdirs:
tr.line("### Watching: {}".format(rootdir), bold=True) tr.line(f"### Watching: {rootdir}", bold=True)
def init_worker_session(channel, args, option_dict): def init_worker_session(channel, args, option_dict):

View File

@@ -18,7 +18,9 @@ def pytest_xdist_auto_num_workers(config):
try: try:
return int(env_var) return int(env_var)
except ValueError: except ValueError:
warnings.warn("PYTEST_XDIST_AUTO_NUM_WORKERS is not a number: {env_var!r}. Ignoring it.") warnings.warn(
"PYTEST_XDIST_AUTO_NUM_WORKERS is not a number: {env_var!r}. Ignoring it."
)
try: try:
import psutil import psutil
@@ -188,7 +190,19 @@ def pytest_addhooks(pluginmanager):
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
def pytest_configure(config): def pytest_configure(config):
if config.getoption("dist") != "no" and not config.getvalue("collectonly"): config_line = (
"xdist_group: specify group for tests should run in same session."
"in relation to one another. Provided by pytest-xdist."
)
config.addinivalue_line("markers", config_line)
# Skip this plugin entirely when only doing collection.
if config.getvalue("collectonly"):
return
# Create the distributed session in case we have a valid distribution
# mode and test environments.
if config.getoption("dist") != "no" and config.getoption("tx"):
from xdist.dsession import DSession from xdist.dsession import DSession
session = DSession(config) session = DSession(config)
@@ -197,6 +211,7 @@ def pytest_configure(config):
if tr: if tr:
tr.showfspath = False tr.showfspath = False
# Deprecation warnings for deprecated command-line/configuration options.
if config.getoption("looponfail", None) or config.getini("looponfailroots"): if config.getoption("looponfail", None) or config.getini("looponfailroots"):
warning = DeprecationWarning( warning = DeprecationWarning(
"The --looponfail command line argument and looponfailroots config variable are deprecated.\n" "The --looponfail command line argument and looponfailroots config variable are deprecated.\n"
@@ -211,12 +226,6 @@ def pytest_configure(config):
) )
config.issue_config_time_warning(warning, 2) config.issue_config_time_warning(warning, 2)
config_line = (
"xdist_group: specify group for tests should run in same session."
"in relation to one another. " + "Provided by pytest-xdist."
)
config.addinivalue_line("markers", config_line)
@pytest.hookimpl(tryfirst=True) @pytest.hookimpl(tryfirst=True)
def pytest_cmdline_main(config): def pytest_cmdline_main(config):

View File

@@ -28,8 +28,8 @@ class Producer:
""" """
Simplified implementation of the same interface as py.log, for backward compatibility Simplified implementation of the same interface as py.log, for backward compatibility
since we dropped the dependency on pylib. since we dropped the dependency on pylib.
Note: this is defined here because this module can't depend on xdist, so we need Note: this is defined here because this module can't depend on xdist, so we need
to have the other way around. to have the other way around.
""" """
def __init__(self, name: str, *, enabled: bool = True): def __init__(self, name: str, *, enabled: bool = True):
@@ -149,7 +149,7 @@ class WorkerInteractor:
if len(mark.args) > 0 if len(mark.args) > 0
else mark.kwargs.get("name", "default") else mark.kwargs.get("name", "default")
) )
item._nodeid = "{}@{}".format(item.nodeid, gname) item._nodeid = f"{item.nodeid}@{gname}"
@pytest.hookimpl @pytest.hookimpl
def pytest_collection_finish(self, session): def pytest_collection_finish(self, session):

View File

@@ -361,12 +361,12 @@ class LoadScopeScheduling:
extra_nodes = len(self.nodes) - len(self.workqueue) extra_nodes = len(self.nodes) - len(self.workqueue)
if extra_nodes > 0: if extra_nodes > 0:
self.log("Shutting down {} nodes".format(extra_nodes)) self.log(f"Shutting down {extra_nodes} nodes")
for _ in range(extra_nodes): for _ in range(extra_nodes):
unused_node, assigned = self.assigned_work.popitem(last=True) unused_node, assigned = self.assigned_work.popitem(last=True)
self.log("Shutting down unused node {}".format(unused_node)) self.log(f"Shutting down unused node {unused_node}")
unused_node.shutdown() unused_node.shutdown()
# Assign initial workload # Assign initial workload

View File

@@ -112,7 +112,7 @@ class NodeManager:
for root in candidates: for root in candidates:
root = Path(root).resolve() root = Path(root).resolve()
if not root.exists(): if not root.exists():
raise pytest.UsageError("rsyncdir doesn't exist: {!r}".format(root)) raise pytest.UsageError(f"rsyncdir doesn't exist: {root!r}")
if root not in roots: if root not in roots:
roots.append(root) roots.append(root)
return roots return roots
@@ -192,7 +192,7 @@ class HostRSync(execnet.RSync):
if self._verbose > 0: if self._verbose > 0:
path = os.path.basename(self._sourcedir) + "/" + modified_rel_path path = os.path.basename(self._sourcedir) + "/" + modified_rel_path
remotepath = gateway.spec.chdir remotepath = gateway.spec.chdir
print("{}:{} <= {}".format(gateway.spec, remotepath, path)) print(f"{gateway.spec}:{remotepath} <= {path}")
def make_reltoroot(roots: Sequence[Path], args: List[str]) -> List[str]: def make_reltoroot(roots: Sequence[Path], args: List[str]) -> List[str]:
@@ -219,7 +219,7 @@ def make_reltoroot(roots: Sequence[Path], args: List[str]) -> List[str]:
parts[0] = root.name + "/" + str(x) parts[0] = root.name + "/" + str(x)
break break
else: else:
raise ValueError("arg {} not relative to an rsync root".format(arg)) raise ValueError(f"arg {arg} not relative to an rsync root")
result.append(splitcode.join(parts)) result.append(splitcode.join(parts))
return result return result
@@ -249,7 +249,7 @@ class WorkerController:
self.log = Producer(f"workerctl-{gateway.id}", enabled=config.option.debug) self.log = Producer(f"workerctl-{gateway.id}", enabled=config.option.debug)
def __repr__(self): def __repr__(self):
return "<{} {}>".format(self.__class__.__name__, self.gateway.id) return f"<{self.__class__.__name__} {self.gateway.id}>"
@property @property
def shutting_down(self): def shutting_down(self):
@@ -310,11 +310,11 @@ class WorkerController:
def sendcommand(self, name, **kwargs): def sendcommand(self, name, **kwargs):
"""send a named parametrized command to the other side.""" """send a named parametrized command to the other side."""
self.log("sending command {}(**{})".format(name, kwargs)) self.log(f"sending command {name}(**{kwargs})")
self.channel.send((name, kwargs)) self.channel.send((name, kwargs))
def notify_inproc(self, eventname, **kwargs): def notify_inproc(self, eventname, **kwargs):
self.log("queuing {}(**{})".format(eventname, kwargs)) self.log(f"queuing {eventname}(**{kwargs})")
self.putevent((eventname, kwargs)) self.putevent((eventname, kwargs))
def process_from_remote(self, eventcall): # noqa too complex def process_from_remote(self, eventcall): # noqa too complex
@@ -336,7 +336,7 @@ class WorkerController:
return return
eventname, kwargs = eventcall eventname, kwargs = eventcall
if eventname in ("collectionstart",): if eventname in ("collectionstart",):
self.log("ignoring {}({})".format(eventname, kwargs)) self.log(f"ignoring {eventname}({kwargs})")
elif eventname == "workerready": elif eventname == "workerready":
self.notify_inproc(eventname, node=self, **kwargs) self.notify_inproc(eventname, node=self, **kwargs)
elif eventname == "internal_error": elif eventname == "internal_error":
@@ -389,7 +389,7 @@ class WorkerController:
location=kwargs["location"], location=kwargs["location"],
) )
else: else:
raise ValueError("unknown event: {}".format(eventname)) raise ValueError(f"unknown event: {eventname}")
except KeyboardInterrupt: except KeyboardInterrupt:
# should not land in receiver-thread # should not land in receiver-thread
raise raise

View File

@@ -1561,3 +1561,21 @@ def test_collection_crash(testdir):
"*= 1 error in *", "*= 1 error in *",
] ]
) )
def test_dist_in_addopts(testdir):
"""Users can set a default distribution in the configuration file (#789)."""
testdir.makepyfile(
"""
def test():
pass
"""
)
testdir.makeini(
"""
[pytest]
addopts = --dist loadscope
"""
)
result = testdir.runpytest()
assert result.ret == 0

View File

@@ -12,7 +12,8 @@ import pytest
@pytest.fixture @pytest.fixture
def monkeypatch_3_cpus(monkeypatch: pytest.MonkeyPatch): def monkeypatch_3_cpus(monkeypatch: pytest.MonkeyPatch):
"""Make pytest-xdist believe the system has 3 CPUs""" """Make pytest-xdist believe the system has 3 CPUs"""
monkeypatch.setitem(sys.modules, "psutil", None) # block import # block import
monkeypatch.setitem(sys.modules, "psutil", None) # type: ignore
monkeypatch.delattr(os, "sched_getaffinity", raising=False) monkeypatch.delattr(os, "sched_getaffinity", raising=False)
monkeypatch.setattr(os, "cpu_count", lambda: 3) monkeypatch.setattr(os, "cpu_count", lambda: 3)

View File

@@ -25,15 +25,14 @@ class EventCall:
self.name, self.kwargs = eventcall self.name, self.kwargs = eventcall
def __str__(self): def __str__(self):
return "<EventCall {}(**{})>".format(self.name, self.kwargs) return f"<EventCall {self.name}(**{self.kwargs})>"
class WorkerSetup: class WorkerSetup:
use_callback = False
def __init__(self, request, pytester: pytest.Pytester) -> None: def __init__(self, request, pytester: pytest.Pytester) -> None:
self.request = request self.request = request
self.pytester = pytester self.pytester = pytester
self.use_callback = False
self.events = Queue() # type: ignore[var-annotated] self.events = Queue() # type: ignore[var-annotated]
def setup(self) -> None: def setup(self) -> None:
@@ -41,7 +40,7 @@ class WorkerSetup:
# import os ; os.environ['EXECNET_DEBUG'] = "2" # import os ; os.environ['EXECNET_DEBUG'] = "2"
self.gateway = execnet.makegateway() self.gateway = execnet.makegateway()
self.config = config = self.pytester.parseconfigure() self.config = config = self.pytester.parseconfigure()
putevent = self.use_callback and self.events.put or None putevent = self.events.put if self.use_callback else None
class DummyMananger: class DummyMananger:
testrunuid = uuid.uuid4().hex testrunuid = uuid.uuid4().hex
@@ -60,7 +59,7 @@ class WorkerSetup:
ev = EventCall(data) ev = EventCall(data)
if name is None or ev.name == name: if name is None or ev.name == name:
return ev return ev
print("skipping {}".format(ev)) print(f"skipping {ev}")
def sendcommand(self, name, **kwargs): def sendcommand(self, name, **kwargs):
self.slp.sendcommand(name, **kwargs) self.slp.sendcommand(name, **kwargs)

16
tox.ini
View File

@@ -1,10 +1,10 @@
[tox] [tox]
envlist= envlist=
linting linting
py{36,37,38,39,310}-pytestlatest py{37,38,39,310,311}-pytestlatest
py38-pytestmain py310-pytestmain
py38-psutil py310-psutil
py38-setproctitle py310-setproctitle
isolated_build = true isolated_build = true
[testenv] [testenv]
extras = testing extras = testing
@@ -14,14 +14,14 @@ deps =
commands= commands=
pytest {posargs} pytest {posargs}
[testenv:py38-psutil] [testenv:py310-psutil]
extras = extras =
testing testing
psutil psutil
commands = commands =
pytest {posargs:-k psutil} pytest {posargs:-k psutil}
[testenv:py38-setproctitle] [testenv:py310-setproctitle]
extras = extras =
testing testing
setproctitle setproctitle
@@ -40,7 +40,7 @@ commands = pre-commit run --all-files --show-diff-on-failure
[testenv:release] [testenv:release]
changedir= changedir=
description = do a release, required posarg of the version number description = do a release, required posarg of the version number
basepython = python3.7 basepython = python3.10
skipsdist = True skipsdist = True
usedevelop = True usedevelop = True
passenv = * passenv = *
@@ -50,7 +50,7 @@ commands =
towncrier build --version {posargs} --yes towncrier build --version {posargs} --yes
[testenv:docs] [testenv:docs]
basepython = python3 basepython = python3.10
usedevelop = True usedevelop = True
deps = deps =
sphinx sphinx