From a9bdf21f32dd4a061cf4a64b48eb54fdb404c449 Mon Sep 17 00:00:00 2001 From: Marcelo Duarte Date: Wed, 1 Jan 2025 14:43:22 -0300 Subject: [PATCH] chore: enable experimental free-threaded python 3.13 on unix --- ci/build-wheel.sh | 3 ++- cx_Freeze/_compat.py | 4 ++- cx_Freeze/executable.py | 14 +++++++--- pyproject.toml | 4 ++- requirements-dev.txt | 2 +- setup.py | 57 +++++++++++++++++++++++---------------- tests/test_executables.py | 13 ++++++--- 7 files changed, 62 insertions(+), 35 deletions(-) diff --git a/ci/build-wheel.sh b/ci/build-wheel.sh index 0443871e9..2f3b4bfcb 100755 --- a/ci/build-wheel.sh +++ b/ci/build-wheel.sh @@ -6,8 +6,9 @@ PY_PLATFORM=$($PYTHON -c "import sysconfig; print(sysconfig.get_platform(), end= PY_VERSION=$($PYTHON -c "import sysconfig; print(sysconfig.get_python_version(), end='')") PY_VERSION_FULL=$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('py_version'), end='')") PY_VERSION_NODOT=$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('py_version_nodot'), end='')") +PY_ABI_THREAD=$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('abi_thread') or '', end='')") -PYTHON_TAG=cp$PY_VERSION_NODOT +PYTHON_TAG=cp$PY_VERSION_NODOT$PY_ABI_THREAD if [[ $PY_PLATFORM == linux* ]]; then PLATFORM_TAG=many$(echo $PY_PLATFORM | sed 's/\-/_/') PLATFORM_TAG_MASK="$(echo $PLATFORM_TAG | sed 's/_/*_/')" diff --git a/cx_Freeze/_compat.py b/cx_Freeze/_compat.py index a5581e282..e712a32b1 100644 --- a/cx_Freeze/_compat.py +++ b/cx_Freeze/_compat.py @@ -7,6 +7,7 @@ from pathlib import Path __all__ = [ + "ABI_THREAD", "BUILD_EXE_DIR", "EXE_SUFFIX", "EXT_SUFFIX", @@ -21,8 +22,9 @@ PLATFORM = sysconfig.get_platform() PYTHON_VERSION = sysconfig.get_python_version() +ABI_THREAD = sysconfig.get_config_var("abi_thread") or "" -BUILD_EXE_DIR = Path(f"build/exe.{PLATFORM}-{PYTHON_VERSION}") +BUILD_EXE_DIR = Path(f"build/exe.{PLATFORM}-{PYTHON_VERSION}{ABI_THREAD}") EXE_SUFFIX = sysconfig.get_config_var("EXE") EXT_SUFFIX = sysconfig.get_config_var("EXT_SUFFIX") diff --git a/cx_Freeze/executable.py b/cx_Freeze/executable.py index 1f00319ee..b19b50555 100644 --- a/cx_Freeze/executable.py +++ b/cx_Freeze/executable.py @@ -11,6 +11,7 @@ from typing import TYPE_CHECKING from cx_Freeze._compat import ( + ABI_THREAD, EXE_SUFFIX, IS_MACOS, IS_MINGW, @@ -74,12 +75,17 @@ def base(self) -> Path: @base.setter def base(self, name: str | Path | None) -> None: - # The default base is the legacy console, except for + # The default base is the legacy console, except for Python 3.13t and # Python 3.13 on macOS, that supports only the new console - if IS_MACOS and sys.version_info[:2] >= (3, 13): - name = name or "console" - else: + version = sys.version_info[:2] + if ( + version <= (3, 13) + and ABI_THREAD == "" + and not (IS_MACOS and version == (3, 13)) + ): name = name or "console_legacy" + else: + name = name or "console" # silently ignore gui and service on non-windows systems if not (IS_WINDOWS or IS_MINGW) and name in ("gui", "service"): name = "console" diff --git a/pyproject.toml b/pyproject.toml index 08315b507..5db1c16ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ dynamic = ["version"] [project.optional-dependencies] dev = [ - "bump-my-version==0.29.0", + "bump-my-version==0.29.0 ;python_version < '3.13'", "cibuildwheel==2.22.0", "pre-commit==4.0.1", # python_version >= 3.9 ] @@ -164,9 +164,11 @@ optional_value = "final" before-build = "uv pip install -r requirements.txt" build-frontend = "build[uv]" build-verbosity = 1 +enable = ["cpython-freethreading"] skip = [ "cp3{9,10,13}-musllinux_*", "cp3{9,10,13}-manylinux_ppc64le", + "cp313t-win*", ] [tool.cibuildwheel.linux] diff --git a/requirements-dev.txt b/requirements-dev.txt index e366973e1..48d4793ef 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,3 @@ -bump-my-version==0.29.0 +bump-my-version==0.29.0 ;python_version < '3.13' cibuildwheel==2.22.0 pre-commit==4.0.1 diff --git a/setup.py b/setup.py index aab1b7a5b..7185a255a 100644 --- a/setup.py +++ b/setup.py @@ -97,7 +97,8 @@ def build_extension(self, ext) -> None: library_dirs.append(get_config_var("LIBPL")) if not ENABLE_SHARED or IS_CONDA: library_dirs.append(get_config_var("LIBDIR")) - libraries.append(f"python{get_python_version()}") + abi_thread = get_config_var("abi_thread") or "" + libraries.append(f"python{get_python_version()}{abi_thread}") if get_config_var("LIBS"): extra_args.extend(get_config_var("LIBS").split()) if get_config_var("LIBM"): @@ -275,38 +276,48 @@ def get_extensions() -> list[Extension]: os.environ.get("CI", "") != "true" or os.environ.get("CIBUILDWHEEL", "0") != "1" ) + abi_thread = get_config_var("abi_thread") or "" + version = sys.version_info[:2] extensions = [ Extension( "cx_Freeze.bases.console", ["source/bases/console.c", "source/bases/_common.c"], optional=optional, - ), - Extension( - "cx_Freeze.bases.console_legacy", - ["source/legacy/console.c"], - depends=["source/legacy/common.c"], - optional=optional - or (sys.version_info[:2] >= (3, 13) and IS_MACOS), - ), + ) ] - - if IS_MINGW or IS_WINDOWS: + if ( + version <= (3, 13) + and abi_thread == "" + and not (IS_MACOS and version == (3, 13)) + ): extensions += [ Extension( - "cx_Freeze.bases.Win32GUI", - ["source/legacy/Win32GUI.c"], + "cx_Freeze.bases.console_legacy", + ["source/legacy/console.c"], depends=["source/legacy/common.c"], - libraries=["user32"], optional=optional, - ), - Extension( - "cx_Freeze.bases.Win32Service", - ["source/legacy/Win32Service.c"], - depends=["source/legacy/common.c"], - extra_link_args=["/DELAYLOAD:cx_Logging"], - libraries=["advapi32"], - optional=optional, - ), + ) + ] + if IS_MINGW or IS_WINDOWS: + if version <= (3, 13) and abi_thread == "": + extensions += [ + Extension( + "cx_Freeze.bases.Win32GUI", + ["source/legacy/Win32GUI.c"], + depends=["source/legacy/common.c"], + libraries=["user32"], + optional=optional, + ), + Extension( + "cx_Freeze.bases.Win32Service", + ["source/legacy/Win32Service.c"], + depends=["source/legacy/common.c"], + extra_link_args=["/DELAYLOAD:cx_Logging"], + libraries=["advapi32"], + optional=optional, + ), + ] + extensions += [ Extension( "cx_Freeze.bases.gui", ["source/bases/Win32GUI.c", "source/bases/_common.c"], diff --git a/tests/test_executables.py b/tests/test_executables.py index b9b5c2ad4..bc93d8188 100644 --- a/tests/test_executables.py +++ b/tests/test_executables.py @@ -12,6 +12,7 @@ from cx_Freeze import Executable from cx_Freeze._compat import ( + ABI_THREAD, BUILD_EXE_DIR, EXE_SUFFIX, IS_MACOS, @@ -241,14 +242,18 @@ def test_executables( ("icon.ico", "icon.icns", "icon.png", "icon.svg"), ), ] -if IS_MACOS and sys.version_info[:2] >= (3, 13): +if ( + sys.version_info[:2] <= (3, 13) + and ABI_THREAD == "" + and not (IS_MACOS and sys.version_info[:2] == (3, 13)) +): TEST_VALID_PARAMETERS += [ - ("base", None, "console-"), + ("base", None, "console_legacy-"), + ("base", "console_legacy", "console_legacy-"), ] else: TEST_VALID_PARAMETERS += [ - ("base", None, "console_legacy-"), - ("base", "console_legacy", "console_legacy-"), + ("base", None, "console-"), ] if IS_WINDOWS or IS_MINGW: TEST_VALID_PARAMETERS += [