Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pika for openSUSE:Factory checked in at 2026-09-02 17:00:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pika (Old) and /work/SRC/openSUSE:Factory/.python-pika.new.1265 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pika" Wed Sep 2 17:00:57 2026 rev:25 rq:1375242 version:1.4.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pika/python-pika.changes 2026-06-15 19:48:26.936006579 +0200 +++ /work/SRC/openSUSE:Factory/.python-pika.new.1265/python-pika.changes 2026-09-02 17:01:09.959043775 +0200 @@ -1,0 +2,11 @@ +Tue Sep 1 22:15:15 UTC 2026 - Dirk Müller <[email protected]> + +- update to 1.4.4: + * Revert "Fix callLater typing by passing keyword arguments" + #1678 (jeremycline) + * Importing pika.adapters can break asyncio subprocesses on + Windows #1639 + * Stop mutating global asyncio event loop policy on import + #1642 (lukebakken) + +------------------------------------------------------------------- Old: ---- 1.4.1.tar.gz New: ---- 1.4.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pika.spec ++++++ --- /var/tmp/diff_new_pack.cwoUl3/_old 2026-09-02 17:01:12.409127940 +0200 +++ /var/tmp/diff_new_pack.cwoUl3/_new 2026-09-02 17:01:12.412128043 +0200 @@ -19,7 +19,7 @@ %define mod_name pika %{?sle15_python_module_pythons} Name: python-%{mod_name} -Version: 1.4.1 +Version: 1.4.4 Release: 0 Summary: Pika Python AMQP Client Library License: BSD-3-Clause ++++++ 1.4.1.tar.gz -> 1.4.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/CHANGELOG.md new/pika-1.4.4/CHANGELOG.md --- old/pika-1.4.1/CHANGELOG.md 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/CHANGELOG.md 2026-08-06 22:45:29.000000000 +0200 @@ -1,5 +1,25 @@ # Changelog +## [1.4.4](https://github.com/pika/pika/tree/1.4.2) (2026-08-06) + +[Full Changelog](https://github.com/pika/pika/compare/1.4.2...1.4.4) + +**Merged pull requests:** + +- Revert "Fix callLater typing by passing keyword arguments" [\#1678](https://github.com/pika/pika/pull/1678) ([jeremycline](https://github.com/jeremycline)) + +## [1.4.2](https://github.com/pika/pika/tree/1.4.2) (2026-07-23) + +[Full Changelog](https://github.com/pika/pika/compare/1.4.1...1.4.2) + +**Closed issues:** + +- Importing pika.adapters can break asyncio subprocesses on Windows [\#1639](https://github.com/pika/pika/issues/1639) + +**Merged pull requests:** + +- Stop mutating global asyncio event loop policy on import [\#1642](https://github.com/pika/pika/pull/1642) ([lukebakken](https://github.com/lukebakken)) + ## [1.4.1](https://github.com/pika/pika/tree/1.4.1) (2026-05-22) **Merged pull requests:** @@ -1300,6 +1320,3 @@ consumption loop. - Updated stop_consuming() to iterate through all the registered consumers in self.\_consumers and issue a basic_cancel. - - -\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/RELEASE.md new/pika-1.4.4/RELEASE.md --- old/pika-1.4.1/RELEASE.md 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/RELEASE.md 2026-08-06 22:45:29.000000000 +0200 @@ -1,22 +1,22 @@ # Release process -* Create `pika-1.5.0` branch +* Create `pika-1.4.4` branch * Update `CHANGELOG.md`. Be sure to use the `--since-tag X.Y.Z` argument: ``` - github_changelog_generator --token ghp_XXXX --user pika --project pika --since-tag 1.4.1 --future-release 1.5.0 + github_changelog_generator --user pika --project pika --token $TOKEN --since-tag 1.4.4 --future-release 1.4.4 --release-branch 1.4.x ``` Review the generated file for invalid entries. More than likely you will have to hand-edit `CHANGELOG.md` * Update version in `pyproject.toml` and `pika/__init__.py` -* Commit changes to `pika-1.5.0` branch and push: +* Commit changes to `pika-1.4.4` branch and push: ``` - git commit -a -m 'pika 1.5.0' && git push + git commit -a -m 'pika 1.4.4' && git push origin pika-1.4.4 ``` * Open PR and ensure build is green * Merge PR * Update local `main` * Tag version (using your GPG keyid) and push: ``` - git tag -a -s -u B1B82CC0CF84BA70147EBD05D99DE30E43EAE440 -m 'pika 1.5.0' '1.5.0' && git push --tags + git tag -a -s -u B1B82CC0CF84BA70147EBD05D99DE30E43EAE440 -m 'pika 1.4.4' '1.4.4' && git push --tags ``` * Ensure build is green (if one triggered) * Note the release's milestone number, then create release via GitHub Release UI or `gh` command like: @@ -25,22 +25,22 @@ python -m build --sdist --wheel --outdir dist/ . # This creates the release on GitHub: - gh release create '1.5.0' --notes 'https://pypi.org/project/pika/1.5.0/ | [GitHub milestone](https://github.com/pika/pika/milestone/23?closed=1)' ./dist/* + gh release create '1.4.4' --notes 'https://pypi.org/project/pika/1.4.4/ | [GitHub milestone](https://github.com/pika/pika/milestone/27?closed=1)' ./dist/* ``` * Ensure the publish build succeeded. Example success output looks like this: ``` - Checking dist/pika-1.5.0-py3-none-any.whl: PASSED - Checking dist/pika-1.5.0.tar.gz: PASSED + Checking dist/pika-1.4.4-py3-none-any.whl: PASSED + Checking dist/pika-1.4.4.tar.gz: PASSED Uploading distributions to https://upload.pypi.org/legacy/ - Uploading pika-1.5.0-py3-none-any.whl + Uploading pika-1.4.4-py3-none-any.whl ... ... ... View at: - https://pypi.org/project/pika/1.5.0/ + https://pypi.org/project/pika/1.4.4/ ``` -* View the release on PyPI: https://pypi.org/project/pika/1.5.0/ +* View the release on PyPI: https://pypi.org/project/pika/1.4.4/ * Ensure the release works! * Start RabbitMQ ``` @@ -51,7 +51,7 @@ cd path/to/pika/examples python -m venv venv source ./venv/bin/activate - pip install pika==1.5.0 + pip install pika==1.4.4 python ./asynchronous_publisher_example.py ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/pika/__init__.py new/pika-1.4.4/pika/__init__.py --- old/pika-1.4.1/pika/__init__.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/pika/__init__.py 2026-08-06 22:45:29.000000000 +0200 @@ -1,4 +1,4 @@ -__version__ = '1.4.1' +__version__ = '1.4.4' import logging diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/pika/adapters/asyncio_connection.py new/pika-1.4.4/pika/adapters/asyncio_connection.py --- old/pika-1.4.1/pika/adapters/asyncio_connection.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/pika/adapters/asyncio_connection.py 2026-08-06 22:45:29.000000000 +0200 @@ -14,9 +14,6 @@ LOGGER = logging.getLogger(__name__) -if sys.platform == 'win32': - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - class AsyncioConnection(base_connection.BaseConnection): """ The AsyncioConnection runs on the Asyncio EventLoop. @@ -133,7 +130,18 @@ try: loop = asyncio.get_running_loop() except RuntimeError: - loop = asyncio.new_event_loop() + if sys.platform == 'win32': + loop = asyncio.SelectorEventLoop() + else: + loop = asyncio.new_event_loop() + if sys.platform == 'win32' and not isinstance( + loop, asyncio.SelectorEventLoop): + raise TypeError( + 'On Windows, Pika requires an asyncio.SelectorEventLoop ' + 'because it uses add_reader/add_writer. The running loop ' + f'is {type(loop).__name__}. Either pass a SelectorEventLoop ' + 'via custom_ioloop, or run with ' + 'asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop).') self._loop = loop def get_native_ioloop(self) -> asyncio.AbstractEventLoop: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/pika/adapters/twisted_connection.py new/pika-1.4.4/pika/adapters/twisted_connection.py --- old/pika-1.4.1/pika/adapters/twisted_connection.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/pika/adapters/twisted_connection.py 2026-08-06 22:45:29.000000000 +0200 @@ -1137,7 +1137,15 @@ on_close_callback=on_close_callback, internal_connection_workflow=False) - self._reactor = custom_reactor or reactor + # Annotated as `Any` rather than as a reactor interface on purpose. + # Reactor implementations rename the parameters the interfaces + # declare: `IReactorTime.callLater(delay, callable, *args, **kw)` but + # `AsyncioSelectorReactor.callLater(seconds, f, *args, **kwargs)`. + # Only positional arguments work across every reactor, and a typed + # attribute makes the type checker reject the positional call because + # `IReactorTime.callLater` is unbound, so `delay` is consumed as + # `self`. See https://github.com/pika/pika/pull/1678. + self._reactor: Any = custom_reactor or reactor self._transport: Optional[ twisted.internet.interfaces. ITransport] = None # to be provided by `connection_made()` @@ -1148,8 +1156,7 @@ """ check_callback_arg(callback, 'callback') - return _TimerHandle(self._reactor.callLater( - delay, callback)) # pyright: ignore[reportAttributeAccessIssue] + return _TimerHandle(self._reactor.callLater(delay, callback)) def _adapter_remove_timeout(self, timeout_id: Any) -> None: """Implement @@ -1165,8 +1172,7 @@ """ check_callback_arg(callback, 'callback') - self._reactor.callFromThread( - callback) # pyright: ignore[reportAttributeAccessIssue] + self._reactor.callFromThread(callback) def _adapter_connect_stream(self) -> None: """Implement pure virtual diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/pyproject.toml new/pika-1.4.4/pyproject.toml --- old/pika-1.4.1/pyproject.toml 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/pyproject.toml 2026-08-06 22:45:29.000000000 +0200 @@ -4,7 +4,7 @@ [project] name = "pika" -version = "1.4.1" +version = "1.4.4" maintainers = [ {name = "Gavin M. Roy", email = "[email protected]"}, {name = "Luke Bakken", email = "[email protected]"} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/acceptance/twisted_adapter_tests.py new/pika-1.4.4/tests/acceptance/twisted_adapter_tests.py --- old/pika-1.4.1/tests/acceptance/twisted_adapter_tests.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/tests/acceptance/twisted_adapter_tests.py 2026-08-06 22:45:29.000000000 +0200 @@ -828,15 +828,36 @@ self.conn._impl.close.assert_not_called() +class _AsyncioReactorSignatureStub: + """ + A reactor stub using `AsyncioSelectorReactor`'s parameter names. + + `AsyncioSelectorReactor.callLater(seconds, f, *args, **kwargs)` renames the parameters that + `IReactorTime.callLater(delay, callable, *args, **kw)` declares, so only positional arguments + work across every reactor. + """ + + def __init__(self): + self.call_later_args = [] + + def callLater(self, seconds, f, *args, **kwargs): + self.call_later_args.append((seconds, f, args, kwargs)) + return mock.Mock() + + class TwistedConnectionAdapterTestCase(TestCase): def setUp(self): self.conn = _TwistedConnectionAdapter(None, None, None, None, None) def tearDown(self): - if self.conn._transport is None: - self.conn._transport = mock.Mock() - self.conn.close() + self._close_adapter(self.conn) + + @staticmethod + def _close_adapter(conn): + if conn._transport is None: + conn._transport = mock.Mock() + conn.close() def test_adapter_disconnect_stream(self): # Verify that the underlying transport is aborted. @@ -861,6 +882,18 @@ self.assertNotIn(timer_id._handle, reactor.getDelayedCalls()) callback.assert_not_called() + def test_timeout_passes_positional_args(self): + # Verify that `callLater` is called positionally. The default reactor + # names its parameters `delay` and `callable`, so a keyword call passes + # here while crashing on `AsyncioSelectorReactor`, which names them + # `seconds` and `f`. + callback = mock.Mock() + stub = _AsyncioReactorSignatureStub() + conn = _TwistedConnectionAdapter(None, None, None, None, stub) + self.addCleanup(self._close_adapter, conn) + conn._adapter_call_later(5, callback) + self.assertEqual(stub.call_later_args, [(5, callback, (), {})]) + @pytest.mark.timeout(5) def test_call_threadsafe(self): # Verify that the method is actually called using the reactor's diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/base/async_test_base.py new/pika-1.4.4/tests/base/async_test_base.py --- old/pika-1.4.1/tests/base/async_test_base.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/tests/base/async_test_base.py 2026-08-06 22:45:29.000000000 +0200 @@ -370,7 +370,7 @@ @run_test_in_thread_with_timeout def test_with_asyncio(self): """AsyncioConnection""" - import asyncio from pika.adapters.asyncio_connection import AsyncioConnection - ioloop_factory = asyncio.new_event_loop + from tests.base.asyncio_loop import new_pika_asyncio_loop + ioloop_factory = new_pika_asyncio_loop self.start(AsyncioConnection, ioloop_factory) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/base/asyncio_loop.py new/pika-1.4.4/tests/base/asyncio_loop.py --- old/pika-1.4.1/tests/base/asyncio_loop.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pika-1.4.4/tests/base/asyncio_loop.py 2026-08-06 22:45:29.000000000 +0200 @@ -0,0 +1,19 @@ +"""Shared helper for creating asyncio event loops in tests.""" + +from __future__ import annotations + +import asyncio +import sys + + +def new_pika_asyncio_loop() -> asyncio.AbstractEventLoop: + """ + Create a new asyncio event loop suitable for Pika's AsyncioConnection. + + On Windows, Pika requires a `SelectorEventLoop` because it uses `add_reader`/`add_writer`, which + the default `ProactorEventLoop` does not support. Elsewhere, the default + `asyncio.new_event_loop()` is selector-based. + """ + if sys.platform == 'win32': + return asyncio.SelectorEventLoop() + return asyncio.new_event_loop() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/stubs/io_services_test_stubs.py new/pika-1.4.4/tests/stubs/io_services_test_stubs.py --- old/pika-1.4.1/tests/stubs/io_services_test_stubs.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/tests/stubs/io_services_test_stubs.py 2026-08-06 22:45:29.000000000 +0200 @@ -113,10 +113,10 @@ # Test entry point for `asyncio` event loop-based io services # implementation. - import asyncio from pika.adapters.asyncio_connection import (_AsyncioIOServicesAdapter) + from tests.base.asyncio_loop import new_pika_asyncio_loop - native_loop = asyncio.new_event_loop() + native_loop = new_pika_asyncio_loop() self._run_start( nbio_factory=lambda: _AsyncioIOServicesAdapter(native_loop), native_loop=native_loop) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/unit/asyncio_connection_tests.py new/pika-1.4.4/tests/unit/asyncio_connection_tests.py --- old/pika-1.4.1/tests/unit/asyncio_connection_tests.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/tests/unit/asyncio_connection_tests.py 2026-08-06 22:45:29.000000000 +0200 @@ -5,8 +5,10 @@ import asyncio import threading import unittest +from unittest.mock import patch from pika.adapters.asyncio_connection import _AsyncioIOServicesAdapter +from tests.base.asyncio_loop import new_pika_asyncio_loop # missing-docstring # pylint: disable=C0111 @@ -19,7 +21,7 @@ """Tests for _AsyncioIOServicesAdapter loop initialisation logic.""" def test_explicit_loop_is_used_as_is(self): - loop = asyncio.new_event_loop() + loop = new_pika_asyncio_loop() try: adapter = _AsyncioIOServicesAdapter(loop) self.assertIs(adapter.get_native_ioloop(), loop) @@ -33,7 +35,7 @@ adapter = _AsyncioIOServicesAdapter() results.append(adapter.get_native_ioloop()) - loop = asyncio.new_event_loop() + loop = new_pika_asyncio_loop() try: loop.run_until_complete(_run()) self.assertIs(results[0], loop) @@ -69,3 +71,51 @@ self.assertEqual(errors, []) self.assertEqual(len(results), 1) self.assertIsInstance(results[0], asyncio.AbstractEventLoop) + + @patch('pika.adapters.asyncio_connection.sys') + def test_windows_proactor_loop_raises_type_error(self, mock_sys): + mock_sys.platform = 'win32' + mock_sys.version_info = (3, 12) + if not hasattr(asyncio, 'ProactorEventLoop'): + self.skipTest('ProactorEventLoop not available on this OS') + loop = asyncio.ProactorEventLoop() + try: + with self.assertRaises(TypeError) as ctx: + _AsyncioIOServicesAdapter(loop) + self.assertIn('SelectorEventLoop', str(ctx.exception)) + finally: + loop.close() + + @patch('pika.adapters.asyncio_connection.sys') + def test_windows_selector_loop_accepted(self, mock_sys): + mock_sys.platform = 'win32' + mock_sys.version_info = (3, 12) + loop = asyncio.SelectorEventLoop() + try: + adapter = _AsyncioIOServicesAdapter(loop) + self.assertIs(adapter.get_native_ioloop(), loop) + finally: + loop.close() + + @patch('pika.adapters.asyncio_connection.sys') + def test_windows_no_loop_creates_selector_event_loop(self, mock_sys): + mock_sys.platform = 'win32' + mock_sys.version_info = (3, 12) + results = [] + errors = [] + + def _thread_target(): + try: + adapter = _AsyncioIOServicesAdapter() + results.append(adapter.get_native_ioloop()) + except Exception as exc: + errors.append(exc) + + t = threading.Thread(target=_thread_target) + t.start() + t.join() + + self.assertEqual(errors, []) + self.assertEqual(len(results), 1) + self.assertIsInstance(results[0], asyncio.SelectorEventLoop) + results[0].close() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pika-1.4.1/tests/unit/io_services_test_stubs_test.py new/pika-1.4.4/tests/unit/io_services_test_stubs_test.py --- old/pika-1.4.1/tests/unit/io_services_test_stubs_test.py 2026-05-22 19:39:45.000000000 +0200 +++ new/pika-1.4.4/tests/unit/io_services_test_stubs_test.py 2026-08-06 22:45:29.000000000 +0200 @@ -2,8 +2,6 @@ Test for io_services_test_stubs.py """ -import pika.compat - try: import asyncio except ImportError: @@ -15,7 +13,7 @@ import tornado.ioloop from pika.adapters import select_connection - +from tests.base.asyncio_loop import new_pika_asyncio_loop from tests.stubs.io_services_test_stubs import IOServicesTestStubs # Suppress invalid-name, since our test names are descriptive and quite long @@ -26,9 +24,7 @@ # pylint: disable=C0111 if asyncio is not None: - if pika.compat.ON_WINDOWS: - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - loop = asyncio.new_event_loop() + loop = new_pika_asyncio_loop() asyncio.set_event_loop(loop) else: loop = None @@ -90,6 +86,16 @@ self.assertNotEqual(threading.current_thread().ident, self._runner_thread_id) - # And make sure the loop actually works using this rudimentary test - nbio.add_callback_threadsafe(nbio.stop) + # And make sure the loop actually works using this rudimentary test. + # + # NOTE: stop is scheduled two turns out rather than one. On Windows, + # Tornado wraps the Proactor loop in an AddThreadSelectorEventLoop + # whose SelectorThread defers starting (and thus awaiting its + # `thread_manager_anext` bootstrap task) until the loop's first turn. + # Stopping after a single turn destroys that task while still pending, + # producing a "coroutine was never awaited" RuntimeWarning. Letting the + # loop run one extra turn lets the task start so close() tears it down + # cleanly. + nbio.add_callback_threadsafe( + lambda: nbio.add_callback_threadsafe(nbio.stop)) nbio.run()
