fix(pipenv): detect 'any-version' packages (#3544)

This commit is contained in:
Dmitry 2019-04-22 09:19:05 +03:00 committed by Rhys Arkins
parent a6e8ea41de
commit 55fb2de89e
3 changed files with 55 additions and 4 deletions

View file

@ -1,4 +1,5 @@
const toml = require('toml');
const is = require('@sindresorhus/is');
// based on https://www.python.org/dev/peps/pep-0508/#names
const packageRegex = /^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$/i;
@ -43,7 +44,14 @@ function extractFromSection(pipfile, section, registryUrls) {
return [];
}
const specifierRegex = new RegExp(`^${specifierPattern}$`);
const deps = Object.entries(pipfile[section])
const pipfileSection = pipfile[section];
Object.keys(pipfileSection).forEach(key => {
if (is.object(pipfileSection[key]))
pipfileSection[key].version = pipfileSection[key].version || '*';
});
const deps = Object.entries(pipfileSection)
.map(x => {
const [depName, requirements] = x;
let currentValue;
@ -63,13 +71,16 @@ function extractFromSection(pipfile, section, registryUrls) {
}
const packageMatches = packageRegex.exec(depName);
const specifierMatches = specifierRegex.exec(currentValue);
let skipReason;
if (!packageMatches) {
logger.debug(
`Skipping dependency with malformed package name "${depName}".`
);
return null;
}
if (!specifierMatches) {
if (currentValue === '*') {
skipReason = 'any-version';
} else if (!specifierMatches) {
logger.debug(
`Skipping dependency with malformed version specifier "${currentValue}".`
);
@ -82,6 +93,9 @@ function extractFromSection(pipfile, section, registryUrls) {
datasource: 'pypi',
depType: section,
};
if (skipReason) {
dep.skipReason = skipReason;
}
if (registryUrls) {
dep.registryUrls = registryUrls;
}

View file

@ -0,0 +1,23 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[[source]]
url = "http://example.com/private-pypi/"
verify_ssl = false
name = "private-pypi"
[packages]
# all of these version specifiers make pipenv use the same version as "*"
raven = {extras = ['flask']}
Flask = "*"
Flask-Caching = '*'
flask-mako = {}
Flask-SQLAlchemy = {version = "*"}
Flask-Login = {editable = true}
[dev-packages]
[requires]
python_version = "3.6"

View file

@ -9,6 +9,10 @@ const pipfile2 = fs.readFileSync(
'test/manager/pipenv/_fixtures/Pipfile2',
'utf8'
);
const pipfile3 = fs.readFileSync(
'test/manager/pipenv/_fixtures/Pipfile3',
'utf8'
);
describe('lib/manager/pipenv/extract', () => {
describe('extractPackageFile()', () => {
@ -27,6 +31,16 @@ describe('lib/manager/pipenv/extract', () => {
expect(res).toMatchSnapshot();
expect(res).toHaveLength(4);
});
it('marks packages with "extras" as skipReason === any-version', () => {
const res = extractPackageFile(pipfile3, {
extends: ['config:base'],
pipenv: { enabled: true },
pip_setup: { enabled: true },
labels: ['dependencies'],
}).deps;
expect(res.filter(r => !r.skipReason)).toHaveLength(0);
expect(res.filter(r => r.skipReason)).toHaveLength(6);
});
it('extracts multiple dependencies', () => {
const res = extractPackageFile(pipfile2, config).deps;
expect(res).toMatchSnapshot();
@ -36,7 +50,7 @@ describe('lib/manager/pipenv/extract', () => {
const content =
'[packages]\r\nflask = {git = "https://github.com/pallets/flask.git"}\r\nwerkzeug = ">=0.14"';
const res = extractPackageFile(content, config).deps;
expect(res).toHaveLength(1);
expect(res.filter(r => !r.skipReason)).toHaveLength(1);
});
it('ignores invalid package names', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\n_invalid = "==1.0.0"';
@ -46,7 +60,7 @@ describe('lib/manager/pipenv/extract', () => {
it('ignores relative path dependencies', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\ntest = {path = "."}';
const res = extractPackageFile(content, config).deps;
expect(res).toHaveLength(1);
expect(res.filter(r => !r.skipReason)).toHaveLength(1);
});
it('ignores invalid versions', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\nsome-package = "==0 0"';