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) diff --git a/dist/setup/index.js b/dist/setup/index.js index e56f96608..39c25cb29 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$/; @@ -91629,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/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$/; 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`;