Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pytest-xdist for openSUSE:Factory checked in at 2023-05-19 11:55:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pytest-xdist (Old) and /work/SRC/openSUSE:Factory/.python-pytest-xdist.new.1533 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-xdist" Fri May 19 11:55:15 2023 rev:22 rq:1087079 version:3.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pytest-xdist/python-pytest-xdist.changes 2023-04-25 16:53:25.934159946 +0200 +++ /work/SRC/openSUSE:Factory/.python-pytest-xdist.new.1533/python-pytest-xdist.changes 2023-05-19 11:55:44.083336425 +0200 @@ -1,0 +2,7 @@ +Sun May 14 09:36:49 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 3.3.0: + * Improved progress output when collecting nodes to be less + verbose. + +------------------------------------------------------------------- Old: ---- pytest-xdist-3.2.1.tar.gz New: ---- pytest-xdist-3.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pytest-xdist.spec ++++++ --- /var/tmp/diff_new_pack.D3yfy8/_old 2023-05-19 11:55:44.699339951 +0200 +++ /var/tmp/diff_new_pack.D3yfy8/_new 2023-05-19 11:55:44.711340019 +0200 @@ -19,7 +19,7 @@ %define skip_python2 1 %{?sle15_python_module_pythons} Name: python-pytest-xdist -Version: 3.2.1 +Version: 3.3.0 Release: 0 Summary: Distributed testing and loop-on-failing for py.test License: MIT ++++++ pytest-xdist-3.2.1.tar.gz -> pytest-xdist-3.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/CHANGELOG.rst new/pytest-xdist-3.3.0/CHANGELOG.rst --- old/pytest-xdist-3.2.1/CHANGELOG.rst 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/CHANGELOG.rst 2023-05-12 22:41:10.000000000 +0200 @@ -1,3 +1,12 @@ +pytest-xdist 3.3.0 (2023-05-12) +=============================== + +Features +-------- + +- `#555 <https://github.com/pytest-dev/pytest-xdist/issues/555>`_: Improved progress output when collecting nodes to be less verbose. + + pytest-xdist 3.2.1 (2023-03-12) =============================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/PKG-INFO new/pytest-xdist-3.3.0/PKG-INFO --- old/pytest-xdist-3.2.1/PKG-INFO 2023-03-12 16:09:27.893345800 +0100 +++ new/pytest-xdist-3.3.0/PKG-INFO 2023-05-12 22:41:30.270263200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest-xdist -Version: 3.2.1 +Version: 3.3.0 Summary: pytest xdist plugin for distributed testing, most importantly across multiple CPUs Home-page: https://github.com/pytest-dev/pytest-xdist Author: holger krekel and contributors @@ -32,6 +32,7 @@ Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.7 +Description-Content-Type: text/x-rst Provides-Extra: testing Provides-Extra: psutil Provides-Extra: setproctitle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/RELEASING.rst new/pytest-xdist-3.3.0/RELEASING.rst --- old/pytest-xdist-3.2.1/RELEASING.rst 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/RELEASING.rst 2023-05-12 22:41:10.000000000 +0200 @@ -32,14 +32,8 @@ $ tox -e release -- X.Y.Z -#. Commit and push the branch for review. +#. Commit and push the branch to ``upstream`` and open a PR. -#. Once PR is **green** and **approved**, create and push a tag:: +#. Once the PR is **green** and **approved**, start the ``deploy`` workflow manually from the branch ``release-VERSION``, passing ``VERSION`` as parameter. - $ export VERSION=X.Y.Z - $ git tag v$VERSION release-$VERSION - $ git push g...@github.com:pytest-dev/pytest-xdist.git v$VERSION - -That will build the package and publish it on ``PyPI`` automatically. - -#. Merge the release PR to `master`. +#. Merge the release PR to ``master``. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/docs/distribution.rst new/pytest-xdist-3.3.0/docs/distribution.rst --- old/pytest-xdist-3.2.1/docs/distribution.rst 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/docs/distribution.rst 2023-05-12 22:41:10.000000000 +0200 @@ -50,7 +50,8 @@ .. _distribution modes: * ``--dist load`` **(default)**: Sends pending tests to any worker that is - available, without any guaranteed order. + available, without any guaranteed order. Scheduling can be fine-tuned with + the `--maxschedchunk` option, see output of `pytest --help`. * ``--dist loadscope``: Tests are grouped by **module** for *test functions* and by **class** for *test methods*. Groups are distributed to available diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/setup.cfg new/pytest-xdist-3.3.0/setup.cfg --- old/pytest-xdist-3.2.1/setup.cfg 2023-03-12 16:09:27.893345800 +0100 +++ new/pytest-xdist-3.3.0/setup.cfg 2023-05-12 22:41:30.270263200 +0200 @@ -2,6 +2,7 @@ name = pytest-xdist description = pytest xdist plugin for distributed testing, most importantly across multiple CPUs long_description = file: README.rst +long_description_content_type = text/x-rst license = MIT author = holger krekel and contributors author_email = pytest-...@python.org,hol...@merlinux.eu diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/src/pytest_xdist.egg-info/PKG-INFO new/pytest-xdist-3.3.0/src/pytest_xdist.egg-info/PKG-INFO --- old/pytest-xdist-3.2.1/src/pytest_xdist.egg-info/PKG-INFO 2023-03-12 16:09:27.000000000 +0100 +++ new/pytest-xdist-3.3.0/src/pytest_xdist.egg-info/PKG-INFO 2023-05-12 22:41:30.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pytest-xdist -Version: 3.2.1 +Version: 3.3.0 Summary: pytest xdist plugin for distributed testing, most importantly across multiple CPUs Home-page: https://github.com/pytest-dev/pytest-xdist Author: holger krekel and contributors @@ -32,6 +32,7 @@ Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.7 +Description-Content-Type: text/x-rst Provides-Extra: testing Provides-Extra: psutil Provides-Extra: setproctitle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/src/xdist/_version.py new/pytest-xdist-3.3.0/src/xdist/_version.py --- old/pytest-xdist-3.2.1/src/xdist/_version.py 2023-03-12 16:09:27.000000000 +0100 +++ new/pytest-xdist-3.3.0/src/xdist/_version.py 2023-05-12 22:41:30.000000000 +0200 @@ -1,4 +1,4 @@ # file generated by setuptools_scm # don't change, don't track in version control -__version__ = version = '3.2.1' -__version_tuple__ = version_tuple = (3, 2, 1) +__version__ = version = '3.3.0' +__version_tuple__ = version_tuple = (3, 3, 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/src/xdist/dsession.py new/pytest-xdist-3.3.0/src/xdist/dsession.py --- old/pytest-xdist-3.2.1/src/xdist/dsession.py 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/src/xdist/dsession.py 2023-05-12 22:41:10.000000000 +0200 @@ -1,3 +1,8 @@ +from __future__ import annotations +import sys +from enum import Enum, auto +from typing import Sequence + import pytest from xdist.remote import Producer @@ -251,14 +256,16 @@ self._session.testscollected = len(ids) self.sched.add_node_collection(node, ids) if self.terminal: - self.trdist.setstatus(node.gateway.spec, "[%d]" % (len(ids))) + self.trdist.setstatus( + node.gateway.spec, WorkerStatus.CollectionDone, tests_collected=len(ids) + ) if self.sched.collection_is_completed: if self.terminal and not self.sched.has_pending: self.trdist.ensure_show_status() self.terminal.write_line("") if self.config.option.verbose > 0: self.terminal.write_line( - "scheduling tests via %s" % (self.sched.__class__.__name__) + f"scheduling tests via {self.sched.__class__.__name__}" ) self.sched.schedule() @@ -345,7 +352,7 @@ if rep.failed: self.countfailures += 1 if self.maxfail and self.countfailures >= self.maxfail: - self.shouldstop = "stopping after %d failures" % (self.countfailures) + self.shouldstop = f"stopping after {self.countfailures} failures" def triggershutdown(self): self.log("triggering shutdown") @@ -372,32 +379,51 @@ self.config.hook.pytest_runtest_logreport(report=rep) +class WorkerStatus(Enum): + """Status of each worker during creation/collection.""" + + # Worker spec has just been created. + Created = auto() + + # Worker has been initialized. + Initialized = auto() + + # Worker is now ready for collection. + ReadyForCollection = auto() + + # Worker has finished collection. + CollectionDone = auto() + + class TerminalDistReporter: - def __init__(self, config): + def __init__(self, config) -> None: self.config = config self.tr = config.pluginmanager.getplugin("terminalreporter") - self._status = {} + self._status: dict[str, tuple[WorkerStatus, int]] = {} self._lastlen = 0 self._isatty = getattr(self.tr, "isatty", self.tr.hasmarkup) - def write_line(self, msg): + def write_line(self, msg: str) -> None: self.tr.write_line(msg) - def ensure_show_status(self): + def ensure_show_status(self) -> None: if not self._isatty: self.write_line(self.getstatus()) - def setstatus(self, spec, status, show=True): - self._status[spec.id] = status + def setstatus( + self, spec, status: WorkerStatus, *, tests_collected: int, show: bool = True + ) -> None: + self._status[spec.id] = (status, tests_collected) if show and self._isatty: self.rewrite(self.getstatus()) - def getstatus(self): + def getstatus(self) -> str: if self.config.option.verbose >= 0: - parts = [f"{spec.id} {self._status[spec.id]}" for spec in self._specs] - return " / ".join(parts) - else: - return "bringing up nodes..." + line = get_workers_status_line(list(self._status.values())) + if line: + return line + + return "bringing up nodes..." def rewrite(self, line, newline=False): pline = line + " " * max(self._lastlen - len(line), 0) @@ -409,37 +435,41 @@ self.tr.rewrite(pline, bold=True) @pytest.hookimpl - def pytest_xdist_setupnodes(self, specs): + def pytest_xdist_setupnodes(self, specs) -> None: self._specs = specs for spec in specs: - self.setstatus(spec, "I", show=False) - self.setstatus(spec, "I", show=True) + self.setstatus(spec, WorkerStatus.Created, tests_collected=0, show=False) + self.setstatus(spec, WorkerStatus.Created, tests_collected=0, show=True) self.ensure_show_status() @pytest.hookimpl - def pytest_xdist_newgateway(self, gateway): - if self.config.option.verbose > 0: - rinfo = gateway._rinfo() + def pytest_xdist_newgateway(self, gateway) -> None: + rinfo = gateway._rinfo() + is_local = rinfo.executable == sys.executable + if self.config.option.verbose > 0 and not is_local: version = "%s.%s.%s" % rinfo.version_info[:3] self.rewrite( "[%s] %s Python %s cwd: %s" % (gateway.id, rinfo.platform, version, rinfo.cwd), newline=True, ) - self.setstatus(gateway.spec, "C") + self.setstatus(gateway.spec, WorkerStatus.Initialized, tests_collected=0) @pytest.hookimpl - def pytest_testnodeready(self, node): - if self.config.option.verbose > 0: - d = node.workerinfo + def pytest_testnodeready(self, node) -> None: + d = node.workerinfo + is_local = d.get("executable") == sys.executable + if self.config.option.verbose > 0 and not is_local: infoline = "[{}] Python {}".format( d["id"], d["version"].replace("\n", " -- ") ) self.rewrite(infoline, newline=True) - self.setstatus(node.gateway.spec, "ok") + self.setstatus( + node.gateway.spec, WorkerStatus.ReadyForCollection, tests_collected=0 + ) @pytest.hookimpl - def pytest_testnodedown(self, node, error): + def pytest_testnodedown(self, node, error) -> None: if not error: return self.write_line(f"[{node.gateway.id}] node down: {error}") @@ -457,3 +487,36 @@ # if --max-worker-restart was not provided, use a reasonable default (#226) result = config.option.numprocesses * 4 return result + + +def get_workers_status_line( + status_and_items: Sequence[tuple[WorkerStatus, int]] +) -> str: + """ + Return the line to display during worker setup/collection based on the + status of the workers and number of tests collected for each. + """ + statuses = [s for s, c in status_and_items] + total_workers = len(statuses) + workers_noun = "worker" if total_workers == 1 else "workers" + if status_and_items and all(s == WorkerStatus.CollectionDone for s in statuses): + # All workers collect the same number of items, so we grab + # the total number of items from the first worker. + first = status_and_items[0] + status, tests_collected = first + tests_noun = "item" if tests_collected == 1 else "items" + return f"{total_workers} {workers_noun} [{tests_collected} {tests_noun}]" + if WorkerStatus.CollectionDone in statuses: + done = sum(1 for s, c in status_and_items if c > 0) + return f"collecting: {done}/{total_workers} {workers_noun}" + if WorkerStatus.ReadyForCollection in statuses: + ready = statuses.count(WorkerStatus.ReadyForCollection) + return f"ready: {ready}/{total_workers} {workers_noun}" + if WorkerStatus.Initialized in statuses: + initialized = statuses.count(WorkerStatus.Initialized) + return f"initialized: {initialized}/{total_workers} {workers_noun}" + if WorkerStatus.Created in statuses: + created = statuses.count(WorkerStatus.Created) + return f"created: {created}/{total_workers} {workers_noun}" + + return "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/src/xdist/plugin.py new/pytest-xdist-3.3.0/src/xdist/plugin.py --- old/pytest-xdist-3.2.1/src/xdist/plugin.py 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/src/xdist/plugin.py 2023-05-12 22:41:10.000000000 +0200 @@ -173,6 +173,8 @@ "one - might be useful for a small number of slow tests. " "Larger numbers will allow the scheduler to submit consecutive " "chunks of tests to workers - allows reusing fixtures. " + "Due to implementation reasons, at least 2 tests are scheduled per " + "worker at the start. Only later tests can be scheduled one by one. " "Unlimited if not set." ), ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/testing/acceptance_test.py new/pytest-xdist-3.3.0/testing/acceptance_test.py --- old/pytest-xdist-3.2.1/testing/acceptance_test.py 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/testing/acceptance_test.py 2023-05-12 22:41:10.000000000 +0200 @@ -101,7 +101,12 @@ """ ) result = pytester.runpytest(p1, "-v", "-d", "--tx=popen", "--tx=popen") - result.stdout.fnmatch_lines(["*1*Python*", "*2 failed, 1 passed, 1 skipped*"]) + result.stdout.fnmatch_lines( + [ + "created: 2/2 workers", + "*2 failed, 1 passed, 1 skipped*", + ] + ) assert result.ret == 1 def test_n1_fail_minus_x(self, pytester: pytest.Pytester) -> None: @@ -151,7 +156,12 @@ """ ) result = pytester.runpytest(p1, "-d", "-v") - result.stdout.fnmatch_lines(["*2*Python*", "*2 failed, 1 passed, 1 skipped*"]) + result.stdout.fnmatch_lines( + [ + "created: 3/3 workers", + "*2 failed, 1 passed, 1 skipped*", + ] + ) assert result.ret == 1 def test_dist_tests_with_crash(self, pytester: pytest.Pytester) -> None: @@ -237,9 +247,6 @@ assert result.ret == 0 result.stdout.fnmatch_lines( [ - "*0* *cwd*", - # "RSyncStart: [G1]", - # "RSyncFinished: [G1]", "*1 passed*", ] ) @@ -276,7 +283,11 @@ p1 = pytester.makepyfile("def test_func(): pass") result = pytester.runpytest("-v", p1, "-d", "--tx=popen") result.stdout.fnmatch_lines( - ["*0*Python*", "*calculated result is 49*", "*1 passed*"] + [ + "created: 1/1 worker", + "*calculated result is 49*", + "*1 passed*", + ] ) assert result.ret == 0 @@ -393,14 +404,14 @@ out = result.stdout.str() if verbosity == "-v": assert "scheduling tests" in out - assert "gw" in out + assert "1 worker [1 item]" in out elif verbosity == "-q": assert "scheduling tests" not in out assert "gw" not in out assert "bringing up nodes..." in out else: assert "scheduling tests" not in out - assert "gw" in out + assert "1 worker [1 item]" in out def test_pass_skip_fail(self, pytester: pytest.Pytester) -> None: pytester.makepyfile( @@ -1099,8 +1110,9 @@ result = pytester.runpytest(*args) assert "test session starts" in result.stdout.str() assert "\x1b[1m" in result.stdout.str() - assert "gw0 [10] / gw1 [10]" in result.stdout.str() - assert "gw0 C / gw1 C" not in result.stdout.str() + assert "created: 2/2 workers" in result.stdout.str() + assert "2 workers [10 items]" in result.stdout.str() + assert "collecting:" not in result.stdout.str() def test_without_terminal_plugin(pytester, request) -> None: @@ -1554,8 +1566,8 @@ assert result.ret == 1 result.stdout.fnmatch_lines( [ - "gw0 I", - "gw0 [[]0[]]", + "created: 1/1 worker", + "1 worker [[]0 items[]]", "*_ ERROR collecting test_collection_crash.py _*", "E assert 0", "*= 1 error in *", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pytest-xdist-3.2.1/testing/test_dsession.py new/pytest-xdist-3.3.0/testing/test_dsession.py --- old/pytest-xdist-3.2.1/testing/test_dsession.py 2023-03-12 16:09:12.000000000 +0100 +++ new/pytest-xdist-3.3.0/testing/test_dsession.py 2023-05-12 22:41:10.000000000 +0200 @@ -1,7 +1,13 @@ -from xdist.dsession import DSession, get_default_max_worker_restart +from __future__ import annotations +from xdist.dsession import ( + DSession, + get_default_max_worker_restart, + get_workers_status_line, + WorkerStatus, +) from xdist.report import report_collection_diff from xdist.scheduler import EachScheduling, LoadScheduling, WorkStealingScheduling -from typing import Optional +from typing import Sequence import pytest import execnet @@ -473,7 +479,7 @@ def test_default_max_worker_restart() -> None: class config: class option: - maxworkerrestart: Optional[str] = None + maxworkerrestart: str | None = None numprocesses: int = 0 assert get_default_max_worker_restart(config) is None @@ -527,3 +533,70 @@ reprec = pytester.inline_run("-n1") reprec.assertoutcome(passed=2) assert 0 + + +Created = WorkerStatus.Created +Initialized = WorkerStatus.Initialized +ReadyForCollection = WorkerStatus.ReadyForCollection +CollectionDone = WorkerStatus.CollectionDone + + +@pytest.mark.parametrize( + "status_and_items, expected", + [ + ( + [], + "", + ), + ( + [(Created, 0)], + "created: 1/1 worker", + ), + ( + [(Created, 0), (Created, 0)], + "created: 2/2 workers", + ), + ( + [(Initialized, 0), (Created, 0)], + "initialized: 1/2 workers", + ), + ( + [(Initialized, 0), (Initialized, 0)], + "initialized: 2/2 workers", + ), + ( + [(ReadyForCollection, 0), (Created, 0)], + "ready: 1/2 workers", + ), + ( + [(ReadyForCollection, 0), (ReadyForCollection, 0)], + "ready: 2/2 workers", + ), + ( + [(CollectionDone, 12), (Created, 0)], + "collecting: 1/2 workers", + ), + ( + [(CollectionDone, 12), (CollectionDone, 12)], + "2 workers [12 items]", + ), + ( + [(CollectionDone, 1), (CollectionDone, 1)], + "2 workers [1 item]", + ), + ( + [(CollectionDone, 1)], + "1 worker [1 item]", + ), + # Different number of tests collected will raise an error and should not happen in practice, + # but we test for it anyway. + ( + [(CollectionDone, 1), (CollectionDone, 12)], + "2 workers [1 item]", + ), + ], +) +def test_get_workers_status_line( + status_and_items: Sequence[tuple[WorkerStatus, int]], expected: str +) -> None: + assert get_workers_status_line(status_and_items) == expected