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 toml = require('toml');
const is = require('@sindresorhus/is');
// based on https://www.python.org/dev/peps/pep-0508/#names // 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; 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 []; return [];
} }
const specifierRegex = new RegExp(`^${specifierPattern}$`); 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 => { .map(x => {
const [depName, requirements] = x; const [depName, requirements] = x;
let currentValue; let currentValue;
@ -63,13 +71,16 @@ function extractFromSection(pipfile, section, registryUrls) {
} }
const packageMatches = packageRegex.exec(depName); const packageMatches = packageRegex.exec(depName);
const specifierMatches = specifierRegex.exec(currentValue); const specifierMatches = specifierRegex.exec(currentValue);
let skipReason;
if (!packageMatches) { if (!packageMatches) {
logger.debug( logger.debug(
`Skipping dependency with malformed package name "${depName}".` `Skipping dependency with malformed package name "${depName}".`
); );
return null; return null;
} }
if (!specifierMatches) { if (currentValue === '*') {
skipReason = 'any-version';
} else if (!specifierMatches) {
logger.debug( logger.debug(
`Skipping dependency with malformed version specifier "${currentValue}".` `Skipping dependency with malformed version specifier "${currentValue}".`
); );
@ -82,6 +93,9 @@ function extractFromSection(pipfile, section, registryUrls) {
datasource: 'pypi', datasource: 'pypi',
depType: section, depType: section,
}; };
if (skipReason) {
dep.skipReason = skipReason;
}
if (registryUrls) { if (registryUrls) {
dep.registryUrls = 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', 'test/manager/pipenv/_fixtures/Pipfile2',
'utf8' 'utf8'
); );
const pipfile3 = fs.readFileSync(
'test/manager/pipenv/_fixtures/Pipfile3',
'utf8'
);
describe('lib/manager/pipenv/extract', () => { describe('lib/manager/pipenv/extract', () => {
describe('extractPackageFile()', () => { describe('extractPackageFile()', () => {
@ -27,6 +31,16 @@ describe('lib/manager/pipenv/extract', () => {
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
expect(res).toHaveLength(4); 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', () => { it('extracts multiple dependencies', () => {
const res = extractPackageFile(pipfile2, config).deps; const res = extractPackageFile(pipfile2, config).deps;
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
@ -36,7 +50,7 @@ describe('lib/manager/pipenv/extract', () => {
const content = const content =
'[packages]\r\nflask = {git = "https://github.com/pallets/flask.git"}\r\nwerkzeug = ">=0.14"'; '[packages]\r\nflask = {git = "https://github.com/pallets/flask.git"}\r\nwerkzeug = ">=0.14"';
const res = extractPackageFile(content, config).deps; const res = extractPackageFile(content, config).deps;
expect(res).toHaveLength(1); expect(res.filter(r => !r.skipReason)).toHaveLength(1);
}); });
it('ignores invalid package names', () => { it('ignores invalid package names', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\n_invalid = "==1.0.0"'; 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', () => { it('ignores relative path dependencies', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\ntest = {path = "."}'; const content = '[packages]\r\nfoo = "==1.0.0"\r\ntest = {path = "."}';
const res = extractPackageFile(content, config).deps; const res = extractPackageFile(content, config).deps;
expect(res).toHaveLength(1); expect(res.filter(r => !r.skipReason)).toHaveLength(1);
}); });
it('ignores invalid versions', () => { it('ignores invalid versions', () => {
const content = '[packages]\r\nfoo = "==1.0.0"\r\nsome-package = "==0 0"'; const content = '[packages]\r\nfoo = "==1.0.0"\r\nsome-package = "==0 0"';