2021-08-15 06:21:08 +00:00
|
|
|
import { getGlobalConfig } from '../../config/global';
|
2021-08-03 11:38:46 +00:00
|
|
|
import { PypiDatasource } from '../../datasource/pypi';
|
2020-05-01 16:03:48 +00:00
|
|
|
import { logger } from '../../logger';
|
2020-03-09 04:34:16 +00:00
|
|
|
import { SkipReason } from '../../types';
|
2020-05-01 16:03:48 +00:00
|
|
|
import { exec } from '../../util/exec';
|
|
|
|
import { isSkipComment } from '../../util/ignore';
|
|
|
|
import { dependencyPattern } from '../pip_requirements/extract';
|
2021-03-02 20:44:55 +00:00
|
|
|
import type { ExtractConfig, PackageDependency, PackageFile } from '../types';
|
2021-05-11 17:08:02 +00:00
|
|
|
import type { PythonSetup } from './types';
|
|
|
|
import { getExtractFile, parseReport } from './util';
|
2018-11-15 17:42:01 +00:00
|
|
|
|
2021-02-02 09:39:26 +00:00
|
|
|
export const pythonVersions = ['python', 'python3', 'python3.8', 'python3.9'];
|
2019-08-22 15:42:35 +00:00
|
|
|
let pythonAlias: string | null = null;
|
2018-11-15 17:42:01 +00:00
|
|
|
|
2019-12-20 07:51:20 +00:00
|
|
|
export function resetModule(): void {
|
|
|
|
pythonAlias = null;
|
|
|
|
}
|
|
|
|
|
2019-08-22 15:42:35 +00:00
|
|
|
export function parsePythonVersion(str: string): number[] {
|
2019-03-17 14:54:31 +00:00
|
|
|
const arr = str.split(' ')[1].split('.');
|
|
|
|
return [parseInt(arr[0], 10), parseInt(arr[1], 10)];
|
|
|
|
}
|
|
|
|
|
2019-08-22 15:42:35 +00:00
|
|
|
export async function getPythonAlias(): Promise<string> {
|
2019-03-17 14:54:31 +00:00
|
|
|
if (pythonAlias) {
|
|
|
|
return pythonAlias;
|
|
|
|
}
|
|
|
|
pythonAlias = pythonVersions[0]; // fallback to 'python'
|
|
|
|
for (const pythonVersion of pythonVersions) {
|
|
|
|
try {
|
|
|
|
const { stdout, stderr } = await exec(`${pythonVersion} --version`);
|
|
|
|
const version = parsePythonVersion(stdout || stderr);
|
|
|
|
if (version[0] >= 3 && version[1] >= 7) {
|
|
|
|
pythonAlias = pythonVersion;
|
2021-02-02 09:39:26 +00:00
|
|
|
break;
|
2019-03-17 14:54:31 +00:00
|
|
|
}
|
2020-07-18 08:03:05 +00:00
|
|
|
} catch (err) {
|
2019-03-17 14:54:31 +00:00
|
|
|
logger.debug(`${pythonVersion} alias not found`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pythonAlias;
|
|
|
|
}
|
2020-04-24 14:17:45 +00:00
|
|
|
|
2019-07-25 06:17:19 +00:00
|
|
|
export async function extractSetupFile(
|
|
|
|
_content: string,
|
|
|
|
packageFile: string,
|
|
|
|
config: ExtractConfig
|
|
|
|
): Promise<PythonSetup> {
|
2020-07-18 08:03:05 +00:00
|
|
|
let cmd = 'python';
|
2021-01-18 13:11:29 +00:00
|
|
|
const extractPy = await getExtractFile();
|
2020-06-25 06:34:15 +00:00
|
|
|
const args = [`"${extractPy}"`, `"${packageFile}"`];
|
2021-08-16 16:00:51 +00:00
|
|
|
if (getGlobalConfig().binarySource !== 'docker') {
|
2020-02-24 07:43:01 +00:00
|
|
|
logger.debug('Running python via global command');
|
2019-03-17 14:54:31 +00:00
|
|
|
cmd = await getPythonAlias();
|
2018-11-15 17:42:01 +00:00
|
|
|
}
|
|
|
|
logger.debug({ cmd, args }, 'python command');
|
2019-05-25 04:23:44 +00:00
|
|
|
const res = await exec(`${cmd} ${args.join(' ')}`, {
|
2021-01-18 13:11:29 +00:00
|
|
|
cwdFile: packageFile,
|
2020-08-11 07:57:15 +00:00
|
|
|
timeout: 30000,
|
2020-07-18 08:03:05 +00:00
|
|
|
docker: {
|
2021-03-15 13:23:38 +00:00
|
|
|
image: 'python',
|
2020-07-18 08:03:05 +00:00
|
|
|
},
|
2019-05-25 04:23:44 +00:00
|
|
|
});
|
|
|
|
if (res.stderr) {
|
2020-01-15 14:43:45 +00:00
|
|
|
const stderr = res.stderr
|
|
|
|
.replace(/.*\n\s*import imp/, '')
|
|
|
|
.trim()
|
|
|
|
.replace('fatal: No names found, cannot describe anything.', '');
|
2019-02-18 11:20:55 +00:00
|
|
|
if (stderr.length) {
|
2020-01-15 14:43:45 +00:00
|
|
|
logger.warn({ stdout: res.stdout, stderr }, 'Error in read setup file');
|
2019-02-18 11:20:55 +00:00
|
|
|
}
|
2018-11-15 17:42:01 +00:00
|
|
|
}
|
2021-01-18 13:11:29 +00:00
|
|
|
return parseReport(packageFile);
|
2018-11-15 17:42:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-25 06:17:19 +00:00
|
|
|
export async function extractPackageFile(
|
|
|
|
content: string,
|
|
|
|
packageFile: string,
|
|
|
|
config: ExtractConfig
|
2019-08-22 15:42:35 +00:00
|
|
|
): Promise<PackageFile | null> {
|
2018-11-15 17:42:01 +00:00
|
|
|
logger.debug('pip_setup.extractPackageFile()');
|
2019-07-25 06:17:19 +00:00
|
|
|
let setup: PythonSetup;
|
2018-11-15 17:42:01 +00:00
|
|
|
try {
|
|
|
|
setup = await extractSetupFile(content, packageFile, config);
|
|
|
|
} catch (err) {
|
2021-05-15 19:48:26 +00:00
|
|
|
logger.debug({ err, content, packageFile }, 'Failed to read setup.py file');
|
2020-10-26 20:11:31 +00:00
|
|
|
}
|
|
|
|
if (!setup) {
|
2018-11-15 17:42:01 +00:00
|
|
|
return null;
|
|
|
|
}
|
2019-07-25 06:17:19 +00:00
|
|
|
const requires: string[] = [];
|
2018-11-15 17:42:01 +00:00
|
|
|
if (setup.install_requires) {
|
|
|
|
requires.push(...setup.install_requires);
|
|
|
|
}
|
|
|
|
if (setup.extras_require) {
|
|
|
|
for (const req of Object.values(setup.extras_require)) {
|
|
|
|
requires.push(...req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const regex = new RegExp(`^${dependencyPattern}`);
|
|
|
|
const lines = content.split('\n');
|
|
|
|
const deps = requires
|
2020-04-12 16:09:36 +00:00
|
|
|
.map((req) => {
|
|
|
|
const lineNumber = lines.findIndex((l) => l.includes(req));
|
2018-11-15 17:42:01 +00:00
|
|
|
if (lineNumber === -1) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const rawline = lines[lineNumber];
|
2019-07-25 06:17:19 +00:00
|
|
|
let dep: PackageDependency = {};
|
2020-04-12 16:09:36 +00:00
|
|
|
const [, comment] = rawline.split('#').map((part) => part.trim());
|
2018-11-15 17:42:01 +00:00
|
|
|
if (isSkipComment(comment)) {
|
2020-03-09 04:34:16 +00:00
|
|
|
dep.skipReason = SkipReason.Ignored;
|
2018-11-15 17:42:01 +00:00
|
|
|
}
|
|
|
|
regex.lastIndex = 0;
|
|
|
|
const matches = regex.exec(req);
|
|
|
|
if (!matches) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const [, depName, , currentValue] = matches;
|
|
|
|
dep = {
|
|
|
|
...dep,
|
|
|
|
depName,
|
|
|
|
currentValue,
|
2019-07-29 06:07:34 +00:00
|
|
|
managerData: { lineNumber },
|
2021-08-03 11:38:46 +00:00
|
|
|
datasource: PypiDatasource.id,
|
2018-11-15 17:42:01 +00:00
|
|
|
};
|
|
|
|
return dep;
|
|
|
|
})
|
2019-01-30 20:32:38 +00:00
|
|
|
.filter(Boolean)
|
|
|
|
.sort((a, b) =>
|
2019-07-29 06:07:34 +00:00
|
|
|
a.managerData.lineNumber === b.managerData.lineNumber
|
2019-08-28 04:46:48 +00:00
|
|
|
? a.depName.localeCompare(b.depName)
|
2019-07-29 06:07:34 +00:00
|
|
|
: a.managerData.lineNumber - b.managerData.lineNumber
|
2019-01-30 20:32:38 +00:00
|
|
|
);
|
2018-11-15 17:42:01 +00:00
|
|
|
if (!deps.length) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return { deps };
|
|
|
|
}
|