Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-billiard for openSUSE:Factory
checked in at 2026-09-28 10:44:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-billiard (Old)
and /work/SRC/openSUSE:Factory/.python-billiard.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-billiard"
Mon Sep 28 10:44:22 2026 rev:30 rq:1380726 version:4.3.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-billiard/python-billiard.changes
2026-06-27 18:07:05.477987292 +0200
+++
/work/SRC/openSUSE:Factory/.python-billiard.new.383539/python-billiard.changes
2026-09-28 10:44:35.693282609 +0200
@@ -1,0 +2,22 @@
+Fri Sep 25 20:33:32 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.3.0:
+ * Propagate MemoryError from pool callbacks
+ * Propagate `BaseException` raised in pool workers instead of
+ raising `WorkerLostError` (#427)
+ * Bind the closerange-based close_open_fds() on Python 3
+ * List the fd directory in close_open_fds() instead of walking
+ the limit (#456)
+ * Exit the pool worker after a task raises `SystemExit` or
+ `KeyboardInterrupt`
+ (for example the SIGTERM sent by a hard time limit) instead
+ of returning to the shared inqueue, which left the blocking
+ pool stalled once the worker was killed while holding the
+ inqueue lock (regression from #452)
+ * Fix pytest deprecation warning (#440)
+ * Make einfo compatible with inspect (fixes #176) (#392)
+ * Update CI workflow to use Python 3.10 and above (#441)
+ * Update tox.ini to remove older Python versions (#442)
+ * Fix macOS fork-safety crash: default to spawn on darwin
+
+-------------------------------------------------------------------
Old:
----
billiard-4.2.4.tar.gz
New:
----
billiard-4.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-billiard.spec ++++++
--- /var/tmp/diff_new_pack.74OE6Q/_old 2026-09-28 10:44:36.425313279 +0200
+++ /var/tmp/diff_new_pack.74OE6Q/_new 2026-09-28 10:44:36.427313362 +0200
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-billiard
-Version: 4.2.4
+Version: 4.3.0
Release: 0
Summary: Python multiprocessing fork
License: BSD-3-Clause
++++++ billiard-4.2.4.tar.gz -> billiard-4.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.cookiecutterrc
new/billiard-4.3.0/.cookiecutterrc
--- old/billiard-4.2.4/.cookiecutterrc 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.cookiecutterrc 1970-01-01 01:00:00.000000000 +0100
@@ -1,10 +0,0 @@
-default_context:
-
- email: '[email protected]'
- full_name: 'Ask Solem'
- github_username: 'celery'
- project_name: 'Billiard'
- project_short_description: 'Python multiprocessing fork with improvements
and bugfixes'
- project_slug: 'billiard'
- version: '1.0.0'
- year: '2009-2016'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.editorconfig
new/billiard-4.3.0/.editorconfig
--- old/billiard-4.2.4/.editorconfig 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.editorconfig 1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-# https://editorconfig.org/
-
-root = true
-
-[*]
-indent_style = space
-indent_size = 4
-trim_trailing_whitespace = true
-insert_final_newline = true
-charset = utf-8
-end_of_line = lf
-
-[Makefile]
-indent_style = tab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.github/dependabot.yml
new/billiard-4.3.0/.github/dependabot.yml
--- old/billiard-4.2.4/.github/dependabot.yml 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/.github/dependabot.yml 1970-01-01 01:00:00.000000000
+0100
@@ -1,13 +0,0 @@
-# Keep GitHub Actions up to date with GitHub's Dependabot...
-#
https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
-#
https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
-version: 2
-updates:
- - package-ecosystem: github-actions
- directory: /
- groups:
- github-actions:
- patterns:
- - "*" # Group all Actions updates into a single larger pull request
- schedule:
- interval: weekly
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.github/workflows/ci.yaml
new/billiard-4.3.0/.github/workflows/ci.yaml
--- old/billiard-4.2.4/.github/workflows/ci.yaml 2025-11-30
14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.github/workflows/ci.yaml 1970-01-01
01:00:00.000000000 +0100
@@ -1,25 +0,0 @@
-name: CI
-on: [pull_request, push]
-jobs:
- #################### Unittests ####################
- unittest:
- runs-on: blacksmith-4vcpu-ubuntu-2204
- strategy:
- fail-fast: false
- matrix:
- python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
- steps:
- - name: Install system packages
- run: sudo apt update && sudo apt-get install libcurl4-openssl-dev
libssl-dev
- - name: Check out code from GitHub
- uses: actions/checkout@v6
- - name: Set up Python ${{ matrix.python-version }}
- id: python
- uses: actions/setup-python@main
- with:
- python-version: ${{ matrix.python-version }}
- allow-prereleases: true
- - name: Install dependencies
- run: pip install --upgrade pip setuptools wheel tox tox-gh-actions
tox-docker
- - name: Run unittest
- run: tox -v -e ${{ matrix.python-version }}-unit -- -v
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.github/workflows/codeql-analysis.yml
new/billiard-4.3.0/.github/workflows/codeql-analysis.yml
--- old/billiard-4.2.4/.github/workflows/codeql-analysis.yml 2025-11-30
14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.github/workflows/codeql-analysis.yml 1970-01-01
01:00:00.000000000 +0100
@@ -1,70 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ main ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ main ]
- schedule:
- - cron: '40 0 * * 5'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: blacksmith-4vcpu-ubuntu-2204
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'python' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript',
'python', 'ruby' ]
- # Learn more about CodeQL language support at
https://git.io/codeql-language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a
config file.
- # By default, queries listed here will override any specified in a
config file.
- # Prefix the list here with "+" to use these queries and those in the
config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually
(see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v4
-
- # ℹ️ Command-line programs to run using the OS shell.
- # 📚 https://git.io/JvXDl
-
- # ✏️ If the Autobuild fails above, remove it and uncomment the following
three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.github/workflows/codespell.yml
new/billiard-4.3.0/.github/workflows/codespell.yml
--- old/billiard-4.2.4/.github/workflows/codespell.yml 2025-11-30
14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.github/workflows/codespell.yml 1970-01-01
01:00:00.000000000 +0100
@@ -1,25 +0,0 @@
-# Codespell configuration is within setup.cfg
----
-name: Codespell
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
-permissions:
- contents: read
-
-jobs:
- codespell:
- name: Check for spelling errors
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout
- uses: actions/checkout@v6
- - name: Annotate locations with typos
- uses: codespell-project/codespell-problem-matcher@v1
- - name: Codespell
- uses: codespell-project/actions-codespell@v2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.gitignore
new/billiard-4.3.0/.gitignore
--- old/billiard-4.2.4/.gitignore 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/.gitignore 1970-01-01 01:00:00.000000000 +0100
@@ -1,29 +0,0 @@
-.DS_Store
-*.pyc
-*~
-*.sqlite
-*.sqlite-journal
-settings_local.py
-local_settings.py
-.*.sw[po]
-dist/
-*.egg-info
-doc/__build/*
-build/
-locale/
-pip-log.txt
-devdatabase.db
-.directory
-bundle_version.gen
-celeryd.log
-celeryd.pid
-coverage.xml
-cover/
-*.so
-.tox/
-.eggs/
-htmlcov/
-.cache/
-.coverage
-.vscode/
-.python-version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/.pre-commit-config.yaml
new/billiard-4.3.0/.pre-commit-config.yaml
--- old/billiard-4.2.4/.pre-commit-config.yaml 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/.pre-commit-config.yaml 1970-01-01 01:00:00.000000000
+0100
@@ -1,13 +0,0 @@
-# Learn more about this config here: https://pre-commit.com/
-
-# To enable these pre-commit hooks run: `uv tool install pre-commit`
-# or `pipx install pre-commit` or `brew install pre-commit`
-# Then in the project root directory run `pre-commit install`
-
-repos:
- - repo: https://github.com/codespell-project/codespell
- rev: v2.4.1
- hooks:
- - id: codespell # See pyproject.toml for args
- additional_dependencies:
- - tomli
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/CHANGES.txt
new/billiard-4.3.0/CHANGES.txt
--- old/billiard-4.2.4/CHANGES.txt 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/CHANGES.txt 2026-09-17 10:30:48.366935500 +0200
@@ -1,3 +1,26 @@
+4.3.0 - 2026-09-26
+------------------
+- Propagate MemoryError from pool callbacks
+- Propagate `BaseException` raised in pool workers instead of raising
+ `WorkerLostError` (#427)
+- Bind the closerange-based close_open_fds() on Python 3
+- List the fd directory in close_open_fds() instead of walking the limit (#456)
+- Exit the pool worker after a task raises `SystemExit` or `KeyboardInterrupt`
+ (for example the SIGTERM sent by a hard time limit) instead of returning to
+ the shared inqueue, which left the blocking pool stalled once the worker was
+ killed while holding the inqueue lock (regression from #452)
+
+
+4.3.0rc1 - 2026-02-26
+--------------------
+- Fix pytest deprecation warning (#440)
+- Make einfo compatible with inspect (fixes #176) (#392)
+- Update CI workflow to use Python 3.10 and above (#441)
+- Update tox.ini to remove older Python versions (#442)
+- Fix macOS fork-safety crash: default to spawn on darwin (#443)
+- Prepare for (pre) release: v4.3.0rc1 (#445)
+
+
4.2.4 - 2025-11-30
--------------------
- Eliminate usage of 'return' in 'finally' blocks (#438)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/PKG-INFO new/billiard-4.3.0/PKG-INFO
--- old/billiard-4.2.4/PKG-INFO 1970-01-01 01:00:00.000000000 +0100
+++ new/billiard-4.3.0/PKG-INFO 2026-09-17 10:35:43.591227800 +0200
@@ -0,0 +1,130 @@
+Metadata-Version: 2.4
+Name: billiard
+Version: 4.3.0
+Summary: Python multiprocessing fork with improvements and bugfixes
+Home-page: https://github.com/celery/billiard
+Author: R Oudkerk / Python Software Foundation
+Author-email: [email protected]
+Maintainer: Asif Saif Uddin
+Maintainer-email: [email protected]
+License: BSD
+Keywords: multiprocessing pool process
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Distributed Computing
+Requires-Python: >=3.10
+License-File: LICENSE.txt
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: maintainer
+Dynamic: maintainer-email
+Dynamic: requires-python
+Dynamic: summary
+
+========
+billiard
+========
+
+|build-status-lin| |build-status-win| |license| |wheel| |pyversion| |pyimp|
+
+:Version: 4.3.0
+:Web: https://billiard.readthedocs.io
+:Download: https://pypi.org/project/billiard/
+:Source: https://github.com/celery/billiard/
+:DeepWiki: |deepwiki|
+
+.. |build-status-lin| image::
https://github.com/celery/billiard/actions/workflows/ci.yaml/badge.svg
+ :alt: Build status on Linux
+ :target: https://github.com/celery/billiard/actions/workflows/ci.yaml
+
+.. |build-status-win| image::
https://ci.appveyor.com/api/projects/status/github/celery/billiard?png=true&branch=main
+ :alt: Build status on Windows
+ :target: https://ci.appveyor.com/project/ask/billiard
+
+.. |license| image:: https://img.shields.io/pypi/l/billiard.svg
+ :alt: BSD License
+ :target: https://opensource.org/licenses/BSD-3-Clause
+
+.. |wheel| image:: https://img.shields.io/pypi/wheel/billiard.svg
+ :alt: Billiard can be installed via wheel
+ :target: https://pypi.org/project/billiard/
+
+.. |pyversion| image:: https://img.shields.io/pypi/pyversions/billiard.svg
+ :alt: Supported Python versions.
+ :target: https://pypi.org/project/billiard/
+
+.. |pyimp| image:: https://img.shields.io/pypi/implementation/billiard.svg
+ :alt: Support Python implementations.
+ :target: https://pypi.org/project/billiard/
+
+.. |deepwiki| image:: https://devin.ai/assets/deepwiki-badge.png
+ :alt: Ask http://DeepWiki.com
+ :target: https://deepwiki.com/celery/billiard
+ :width: 125px
+
+About
+-----
+
+``billiard`` is a fork of the Python 2.7 `multiprocessing
<https://docs.python.org/library/multiprocessing.html>`_
+package. The multiprocessing package itself is a renamed and updated version of
+R Oudkerk's `pyprocessing <https://pypi.org/project/processing/>`_ package.
+This standalone variant draws its fixes/improvements from python-trunk and
provides
+additional bug fixes and improvements.
+
+- This package would not be possible if not for the contributions of not only
+ the current maintainers but all of the contributors to the original
pyprocessing
+ package listed `here <http://pyprocessing.berlios.de/doc/THANKS.html>`_.
+
+- Also, it is a fork of the multiprocessing backport package by Christian
Heims.
+
+- It includes the no-execv patch contributed by R. Oudkerk.
+
+- And the Pool improvements previously located in `Celery`_.
+
+- Billiard is used in and is a dependency for `Celery`_ and is maintained by
the
+ Celery team.
+
+.. _`Celery`: http://celeryproject.org
+
+Documentation
+-------------
+
+The documentation for ``billiard`` is available on `Read the Docs
<https://billiard.readthedocs.io>`_.
+
+Bug reporting
+-------------
+
+Please report bugs related to multiprocessing at the
+`Python bug tracker <https://bugs.python.org/>`_. Issues related to billiard
+should be reported at https://github.com/celery/billiard/issues.
+
+billiard is part of the Tidelift Subscription
+---------------------------------------------
+
+The maintainers of ``billiard`` and thousands of other packages are working
+with Tidelift to deliver commercial support and maintenance for the open source
+dependencies you use to build your applications. Save time, reduce risk, and
+improve code health, while paying the maintainers of the exact dependencies you
+use. `Learn more`_.
+
+.. _`Learn more`:
https://tidelift.com/subscription/pkg/pypi-billiard?utm_source=pypi-billiard&utm_medium=referral&utm_campaign=readme&utm_term=repo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/README.rst
new/billiard-4.3.0/README.rst
--- old/billiard-4.2.4/README.rst 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/README.rst 2026-09-17 10:30:48.367270500 +0200
@@ -4,7 +4,7 @@
|build-status-lin| |build-status-win| |license| |wheel| |pyversion| |pyimp|
-:Version: 4.2.4
+:Version: 4.3.0
:Web: https://billiard.readthedocs.io
:Download: https://pypi.org/project/billiard/
:Source: https://github.com/celery/billiard/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/SECURITY.md
new/billiard-4.3.0/SECURITY.md
--- old/billiard-4.2.4/SECURITY.md 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/SECURITY.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-# Security Policy
-
-## Supported Versions
-
-
-| Version | Supported |
-| ------- | ------------------ |
-| 3.6.x | :white_check_mark: |
-| 3.5.x | :x: |
-| 4.0.x | :white_check_mark: |
-| < 3.6 | :x: |
-
-## Reporting a Vulnerability
-
-Please contact [email protected] for reporting Vulnerability issues.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard/__init__.py
new/billiard-4.3.0/billiard/__init__.py
--- old/billiard-4.2.4/billiard/__init__.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/billiard/__init__.py 2026-09-17 10:30:48.367686700
+0200
@@ -18,12 +18,12 @@
#
+import re
import sys
from . import context
-VERSION = (4, 2, 4)
-__version__ = '.'.join(map(str, VERSION[0:4])) + "".join(VERSION[4:])
+__version__ = '4.3.0'
__author__ = 'R Oudkerk / Python Software Foundation'
__author_email__ = '[email protected]'
__maintainer__ = 'Asif Saif Uddin'
@@ -33,6 +33,14 @@
# -eof meta-
+# bumpversion can only search for {current_version}
+# so we have to parse the version here.
+_temp = re.match(
+ r'(\d+)\.(\d+)\.(\d+)(.+)?', __version__).groups()
+VERSION = (int(_temp[0]), int(_temp[1]), int(_temp[2]), _temp[3] or '')
+del _temp
+del re
+
#
# Copy stuff from default context
#
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard/compat.py
new/billiard-4.3.0/billiard/compat.py
--- old/billiard-4.2.4/billiard/compat.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/billiard/compat.py 2026-09-17 08:52:08.068636400
+0200
@@ -1,4 +1,3 @@
-import errno
import numbers
import os
import subprocess
@@ -118,39 +117,67 @@
return (seen.add(obj) or obj for obj in it if obj not in seen)
-try:
- closerange = os.closerange
-except AttributeError:
+# Directory listing the descriptors open in the calling process. Same
+# choice as CPython's FD_DIR (Modules/_posixsubprocess.c, Cygwin added in
+# gh-148575): /dev/fd on macOS, Cygwin, FreeBSD and DragonFly, /proc/self/fd
+# elsewhere. On FreeBSD and DragonFly it is only trusted when fdescfs is
+# mounted, see _open_fds().
+_BSD_WITH_FDESCFS = ('freebsd', 'dragonfly')
+if sys.platform.startswith(('cygwin', 'darwin') + _BSD_WITH_FDESCFS):
+ _FD_DIR = '/dev/fd'
+else:
+ _FD_DIR = '/proc/self/fd'
+
+
+def _dev_fd_is_fdescfs():
+ # devfs alone creates only /dev/fd/0-2, while fdescfs creates entries for
+ # every descriptor the process has open. Same check as CPython's
+ # _is_fdescfs_mounted_on_dev_fd().
+ try:
+ return os.stat('/dev').st_dev != os.stat(_FD_DIR).st_dev
+ except OSError:
+ return False
- def closerange(fd_low, fd_high): # noqa
- for fd in reversed(range(fd_low, fd_high)):
+
+def _open_fds():
+ """Return the descriptors open in this process, or None.
+
+ None means this platform has no directory listing them, and the caller
+ has to fall back to a numeric range.
+ """
+ if sys.platform.startswith(_BSD_WITH_FDESCFS) and not _dev_fd_is_fdescfs():
+ return None
+ try:
+ names = os.listdir(_FD_DIR)
+ except OSError:
+ return None
+ return sorted(int(name) for name in names if name.isdigit())
+
+
+def close_open_fds(keep=None):
+ # must make sure this is 0-inclusive (Issue #celery/1882)
+ keep = list(uniq(sorted(
+ f for f in map(maybe_fileno, keep or []) if f is not None
+ )))
+ fds = _open_fds()
+ if fds is not None:
+ # Only the descriptors actually open are touched, so the cost does
+ # not depend on RLIMIT_NOFILE (celery/celery#9886).
+ keep = set(keep)
+ for fd in fds:
+ if fd in keep:
+ continue
try:
os.close(fd)
- except OSError as exc:
- if exc.errno != errno.EBADF:
- raise
-
- def close_open_fds(keep=None):
- # must make sure this is 0-inclusive (Issue #celery/1882)
- keep = list(uniq(sorted(
- f for f in map(maybe_fileno, keep or []) if f is not None
- )))
- maxfd = get_fdmax(default=2048)
- kL, kH = iter([-1] + keep), iter(keep + [maxfd])
- for low, high in zip_longest(kL, kH):
- if low + 1 != high:
- closerange(low + 1, high)
-else:
- def close_open_fds(keep=None): # noqa
- keep = [maybe_fileno(f)
- for f in (keep or []) if maybe_fileno(f) is not None]
- for fd in reversed(range(get_fdmax(default=2048))):
- if fd not in keep:
- try:
- os.close(fd)
- except OSError as exc:
- if exc.errno != errno.EBADF:
- raise
+ except OSError:
+ # Same as os.closerange() below: closing is best effort.
+ pass
+ return
+ maxfd = get_fdmax(default=2048)
+ kL, kH = iter([-1] + keep), iter(keep + [maxfd])
+ for low, high in zip_longest(kL, kH):
+ if low + 1 != high:
+ os.closerange(low + 1, high)
def get_errno(exc):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard/context.py
new/billiard-4.3.0/billiard/context.py
--- old/billiard-4.2.4/billiard/context.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/billiard/context.py 2026-09-17 08:52:08.068911800
+0200
@@ -368,7 +368,14 @@
'spawn': SpawnContext(),
'forkserver': ForkServerContext(),
}
- _default_context = DefaultContext(_concrete_contexts['fork'])
+ if sys.platform == 'darwin':
+ # bpo-33725: running arbitrary code after fork() is no longer
+ # reliable on macOS since macOS 10.14 (Mojave). Use spawn by
+ # default instead.
+ # See https://github.com/celery/celery/issues/9894
+ _default_context = DefaultContext(_concrete_contexts['spawn'])
+ else:
+ _default_context = DefaultContext(_concrete_contexts['fork'])
else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard/einfo.py
new/billiard-4.3.0/billiard/einfo.py
--- old/billiard-4.2.4/billiard/einfo.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/billiard/einfo.py 2026-09-17 08:52:08.069211200
+0200
@@ -1,5 +1,6 @@
import sys
import traceback
+import types
__all__ = ['ExceptionInfo', 'Traceback']
@@ -26,6 +27,13 @@
self.co_qualname = code.co_qualname
self._co_positions = list(code.co_positions())
+ @property
+ def __class__(self):
+ return types.CodeType
+
+ def __reduce__(self):
+ return _Code.__new__, (_Code,), self.__dict__
+
if sys.version_info >= (3, 11):
@property
def co_positions(self):
@@ -58,6 +66,13 @@
# don't want to hit https://bugs.python.org/issue21967
self.f_restricted = False
+ @property
+ def __class__(self):
+ return types.FrameType
+
+ def __reduce__(self):
+ return _Frame.__new__, (_Frame,), self.__dict__
+
if sys.version_info >= (3, 11):
@property
def co_positions(self):
@@ -100,6 +115,13 @@
self.tb_next = None
self.tb_lasti = 0
+ @property
+ def __class__(self):
+ return types.TracebackType
+
+ def __reduce__(self):
+ return _Truncated.__new__, (_Truncated,), self.__dict__
+
if sys.version_info >= (3, 11):
@property
def co_positions(self):
@@ -120,6 +142,13 @@
else:
self.tb_next = _Truncated()
+ @property
+ def __class__(self):
+ return types.TracebackType
+
+ def __reduce__(self):
+ return Traceback.__new__, (Traceback,), self.__dict__
+
class RemoteTraceback(Exception):
def __init__(self, tb):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard/pool.py
new/billiard-4.3.0/billiard/pool.py
--- old/billiard-4.2.4/billiard/pool.py 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/billiard/pool.py 2026-09-17 08:52:08.069891700 +0200
@@ -358,10 +358,16 @@
confirm = wait_for_syn(job)
if not confirm:
continue # received NACK
+ exit_exc = None
try:
result = (True, prepare_result(fun(*args, **kwargs)))
- except Exception:
+ except BaseException as exc:
result = (False, ExceptionInfo())
+ if isinstance(exc, (SystemExit, KeyboardInterrupt)):
+ # The worker was told to exit (e.g. SIGTERM from a
+ # hard time limit): report the result, then leave
+ # instead of re-entering the shared inqueue.
+ exit_exc = exc
try:
put((READY, (job, i, result, inqW_fd)))
except Exception as exc:
@@ -375,6 +381,8 @@
finally:
del(tb)
completed += 1
+ if exit_exc is not None:
+ raise exit_exc
if max_memory_per_child > 0:
used_kb = mem_rss()
if used_kb <= 0:
@@ -1797,6 +1805,8 @@
if fun:
try:
fun(*args, **kwargs)
+ except MemoryError:
+ raise
except self._callbacks_propagate:
raise
except Exception as exc:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard.egg-info/PKG-INFO
new/billiard-4.3.0/billiard.egg-info/PKG-INFO
--- old/billiard-4.2.4/billiard.egg-info/PKG-INFO 1970-01-01
01:00:00.000000000 +0100
+++ new/billiard-4.3.0/billiard.egg-info/PKG-INFO 2026-09-17
10:35:43.561669800 +0200
@@ -0,0 +1,130 @@
+Metadata-Version: 2.4
+Name: billiard
+Version: 4.3.0
+Summary: Python multiprocessing fork with improvements and bugfixes
+Home-page: https://github.com/celery/billiard
+Author: R Oudkerk / Python Software Foundation
+Author-email: [email protected]
+Maintainer: Asif Saif Uddin
+Maintainer-email: [email protected]
+License: BSD
+Keywords: multiprocessing pool process
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Distributed Computing
+Requires-Python: >=3.10
+License-File: LICENSE.txt
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: maintainer
+Dynamic: maintainer-email
+Dynamic: requires-python
+Dynamic: summary
+
+========
+billiard
+========
+
+|build-status-lin| |build-status-win| |license| |wheel| |pyversion| |pyimp|
+
+:Version: 4.3.0
+:Web: https://billiard.readthedocs.io
+:Download: https://pypi.org/project/billiard/
+:Source: https://github.com/celery/billiard/
+:DeepWiki: |deepwiki|
+
+.. |build-status-lin| image::
https://github.com/celery/billiard/actions/workflows/ci.yaml/badge.svg
+ :alt: Build status on Linux
+ :target: https://github.com/celery/billiard/actions/workflows/ci.yaml
+
+.. |build-status-win| image::
https://ci.appveyor.com/api/projects/status/github/celery/billiard?png=true&branch=main
+ :alt: Build status on Windows
+ :target: https://ci.appveyor.com/project/ask/billiard
+
+.. |license| image:: https://img.shields.io/pypi/l/billiard.svg
+ :alt: BSD License
+ :target: https://opensource.org/licenses/BSD-3-Clause
+
+.. |wheel| image:: https://img.shields.io/pypi/wheel/billiard.svg
+ :alt: Billiard can be installed via wheel
+ :target: https://pypi.org/project/billiard/
+
+.. |pyversion| image:: https://img.shields.io/pypi/pyversions/billiard.svg
+ :alt: Supported Python versions.
+ :target: https://pypi.org/project/billiard/
+
+.. |pyimp| image:: https://img.shields.io/pypi/implementation/billiard.svg
+ :alt: Support Python implementations.
+ :target: https://pypi.org/project/billiard/
+
+.. |deepwiki| image:: https://devin.ai/assets/deepwiki-badge.png
+ :alt: Ask http://DeepWiki.com
+ :target: https://deepwiki.com/celery/billiard
+ :width: 125px
+
+About
+-----
+
+``billiard`` is a fork of the Python 2.7 `multiprocessing
<https://docs.python.org/library/multiprocessing.html>`_
+package. The multiprocessing package itself is a renamed and updated version of
+R Oudkerk's `pyprocessing <https://pypi.org/project/processing/>`_ package.
+This standalone variant draws its fixes/improvements from python-trunk and
provides
+additional bug fixes and improvements.
+
+- This package would not be possible if not for the contributions of not only
+ the current maintainers but all of the contributors to the original
pyprocessing
+ package listed `here <http://pyprocessing.berlios.de/doc/THANKS.html>`_.
+
+- Also, it is a fork of the multiprocessing backport package by Christian
Heims.
+
+- It includes the no-execv patch contributed by R. Oudkerk.
+
+- And the Pool improvements previously located in `Celery`_.
+
+- Billiard is used in and is a dependency for `Celery`_ and is maintained by
the
+ Celery team.
+
+.. _`Celery`: http://celeryproject.org
+
+Documentation
+-------------
+
+The documentation for ``billiard`` is available on `Read the Docs
<https://billiard.readthedocs.io>`_.
+
+Bug reporting
+-------------
+
+Please report bugs related to multiprocessing at the
+`Python bug tracker <https://bugs.python.org/>`_. Issues related to billiard
+should be reported at https://github.com/celery/billiard/issues.
+
+billiard is part of the Tidelift Subscription
+---------------------------------------------
+
+The maintainers of ``billiard`` and thousands of other packages are working
+with Tidelift to deliver commercial support and maintenance for the open source
+dependencies you use to build your applications. Save time, reduce risk, and
+improve code health, while paying the maintainers of the exact dependencies you
+use. `Learn more`_.
+
+.. _`Learn more`:
https://tidelift.com/subscription/pkg/pypi-billiard?utm_source=pypi-billiard&utm_medium=referral&utm_campaign=readme&utm_term=repo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard.egg-info/SOURCES.txt
new/billiard-4.3.0/billiard.egg-info/SOURCES.txt
--- old/billiard-4.2.4/billiard.egg-info/SOURCES.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/billiard-4.3.0/billiard.egg-info/SOURCES.txt 2026-09-17
10:35:43.569574000 +0200
@@ -0,0 +1,76 @@
+CHANGES.txt
+INSTALL.txt
+LICENSE.txt
+MANIFEST.in
+Makefile
+README.rst
+setup.cfg
+setup.py
+Doc/conf.py
+Doc/glossary.rst
+Doc/index.rst
+Doc/includes/__init__.py
+Doc/includes/mp_benchmarks.py
+Doc/includes/mp_newtype.py
+Doc/includes/mp_pool.py
+Doc/includes/mp_synchronize.py
+Doc/includes/mp_webserver.py
+Doc/includes/mp_workers.py
+Doc/library/multiprocessing.rst
+Modules/_billiard/multiprocessing.c
+Modules/_billiard/multiprocessing.h
+Modules/_billiard/semaphore.c
+Modules/_billiard/win32_functions.c
+billiard/__init__.py
+billiard/_ext.py
+billiard/_win.py
+billiard/common.py
+billiard/compat.py
+billiard/connection.py
+billiard/context.py
+billiard/einfo.py
+billiard/exceptions.py
+billiard/forkserver.py
+billiard/heap.py
+billiard/managers.py
+billiard/pool.py
+billiard/popen_fork.py
+billiard/popen_forkserver.py
+billiard/popen_spawn_posix.py
+billiard/popen_spawn_win32.py
+billiard/process.py
+billiard/queues.py
+billiard/reduction.py
+billiard/resource_sharer.py
+billiard/semaphore_tracker.py
+billiard/sharedctypes.py
+billiard/spawn.py
+billiard/synchronize.py
+billiard/util.py
+billiard.egg-info/PKG-INFO
+billiard.egg-info/SOURCES.txt
+billiard.egg-info/dependency_links.txt
+billiard.egg-info/not-zip-safe
+billiard.egg-info/top_level.txt
+billiard/dummy/__init__.py
+billiard/dummy/connection.py
+requirements/pkgutils.txt
+requirements/test-ci.txt
+requirements/test.txt
+t/__init__.py
+t/skip.py
+t/integration/__init__.py
+t/integration/setup.py
+t/integration/tests/__init__.py
+t/integration/tests/test_multiprocessing.py
+t/unit/__init__.py
+t/unit/test_common.py
+t/unit/test_compat.py
+t/unit/test_context.py
+t/unit/test_dummy.py
+t/unit/test_einfo.py
+t/unit/test_package.py
+t/unit/test_pool.py
+t/unit/test_spawn.py
+t/unit/test_values.py
+t/unit/test_win32.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/billiard-4.2.4/billiard.egg-info/dependency_links.txt
new/billiard-4.3.0/billiard.egg-info/dependency_links.txt
--- old/billiard-4.2.4/billiard.egg-info/dependency_links.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/billiard-4.3.0/billiard.egg-info/dependency_links.txt 2026-09-17
10:35:43.562014600 +0200
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard.egg-info/not-zip-safe
new/billiard-4.3.0/billiard.egg-info/not-zip-safe
--- old/billiard-4.2.4/billiard.egg-info/not-zip-safe 1970-01-01
01:00:00.000000000 +0100
+++ new/billiard-4.3.0/billiard.egg-info/not-zip-safe 2026-09-17
10:35:43.423162700 +0200
@@ -0,0 +1 @@
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/billiard.egg-info/top_level.txt
new/billiard-4.3.0/billiard.egg-info/top_level.txt
--- old/billiard-4.2.4/billiard.egg-info/top_level.txt 1970-01-01
01:00:00.000000000 +0100
+++ new/billiard-4.3.0/billiard.egg-info/top_level.txt 2026-09-17
10:35:43.562198200 +0200
@@ -0,0 +1 @@
+billiard
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/setup.cfg new/billiard-4.3.0/setup.cfg
--- old/billiard-4.2.4/setup.cfg 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/setup.cfg 2026-09-17 10:35:43.591632100 +0200
@@ -3,8 +3,6 @@
python_classes = test_*
[flake8]
-# classes can be lowercase, arguments and variables can be uppercase
-# whenever it makes the code more readable.
ignore = N806, N802, N801, N803, E305
[pep257]
@@ -14,8 +12,11 @@
license_file = LICENSE.txt
[codespell]
-# Ref: https://github.com/codespell-project/codespell#using-a-config-file
skip = .git*
check-hidden = true
-# ignore-regex =
ignore-words-list = assertin
+
+[egg_info]
+tag_build =
+tag_date = 0
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/setup.py new/billiard-4.3.0/setup.py
--- old/billiard-4.2.4/setup.py 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/setup.py 2026-09-17 10:30:48.368135200 +0200
@@ -46,7 +46,6 @@
import re
re_meta = re.compile(r'__(\w+?)__\s*=\s*(.*)')
-re_vers = re.compile(r'VERSION\s*=\s*\((.*?)\)')
re_doc = re.compile(r'^"""(.+?)"""')
rq = lambda s: s.strip("\"'")
@@ -56,16 +55,10 @@
return ((attr_name, rq(attr_value)), )
-def add_version(m):
- v = list(map(rq, m.groups()[0].split(', ')))
- return (('VERSION', '.'.join(v[0:4]) + ''.join(v[4:])), )
-
-
def add_doc(m):
return (('doc', m.groups()[0]), )
pats = {re_meta: add_default,
- re_vers: add_version,
re_doc: add_doc}
here = os.path.abspath(os.path.dirname(__file__))
meta_fh = open(os.path.join(here, 'billiard/__init__.py'))
@@ -82,8 +75,8 @@
meta_fh.close()
-if sys.version_info < (3, 7):
- raise ValueError('Versions of Python before 3.7 are not supported')
+if sys.version_info < (3, 10):
+ raise ValueError('Versions of Python before 3.10 are not supported')
if sys.platform == 'win32': # Windows
macros = dict()
@@ -193,7 +186,7 @@
packages = setuptools.find_packages(exclude=['ez_setup', 't', 't.*'])
setuptools.setup(
name='billiard',
- version=meta['VERSION'],
+ version=meta['version'],
description=meta['doc'],
long_description=long_description,
packages=packages,
@@ -206,19 +199,18 @@
url=meta['homepage'],
zip_safe=False,
license='BSD',
- python_requires='>=3.7',
+ python_requires='>=3.10',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Programming Language :: Python',
'Programming Language :: C',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.12',
- 'Programming Language :: Python :: 3.8',
- 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
+ 'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
+ 'Programming Language :: Python :: 3.14',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Operating System :: Microsoft :: Windows',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/integration/setup.cfg
new/billiard-4.3.0/t/integration/setup.cfg
--- old/billiard-4.2.4/t/integration/setup.cfg 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/t/integration/setup.cfg 1970-01-01 01:00:00.000000000
+0100
@@ -1,4 +0,0 @@
-[nosetests]
-verbosity = 1
-detailed-errors = 1
-where = tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/unit/test_compat.py
new/billiard-4.3.0/t/unit/test_compat.py
--- old/billiard-4.2.4/t/unit/test_compat.py 1970-01-01 01:00:00.000000000
+0100
+++ new/billiard-4.3.0/t/unit/test_compat.py 2026-09-17 08:52:08.072760800
+0200
@@ -0,0 +1,138 @@
+import errno
+import sys
+import tempfile
+from unittest.mock import Mock, call, patch
+
+import pytest
+
+from billiard import compat
+from billiard.compat import close_open_fds
+
+
+class test_close_open_fds:
+
+ def test_closes_only_listed_descriptors(self):
+ with patch('billiard.compat._open_fds',
+ return_value=[0, 1, 2, 7, 9]), \
+ patch('os.close') as close, \
+ patch('os.closerange') as closerange:
+ close_open_fds([1, 2])
+ assert close.call_args_list == [call(0), call(7), call(9)]
+ closerange.assert_not_called()
+
+ def test_accepts_file_objects_and_ignores_none(self):
+ fh = Mock(name='fh')
+ fh.fileno.return_value = 3
+ with patch('billiard.compat._open_fds', return_value=[3, 4]), \
+ patch('os.close') as close, \
+ patch('os.closerange'):
+ close_open_fds([None, fh])
+ close.assert_called_once_with(4)
+
+ @pytest.mark.parametrize('code', [errno.EBADF, errno.EINTR, errno.EIO])
+ def test_ignores_close_errors_like_closerange(self, code):
+ exc = OSError()
+ exc.errno = code
+ with patch('billiard.compat._open_fds', return_value=[5, 6]), \
+ patch('os.close', side_effect=exc) as close, \
+ patch('os.closerange'):
+ close_open_fds()
+ assert close.call_args_list == [call(5), call(6)]
+
+ def test_never_calls_get_fdmax_with_fd_dir(self):
+ """The limit is irrelevant when the open descriptors can be listed."""
+ with patch('billiard.compat._open_fds', return_value=[0, 1, 2]), \
+ patch('billiard.compat.get_fdmax') as get_fdmax, \
+ patch('os.close'), \
+ patch('os.closerange') as closerange:
+ close_open_fds([0, 1, 2])
+ get_fdmax.assert_not_called()
+ closerange.assert_not_called()
+
+
+class test_close_open_fds_fallback:
+ """Without an fd directory the gaps between kept descriptors are closed."""
+
+ def test_closes_ranges_between_kept_descriptors(self):
+ with patch('billiard.compat._open_fds', return_value=None), \
+ patch('billiard.compat.get_fdmax', return_value=10), \
+ patch('os.closerange') as closerange, \
+ patch('os.close') as close:
+ close_open_fds([1, 2, 5])
+ assert closerange.call_args_list == [
+ call(0, 1), call(3, 5), call(6, 10),
+ ]
+ close.assert_not_called()
+
+ def test_accepts_file_objects_and_ignores_none(self):
+ fh = Mock(name='fh')
+ fh.fileno.return_value = 4
+ with patch('billiard.compat._open_fds', return_value=None), \
+ patch('billiard.compat.get_fdmax', return_value=6), \
+ patch('os.closerange') as closerange, \
+ patch('os.close'):
+ close_open_fds([None, fh, 4, 0])
+ assert closerange.call_args_list == [call(1, 4), call(5, 6)]
+
+ def test_without_keep_closes_whole_range(self):
+ with patch('billiard.compat._open_fds', return_value=None), \
+ patch('billiard.compat.get_fdmax', return_value=3), \
+ patch('os.closerange') as closerange, \
+ patch('os.close'):
+ close_open_fds()
+ closerange.assert_called_once_with(0, 3)
+
+ def test_cost_does_not_grow_with_fdmax(self):
+ """One closerange() call per gap, however large the limit is.
+
+ The body before celery/billiard#455 called os.close() once per
+ descriptor up to get_fdmax(), which stalls for minutes in containers
+ where the limit is ~1e9 (celery/celery#9886).
+ """
+ with patch('billiard.compat._open_fds', return_value=None), \
+ patch('billiard.compat.get_fdmax', return_value=2 ** 20), \
+ patch('os.closerange') as closerange, \
+ patch('os.close') as close:
+ close_open_fds([0, 1, 2])
+ closerange.assert_called_once_with(3, 2 ** 20)
+ close.assert_not_called()
+
+
+class test_open_fds:
+
+ def test_fd_dir_matches_this_platform(self):
+ if sys.platform == 'darwin':
+ assert compat._FD_DIR == '/dev/fd'
+ elif sys.platform.startswith(('cygwin', 'freebsd', 'dragonfly')):
+ assert compat._FD_DIR == '/dev/fd'
+ else:
+ assert compat._FD_DIR == '/proc/self/fd'
+
+ @pytest.mark.skipif(sys.platform == 'win32', reason='no fd directory')
+ def test_lists_a_descriptor_this_process_opened(self):
+ with tempfile.TemporaryFile() as fh:
+ fds = compat._open_fds()
+ if fds is None:
+ pytest.skip(
+ f'fd directory {compat._FD_DIR!r} not available here')
+ assert fh.fileno() in fds
+
+ def test_returns_none_when_fd_dir_is_missing(self):
+ with patch('os.listdir', side_effect=OSError()):
+ assert compat._open_fds() is None
+
+ def test_returns_none_on_freebsd_without_fdescfs(self):
+ with patch.object(sys, 'platform', 'freebsd14'), \
+ patch('billiard.compat._dev_fd_is_fdescfs',
+ return_value=False), \
+ patch('os.listdir') as listdir:
+ assert compat._open_fds() is None
+ listdir.assert_not_called()
+
+ def test_dev_fd_is_fdescfs(self):
+ with patch('os.stat', side_effect=[Mock(st_dev=1), Mock(st_dev=2)]):
+ assert compat._dev_fd_is_fdescfs()
+ with patch('os.stat', side_effect=[Mock(st_dev=1), Mock(st_dev=1)]):
+ assert not compat._dev_fd_is_fdescfs()
+ with patch('os.stat', side_effect=OSError()):
+ assert not compat._dev_fd_is_fdescfs()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/unit/test_context.py
new/billiard-4.3.0/t/unit/test_context.py
--- old/billiard-4.2.4/t/unit/test_context.py 1970-01-01 01:00:00.000000000
+0100
+++ new/billiard-4.3.0/t/unit/test_context.py 2026-09-17 08:52:08.072840200
+0200
@@ -0,0 +1,55 @@
+import subprocess
+import sys
+
+
+def _get_default_context_attr(platform, attr):
+ """Run a subprocess that patches sys.platform before importing billiard.
+
+ This exercises the actual import-time conditional without corrupting
+ the module state of the current process.
+ """
+ result = subprocess.run(
+ [sys.executable, '-c',
+ 'import unittest.mock; '
+ f'unittest.mock.patch("sys.platform", {platform!r}).start(); '
+ 'import billiard.context; '
+ f'print(getattr(billiard.context._default_context, {attr!r})())'],
+ capture_output=True, text=True,
+ )
+ assert result.returncode == 0, result.stderr
+ return result.stdout.strip()
+
+
+class test_default_context_darwin:
+ """Tests that macOS defaults to spawn, matching CPython (bpo-33725)."""
+
+ def test_default_start_method_is_spawn_on_darwin(self):
+ method = _get_default_context_attr('darwin', 'get_start_method')
+ assert method == 'spawn'
+
+ def test_default_start_method_is_fork_on_linux(self):
+ method = _get_default_context_attr('linux', 'get_start_method')
+ assert method == 'fork'
+
+ def test_set_start_method_override_on_darwin(self):
+ result = subprocess.run(
+ [sys.executable, '-c',
+ 'import unittest.mock; '
+ 'unittest.mock.patch("sys.platform", "darwin").start(); '
+ 'import billiard.context; '
+ 'ctx = billiard.context._default_context; '
+ 'assert ctx.get_start_method() == "spawn"; '
+ 'ctx.set_start_method("fork", force=True); '
+ 'print(ctx.get_start_method())'],
+ capture_output=True, text=True,
+ )
+ assert result.returncode == 0, result.stderr
+ assert result.stdout.strip() == 'fork'
+
+ def test_forking_is_enabled_false_on_darwin(self):
+ enabled = _get_default_context_attr('darwin', 'forking_is_enabled')
+ assert enabled == 'False'
+
+ def test_forking_is_enabled_true_on_linux(self):
+ enabled = _get_default_context_attr('linux', 'forking_is_enabled')
+ assert enabled == 'True'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/unit/test_einfo.py
new/billiard-4.3.0/t/unit/test_einfo.py
--- old/billiard-4.2.4/t/unit/test_einfo.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/t/unit/test_einfo.py 2026-09-17 08:52:08.073014500
+0200
@@ -1,6 +1,8 @@
+import inspect
import logging
import pickle
import sys
+import types
from billiard.einfo import _Code # noqa
from billiard.einfo import _Frame # noqa
@@ -86,7 +88,7 @@
assert isinstance(code.co_argcount, int)
if sys.version_info >= (3, 11):
assert callable(code.co_positions)
- assert next(code.co_positions()) == (77, 77, 0, 0)
+ assert next(code.co_positions()) == (79, 79, 0, 0)
def test_object_init():
@@ -116,3 +118,94 @@
assert list(iter(truncated.co_positions())) == list(
iter(truncated.tb_frame.co_positions())
)
+
+
+def make_python_tb():
+ tb = None
+ depth = 0
+ while True:
+ try:
+ frame = sys._getframe(depth)
+ except ValueError:
+ break
+ else:
+ depth += 1
+
+ tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
+
+ assert tb is not None, "Failed to create a traceback object"
+
+ return tb
+
+
+def test_isinstance():
+ tb = Traceback(tb=make_python_tb())
+ frame = tb.tb_frame
+ code = frame.f_code
+
+ assert isinstance(tb, types.TracebackType)
+ assert isinstance(tb, Traceback)
+ assert isinstance(frame, types.FrameType)
+ assert isinstance(frame, _Frame)
+ assert isinstance(code, types.CodeType)
+ assert isinstance(code, _Code)
+
+
+def repickle(obj):
+ """Round-trip an object through pickle."""
+ return pickle.loads(pickle.dumps(obj))
+
+
+def test_pickle():
+ """
+ While `__class__` is overridden to return the built-in types,
+ this would break unpickling in Python versions prior to 3.10
+ """
+ tb = Traceback(tb=make_python_tb())
+ tb2 = repickle(tb)
+
+ assert type(tb2) == type(tb)
+ assert tb2.tb_lineno == tb.tb_lineno
+
+ frame = tb.tb_frame
+ frame2 = repickle(frame)
+
+ assert type(frame2) == type(frame)
+ assert frame2.f_lineno == frame.f_lineno
+
+ code = frame.f_code
+ code2 = repickle(code)
+
+ assert type(code2) == type(code)
+ assert code2.co_name == code.co_name
+
+
+class TestInspect:
+ def test_istraceback(self):
+ tb = Traceback(tb=make_python_tb())
+ assert inspect.istraceback(tb)
+ assert inspect.istraceback(repickle(tb))
+
+ def test_isframe(self):
+ frame = _Frame(make_python_tb().tb_frame)
+ assert inspect.isframe(frame)
+ assert inspect.isframe(repickle(frame))
+
+ def test_iscode(self):
+ code = _Code(make_python_tb().tb_frame.f_code)
+ assert inspect.iscode(code)
+ assert inspect.iscode(repickle(code))
+
+ def test_getframeinfo(self):
+ tb = Traceback(make_python_tb())
+ assert inspect.getframeinfo(tb)
+ assert inspect.getframeinfo(repickle(tb))
+
+ frame = tb.tb_frame
+ assert inspect.getframeinfo(frame)
+ assert inspect.getframeinfo(repickle(frame))
+
+ def test_getinnerframes(self):
+ tb = Traceback(tb=make_python_tb())
+ assert inspect.getinnerframes(tb)
+ assert inspect.getinnerframes(repickle(tb))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/unit/test_pool.py
new/billiard-4.3.0/t/unit/test_pool.py
--- old/billiard-4.2.4/t/unit/test_pool.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/t/unit/test_pool.py 2026-09-17 08:52:08.073204800
+0200
@@ -1,8 +1,13 @@
-import billiard.pool
-from billiard import get_context
+import os
+import signal
import time
+
import pytest
+import billiard.pool
+from billiard import get_context
+from billiard.exceptions import TimeLimitExceeded
+
def func(x):
if x == 2:
@@ -18,7 +23,23 @@
def simple_task(x):
return x * 2
+def raise_base_exception():
+ raise BaseException("base exception test")
+
+
+def raise_system_exit():
+ raise SystemExit(1)
+
class test_pool:
+ def test_memory_error_from_callback_propagates(self):
+ def callback(value):
+ raise MemoryError(value)
+
+ result = billiard.pool.ApplyResult({}, callback)
+
+ with pytest.raises(MemoryError, match='out of memory'):
+ result._set(None, (True, 'out of memory'))
+
def test_raises(self):
pool = billiard.pool.Pool()
assert pool.did_start_ok() is True
@@ -49,6 +70,61 @@
with pytest.raises(ValueError):
res.get()
+ def test_base_exception_propagates(self):
+ pool = billiard.pool.Pool(1)
+ result = pool.apply_async(raise_base_exception)
+
+ with pytest.raises(BaseException, match="base exception test"):
+ result.get(timeout=10)
+
+ pool.close()
+ pool.join()
+ pool.terminate()
+
+ def test_system_exit_from_task_replaces_worker(self):
+ pool = billiard.pool.Pool(1)
+ try:
+ pid_before = pool.apply_async(os.getpid).get(timeout=10)
+ result = pool.apply_async(raise_system_exit)
+ with pytest.raises(SystemExit):
+ result.get(timeout=10)
+ pid_after = pool.apply_async(os.getpid).get(timeout=10)
+ assert pid_after != pid_before
+ finally:
+ pool.terminate()
+ pool.join()
+
+ def test_hard_timeout_does_not_stall_pool(self):
+ pool = billiard.pool.Pool(1, timeout=1)
+ try:
+ pid_before = pool.apply_async(os.getpid).get(timeout=10)
+ result = pool.apply_async(time.sleep, (30,))
+ with pytest.raises(Exception) as excinfo:
+ result.get(timeout=10)
+ exc = getattr(excinfo.value, 'exc', excinfo.value)
+ assert isinstance(exc, TimeLimitExceeded)
+ # Only submit again once the killed worker has been replaced;
+ # otherwise the job may still be picked up by the dying worker.
+ deadline = time.monotonic() + 10
+ while time.monotonic() < deadline:
+ if pid_before not in [w.pid for w in pool._pool]:
+ break
+ time.sleep(0.1)
+ assert pool.apply_async(simple_task, (21,)).get(timeout=10) == 42
+ except BaseException:
+ # A stalled pool cannot be terminated either (terminate() blocks
+ # on the same inqueue lock), so kill the workers outright and
+ # skip the terminate-at-exit finalizer.
+ for worker in pool._pool:
+ try:
+ os.kill(worker.pid, signal.SIGKILL)
+ except OSError:
+ pass
+ pool._terminate.cancel()
+ raise
+ pool.terminate()
+ pool.join()
+
def test_on_ready_counter_is_synchronized(self):
for ctx in ('spawn', 'fork', 'forkserver'):
pool = billiard.pool.Pool(processes=1, context=get_context(ctx))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/t/unit/test_spawn.py
new/billiard-4.3.0/t/unit/test_spawn.py
--- old/billiard-4.2.4/t/unit/test_spawn.py 2025-11-30 14:27:54.000000000
+0100
+++ new/billiard-4.3.0/t/unit/test_spawn.py 2026-09-17 08:52:08.073280300
+0200
@@ -14,7 +14,7 @@
p = ctx.Process(target=task_from_process, args=('opa',))
p.start()
p.join()
- return p.exitcode
+ assert p.exitcode == 0
@pytest.mark.skipif(not sys.platform.startswith('linux'),
reason='set_pdeathsig() is supported only in Linux')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/billiard-4.2.4/tox.ini new/billiard-4.3.0/tox.ini
--- old/billiard-4.2.4/tox.ini 2025-11-30 14:27:54.000000000 +0100
+++ new/billiard-4.3.0/tox.ini 1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-[tox]
-requires =
- tox-gh-actions
-envlist = {3.8,3.9,3.10,3.11,3.12,3.13,3.14}-unit
-skip_missing_interpreters = True
-
-[gh-actions]
-python =
- 3.8: 3.8-unit
- 3.9: 3.9-unit
- 3.10: 3.10-unit
- 3.11: 3.11-unit
- 3.12: 3.12-unit
- 3.13: 3.13-unit
- 3.14: 3.14-unit
-
-[testenv]
-distribute = True
-sitepackages = False
-commands = py.test -xv
-deps=
- -r{toxinidir}/requirements/test-ci.txt
- -r{toxinidir}/requirements/test.txt