From 633e6aa0eb1b5d92636a702c55e875474895f6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Mon, 26 Jan 2026 13:25:42 +0100 Subject: [PATCH 01/11] Validate wheel contain license and notice files --- ci/scripts/python_wheel_validate_contents.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ci/scripts/python_wheel_validate_contents.py b/ci/scripts/python_wheel_validate_contents.py index 84fcaba42e6..e8561f584d0 100644 --- a/ci/scripts/python_wheel_validate_contents.py +++ b/ci/scripts/python_wheel_validate_contents.py @@ -33,6 +33,18 @@ def validate_wheel(path): ) ] assert not outliers, f"Unexpected contents in wheel: {sorted(outliers)}" + license = [ + info.filename for info in f.filelist if re.search( + r'LICENSE.txt$', info.filename + ) + ] + assert license, "LICENSE.txt is missing from the wheel." + notice = [ + info.filename for info in f.filelist if re.search( + r'NOTICE.txt$', info.filename + ) + ] + assert notice, "NOTICE.txt is missing from the wheel." print(f"The wheel: {wheels[0]} seems valid.") From 30fdd88da27268e328f25a762324c1d852bbb402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Mon, 26 Jan 2026 13:38:06 +0100 Subject: [PATCH 02/11] Try the same approach for bdist_wheel as we do for sdist --- python/setup.py | 51 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/python/setup.py b/python/setup.py index a27bd3baefd..cd397b46d94 100755 --- a/python/setup.py +++ b/python/setup.py @@ -35,6 +35,7 @@ from setuptools import setup, Extension, Distribution from setuptools.command.sdist import sdist +from setuptools.command.bdist_wheel import bdist_wheel from Cython.Distutils import build_ext as _build_ext import Cython @@ -397,33 +398,40 @@ def has_ext_modules(foo): return True -class CopyLicenseSdist(sdist): - """Custom sdist command that copies license files from parent directory.""" +def copy_license_files(dest_dir): + license_files = [ + ("LICENSE.txt", "../LICENSE.txt"), + ("NOTICE.txt", "../NOTICE.txt"), + ] - def make_release_tree(self, base_dir, files): - # Call parent to do the normal work - super().make_release_tree(base_dir, files) + for dest_name, src_path in license_files: + src_full = os.path.join(os.path.dirname(__file__), src_path) + dest_full = os.path.join(dest_dir, dest_name) + + if not os.path.exists(src_full): + msg = f"Required license file not found: {src_full}" + raise FileNotFoundError(msg) + + shutil.copy2(src_full, dest_full) + print(f"Copied {src_path} to {dest_full}") - # Define source (parent dir) and destination (sdist root) for license files - license_files = [ - ("LICENSE.txt", "../LICENSE.txt"), - ("NOTICE.txt", "../NOTICE.txt"), - ] - for dest_name, src_path in license_files: - src_full = os.path.join(os.path.dirname(__file__), src_path) - dest_full = os.path.join(base_dir, dest_name) +class CopyLicenseBdistWheel(bdist_wheel): + """Custom bdist_wheel command that copies license files from parent directory.""" + + def run(self): + copy_license_files(os.path.dirname(__file__)) + # Call parent to do the normal wheel building + super().run() - # Remove any existing file/symlink at destination - if os.path.exists(dest_full) or os.path.islink(dest_full): - os.unlink(dest_full) - if not os.path.exists(src_full): - msg = f"Required license file not found: {src_full}" - raise FileNotFoundError(msg) +class CopyLicenseSdist(sdist): + """Custom sdist command that copies license files from parent directory.""" - shutil.copy2(src_full, dest_full) - print(f"Copied {src_path} to {dest_name} in sdist") + def make_release_tree(self, base_dir, files): + # Call parent to do the normal work + super().make_release_tree(base_dir, files) + copy_license_files(base_dir) setup( @@ -433,5 +441,6 @@ def make_release_tree(self, base_dir, files): cmdclass={ 'build_ext': build_ext, 'sdist': CopyLicenseSdist, + 'bdist_wheel': CopyLicenseBdistWheel, }, ) From d922193936b10c898659ca53c9a76623dfc7ed51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Mon, 26 Jan 2026 13:59:04 +0100 Subject: [PATCH 03/11] Revert "Try the same approach for bdist_wheel as we do for sdist" This reverts commit 9566c3b2ca30a86736083802489e64a6aac30b70. --- python/setup.py | 51 ++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/python/setup.py b/python/setup.py index cd397b46d94..a27bd3baefd 100755 --- a/python/setup.py +++ b/python/setup.py @@ -35,7 +35,6 @@ from setuptools import setup, Extension, Distribution from setuptools.command.sdist import sdist -from setuptools.command.bdist_wheel import bdist_wheel from Cython.Distutils import build_ext as _build_ext import Cython @@ -398,40 +397,33 @@ def has_ext_modules(foo): return True -def copy_license_files(dest_dir): - license_files = [ - ("LICENSE.txt", "../LICENSE.txt"), - ("NOTICE.txt", "../NOTICE.txt"), - ] - - for dest_name, src_path in license_files: - src_full = os.path.join(os.path.dirname(__file__), src_path) - dest_full = os.path.join(dest_dir, dest_name) - - if not os.path.exists(src_full): - msg = f"Required license file not found: {src_full}" - raise FileNotFoundError(msg) - - shutil.copy2(src_full, dest_full) - print(f"Copied {src_path} to {dest_full}") - - -class CopyLicenseBdistWheel(bdist_wheel): - """Custom bdist_wheel command that copies license files from parent directory.""" - - def run(self): - copy_license_files(os.path.dirname(__file__)) - # Call parent to do the normal wheel building - super().run() - - class CopyLicenseSdist(sdist): """Custom sdist command that copies license files from parent directory.""" def make_release_tree(self, base_dir, files): # Call parent to do the normal work super().make_release_tree(base_dir, files) - copy_license_files(base_dir) + + # Define source (parent dir) and destination (sdist root) for license files + license_files = [ + ("LICENSE.txt", "../LICENSE.txt"), + ("NOTICE.txt", "../NOTICE.txt"), + ] + + for dest_name, src_path in license_files: + src_full = os.path.join(os.path.dirname(__file__), src_path) + dest_full = os.path.join(base_dir, dest_name) + + # Remove any existing file/symlink at destination + if os.path.exists(dest_full) or os.path.islink(dest_full): + os.unlink(dest_full) + + if not os.path.exists(src_full): + msg = f"Required license file not found: {src_full}" + raise FileNotFoundError(msg) + + shutil.copy2(src_full, dest_full) + print(f"Copied {src_path} to {dest_name} in sdist") setup( @@ -441,6 +433,5 @@ def make_release_tree(self, base_dir, files): cmdclass={ 'build_ext': build_ext, 'sdist': CopyLicenseSdist, - 'bdist_wheel': CopyLicenseBdistWheel, }, ) From f8c4faf94c0f4a9c9138d4b2c44f18294db61076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Mon, 26 Jan 2026 14:02:12 +0100 Subject: [PATCH 04/11] Manually copy files and forget about setuptools --- ci/scripts/python_wheel_macos_build.sh | 4 ++++ ci/scripts/python_wheel_windows_build.bat | 4 ++++ ci/scripts/python_wheel_xlinux_build.sh | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/ci/scripts/python_wheel_macos_build.sh b/ci/scripts/python_wheel_macos_build.sh index bd61154430e..c230a02570c 100755 --- a/ci/scripts/python_wheel_macos_build.sh +++ b/ci/scripts/python_wheel_macos_build.sh @@ -177,6 +177,10 @@ export CMAKE_PREFIX_PATH=${build_dir}/install export SETUPTOOLS_SCM_PRETEND_VERSION=${PYARROW_VERSION} pushd ${source_dir}/python +# Setuptools is really opinionated about where Licenses/Notices go in the source tree. +# Copy them to the python/ directory so they end up in the wheel root. +cp ${source_dir}/LICENSE.txt . +cp ${source_dir}/NOTICE.txt . python setup.py bdist_wheel popd diff --git a/ci/scripts/python_wheel_windows_build.bat b/ci/scripts/python_wheel_windows_build.bat index b4b7fed99fd..7f7abe4f925 100644 --- a/ci/scripts/python_wheel_windows_build.bat +++ b/ci/scripts/python_wheel_windows_build.bat @@ -131,6 +131,10 @@ set ARROW_HOME=C:\arrow-dist set CMAKE_PREFIX_PATH=C:\arrow-dist pushd C:\arrow\python +@REM Setuptools is really opinionated about where Licenses/Notices go in the source tree. +@REM Copy them to the python/ directory so they end up in the wheel root. +copy C:\arrow\LICENSE.txt . +copy C:\arrow\NOTICE.txt . @REM Build wheel %PYTHON_CMD% setup.py bdist_wheel || exit /B 1 diff --git a/ci/scripts/python_wheel_xlinux_build.sh b/ci/scripts/python_wheel_xlinux_build.sh index a3fbeb3c0b3..0bec4103bbe 100755 --- a/ci/scripts/python_wheel_xlinux_build.sh +++ b/ci/scripts/python_wheel_xlinux_build.sh @@ -167,6 +167,11 @@ export ARROW_HOME=/tmp/arrow-dist export CMAKE_PREFIX_PATH=/tmp/arrow-dist pushd /arrow/python +# Setuptools is really opinionated about where Licenses/Notices go in the source tree. +# Copy them to the python/ directory so they end up in the wheel root. +cp /arrow/LICENSE.txt . +cp /arrow/NOTICE.txt . + python setup.py bdist_wheel echo "=== Strip symbols from wheel ===" From be696f27194267c9609ef31f4a05c8b27c65a6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Tue, 27 Jan 2026 11:39:47 +0100 Subject: [PATCH 05/11] Install build for wheel build requirements and try using it without manually copying licenses --- .env | 4 ++-- ci/scripts/python_wheel_macos_build.sh | 6 +----- ci/scripts/python_wheel_windows_build.bat | 6 +----- ci/scripts/python_wheel_xlinux_build.sh | 7 +------ python/requirements-wheel-build.txt | 1 + 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.env b/.env index 6d64d284780..6f830ca229b 100644 --- a/.env +++ b/.env @@ -102,8 +102,8 @@ VCPKG="4334d8b4c8916018600212ab4dd4bbdc343065d1" # 2025.09.17 Release # ci/docker/python-*-windows-*.dockerfile or the vcpkg config. # This is a workaround for our CI problem that "archery docker build" doesn't # use pulled built images in dev/tasks/python-wheels/github.windows.yml. -PYTHON_WHEEL_WINDOWS_IMAGE_REVISION=2026-01-22 -PYTHON_WHEEL_WINDOWS_TEST_IMAGE_REVISION=2026-01-22 +PYTHON_WHEEL_WINDOWS_IMAGE_REVISION=2026-01-27 +PYTHON_WHEEL_WINDOWS_TEST_IMAGE_REVISION=2026-01-27 # Use conanio/${CONAN_BASE}:{CONAN_VERSION} for "docker compose run --rm conan". # See https://github.com/conan-io/conan-docker-tools#readme and diff --git a/ci/scripts/python_wheel_macos_build.sh b/ci/scripts/python_wheel_macos_build.sh index c230a02570c..774b25b42f3 100755 --- a/ci/scripts/python_wheel_macos_build.sh +++ b/ci/scripts/python_wheel_macos_build.sh @@ -177,11 +177,7 @@ export CMAKE_PREFIX_PATH=${build_dir}/install export SETUPTOOLS_SCM_PRETEND_VERSION=${PYARROW_VERSION} pushd ${source_dir}/python -# Setuptools is really opinionated about where Licenses/Notices go in the source tree. -# Copy them to the python/ directory so they end up in the wheel root. -cp ${source_dir}/LICENSE.txt . -cp ${source_dir}/NOTICE.txt . -python setup.py bdist_wheel +python -m build --wheel . popd echo "=== (${PYTHON_VERSION}) Show dynamic libraries the wheel depend on ===" diff --git a/ci/scripts/python_wheel_windows_build.bat b/ci/scripts/python_wheel_windows_build.bat index 7f7abe4f925..d062971c4ab 100644 --- a/ci/scripts/python_wheel_windows_build.bat +++ b/ci/scripts/python_wheel_windows_build.bat @@ -131,13 +131,9 @@ set ARROW_HOME=C:\arrow-dist set CMAKE_PREFIX_PATH=C:\arrow-dist pushd C:\arrow\python -@REM Setuptools is really opinionated about where Licenses/Notices go in the source tree. -@REM Copy them to the python/ directory so they end up in the wheel root. -copy C:\arrow\LICENSE.txt . -copy C:\arrow\NOTICE.txt . @REM Build wheel -%PYTHON_CMD% setup.py bdist_wheel || exit /B 1 +%PYTHON_CMD% -m build --wheel . || exit /B 1 @REM Repair the wheel with delvewheel @REM diff --git a/ci/scripts/python_wheel_xlinux_build.sh b/ci/scripts/python_wheel_xlinux_build.sh index 0bec4103bbe..fcf0aac4bbb 100755 --- a/ci/scripts/python_wheel_xlinux_build.sh +++ b/ci/scripts/python_wheel_xlinux_build.sh @@ -167,12 +167,7 @@ export ARROW_HOME=/tmp/arrow-dist export CMAKE_PREFIX_PATH=/tmp/arrow-dist pushd /arrow/python -# Setuptools is really opinionated about where Licenses/Notices go in the source tree. -# Copy them to the python/ directory so they end up in the wheel root. -cp /arrow/LICENSE.txt . -cp /arrow/NOTICE.txt . - -python setup.py bdist_wheel +python -m build --wheel . echo "=== Strip symbols from wheel ===" mkdir -p dist/temp-fix-wheel diff --git a/python/requirements-wheel-build.txt b/python/requirements-wheel-build.txt index ac6388762b4..769435f4dd8 100644 --- a/python/requirements-wheel-build.txt +++ b/python/requirements-wheel-build.txt @@ -1,3 +1,4 @@ +build cython>=3.1 numpy>=2.0.0 setuptools_scm From 6ebf9d30a4c1a543170b7721333ce0aecf5dc896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Wed, 28 Jan 2026 12:21:58 +0100 Subject: [PATCH 06/11] Add --no-isolation --- ci/scripts/python_wheel_macos_build.sh | 2 +- ci/scripts/python_wheel_windows_build.bat | 2 +- ci/scripts/python_wheel_xlinux_build.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/scripts/python_wheel_macos_build.sh b/ci/scripts/python_wheel_macos_build.sh index 774b25b42f3..d491d26828e 100755 --- a/ci/scripts/python_wheel_macos_build.sh +++ b/ci/scripts/python_wheel_macos_build.sh @@ -177,7 +177,7 @@ export CMAKE_PREFIX_PATH=${build_dir}/install export SETUPTOOLS_SCM_PRETEND_VERSION=${PYARROW_VERSION} pushd ${source_dir}/python -python -m build --wheel . +python -m build --wheel . --no-isolation popd echo "=== (${PYTHON_VERSION}) Show dynamic libraries the wheel depend on ===" diff --git a/ci/scripts/python_wheel_windows_build.bat b/ci/scripts/python_wheel_windows_build.bat index d062971c4ab..1559ef2d45a 100644 --- a/ci/scripts/python_wheel_windows_build.bat +++ b/ci/scripts/python_wheel_windows_build.bat @@ -133,7 +133,7 @@ set CMAKE_PREFIX_PATH=C:\arrow-dist pushd C:\arrow\python @REM Build wheel -%PYTHON_CMD% -m build --wheel . || exit /B 1 +%PYTHON_CMD% -m build --wheel . --no-isolation || exit /B 1 @REM Repair the wheel with delvewheel @REM diff --git a/ci/scripts/python_wheel_xlinux_build.sh b/ci/scripts/python_wheel_xlinux_build.sh index fcf0aac4bbb..ef9370c8f23 100755 --- a/ci/scripts/python_wheel_xlinux_build.sh +++ b/ci/scripts/python_wheel_xlinux_build.sh @@ -167,7 +167,7 @@ export ARROW_HOME=/tmp/arrow-dist export CMAKE_PREFIX_PATH=/tmp/arrow-dist pushd /arrow/python -python -m build --wheel . +python -m build --wheel . --no-isolation echo "=== Strip symbols from wheel ===" mkdir -p dist/temp-fix-wheel From 5f3e1d279f1fcfbc778d138fe224b53f291e7359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Wed, 28 Jan 2026 13:52:38 +0100 Subject: [PATCH 07/11] Test building sdist first --- ci/scripts/python_wheel_xlinux_build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/scripts/python_wheel_xlinux_build.sh b/ci/scripts/python_wheel_xlinux_build.sh index ef9370c8f23..fa4c2167558 100755 --- a/ci/scripts/python_wheel_xlinux_build.sh +++ b/ci/scripts/python_wheel_xlinux_build.sh @@ -167,6 +167,7 @@ export ARROW_HOME=/tmp/arrow-dist export CMAKE_PREFIX_PATH=/tmp/arrow-dist pushd /arrow/python +python -m build --sdist . --no-isolation python -m build --wheel . --no-isolation echo "=== Strip symbols from wheel ===" From 85879de5bba5935cf6b75e9953f0f8f3001162b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Wed, 28 Jan 2026 17:55:02 +0100 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: Rok Mihevc --- ci/scripts/python_wheel_validate_contents.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/ci/scripts/python_wheel_validate_contents.py b/ci/scripts/python_wheel_validate_contents.py index e8561f584d0..c0c060c5def 100644 --- a/ci/scripts/python_wheel_validate_contents.py +++ b/ci/scripts/python_wheel_validate_contents.py @@ -33,18 +33,9 @@ def validate_wheel(path): ) ] assert not outliers, f"Unexpected contents in wheel: {sorted(outliers)}" - license = [ - info.filename for info in f.filelist if re.search( - r'LICENSE.txt$', info.filename - ) - ] - assert license, "LICENSE.txt is missing from the wheel." - notice = [ - info.filename for info in f.filelist if re.search( - r'NOTICE.txt$', info.filename - ) - ] - assert notice, "NOTICE.txt is missing from the wheel." + for filename in ('LICENSE.txt', 'NOTICE.txt'): + assert any(info.filename.endswith(filename) for info in f.filelist), \ + f"{filename} is missing from the wheel." print(f"The wheel: {wheels[0]} seems valid.") From 04c794e2313ce822e04f25728c7e727781bea1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Wed, 28 Jan 2026 18:15:43 +0100 Subject: [PATCH 09/11] Fix identation from code review suggestion --- ci/scripts/python_wheel_validate_contents.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/scripts/python_wheel_validate_contents.py b/ci/scripts/python_wheel_validate_contents.py index c0c060c5def..2d8c7029e96 100644 --- a/ci/scripts/python_wheel_validate_contents.py +++ b/ci/scripts/python_wheel_validate_contents.py @@ -33,9 +33,9 @@ def validate_wheel(path): ) ] assert not outliers, f"Unexpected contents in wheel: {sorted(outliers)}" - for filename in ('LICENSE.txt', 'NOTICE.txt'): - assert any(info.filename.endswith(filename) for info in f.filelist), \ - f"{filename} is missing from the wheel." + for filename in ('LICENSE.txt', 'NOTICE.txt'): + assert any(info.filename.endswith(filename) for info in f.filelist), \ + f"{filename} is missing from the wheel." print(f"The wheel: {wheels[0]} seems valid.") From afdc583718aa590c55adca718eb016a54a5f23f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Wed, 28 Jan 2026 19:41:10 +0100 Subject: [PATCH 10/11] Use both --sdist and --wheel so build uses the built sdist to build the wheel --- ci/scripts/python_wheel_macos_build.sh | 2 +- ci/scripts/python_wheel_windows_build.bat | 2 +- ci/scripts/python_wheel_xlinux_build.sh | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ci/scripts/python_wheel_macos_build.sh b/ci/scripts/python_wheel_macos_build.sh index d491d26828e..2234fc6f310 100755 --- a/ci/scripts/python_wheel_macos_build.sh +++ b/ci/scripts/python_wheel_macos_build.sh @@ -177,7 +177,7 @@ export CMAKE_PREFIX_PATH=${build_dir}/install export SETUPTOOLS_SCM_PRETEND_VERSION=${PYARROW_VERSION} pushd ${source_dir}/python -python -m build --wheel . --no-isolation +python -m build --sdist --wheel . --no-isolation popd echo "=== (${PYTHON_VERSION}) Show dynamic libraries the wheel depend on ===" diff --git a/ci/scripts/python_wheel_windows_build.bat b/ci/scripts/python_wheel_windows_build.bat index 1559ef2d45a..fc256d72785 100644 --- a/ci/scripts/python_wheel_windows_build.bat +++ b/ci/scripts/python_wheel_windows_build.bat @@ -133,7 +133,7 @@ set CMAKE_PREFIX_PATH=C:\arrow-dist pushd C:\arrow\python @REM Build wheel -%PYTHON_CMD% -m build --wheel . --no-isolation || exit /B 1 +%PYTHON_CMD% -m build --sdist --wheel . --no-isolation || exit /B 1 @REM Repair the wheel with delvewheel @REM diff --git a/ci/scripts/python_wheel_xlinux_build.sh b/ci/scripts/python_wheel_xlinux_build.sh index fa4c2167558..ceebbc5ad01 100755 --- a/ci/scripts/python_wheel_xlinux_build.sh +++ b/ci/scripts/python_wheel_xlinux_build.sh @@ -167,8 +167,7 @@ export ARROW_HOME=/tmp/arrow-dist export CMAKE_PREFIX_PATH=/tmp/arrow-dist pushd /arrow/python -python -m build --sdist . --no-isolation -python -m build --wheel . --no-isolation +python -m build --sdist --wheel . --no-isolation echo "=== Strip symbols from wheel ===" mkdir -p dist/temp-fix-wheel From 84e59c3ee1d592e9b2844e9e9b597471d8ca85d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Cumplido?= Date: Thu, 29 Jan 2026 12:21:55 +0100 Subject: [PATCH 11/11] Stricter check for LICENSE.txt and NOTICE.txt Co-authored-by: Antoine Pitrou --- ci/scripts/python_wheel_validate_contents.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/scripts/python_wheel_validate_contents.py b/ci/scripts/python_wheel_validate_contents.py index 2d8c7029e96..75815dadb85 100644 --- a/ci/scripts/python_wheel_validate_contents.py +++ b/ci/scripts/python_wheel_validate_contents.py @@ -34,7 +34,8 @@ def validate_wheel(path): ] assert not outliers, f"Unexpected contents in wheel: {sorted(outliers)}" for filename in ('LICENSE.txt', 'NOTICE.txt'): - assert any(info.filename.endswith(filename) for info in f.filelist), \ + assert any(info.filename.split("/")[-1] == filename + for info in f.filelist), \ f"{filename} is missing from the wheel." print(f"The wheel: {wheels[0]} seems valid.")