diff --git a/lib/manager/pipenv/extract.js b/lib/manager/pipenv/extract.js index e378ed5dd437d8d641bd40548dbadd44eb4f358f..2a404a366a8fda5aac448256a9ff41d041f13529 100644 --- a/lib/manager/pipenv/extract.js +++ b/lib/manager/pipenv/extract.js @@ -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; } diff --git a/test/manager/pipenv/_fixtures/Pipfile3 b/test/manager/pipenv/_fixtures/Pipfile3 new file mode 100644 index 0000000000000000000000000000000000000000..c42901a043ca54abaeb8dcd75d1c4abab8159217 --- /dev/null +++ b/test/manager/pipenv/_fixtures/Pipfile3 @@ -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" diff --git a/test/manager/pipenv/extract.spec.js b/test/manager/pipenv/extract.spec.js index 4ce37a49d1c0ebf38d350f334158845e4112bf62..ec888afa28e0f90c04085f721e726b57fa5651b8 100644 --- a/test/manager/pipenv/extract.spec.js +++ b/test/manager/pipenv/extract.spec.js @@ -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"';