From 2482bebdad9baca66b2d85ce43e7fc93668ff1cf Mon Sep 17 00:00:00 2001 From: Jonas Kuske Date: Wed, 12 Nov 2025 19:17:40 +0100 Subject: [PATCH 1/2] feat: use poetry deps to enrich equivalent pep621/pep735 deps --- .../poetry/__snapshots__/extract.spec.ts.snap | 1 + lib/modules/manager/poetry/extract.spec.ts | 43 +++++++++++++++++++ lib/modules/manager/poetry/schema.ts | 42 ++++++++++++------ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/lib/modules/manager/poetry/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/poetry/__snapshots__/extract.spec.ts.snap index 5e63f9d8318..d334f1955e6 100644 --- a/lib/modules/manager/poetry/__snapshots__/extract.spec.ts.snap +++ b/lib/modules/manager/poetry/__snapshots__/extract.spec.ts.snap @@ -389,6 +389,7 @@ exports[`modules/manager/poetry/extract > extractPackageFile() > extracts multip "datasource": "pypi", "depName": "dep5", "depType": "dependencies", + "managerData": {}, "skipReason": "unspecified-version", }, { diff --git a/lib/modules/manager/poetry/extract.spec.ts b/lib/modules/manager/poetry/extract.spec.ts index e4882209134..d4cffed4e85 100644 --- a/lib/modules/manager/poetry/extract.spec.ts +++ b/lib/modules/manager/poetry/extract.spec.ts @@ -616,4 +616,47 @@ describe('modules/manager/poetry/extract', () => { }, ]); }); + + it('enriches pep621/pep735 dependencies with poetry managerData', async () => { + const content = codeBlock` + [project] + dependencies = ["click"] + [dependency-groups] + typing = ["mypy==1.13.0", "types-requests"] + [tool.poetry.dependencies] + click = { source = "artifactory" } + [tool.poetry.group.typing.dependencies] + types-requests = { source = "artifactory" } + [tool.poetry.group.typing.dependencies.mypy] + source = "artifactory" + `; + const res = await extractPackageFile(content, 'pyproject.toml'); + expect(res?.deps).toMatchObject([ + { + packageName: 'click', + datasource: 'pypi', + depType: 'project.dependencies', + skipReason: 'unspecified-version', + depName: 'click', + managerData: { sourceName: 'artifactory' }, + }, + { + packageName: 'mypy', + datasource: 'pypi', + depType: 'dependency-groups', + currentValue: '==1.13.0', + currentVersion: '1.13.0', + depName: 'mypy', + managerData: { depGroup: 'typing', sourceName: 'artifactory' }, + }, + { + packageName: 'types-requests', + datasource: 'pypi', + depType: 'dependency-groups', + skipReason: 'unspecified-version', + depName: 'types-requests', + managerData: { depGroup: 'typing', sourceName: 'artifactory' }, + }, + ]); + }); }); diff --git a/lib/modules/manager/poetry/schema.ts b/lib/modules/manager/poetry/schema.ts index c2a55aea7d9..d0ab6f6600a 100644 --- a/lib/modules/manager/poetry/schema.ts +++ b/lib/modules/manager/poetry/schema.ts @@ -108,16 +108,17 @@ const PoetryPypiDependency = z.union([ z .object({ version: z.string().optional(), source: z.string().optional() }) .transform(({ version: currentValue, source }): PackageDependency => { + const managerData = { + ...(source ? { sourceName: source.toLowerCase() } : {}), + }; + if (!currentValue) { - return { datasource: PypiDatasource.id }; + return { datasource: PypiDatasource.id, managerData }; } return { datasource: PypiDatasource.id, - managerData: { - nestedVersion: true, - ...(source ? { sourceName: source.toLowerCase() } : {}), - }, + managerData: { ...managerData, nestedVersion: true }, currentValue, }; }) @@ -353,19 +354,34 @@ export const PoetryPyProject = Toml.pipe( deps.push(...dependencyGroups); + const projectDepsByName = [ + ...(projectDependencies ?? []), + ...(dependencyGroups ?? []), + ...(projectOptionalDependencies ?? []), + ].reduce( + (obj, dep) => ((obj[dep.depName!] = dep), obj), + {} as Record, + ); + const poetryDependencies = tool?.poetry?.dependencies; - if (poetryDependencies) { - deps.push(...poetryDependencies); - } const poetryDevDependencies = tool?.poetry?.['dev-dependencies']; - if (poetryDevDependencies) { - deps.push(...poetryDevDependencies); - } const poetryGroupDependencies = tool?.poetry?.group; - if (poetryGroupDependencies) { - deps.push(...poetryGroupDependencies); + + for (const poetryDep of [ + ...(poetryDependencies ?? []), + ...(poetryDevDependencies ?? []), + ...(poetryGroupDependencies ?? []), + ]) { + // When the same dep exists in project.dependencies or dependency-groups, + // Poetry just uses the Poetry dep for enrichment with source info. + if (poetryDep.depName && projectDepsByName[poetryDep.depName]) { + const dep = projectDepsByName[poetryDep.depName]; + dep.managerData = { ...poetryDep.managerData, ...dep.managerData }; + } else { + deps.push(poetryDep); + } } const packageFileVersion = tool?.poetry?.version; From 74b8807f9a73944bfec8571904c5eb974ae18ec3 Mon Sep 17 00:00:00 2001 From: Jonas Kuske Date: Thu, 13 Nov 2025 08:29:57 +0100 Subject: [PATCH 2/2] test: cover project.optional-dependencies in enrichment test --- lib/modules/manager/poetry/extract.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/modules/manager/poetry/extract.spec.ts b/lib/modules/manager/poetry/extract.spec.ts index d4cffed4e85..76e40be9646 100644 --- a/lib/modules/manager/poetry/extract.spec.ts +++ b/lib/modules/manager/poetry/extract.spec.ts @@ -621,10 +621,13 @@ describe('modules/manager/poetry/extract', () => { const content = codeBlock` [project] dependencies = ["click"] + [project.optional-dependencies] + other = ["zoom==1.0.0"] [dependency-groups] typing = ["mypy==1.13.0", "types-requests"] [tool.poetry.dependencies] click = { source = "artifactory" } + zoom = { source = "artifactory" } [tool.poetry.group.typing.dependencies] types-requests = { source = "artifactory" } [tool.poetry.group.typing.dependencies.mypy] @@ -640,6 +643,15 @@ describe('modules/manager/poetry/extract', () => { depName: 'click', managerData: { sourceName: 'artifactory' }, }, + { + packageName: 'zoom', + datasource: 'pypi', + depType: 'project.optional-dependencies', + currentValue: '==1.0.0', + currentVersion: '1.0.0', + depName: 'zoom', + managerData: { depGroup: 'other', sourceName: 'artifactory' }, + }, { packageName: 'mypy', datasource: 'pypi',