mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 04:22:12 +00:00
[python] Add support for pip3.10 and pip3.11 in vc build (#10648)
This commit is contained in:
5
.changeset/polite-peaches-boil.md
Normal file
5
.changeset/polite-peaches-boil.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@vercel/python': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add support for pip3.10 and pip3.11
|
||||||
@@ -22,10 +22,14 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/execa": "^0.9.0",
|
"@types/execa": "^0.9.0",
|
||||||
|
"@types/fs-extra": "11.0.2",
|
||||||
"@types/jest": "27.4.1",
|
"@types/jest": "27.4.1",
|
||||||
"@types/node": "14.18.33",
|
"@types/node": "14.18.33",
|
||||||
|
"@types/which": "3.0.0",
|
||||||
"@vercel/build-utils": "7.2.2",
|
"@vercel/build-utils": "7.2.2",
|
||||||
"execa": "^1.0.0",
|
"execa": "^1.0.0",
|
||||||
"jest-junit": "16.0.0"
|
"fs-extra": "11.1.1",
|
||||||
|
"jest-junit": "16.0.0",
|
||||||
|
"which": "3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { NowBuildError } from '@vercel/build-utils';
|
import { NowBuildError } from '@vercel/build-utils';
|
||||||
|
import which from 'which';
|
||||||
|
|
||||||
interface PythonVersion {
|
interface PythonVersion {
|
||||||
version: string;
|
version: string;
|
||||||
@@ -10,6 +11,18 @@ interface PythonVersion {
|
|||||||
|
|
||||||
// The order must be most recent first
|
// The order must be most recent first
|
||||||
const allOptions: PythonVersion[] = [
|
const allOptions: PythonVersion[] = [
|
||||||
|
{
|
||||||
|
version: '3.11',
|
||||||
|
pipPath: 'pip3.11',
|
||||||
|
pythonPath: 'python3.11',
|
||||||
|
runtime: 'python3.11',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '3.10',
|
||||||
|
pipPath: 'pip3.10',
|
||||||
|
pythonPath: 'python3.10',
|
||||||
|
runtime: 'python3.10',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
version: '3.9',
|
version: '3.9',
|
||||||
pipPath: 'pip3.9',
|
pipPath: 'pip3.9',
|
||||||
@@ -34,6 +47,7 @@ function getDevPythonVersion(): PythonVersion {
|
|||||||
runtime: 'python3',
|
runtime: 'python3',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLatestPythonVersion({
|
export function getLatestPythonVersion({
|
||||||
isDev,
|
isDev,
|
||||||
}: {
|
}: {
|
||||||
@@ -42,7 +56,16 @@ export function getLatestPythonVersion({
|
|||||||
if (isDev) {
|
if (isDev) {
|
||||||
return getDevPythonVersion();
|
return getDevPythonVersion();
|
||||||
}
|
}
|
||||||
return allOptions[0];
|
|
||||||
|
const selection = allOptions.find(isInstalled);
|
||||||
|
if (!selection) {
|
||||||
|
throw new NowBuildError({
|
||||||
|
code: 'PYTHON_NOT_FOUND',
|
||||||
|
link: 'http://vercel.link/python-version',
|
||||||
|
message: `Unable to find any supported Python versions.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSupportedPythonVersion({
|
export function getSupportedPythonVersion({
|
||||||
@@ -55,10 +78,13 @@ export function getSupportedPythonVersion({
|
|||||||
if (isDev) {
|
if (isDev) {
|
||||||
return getDevPythonVersion();
|
return getDevPythonVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
let selection = getLatestPythonVersion({ isDev: false });
|
let selection = getLatestPythonVersion({ isDev: false });
|
||||||
|
|
||||||
if (typeof pipLockPythonVersion === 'string') {
|
if (typeof pipLockPythonVersion === 'string') {
|
||||||
const found = allOptions.find(o => o.version === pipLockPythonVersion);
|
const found = allOptions.find(
|
||||||
|
o => o.version === pipLockPythonVersion && isInstalled(o)
|
||||||
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
selection = found;
|
selection = found;
|
||||||
} else {
|
} else {
|
||||||
@@ -90,3 +116,10 @@ function isDiscontinued({ discontinueDate }: PythonVersion): boolean {
|
|||||||
const today = Date.now();
|
const today = Date.now();
|
||||||
return discontinueDate !== undefined && discontinueDate.getTime() <= today;
|
return discontinueDate !== undefined && discontinueDate.getTime() <= today;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInstalled({ pipPath, pythonPath }: PythonVersion): boolean {
|
||||||
|
return (
|
||||||
|
Boolean(which.sync(pipPath, { nothrow: true })) &&
|
||||||
|
Boolean(which.sync(pythonPath, { nothrow: true }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
67
packages/python/test/unit.test.ts
vendored
67
packages/python/test/unit.test.ts
vendored
@@ -1,8 +1,17 @@
|
|||||||
import { getSupportedPythonVersion } from '../src/version';
|
import { getSupportedPythonVersion } from '../src/version';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import path from 'path';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
|
||||||
|
const tmpPythonDir = path.join(
|
||||||
|
tmpdir(),
|
||||||
|
`vc-test-python-${Math.floor(Math.random() * 1e6)}`
|
||||||
|
);
|
||||||
let warningMessages: string[];
|
let warningMessages: string[];
|
||||||
const originalConsoleWarn = console.warn;
|
const originalConsoleWarn = console.warn;
|
||||||
const realDateNow = Date.now.bind(global.Date);
|
const realDateNow = Date.now.bind(global.Date);
|
||||||
|
const origPath = process.env.PATH;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
warningMessages = [];
|
warningMessages = [];
|
||||||
console.warn = m => {
|
console.warn = m => {
|
||||||
@@ -13,15 +22,19 @@ beforeEach(() => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
console.warn = originalConsoleWarn;
|
console.warn = originalConsoleWarn;
|
||||||
global.Date.now = realDateNow;
|
global.Date.now = realDateNow;
|
||||||
|
process.env.PATH = origPath;
|
||||||
|
if (fs.existsSync(tmpPythonDir)) {
|
||||||
|
fs.removeSync(tmpPythonDir);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should only match supported versions, otherwise throw an error', async () => {
|
it('should only match supported versions, otherwise throw an error', () => {
|
||||||
expect(
|
makeMockPython('3.9');
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: '3.9' })
|
const result = getSupportedPythonVersion({ pipLockPythonVersion: '3.9' });
|
||||||
).toHaveProperty('runtime', 'python3.9');
|
expect(result).toHaveProperty('runtime', 'python3.9');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore minor version in vercel dev', async () => {
|
it('should ignore minor version in vercel dev', () => {
|
||||||
expect(
|
expect(
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: '3.9', isDev: true })
|
getSupportedPythonVersion({ pipLockPythonVersion: '3.9', isDev: true })
|
||||||
).toHaveProperty('runtime', 'python3');
|
).toHaveProperty('runtime', 'python3');
|
||||||
@@ -34,24 +47,34 @@ it('should ignore minor version in vercel dev', async () => {
|
|||||||
expect(warningMessages).toStrictEqual([]);
|
expect(warningMessages).toStrictEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select latest version when no Piplock detected', async () => {
|
it('should select latest supported installed version when no Piplock detected', () => {
|
||||||
expect(
|
const result = getSupportedPythonVersion({ pipLockPythonVersion: undefined });
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: undefined })
|
expect(result).toHaveProperty('runtime');
|
||||||
).toHaveProperty('runtime', 'python3.9');
|
expect(result.runtime).toMatch(/^python3\.\d+$/);
|
||||||
expect(warningMessages).toStrictEqual([]);
|
expect(warningMessages).toStrictEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should select latest version and warn when invalid Piplock detected', async () => {
|
it('should select latest supported installed version and warn when invalid Piplock detected', () => {
|
||||||
expect(
|
const result = getSupportedPythonVersion({ pipLockPythonVersion: '999' });
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: '999' })
|
expect(result).toHaveProperty('runtime');
|
||||||
).toHaveProperty('runtime', 'python3.9');
|
expect(result.runtime).toMatch(/^python3\.\d+$/);
|
||||||
expect(warningMessages).toStrictEqual([
|
expect(warningMessages).toStrictEqual([
|
||||||
'Warning: Python version "999" detected in Pipfile.lock is invalid and will be ignored. http://vercel.link/python-version',
|
'Warning: Python version "999" detected in Pipfile.lock is invalid and will be ignored. http://vercel.link/python-version',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for discontinued versions', async () => {
|
it('should throw if python not found', () => {
|
||||||
|
process.env.PATH = '.';
|
||||||
|
expect(() =>
|
||||||
|
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
|
||||||
|
).toThrow('Unable to find any supported Python versions.');
|
||||||
|
expect(warningMessages).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw for discontinued versions', () => {
|
||||||
global.Date.now = () => new Date('2022-07-31').getTime();
|
global.Date.now = () => new Date('2022-07-31').getTime();
|
||||||
|
makeMockPython('3.6');
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
|
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
|
||||||
).toThrow(
|
).toThrow(
|
||||||
@@ -60,8 +83,9 @@ it('should throw for discontinued versions', async () => {
|
|||||||
expect(warningMessages).toStrictEqual([]);
|
expect(warningMessages).toStrictEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should warn for deprecated versions, soon to be discontinued', async () => {
|
it('should warn for deprecated versions, soon to be discontinued', () => {
|
||||||
global.Date.now = () => new Date('2021-07-01').getTime();
|
global.Date.now = () => new Date('2021-07-01').getTime();
|
||||||
|
makeMockPython('3.6');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
|
getSupportedPythonVersion({ pipLockPythonVersion: '3.6' })
|
||||||
@@ -70,3 +94,16 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
|||||||
'Error: Python version "3.6" detected in Pipfile.lock has reached End-of-Life. Deployments created on or after 2022-07-18 will fail to build. http://vercel.link/python-version',
|
'Error: Python version "3.6" detected in Pipfile.lock has reached End-of-Life. Deployments created on or after 2022-07-18 will fail to build. http://vercel.link/python-version',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function makeMockPython(version: string) {
|
||||||
|
fs.mkdirSync(tmpPythonDir);
|
||||||
|
for (const name of ['python', 'pip']) {
|
||||||
|
const bin = path.join(
|
||||||
|
tmpPythonDir,
|
||||||
|
`${name}${version}${process.platform === 'win32' ? '.exe' : ''}`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(bin, '');
|
||||||
|
fs.chmodSync(bin, 0o755);
|
||||||
|
}
|
||||||
|
process.env.PATH = `${tmpPythonDir}${path.delimiter}${process.env.PATH}`;
|
||||||
|
}
|
||||||
|
|||||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -1305,21 +1305,33 @@ importers:
|
|||||||
'@types/execa':
|
'@types/execa':
|
||||||
specifier: ^0.9.0
|
specifier: ^0.9.0
|
||||||
version: 0.9.0
|
version: 0.9.0
|
||||||
|
'@types/fs-extra':
|
||||||
|
specifier: 11.0.2
|
||||||
|
version: 11.0.2
|
||||||
'@types/jest':
|
'@types/jest':
|
||||||
specifier: 27.4.1
|
specifier: 27.4.1
|
||||||
version: 27.4.1
|
version: 27.4.1
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: 14.18.33
|
specifier: 14.18.33
|
||||||
version: 14.18.33
|
version: 14.18.33
|
||||||
|
'@types/which':
|
||||||
|
specifier: 3.0.0
|
||||||
|
version: 3.0.0
|
||||||
'@vercel/build-utils':
|
'@vercel/build-utils':
|
||||||
specifier: 7.2.2
|
specifier: 7.2.2
|
||||||
version: link:../build-utils
|
version: link:../build-utils
|
||||||
execa:
|
execa:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
fs-extra:
|
||||||
|
specifier: 11.1.1
|
||||||
|
version: 11.1.1
|
||||||
jest-junit:
|
jest-junit:
|
||||||
specifier: 16.0.0
|
specifier: 16.0.0
|
||||||
version: 16.0.0
|
version: 16.0.0
|
||||||
|
which:
|
||||||
|
specifier: 3.0.0
|
||||||
|
version: 3.0.0
|
||||||
|
|
||||||
packages/redwood:
|
packages/redwood:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4193,7 +4205,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/http-cache-semantics': 4.0.1
|
'@types/http-cache-semantics': 4.0.1
|
||||||
'@types/keyv': 3.1.4
|
'@types/keyv': 3.1.4
|
||||||
'@types/node': 14.18.33
|
'@types/node': 16.18.11
|
||||||
'@types/responselike': 1.0.0
|
'@types/responselike': 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -4315,6 +4327,13 @@ packages:
|
|||||||
'@types/node': 16.18.11
|
'@types/node': 16.18.11
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/fs-extra@11.0.2:
|
||||||
|
resolution: {integrity: sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==}
|
||||||
|
dependencies:
|
||||||
|
'@types/jsonfile': 6.1.1
|
||||||
|
'@types/node': 16.18.11
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/fs-extra@5.1.0:
|
/@types/fs-extra@5.1.0:
|
||||||
resolution: {integrity: sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==}
|
resolution: {integrity: sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4493,7 +4512,7 @@ packages:
|
|||||||
/@types/keyv@3.1.4:
|
/@types/keyv@3.1.4:
|
||||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 14.18.33
|
'@types/node': 16.18.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/load-json-file@2.0.7:
|
/@types/load-json-file@2.0.7:
|
||||||
@@ -4669,7 +4688,7 @@ packages:
|
|||||||
/@types/responselike@1.0.0:
|
/@types/responselike@1.0.0:
|
||||||
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
|
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 14.18.33
|
'@types/node': 16.18.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/retry@0.12.2:
|
/@types/retry@0.12.2:
|
||||||
@@ -9139,6 +9158,15 @@ packages:
|
|||||||
jsonfile: 6.1.0
|
jsonfile: 6.1.0
|
||||||
universalify: 2.0.0
|
universalify: 2.0.0
|
||||||
|
|
||||||
|
/fs-extra@11.1.1:
|
||||||
|
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
|
||||||
|
engines: {node: '>=14.14'}
|
||||||
|
dependencies:
|
||||||
|
graceful-fs: 4.2.11
|
||||||
|
jsonfile: 6.1.0
|
||||||
|
universalify: 2.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
/fs-extra@7.0.0:
|
/fs-extra@7.0.0:
|
||||||
resolution: {integrity: sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==}
|
resolution: {integrity: sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==}
|
||||||
engines: {node: '>=6 <7 || >=8'}
|
engines: {node: '>=6 <7 || >=8'}
|
||||||
|
|||||||
Reference in New Issue
Block a user