mirror of
https://github.com/actions/setup-python.git
synced 2025-02-23 07:10:31 +07:00
Add freethreaded input and fix handling of prerelease versions
This commit is contained in:
parent
d653c0b66e
commit
72902a03bd
@ -1,18 +1,54 @@
|
|||||||
import {desugarVersion} from '../src/find-python';
|
import {desugarVersion, pythonVersionToSemantic} from '../src/find-python';
|
||||||
|
|
||||||
describe('desugarVersion', () => {
|
describe('desugarVersion', () => {
|
||||||
it.each([
|
it.each([
|
||||||
['3.13', ['3.13', '']],
|
['3.13', {version: '3.13', freethreaded: false}],
|
||||||
['3.13t', ['3.13', '-freethreaded']],
|
['3.13t', {version: '3.13', freethreaded: true}],
|
||||||
['3.13.1', ['3.13.1', '']],
|
['3.13.1', {version: '3.13.1', freethreaded: false}],
|
||||||
['3.13.1t', ['3.13.1', '-freethreaded']],
|
['3.13.1t', {version: '3.13.1', freethreaded: true}],
|
||||||
['3.14-dev', ['~3.14.0-0', '']],
|
['3.14-dev', {version: '~3.14.0-0', freethreaded: false}],
|
||||||
['3.14t-dev', ['~3.14.0-0', '-freethreaded']],
|
['3.14t-dev', {version: '~3.14.0-0', freethreaded: true}],
|
||||||
['3.14.0a4', ['3.14.0a4', '']],
|
['3.14.0a4', {version: '3.14.0a4', freethreaded: false}],
|
||||||
['3.14.0ta4', ['3.14.0a4', '-freethreaded']],
|
['3.14.0rc1', {version: '3.14.0rc1', freethreaded: false}],
|
||||||
['3.14.0rc1', ['3.14.0rc1', '']],
|
['3.14.0rc1t', {version: '3.14.0rc1', freethreaded: true}]
|
||||||
['3.14.0trc1', ['3.14.0rc1', '-freethreaded']]
|
|
||||||
])('%s -> %s', (input, expected) => {
|
])('%s -> %s', (input, expected) => {
|
||||||
expect(desugarVersion(input)).toEqual(expected);
|
expect(desugarVersion(input)).toEqual(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Test the combined desugarVersion and pythonVersionToSemantic functions
|
||||||
|
describe('pythonVersions', () => {
|
||||||
|
it.each([
|
||||||
|
['3.13', {version: '3.13', freethreaded: false}],
|
||||||
|
['3.13t', {version: '3.13', freethreaded: true}],
|
||||||
|
['3.13.1', {version: '3.13.1', freethreaded: false}],
|
||||||
|
['3.13.1t', {version: '3.13.1', freethreaded: true}],
|
||||||
|
['3.14-dev', {version: '~3.14.0-0', freethreaded: false}],
|
||||||
|
['3.14t-dev', {version: '~3.14.0-0', freethreaded: true}],
|
||||||
|
['3.14.0a4', {version: '3.14.0-alpha.4', freethreaded: false}],
|
||||||
|
['3.14.0a4t', {version: '3.14.0-alpha.4', freethreaded: true}],
|
||||||
|
['3.14.0rc1', {version: '3.14.0-rc.1', freethreaded: false}],
|
||||||
|
['3.14.0rc1t', {version: '3.14.0-rc.1', freethreaded: true}]
|
||||||
|
])('%s -> %s', (input, expected) => {
|
||||||
|
const {version, freethreaded} = desugarVersion(input);
|
||||||
|
let semanticVersionSpec = pythonVersionToSemantic(version, false);
|
||||||
|
expect({version: semanticVersionSpec, freethreaded}).toEqual(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['3.13', {version: '~3.13.0-0', freethreaded: false}],
|
||||||
|
['3.13t', {version: '~3.13.0-0', freethreaded: true}],
|
||||||
|
['3.13.1', {version: '3.13.1', freethreaded: false}],
|
||||||
|
['3.13.1t', {version: '3.13.1', freethreaded: true}],
|
||||||
|
['3.14-dev', {version: '~3.14.0-0', freethreaded: false}],
|
||||||
|
['3.14t-dev', {version: '~3.14.0-0', freethreaded: true}],
|
||||||
|
['3.14.0a4', {version: '3.14.0-alpha.4', freethreaded: false}],
|
||||||
|
['3.14.0a4t', {version: '3.14.0-alpha.4', freethreaded: true}],
|
||||||
|
['3.14.0rc1', {version: '3.14.0-rc.1', freethreaded: false}],
|
||||||
|
['3.14.0rc1t', {version: '3.14.0-rc.1', freethreaded: true}]
|
||||||
|
])('%s (allowPreReleases=true) -> %s', (input, expected) => {
|
||||||
|
const {version, freethreaded} = desugarVersion(input);
|
||||||
|
let semanticVersionSpec = pythonVersionToSemantic(version, true);
|
||||||
|
expect({version: semanticVersionSpec, freethreaded}).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -56,7 +56,7 @@ describe('Finder tests', () => {
|
|||||||
await io.mkdirP(pythonDir);
|
await io.mkdirP(pythonDir);
|
||||||
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await finder.useCpythonVersion('3.x', 'x64', true, false, false);
|
await finder.useCpythonVersion('3.x', 'x64', true, false, false, false);
|
||||||
expect(spyCoreAddPath).toHaveBeenCalled();
|
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
'pythonLocation',
|
'pythonLocation',
|
||||||
@ -73,7 +73,7 @@ describe('Finder tests', () => {
|
|||||||
await io.mkdirP(pythonDir);
|
await io.mkdirP(pythonDir);
|
||||||
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await finder.useCpythonVersion('3.x', 'x64', false, false, false);
|
await finder.useCpythonVersion('3.x', 'x64', false, false, false, false);
|
||||||
expect(spyCoreAddPath).not.toHaveBeenCalled();
|
expect(spyCoreAddPath).not.toHaveBeenCalled();
|
||||||
expect(spyCoreExportVariable).not.toHaveBeenCalled();
|
expect(spyCoreExportVariable).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -96,7 +96,7 @@ describe('Finder tests', () => {
|
|||||||
});
|
});
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.2.3', 'x64', true, false, false)
|
finder.useCpythonVersion('1.2.3', 'x64', true, false, false, false)
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
impl: 'CPython',
|
impl: 'CPython',
|
||||||
version: '1.2.3'
|
version: '1.2.3'
|
||||||
@ -135,7 +135,14 @@ describe('Finder tests', () => {
|
|||||||
});
|
});
|
||||||
// This will throw if it doesn't find it in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the manifest (because no such version exists)
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.2.4-beta.2', 'x64', false, false, false)
|
finder.useCpythonVersion(
|
||||||
|
'1.2.4-beta.2',
|
||||||
|
'x64',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
impl: 'CPython',
|
impl: 'CPython',
|
||||||
version: '1.2.4-beta.2'
|
version: '1.2.4-beta.2'
|
||||||
@ -186,7 +193,7 @@ describe('Finder tests', () => {
|
|||||||
|
|
||||||
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
fs.writeFileSync(`${pythonDir}.complete`, 'hello');
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await finder.useCpythonVersion('1.2', 'x64', true, true, false);
|
await finder.useCpythonVersion('1.2', 'x64', true, true, false, false);
|
||||||
|
|
||||||
expect(infoSpy).toHaveBeenCalledWith("Resolved as '1.2.3'");
|
expect(infoSpy).toHaveBeenCalledWith("Resolved as '1.2.3'");
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
@ -197,7 +204,14 @@ describe('Finder tests', () => {
|
|||||||
);
|
);
|
||||||
expect(installSpy).toHaveBeenCalled();
|
expect(installSpy).toHaveBeenCalled();
|
||||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
await finder.useCpythonVersion('1.2.4-beta.2', 'x64', false, true, false);
|
await finder.useCpythonVersion(
|
||||||
|
'1.2.4-beta.2',
|
||||||
|
'x64',
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
expect(spyCoreAddPath).toHaveBeenCalled();
|
expect(spyCoreAddPath).toHaveBeenCalled();
|
||||||
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
expect(spyCoreExportVariable).toHaveBeenCalledWith(
|
||||||
'pythonLocation',
|
'pythonLocation',
|
||||||
@ -224,7 +238,7 @@ describe('Finder tests', () => {
|
|||||||
});
|
});
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.2', 'x64', false, false, false)
|
finder.useCpythonVersion('1.2', 'x64', false, false, false, false)
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
impl: 'CPython',
|
impl: 'CPython',
|
||||||
version: '1.2.3'
|
version: '1.2.3'
|
||||||
@ -251,17 +265,17 @@ describe('Finder tests', () => {
|
|||||||
});
|
});
|
||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.1', 'x64', false, false, false)
|
finder.useCpythonVersion('1.1', 'x64', false, false, false, false)
|
||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.1', 'x64', false, false, true)
|
finder.useCpythonVersion('1.1', 'x64', false, false, true, false)
|
||||||
).resolves.toEqual({
|
).resolves.toEqual({
|
||||||
impl: 'CPython',
|
impl: 'CPython',
|
||||||
version: '1.1.0-beta.2'
|
version: '1.1.0-beta.2'
|
||||||
});
|
});
|
||||||
// Check 1.1.0 version specifier does not fallback to '1.1.0-beta.2'
|
// Check 1.1.0 version specifier does not fallback to '1.1.0-beta.2'
|
||||||
await expect(
|
await expect(
|
||||||
finder.useCpythonVersion('1.1.0', 'x64', false, false, true)
|
finder.useCpythonVersion('1.1.0', 'x64', false, false, true, false)
|
||||||
).rejects.toThrow();
|
).rejects.toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -269,7 +283,14 @@ describe('Finder tests', () => {
|
|||||||
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
// This will throw if it doesn't find it in the cache and in the manifest (because no such version exists)
|
||||||
let thrown = false;
|
let thrown = false;
|
||||||
try {
|
try {
|
||||||
await finder.useCpythonVersion('3.300000', 'x64', true, false, false);
|
await finder.useCpythonVersion(
|
||||||
|
'3.300000',
|
||||||
|
'x64',
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
thrown = true;
|
thrown = true;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,9 @@ inputs:
|
|||||||
allow-prereleases:
|
allow-prereleases:
|
||||||
description: "When 'true', a version range passed to 'python-version' input will match prerelease versions if no GA versions are found. Only 'x.y' version range is supported for CPython."
|
description: "When 'true', a version range passed to 'python-version' input will match prerelease versions if no GA versions are found. Only 'x.y' version range is supported for CPython."
|
||||||
default: false
|
default: false
|
||||||
|
freethreaded:
|
||||||
|
description: "When 'true', use the freethreaded version of Python."
|
||||||
|
default: false
|
||||||
outputs:
|
outputs:
|
||||||
python-version:
|
python-version:
|
||||||
description: "The installed Python or PyPy version. Useful when given a version range as input."
|
description: "The installed Python or PyPy version. Useful when given a version range as input."
|
||||||
|
48
dist/setup/index.js
vendored
48
dist/setup/index.js
vendored
@ -91034,17 +91034,21 @@ function binDir(installDir) {
|
|||||||
return path.join(installDir, 'bin');
|
return path.join(installDir, 'bin');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function useCpythonVersion(version, architecture, updateEnvironment, checkLatest, allowPreReleases) {
|
function useCpythonVersion(version, architecture, updateEnvironment, checkLatest, allowPreReleases, freethreaded) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
var _a;
|
var _a;
|
||||||
let manifest = null;
|
let manifest = null;
|
||||||
const [desugaredVersionSpec, freethreaded] = desugarVersion(version);
|
const { version: desugaredVersionSpec, freethreaded: versionFreethreaded } = desugarVersion(version);
|
||||||
let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec, allowPreReleases);
|
let semanticVersionSpec = pythonVersionToSemantic(desugaredVersionSpec, allowPreReleases);
|
||||||
|
if (versionFreethreaded) {
|
||||||
|
// Use the freethreaded version if it was specified in the input, e.g., 3.13t
|
||||||
|
freethreaded = true;
|
||||||
|
}
|
||||||
core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
|
core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
|
||||||
if (freethreaded) {
|
if (freethreaded) {
|
||||||
// Free threaded versions use an architecture suffix like `x64-freethreaded`
|
// Free threaded versions use an architecture suffix like `x64-freethreaded`
|
||||||
core.debug(`Using freethreaded version of ${semanticVersionSpec}`);
|
core.debug(`Using freethreaded version of ${semanticVersionSpec}`);
|
||||||
architecture += freethreaded;
|
architecture += '-freethreaded';
|
||||||
}
|
}
|
||||||
if (checkLatest) {
|
if (checkLatest) {
|
||||||
manifest = yield installer.getManifest();
|
manifest = yield installer.getManifest();
|
||||||
@ -91122,27 +91126,33 @@ function useCpythonVersion(version, architecture, updateEnvironment, checkLatest
|
|||||||
exports.useCpythonVersion = useCpythonVersion;
|
exports.useCpythonVersion = useCpythonVersion;
|
||||||
/* Desugar free threaded and dev versions */
|
/* Desugar free threaded and dev versions */
|
||||||
function desugarVersion(versionSpec) {
|
function desugarVersion(versionSpec) {
|
||||||
const [desugaredVersionSpec, freethreaded] = desugarFreeThreadedVersion(versionSpec);
|
const { version, freethreaded } = desugarFreeThreadedVersion(versionSpec);
|
||||||
const desugaredVersionSpec2 = desugarDevVersion(desugaredVersionSpec);
|
return { version: desugarDevVersion(version), freethreaded };
|
||||||
return [desugaredVersionSpec2, freethreaded];
|
|
||||||
}
|
}
|
||||||
exports.desugarVersion = desugarVersion;
|
exports.desugarVersion = desugarVersion;
|
||||||
/* Identify freethreaded versions like, 3.13t, 3.13.1t, 3.13t-dev, 3.14.0a1t.
|
/* Identify freethreaded versions like, 3.13t, 3.13.1t, 3.13t-dev, 3.14.0a1t.
|
||||||
* Returns the version without the `t` and the architectures suffix, if freethreaded */
|
* Returns the version without the `t` and the architectures suffix, if freethreaded */
|
||||||
function desugarFreeThreadedVersion(versionSpec) {
|
function desugarFreeThreadedVersion(versionSpec) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)(t)((?:a|b|rc)\d*)/g;
|
// e.g., 3.14.0a1t -> 3.14.0a1
|
||||||
|
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)(t)/g;
|
||||||
if (prereleaseVersion.test(versionSpec)) {
|
if (prereleaseVersion.test(versionSpec)) {
|
||||||
return [versionSpec.replace(prereleaseVersion, '$1$3'), '-freethreaded'];
|
return {
|
||||||
|
version: versionSpec.replace(prereleaseVersion, '$1$2'),
|
||||||
|
freethreaded: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const majorMinor = /^(\d+\.\d+(\.\d+)?)(t)$/;
|
const majorMinor = /^(\d+\.\d+(\.\d+)?)(t)$/;
|
||||||
if (majorMinor.test(versionSpec)) {
|
if (majorMinor.test(versionSpec)) {
|
||||||
return [versionSpec.replace(majorMinor, '$1'), '-freethreaded'];
|
return { version: versionSpec.replace(majorMinor, '$1'), freethreaded: true };
|
||||||
}
|
}
|
||||||
const devVersion = /^(\d+\.\d+)(t)(-dev)$/;
|
const devVersion = /^(\d+\.\d+)(t)(-dev)$/;
|
||||||
if (devVersion.test(versionSpec)) {
|
if (devVersion.test(versionSpec)) {
|
||||||
return [versionSpec.replace(devVersion, '$1$3'), '-freethreaded'];
|
return {
|
||||||
|
version: versionSpec.replace(devVersion, '$1$3'),
|
||||||
|
freethreaded: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return [versionSpec, ''];
|
return { version: versionSpec, freethreaded: false };
|
||||||
}
|
}
|
||||||
/** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */
|
/** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */
|
||||||
function desugarDevVersion(versionSpec) {
|
function desugarDevVersion(versionSpec) {
|
||||||
@ -91157,15 +91167,22 @@ function versionFromPath(installDir) {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Python's prelease versions look like `3.7.0b2`.
|
* Python's prelease versions look like `3.7.0b2`.
|
||||||
* This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-b2`.
|
* This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-beta.2`.
|
||||||
* If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
|
* If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
|
||||||
*
|
*
|
||||||
* For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
|
* For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
|
||||||
*/
|
*/
|
||||||
function pythonVersionToSemantic(versionSpec, allowPreReleases) {
|
function pythonVersionToSemantic(versionSpec, allowPreReleases) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)/g;
|
const preleaseMap = {
|
||||||
|
a: 'alpha',
|
||||||
|
b: 'beta',
|
||||||
|
rc: 'rc'
|
||||||
|
};
|
||||||
|
const prereleaseVersion = /(\d+\.\d+\.\d+)(a|b|rc)(\d+)/g;
|
||||||
|
let result = versionSpec.replace(prereleaseVersion, (_, p1, p2, p3) => {
|
||||||
|
return `${p1}-${preleaseMap[p2]}.${p3}`;
|
||||||
|
});
|
||||||
const majorMinor = /^(\d+)\.(\d+)$/;
|
const majorMinor = /^(\d+)\.(\d+)$/;
|
||||||
let result = versionSpec.replace(prereleaseVersion, '$1-$2');
|
|
||||||
if (allowPreReleases) {
|
if (allowPreReleases) {
|
||||||
result = result.replace(majorMinor, '~$1.$2.0-0');
|
result = result.replace(majorMinor, '~$1.$2.0-0');
|
||||||
}
|
}
|
||||||
@ -91881,6 +91898,7 @@ function run() {
|
|||||||
const versions = resolveVersionInput();
|
const versions = resolveVersionInput();
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
const checkLatest = core.getBooleanInput('check-latest');
|
||||||
const allowPreReleases = core.getBooleanInput('allow-prereleases');
|
const allowPreReleases = core.getBooleanInput('allow-prereleases');
|
||||||
|
const freethreaded = core.getBooleanInput('freethreaded');
|
||||||
if (versions.length) {
|
if (versions.length) {
|
||||||
let pythonVersion = '';
|
let pythonVersion = '';
|
||||||
const arch = core.getInput('architecture') || os.arch();
|
const arch = core.getInput('architecture') || os.arch();
|
||||||
@ -91901,7 +91919,7 @@ function run() {
|
|||||||
if (version.startsWith('2')) {
|
if (version.startsWith('2')) {
|
||||||
core.warning('The support for python 2.7 was removed on June 19, 2023. Related issue: https://github.com/actions/setup-python/issues/672');
|
core.warning('The support for python 2.7 was removed on June 19, 2023. Related issue: https://github.com/actions/setup-python/issues/672');
|
||||||
}
|
}
|
||||||
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest, allowPreReleases);
|
const installed = yield finder.useCpythonVersion(version, arch, updateEnvironment, checkLatest, allowPreReleases, freethreaded);
|
||||||
pythonVersion = installed.version;
|
pythonVersion = installed.version;
|
||||||
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ steps:
|
|||||||
- run: python my_script.py
|
- run: python my_script.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the **t** suffix to select the [free threading](https://docs.python.org/3/howto/free-threading-python.html) version of Python.
|
You can specify the [free threading](https://docs.python.org/3/howto/free-threading-python.html) version of Python by setting the `freethreaded` input to `true` or by using the special **t** suffix in some cases. Pre-release free threading versions can be specified like `3.14.0a3t` or `3.14t-dev`.
|
||||||
Free threaded Python is only available starting with the 3.13 release.
|
Free threaded Python is only available starting with the 3.13 release.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -89,7 +89,17 @@ steps:
|
|||||||
- run: python my_script.py
|
- run: python my_script.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Pre-release free threading versions should be specified like `3.14.0ta3` or `3.14t-dev`.
|
Note that the **t** suffix is not `semver` syntax. If you wish to specify a range, you must use the `freethreaded` input instead of the `t` suffix.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '>=3.13'
|
||||||
|
freethreaded: true
|
||||||
|
- run: python my_script.py
|
||||||
|
```
|
||||||
|
|
||||||
You can also use several types of ranges that are specified in [semver](https://github.com/npm/node-semver#ranges), for instance:
|
You can also use several types of ranges that are specified in [semver](https://github.com/npm/node-semver#ranges), for instance:
|
||||||
|
|
||||||
|
@ -35,20 +35,26 @@ export async function useCpythonVersion(
|
|||||||
architecture: string,
|
architecture: string,
|
||||||
updateEnvironment: boolean,
|
updateEnvironment: boolean,
|
||||||
checkLatest: boolean,
|
checkLatest: boolean,
|
||||||
allowPreReleases: boolean
|
allowPreReleases: boolean,
|
||||||
|
freethreaded: boolean
|
||||||
): Promise<InstalledVersion> {
|
): Promise<InstalledVersion> {
|
||||||
let manifest: tc.IToolRelease[] | null = null;
|
let manifest: tc.IToolRelease[] | null = null;
|
||||||
const [desugaredVersionSpec, freethreaded] = desugarVersion(version);
|
const {version: desugaredVersionSpec, freethreaded: versionFreethreaded} =
|
||||||
|
desugarVersion(version);
|
||||||
let semanticVersionSpec = pythonVersionToSemantic(
|
let semanticVersionSpec = pythonVersionToSemantic(
|
||||||
desugaredVersionSpec,
|
desugaredVersionSpec,
|
||||||
allowPreReleases
|
allowPreReleases
|
||||||
);
|
);
|
||||||
|
if (versionFreethreaded) {
|
||||||
|
// Use the freethreaded version if it was specified in the input, e.g., 3.13t
|
||||||
|
freethreaded = true;
|
||||||
|
}
|
||||||
core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
|
core.debug(`Semantic version spec of ${version} is ${semanticVersionSpec}`);
|
||||||
|
|
||||||
if (freethreaded) {
|
if (freethreaded) {
|
||||||
// Free threaded versions use an architecture suffix like `x64-freethreaded`
|
// Free threaded versions use an architecture suffix like `x64-freethreaded`
|
||||||
core.debug(`Using freethreaded version of ${semanticVersionSpec}`);
|
core.debug(`Using freethreaded version of ${semanticVersionSpec}`);
|
||||||
architecture += freethreaded;
|
architecture += '-freethreaded';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkLatest) {
|
if (checkLatest) {
|
||||||
@ -167,28 +173,33 @@ export async function useCpythonVersion(
|
|||||||
|
|
||||||
/* Desugar free threaded and dev versions */
|
/* Desugar free threaded and dev versions */
|
||||||
export function desugarVersion(versionSpec: string) {
|
export function desugarVersion(versionSpec: string) {
|
||||||
const [desugaredVersionSpec, freethreaded] =
|
const {version, freethreaded} = desugarFreeThreadedVersion(versionSpec);
|
||||||
desugarFreeThreadedVersion(versionSpec);
|
return {version: desugarDevVersion(version), freethreaded};
|
||||||
const desugaredVersionSpec2 = desugarDevVersion(desugaredVersionSpec);
|
|
||||||
return [desugaredVersionSpec2, freethreaded];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Identify freethreaded versions like, 3.13t, 3.13.1t, 3.13t-dev, 3.14.0a1t.
|
/* Identify freethreaded versions like, 3.13t, 3.13.1t, 3.13t-dev, 3.14.0a1t.
|
||||||
* Returns the version without the `t` and the architectures suffix, if freethreaded */
|
* Returns the version without the `t` and the architectures suffix, if freethreaded */
|
||||||
function desugarFreeThreadedVersion(versionSpec: string) {
|
function desugarFreeThreadedVersion(versionSpec: string) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)(t)((?:a|b|rc)\d*)/g;
|
// e.g., 3.14.0a1t -> 3.14.0a1
|
||||||
|
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)(t)/g;
|
||||||
if (prereleaseVersion.test(versionSpec)) {
|
if (prereleaseVersion.test(versionSpec)) {
|
||||||
return [versionSpec.replace(prereleaseVersion, '$1$3'), '-freethreaded'];
|
return {
|
||||||
|
version: versionSpec.replace(prereleaseVersion, '$1$2'),
|
||||||
|
freethreaded: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const majorMinor = /^(\d+\.\d+(\.\d+)?)(t)$/;
|
const majorMinor = /^(\d+\.\d+(\.\d+)?)(t)$/;
|
||||||
if (majorMinor.test(versionSpec)) {
|
if (majorMinor.test(versionSpec)) {
|
||||||
return [versionSpec.replace(majorMinor, '$1'), '-freethreaded'];
|
return {version: versionSpec.replace(majorMinor, '$1'), freethreaded: true};
|
||||||
}
|
}
|
||||||
const devVersion = /^(\d+\.\d+)(t)(-dev)$/;
|
const devVersion = /^(\d+\.\d+)(t)(-dev)$/;
|
||||||
if (devVersion.test(versionSpec)) {
|
if (devVersion.test(versionSpec)) {
|
||||||
return [versionSpec.replace(devVersion, '$1$3'), '-freethreaded'];
|
return {
|
||||||
|
version: versionSpec.replace(devVersion, '$1$3'),
|
||||||
|
freethreaded: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return [versionSpec, ''];
|
return {version: versionSpec, freethreaded: false};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */
|
/** Convert versions like `3.8-dev` to a version like `~3.8.0-0`. */
|
||||||
@ -212,7 +223,7 @@ interface InstalledVersion {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Python's prelease versions look like `3.7.0b2`.
|
* Python's prelease versions look like `3.7.0b2`.
|
||||||
* This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-b2`.
|
* This is the one part of Python versioning that does not look like semantic versioning, which specifies `3.7.0-beta.2`.
|
||||||
* If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
|
* If the version spec contains prerelease versions, we need to convert them to the semantic version equivalent.
|
||||||
*
|
*
|
||||||
* For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
|
* For easier use of the action, we also map 'x.y' to allow pre-release before 'x.y.0' release if allowPreReleases is true
|
||||||
@ -221,9 +232,16 @@ export function pythonVersionToSemantic(
|
|||||||
versionSpec: string,
|
versionSpec: string,
|
||||||
allowPreReleases: boolean
|
allowPreReleases: boolean
|
||||||
) {
|
) {
|
||||||
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)/g;
|
const preleaseMap: {[key: string]: string} = {
|
||||||
|
a: 'alpha',
|
||||||
|
b: 'beta',
|
||||||
|
rc: 'rc'
|
||||||
|
};
|
||||||
|
const prereleaseVersion = /(\d+\.\d+\.\d+)(a|b|rc)(\d+)/g;
|
||||||
|
let result = versionSpec.replace(prereleaseVersion, (_, p1, p2, p3) => {
|
||||||
|
return `${p1}-${preleaseMap[p2]}.${p3}`;
|
||||||
|
});
|
||||||
const majorMinor = /^(\d+)\.(\d+)$/;
|
const majorMinor = /^(\d+)\.(\d+)$/;
|
||||||
let result = versionSpec.replace(prereleaseVersion, '$1-$2');
|
|
||||||
if (allowPreReleases) {
|
if (allowPreReleases) {
|
||||||
result = result.replace(majorMinor, '~$1.$2.0-0');
|
result = result.replace(majorMinor, '~$1.$2.0-0');
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ async function run() {
|
|||||||
const versions = resolveVersionInput();
|
const versions = resolveVersionInput();
|
||||||
const checkLatest = core.getBooleanInput('check-latest');
|
const checkLatest = core.getBooleanInput('check-latest');
|
||||||
const allowPreReleases = core.getBooleanInput('allow-prereleases');
|
const allowPreReleases = core.getBooleanInput('allow-prereleases');
|
||||||
|
const freethreaded = core.getBooleanInput('freethreaded');
|
||||||
|
|
||||||
if (versions.length) {
|
if (versions.length) {
|
||||||
let pythonVersion = '';
|
let pythonVersion = '';
|
||||||
@ -132,7 +133,8 @@ async function run() {
|
|||||||
arch,
|
arch,
|
||||||
updateEnvironment,
|
updateEnvironment,
|
||||||
checkLatest,
|
checkLatest,
|
||||||
allowPreReleases
|
allowPreReleases,
|
||||||
|
freethreaded
|
||||||
);
|
);
|
||||||
pythonVersion = installed.version;
|
pythonVersion = installed.version;
|
||||||
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
core.info(`Successfully set up ${installed.impl} (${pythonVersion})`);
|
||||||
|
Loading…
Reference in New Issue
Block a user