From eb2a6532f967fae3f4abd15c7923b67fef608b67 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 28 Oct 2024 18:14:11 +0000 Subject: [PATCH 1/3] Support free threaded Python versions like '3.13t' Python wheels, pyenv, and a number of other tools use 't' in the Python version number to identify free threaded builds. For example, '3.13t', '3.14.0a1', '3.14t-dev'. This PR supports that syntax in `actions/setup-python`, strips the "t", and adds "-freethreading" to the architecture to select the correct Python version. See #771 --- dist/setup/index.js | 27 +++++++++++++++++++++++++-- src/find-python.ts | 30 ++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/dist/setup/index.js b/dist/setup/index.js index e56f96608..97b5e5ceb 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -91038,9 +91038,15 @@ function useCpythonVersion(version, architecture, updateEnvironment, checkLatest return __awaiter(this, void 0, void 0, function* () { var _a; let manifest = null; - const desugaredVersionSpec = desugarDevVersion(version); - let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec, allowPreReleases); + const [desugaredVersionSpec, freethreaded] = desugarFreeThreadedVersion(version); + const desugaredVersionSpec2 = desugarDevVersion(desugaredVersionSpec); + let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec2, allowPreReleases); core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`); + if (freethreaded) { + // Free threaded versions use an architecture suffix like `x64-freethreaded` + core.debug(`Using freethreaded version of ${semanticVersionSpec}`); + architecture += freethreaded; + } if (checkLatest) { manifest = yield installer.getManifest(); const resolvedVersion = (_a = (yield installer.findReleaseFromManifest(semanticVersionSpec, architecture, manifest))) === null || _a === void 0 ? void 0 : _a.version; @@ -91115,6 +91121,23 @@ function useCpythonVersion(version, architecture, updateEnvironment, checkLatest }); } exports.useCpythonVersion = useCpythonVersion; +/* Identify freethreaded versions like, 3.13t, 3.13t-dev, 3.14.0a1t. Returns + * the version without the `t` and the architectures suffix, if freethreaded */ +function desugarFreeThreadedVersion(versionSpec) { + const prereleaseVersion = /(\d+\.\d+\.\d+)(t)((?:a|b|rc)\d*)/g; + if (prereleaseVersion.test(versionSpec)) { + return [versionSpec.replace(prereleaseVersion, '$1$3'), '-freethreaded']; + } + const majorMinor = /^(\d+\.\d+)(t)$/; + if (majorMinor.test(versionSpec)) { + return [versionSpec.replace(majorMinor, '$1'), '-freethreaded']; + } + const devVersion = /^(\d+\.\d+)(t)(-dev)$/; + if (devVersion.test(versionSpec)) { + return [versionSpec.replace(devVersion, '$1$3'), '-freethreaded']; + } + return [versionSpec, '']; +} /** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */ function desugarDevVersion(versionSpec) { const devVersion = /^(\d+)\.(\d+)-dev$/; diff --git a/src/find-python.ts b/src/find-python.ts index 77278770a..3c2b8b135 100644 --- a/src/find-python.ts +++ b/src/find-python.ts @@ -38,13 +38,21 @@ export async function useCpythonVersion( allowPreReleases: boolean ): Promise { let manifest: tc.IToolRelease[] | null = null; - const desugaredVersionSpec = desugarDevVersion(version); + const [desugaredVersionSpec, freethreaded] = + desugarFreeThreadedVersion(version); + const desugaredVersionSpec2 = desugarDevVersion(desugaredVersionSpec); let semanticVersionSpec = pythonVersionToSemantic( - desugaredVersionSpec, + desugaredVersionSpec2, allowPreReleases ); core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`); + if (freethreaded) { + // Free threaded versions use an architecture suffix like `x64-freethreaded` + core.debug(`Using freethreaded version of ${semanticVersionSpec}`); + architecture += freethreaded; + } + if (checkLatest) { manifest = await installer.getManifest(); const resolvedVersion = ( @@ -159,6 +167,24 @@ export async function useCpythonVersion( return {impl: 'CPython', version: installed}; } +/* Identify freethreaded versions like, 3.13t, 3.13t-dev, 3.14.0a1t. Returns + * the version without the `t` and the architectures suffix, if freethreaded */ +function desugarFreeThreadedVersion(versionSpec: string) { + const prereleaseVersion = /(\d+\.\d+\.\d+)(t)((?:a|b|rc)\d*)/g; + if (prereleaseVersion.test(versionSpec)) { + return [versionSpec.replace(prereleaseVersion, '$1$3'), '-freethreaded']; + } + const majorMinor = /^(\d+\.\d+)(t)$/; + if (majorMinor.test(versionSpec)) { + return [versionSpec.replace(majorMinor, '$1'), '-freethreaded']; + } + const devVersion = /^(\d+\.\d+)(t)(-dev)$/; + if (devVersion.test(versionSpec)) { + return [versionSpec.replace(devVersion, '$1$3'), '-freethreaded']; + } + return [versionSpec, '']; +} + /** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */ function desugarDevVersion(versionSpec: string) { const devVersion = /^(\d+)\.(\d+)-dev$/; From 079f76b3839e2c54bf7f374e1597627dc0784397 Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Mon, 4 Nov 2024 19:24:28 +0000 Subject: [PATCH 2/3] Use 'Quansight-Labs' repo --- dist/setup/index.js | 2 +- src/install-python.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/setup/index.js b/dist/setup/index.js index 97b5e5ceb..39c25cb29 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -91652,7 +91652,7 @@ const httpm = __importStar(__nccwpck_require__(6255)); const utils_1 = __nccwpck_require__(1314); const TOKEN = core.getInput('token'); const AUTH = !TOKEN ? undefined : `token ${TOKEN}`; -const MANIFEST_REPO_OWNER = 'actions'; +const MANIFEST_REPO_OWNER = 'Quansight-Labs'; const MANIFEST_REPO_NAME = 'python-versions'; const MANIFEST_REPO_BRANCH = 'main'; exports.MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; diff --git a/src/install-python.ts b/src/install-python.ts index d3421bf84..7da7dc5c2 100644 --- a/src/install-python.ts +++ b/src/install-python.ts @@ -8,7 +8,7 @@ import {IS_WINDOWS, IS_LINUX, getDownloadFileName} from './utils'; const TOKEN = core.getInput('token'); const AUTH = !TOKEN ? undefined : `token ${TOKEN}`; -const MANIFEST_REPO_OWNER = 'actions'; +const MANIFEST_REPO_OWNER = 'Quansight-Labs'; const MANIFEST_REPO_NAME = 'python-versions'; const MANIFEST_REPO_BRANCH = 'main'; export const MANIFEST_URL = `https://raw.githubusercontent.com/${MANIFEST_REPO_OWNER}/${MANIFEST_REPO_NAME}/${MANIFEST_REPO_BRANCH}/versions-manifest.json`; From c33752f95c5ca75dbfa90de8de3b14fe1dae1b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Andr=C3=A9s=20Margffoy=20Tuay?= Date: Tue, 5 Nov 2024 15:56:28 -0500 Subject: [PATCH 3/3] Update README to add examples to install 3.13t --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6ffab1fab..1448405f4 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ See [action.yml](action.yml) ```yaml steps: - uses: actions/checkout@v4 -- uses: actions/setup-python@v5 +- uses: Quansight-Labs/setup-python@v5 with: - python-version: '3.13' + python-version: '3.13t' # Use free-threaded version - run: python my_script.py ``` @@ -29,9 +29,9 @@ steps: ```yaml steps: - uses: actions/checkout@v4 -- uses: actions/setup-python@v5 +- uses: Quansight-Labs/setup-python@v5 with: - python-version: 'pypy3.10' + python-version: 'pypy3.10' - run: python my_script.py ``` @@ -39,9 +39,9 @@ steps: ```yaml steps: - uses: actions/checkout@v4 -- uses: actions/setup-python@v5 +- uses: Quansight-Labs/setup-python@v5 with: - python-version: 'graalpy-24.0' + python-version: 'graalpy-24.0' - run: python my_script.py ``` @@ -55,6 +55,8 @@ For information regarding locally cached versions of Python or PyPy on GitHub ho The `python-version` input supports the [Semantic Versioning Specification](https://semver.org/) and some special version notations (e.g. `semver ranges`, `x.y-dev syntax`, etc.), for detailed examples please refer to the section: [Using python-version input](docs/advanced-usage.md#using-the-python-version-input) of the [Advanced usage](docs/advanced-usage.md) guide. +Additionally, this action supports free-threaded CPython distributions, using the `x.yt` syntax. + ## Supported architectures Using the `architecture` input, it is possible to specify the required Python or PyPy interpreter architecture: `x86`, `x64`, or `arm64`. If the input is not specified, the architecture defaults to the host OS architecture. @@ -94,7 +96,7 @@ See examples of using `cache` and `cache-dependency-path` for `pipenv` and `poet - [Caching packages](docs/advanced-usage.md#caching-packages) - [Outputs and environment variables](docs/advanced-usage.md#outputs-and-environment-variables) - [Available versions of Python, PyPy and GraalPy](docs/advanced-usage.md#available-versions-of-python-pypy-and-graalpy) -- [Hosted tool cache](docs/advanced-usage.md#hosted-tool-cache) +- [Hosted tool cache](docs/advanced-usage.md#hosted-tool-cache) - [Using `setup-python` with a self-hosted runner](docs/advanced-usage.md#using-setup-python-with-a-self-hosted-runner) - [Using `setup-python` on GHES](docs/advanced-usage.md#using-setup-python-on-ghes) - [Allow pre-releases](docs/advanced-usage.md#allow-pre-releases)