From 946ea95fa741e4e59ae590729690ef8c740cb967 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Thu, 25 May 2023 08:46:54 -0400 Subject: [PATCH] Enable new tarfile unpacking protections in Python 3.12 --- changes/1290.misc.rst | 1 + pyproject.toml | 3 ++- src/briefcase/commands/create.py | 10 ++++++++++ src/briefcase/integrations/android_sdk.py | 7 ++++++- .../create/test_install_app_support_package.py | 3 +++ .../AndroidSDK/test_verify_emulator_skin.py | 3 +++ 6 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 changes/1290.misc.rst diff --git a/changes/1290.misc.rst b/changes/1290.misc.rst new file mode 100644 index 000000000..e864781a2 --- /dev/null +++ b/changes/1290.misc.rst @@ -0,0 +1 @@ +The test suite is now compatible with Python 3.12. diff --git a/pyproject.toml b/pyproject.toml index 8a3bf1a23..bd2a7b11f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ no-cover-if-is-macos = "'darwin' == os_environ.get('COVERAGE_PLATFORM', sys_plat no-cover-if-not-macos = "'darwin' != os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" no-cover-if-is-windows = "'win32' == os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" no-cover-if-not-windows = "'win32' != os_environ.get('COVERAGE_PLATFORM', sys_platform) and os_environ.get('COVERAGE_EXCLUDE_PLATFORM') != 'disable'" +no-cover-if-lt-py312 = "sys_version_info < (3, 12) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-is-py310 = "python_version == '3.10' and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-lt-py310 = "sys_version_info < (3, 10) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" no-cover-if-gte-py310 = "sys_version_info > (3, 10) and os_environ.get('COVERAGE_EXCLUDE_PYTHON_VERSION') != 'disable'" @@ -73,4 +74,4 @@ type = [ { directory = "removal", name = "Backward Incompatible Changes", showcontent = true }, { directory = "doc", name = "Documentation", showcontent = true }, { directory = "misc", name = "Misc", showcontent = false }, - ] +] diff --git a/src/briefcase/commands/create.py b/src/briefcase/commands/create.py index 05ef87058..aefa2a220 100644 --- a/src/briefcase/commands/create.py +++ b/src/briefcase/commands/create.py @@ -263,12 +263,22 @@ def _unpack_support_package(self, support_file_path, support_path): :param support_file_path: The path to the support file to be unpacked. :param support_path: The path where support files should be unpacked. """ + # Additional protections for unpacking tar files were introduced in Python 3.12. + # This enables the behavior that will be the default in Python 3.14. + # However, the protections can only be enabled for tar files...not zip files. + is_zip = support_file_path.name.endswith("zip") + if sys.version_info >= (3, 12) and not is_zip: # pragma: no-cover-if-lt-py312 + tarfile_kwargs = {"filter": "data"} + else: + tarfile_kwargs = {} + try: with self.input.wait_bar("Unpacking support package..."): support_path.mkdir(parents=True, exist_ok=True) self.tools.shutil.unpack_archive( support_file_path, extract_dir=support_path, + **tarfile_kwargs, ) except (shutil.ReadError, EOFError) as e: raise InvalidSupportPackage(support_file_path) from e diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index d3a8e4a58..c73bb6b4a 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -6,6 +6,7 @@ import shlex import shutil import subprocess +import sys import time from contextlib import suppress from datetime import datetime @@ -689,7 +690,11 @@ def verify_emulator_skin(self, skin: str): # Unpack skin archive with self.tools.input.wait_bar("Installing device skin..."): try: - self.tools.shutil.unpack_archive(skin_tgz_path, extract_dir=skin_path) + self.tools.shutil.unpack_archive( + skin_tgz_path, + extract_dir=skin_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), + ) except (shutil.ReadError, EOFError) as err: raise BriefcaseCommandError( f"Unable to unpack {skin} device skin." diff --git a/tests/commands/create/test_install_app_support_package.py b/tests/commands/create/test_install_app_support_package.py index 4b286ae95..3d7205ba7 100644 --- a/tests/commands/create/test_install_app_support_package.py +++ b/tests/commands/create/test_install_app_support_package.py @@ -1,5 +1,6 @@ import os import shutil +import sys from unittest import mock import pytest @@ -55,6 +56,7 @@ def test_install_app_support_package( create_command.tools.shutil.unpack_archive.assert_called_with( tmp_path / "data" / "support" / "Python-3.X-tester-support.b37.tar.gz", extract_dir=support_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Confirm that the full path to the support file @@ -100,6 +102,7 @@ def test_install_pinned_app_support_package( create_command.tools.shutil.unpack_archive.assert_called_with( tmp_path / "data" / "support" / "Python-3.X-Tester-support.b42.tar.gz", extract_dir=support_path, + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Confirm that the full path to the support file diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py index 2981f4581..35363d6d4 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_verify_emulator_skin.py @@ -1,3 +1,4 @@ +import sys from pathlib import Path from unittest.mock import MagicMock @@ -42,6 +43,7 @@ def test_new_skin(mock_tools, android_sdk): mock_tools.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=android_sdk.root_path / "skins" / "pixel_X", + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Original file was deleted. @@ -108,6 +110,7 @@ def test_unpack_failure(mock_tools, android_sdk, tmp_path): mock_tools.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=android_sdk.root_path / "skins" / "pixel_X", + **({"filter": "data"} if sys.version_info >= (3, 12) else {}), ) # Original file wasn't deleted.