Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-Pebble for openSUSE:Factory checked in at 2025-08-15 21:52:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Pebble (Old) and /work/SRC/openSUSE:Factory/.python-Pebble.new.1085 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Pebble" Fri Aug 15 21:52:29 2025 rev:19 rq:1299497 version:5.1.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Pebble/python-Pebble.changes 2025-04-24 17:27:56.099431093 +0200 +++ /work/SRC/openSUSE:Factory/.python-Pebble.new.1085/python-Pebble.changes 2025-08-15 21:53:59.664945075 +0200 @@ -1,0 +2,10 @@ +Fri Aug 15 02:17:49 UTC 2025 - Steve Kowalik <steven.kowa...@suse.com> + +- Update to 5.1.3: + * issue #152: fix crash when scheduling non copy-able functions + * issue #101: allow `atexit.register` callbacks on pool shutdown + * Fix deadlock with `waitforthreads` function + * Fix bug causing threads to crash when using `waitforthreads` + function + +------------------------------------------------------------------- Old: ---- pebble-5.1.1.tar.gz New: ---- pebble-5.1.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Pebble.spec ++++++ --- /var/tmp/diff_new_pack.kiuFZK/_old 2025-08-15 21:54:00.156965535 +0200 +++ /var/tmp/diff_new_pack.kiuFZK/_new 2025-08-15 21:54:00.160965700 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-Pebble # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,19 +18,19 @@ %{?sle15_python_module_pythons} Name: python-Pebble -Version: 5.1.1 +Version: 5.1.3 Release: 0 Summary: Threading and multiprocessing eye-candy for Python License: LGPL-3.0-only URL: https://github.com/noxdafox/pebble Source: https://files.pythonhosted.org/packages/source/p/pebble/pebble-%{version}.tar.gz +BuildRequires: %{python_module base >= 3.8} BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: git-core BuildRequires: python-rpm-macros -BuildRequires: python3-base >= 3.7 BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module pytest} @@ -52,11 +52,12 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} %check -%pytest +# Seems to actually deadlock +%pytest -k 'not test_pool_deadlock_stop_cancel' %files %{python_files} %doc README.rst %license LICENSE %{python_sitelib}/pebble -%{python_sitelib}/[Pp]ebble-%{version}* +%{python_sitelib}/[Pp]ebble-%{version}.dist-info ++++++ pebble-5.1.1.tar.gz -> pebble-5.1.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/PKG-INFO new/pebble-5.1.3/PKG-INFO --- old/pebble-5.1.1/PKG-INFO 2025-03-16 17:17:08.963711700 +0100 +++ new/pebble-5.1.3/PKG-INFO 2025-07-30 23:14:44.652518000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.2 Name: Pebble -Version: 5.1.1 +Version: 5.1.3 Summary: Threading and multiprocessing eye-candy. Home-page: https://github.com/noxdafox/pebble Author: Matteo Cafasso diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/Pebble.egg-info/PKG-INFO new/pebble-5.1.3/Pebble.egg-info/PKG-INFO --- old/pebble-5.1.1/Pebble.egg-info/PKG-INFO 2025-03-16 17:17:08.000000000 +0100 +++ new/pebble-5.1.3/Pebble.egg-info/PKG-INFO 2025-07-30 23:14:44.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.2 Name: Pebble -Version: 5.1.1 +Version: 5.1.3 Summary: Threading and multiprocessing eye-candy. Home-page: https://github.com/noxdafox/pebble Author: Matteo Cafasso diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/pebble/__init__.py new/pebble-5.1.3/pebble/__init__.py --- old/pebble-5.1.1/pebble/__init__.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/pebble/__init__.py 2025-07-30 23:13:34.000000000 +0200 @@ -1,5 +1,5 @@ __author__ = 'Matteo Cafasso' -__version__ = '5.1.1' +__version__ = '5.1.3' __license__ = 'LGPL' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/pebble/functions.py new/pebble-5.1.3/pebble/functions.py --- old/pebble-5.1.1/pebble/functions.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/pebble/functions.py 2025-07-23 22:16:02.000000000 +0200 @@ -20,8 +20,6 @@ from types import MethodType from typing import Callable, Optional -_waitforthreads_lock = threading.Lock() - def waitforqueues(queues: list, timeout: float = None) -> filter: """Waits for one or more *Queue* to be ready or until *timeout* expires. @@ -84,51 +82,51 @@ The function returns a list containing the ready *Threads*. """ - old_function = None - lock = threading.Condition(threading.Lock()) + old_get_ident = None + event = threading.Event() - def new_function(*args): - old_function(*args) - with lock: - lock.notify_all() + def new_get_ident(*args) -> int: + retval = old_get_ident(*args) + event.set() - old_function = prepare_threads(new_function) + return retval + + old_get_ident = prepare_threads(new_get_ident) try: - wait_threads(threads, lock, timeout) + wait_threads(threads, event, timeout) finally: - reset_threads(old_function) + reset_threads(old_get_ident) return filter(lambda t: not t.is_alive(), threads) -def prepare_threads(new_function: Callable) -> Callable: - """Replaces threading._get_ident() function in order to notify +def prepare_threads(new_get_ident: Callable) -> Callable: + """Replaces threading.get_ident() function in order to notify the waiting Condition.""" - with _waitforthreads_lock: - old_function = threading.get_ident - threading.get_ident = new_function + with threading._active_limbo_lock: + old_get_ident = threading.get_ident + threading.get_ident = new_get_ident - return old_function + return old_get_ident def wait_threads(threads: list, - lock: threading.Condition, + event: threading.Event, timeout: Optional[float]): timestamp = time() - with lock: - while not any(map(lambda t: not t.is_alive(), threads)): - if timeout is None: - lock.wait() - elif timeout - (time() - timestamp) > 0: - lock.wait(timeout - (time() - timestamp)) - else: - return + while not any(map(lambda t: not t.is_alive(), threads)): + if timeout is None: + event.wait() + elif timeout - (time() - timestamp) > 0: + event.wait(timeout - (time() - timestamp)) + else: + return def reset_threads(old_function: Callable): """Resets original threading.get_ident() function.""" - with _waitforthreads_lock: + with threading._active_limbo_lock: threading.get_ident = old_function diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/pebble/pool/process.py new/pebble-5.1.3/pebble/pool/process.py --- old/pebble-5.1.1/pebble/pool/process.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/pebble/pool/process.py 2025-07-30 23:09:56.000000000 +0200 @@ -430,16 +430,20 @@ os._exit(1) try: - for task in worker_get_next_task(channel, params.max_tasks): - payload = task.payload - result = process_execute( - payload.function, *payload.args, **payload.kwargs) - send_result(channel, TaskResult(task.id, result)) + process_tasks(params, channel) except (OSError, RuntimeError) as error: - errno = getattr(error, 'errno', 1) - os._exit(errno if isinstance(errno, int) else 1) - except EOFError as error: - os._exit(0) + os._exit(getattr(error, 'errno', None) or 1) + except EOFError: # pipe closed on main loop + pass + + +def process_tasks(params: Worker, channel: WorkerChannel): + for task in worker_get_next_task(channel, params.max_tasks): + result = process_execute(task.payload.function, + *task.payload.args, + **task.payload.kwargs) + + send_result(channel, TaskResult(task.id, result)) def worker_get_next_task(channel: WorkerChannel, max_tasks: int): @@ -490,6 +494,7 @@ return [process_execute(function, *args) for args in chunk] +@atexit.register def interpreter_shutdown(): global GLOBAL_SHUTDOWN GLOBAL_SHUTDOWN = True @@ -510,9 +515,6 @@ pass -atexit.register(interpreter_shutdown) - - GLOBAL_SHUTDOWN = False WORKERS_NAME = 'pebble_pool_worker' PICKLING_ERRORS = AttributeError, pickle.PicklingError, TypeError diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/test/test_pebble.py new/pebble-5.1.3/test/test_pebble.py --- old/pebble-5.1.1/test/test_pebble.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/test/test_pebble.py 2025-07-23 22:02:58.000000000 +0200 @@ -144,15 +144,12 @@ """Waitforthreads get_ident is restored to original one.""" if hasattr(threading, 'get_ident'): expected = threading.get_ident - else: - expected = threading._get_ident + thread = launch_thread(None, thread_function, True, 0) time.sleep(0.01) waitforthreads([thread]) if hasattr(threading, 'get_ident'): self.assertEqual(threading.get_ident, expected) - else: - self.assertEqual(threading._get_ident, expected) def test_waitforthreads_spurious(self): """Waitforthreads tolerates spurious wakeups.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/test/test_process_pool_fork.py new/pebble-5.1.3/test/test_process_pool_fork.py --- old/pebble-5.1.1/test/test_process_pool_fork.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/test/test_process_pool_fork.py 2025-07-30 23:10:21.000000000 +0200 @@ -583,6 +583,13 @@ future = pool.schedule(pebble_function) self.assertEqual(future.result(), 1) + def test_process_pool_pickle_function(self): + """Process Pool Fork picklable functions.""" + queue = mp_context.Manager().Queue() + with ProcessPool(max_workers=1, context=mp_context) as pool: + pool.schedule(queue.put, args=[1]) + self.assertEqual(queue.get(timeout=1), 1) + @unittest.skipIf(not supported, "Start method is not supported") class TestAsyncIOProcessPool(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/test/test_process_pool_forkserver.py new/pebble-5.1.3/test/test_process_pool_forkserver.py --- old/pebble-5.1.1/test/test_process_pool_forkserver.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/test/test_process_pool_forkserver.py 2025-07-30 23:11:23.000000000 +0200 @@ -584,6 +584,13 @@ future = pool.schedule(pebble_function) self.assertEqual(future.result(), 1) + def test_process_pool_pickle_function(self): + """Process Pool Forkserver picklable functions.""" + queue = mp_context.Manager().Queue() + with ProcessPool(max_workers=1, context=mp_context) as pool: + pool.schedule(queue.put, args=[1]) + self.assertEqual(queue.get(timeout=1), 1) + @unittest.skipIf(not supported, "Start method is not supported") class TestAsyncIOProcessPool(unittest.TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pebble-5.1.1/test/test_process_pool_spawn.py new/pebble-5.1.3/test/test_process_pool_spawn.py --- old/pebble-5.1.1/test/test_process_pool_spawn.py 2025-03-16 17:17:02.000000000 +0100 +++ new/pebble-5.1.3/test/test_process_pool_spawn.py 2025-07-30 23:11:00.000000000 +0200 @@ -582,6 +582,13 @@ future = pool.schedule(pebble_function) self.assertEqual(future.result(), 1) + def test_process_pool_pickle_function(self): + """Process Pool Spawn picklable functions.""" + queue = mp_context.Manager().Queue() + with ProcessPool(max_workers=1, context=mp_context) as pool: + pool.schedule(queue.put, args=[1]) + self.assertEqual(queue.get(timeout=1), 1) + @unittest.skipIf(not supported, "Start method is not supported") class TestAsyncIOProcessPool(unittest.TestCase):