diff --git a/lib/modules/manager/pipenv/__fixtures__/Pipfile1 b/lib/modules/manager/pipenv/__fixtures__/Pipfile1 index 2780a7a308442a..2eca96c310aacb 100644 --- a/lib/modules/manager/pipenv/__fixtures__/Pipfile1 +++ b/lib/modules/manager/pipenv/__fixtures__/Pipfile1 @@ -10,6 +10,8 @@ name = "private-pypi" [packages] some-package = "==0.3.1" +"splinter[django]" = "==3.43.1" +"requests[django][security][something]" = "==1.1.1" some-other-package = "==1.0.0" "_invalid-package" = "==1.0.0" invalid-version = "==0 0" diff --git a/lib/modules/manager/pipenv/extract.spec.ts b/lib/modules/manager/pipenv/extract.spec.ts index fc90ecc04283f3..608047817f84ea 100644 --- a/lib/modules/manager/pipenv/extract.spec.ts +++ b/lib/modules/manager/pipenv/extract.spec.ts @@ -33,6 +33,20 @@ describe('modules/manager/pipenv/extract', () => { currentVersion: '0.3.1', datasource: 'pypi', }, + { + depType: 'packages', + depName: 'splinter', + currentValue: '==3.43.1', + currentVersion: '3.43.1', + datasource: 'pypi', + }, + { + depType: 'packages', + depName: 'requests', + currentValue: '==1.1.1', + currentVersion: '1.1.1', + datasource: 'pypi', + }, { depType: 'packages', depName: 'some-other-package', @@ -80,7 +94,7 @@ describe('modules/manager/pipenv/extract', () => { ], }); - expect(res?.deps.filter((dep) => !dep.skipReason)).toHaveLength(4); + expect(res?.deps.filter((dep) => !dep.skipReason)).toHaveLength(6); }); it('marks packages with "extras" as skipReason === unspecified-version', async () => { diff --git a/lib/modules/manager/pipenv/extract.ts b/lib/modules/manager/pipenv/extract.ts index fb0cf43d6ae268..97ccf1a5350d51 100644 --- a/lib/modules/manager/pipenv/extract.ts +++ b/lib/modules/manager/pipenv/extract.ts @@ -10,7 +10,10 @@ import type { PackageDependency, PackageFileContent } from '../types'; import type { PipFile } from './types'; // based on https://www.python.org/dev/peps/pep-0508/#names -const packageRegex = regEx(/^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$/i); +export const packagePattern = '[A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9]'; +export const extrasPattern = '(?:\\s*\\[[^\\]]+\\])*'; +const packageRegex = regEx(`^(${packagePattern})(${extrasPattern})$`, 'i'); + const rangePattern: string = RANGE_PATTERN; const specifierPartPattern = `\\s*${rangePattern.replace( @@ -30,7 +33,9 @@ function extractFromSection( const deps = Object.entries(pipfileSection) .map((x) => { - const [depName, requirements] = x; + const [packageNameString, requirements] = x; + let depName = packageNameString; + let currentValue: string | undefined; let nestedVersion = false; let skipReason: SkipReason | undefined; @@ -52,10 +57,12 @@ function extractFromSection( skipReason = 'unspecified-version'; } if (!skipReason) { - const packageMatches = packageRegex.exec(depName); - if (!packageMatches) { + const packageMatches = packageRegex.exec(packageNameString); + if (packageMatches) { + depName = packageMatches[1]; + } else { logger.debug( - `Skipping dependency with malformed package name "${depName}".`, + `Skipping dependency with malformed package name "${packageNameString}".`, ); skipReason = 'invalid-name'; }