import { parse } from '@iarna/toml' ; import is from '@sindresorhus/is' ; import { PypiDatasource } from '../../datasource/pypi' ; import { logger } from '../../logger' ; import { SkipReason } from '../../types' ; import { getSiblingFileName , localPathExists , readLocalFile , } from '../../util/fs' ; import * as pep440Versioning from '../../versioning/pep440' ; import * as poetryVersioning from '../../versioning/poetry' ; import type { PackageDependency , PackageFile } from '../types' ; import type { PoetryFile , PoetryLock , PoetryLockSection , PoetrySection , } from './types' ; function extractFromSection ( parsedFile , section , poetryLockfile ) { const deps = [ ] ; const sectionContent = parsedFile . tool . poetry [ section ] ; if ( ! sectionContent ) { return [ ] ; } Object . keys ( sectionContent ) . forEach ( ( depName ) => { if ( depName === 'python' ) { return ; } let skipReason ; let currentValue = sectionContent [ depName ] ; let nestedVersion = false ; if ( typeof currentValue !== 'string' ) { const version = currentValue . version ; const path = currentValue . path ; const git = currentValue . git ; if ( version ) { currentValue = version ; nestedVersion = true ; if ( path ) { skipReason = SkipReason . PathDependency ; } if ( git ) { skipReason = SkipReason . GitDependency ; } } else if ( path ) { currentValue = '' ; skipReason = SkipReason . PathDependency ; } else if ( git ) { currentValue = '' ; skipReason = SkipReason . GitDependency ; } else { currentValue = '' ; skipReason = SkipReason . MultipleConstraintDep ; } } const dep = { depName , depType : section , currentValue : currentValue as string , managerData : { nestedVersion } , datasource : PypiDatasource . id , } ; if ( dep . depName in poetryLockfile ) { dep . lockedVersion = poetryLockfile [ dep . depName ] . version ; } if ( skipReason ) { dep . skipReason = skipReason ; } else if ( pep440Versioning . isValid ( dep . currentValue ) ) { dep . versioning = pep440Versioning . id ; } else if ( poetryVersioning . isValid ( dep . currentValue ) ) { dep . versioning = poetryVersioning . id ; } else { dep . skipReason = SkipReason . UnknownVersion ; } deps . push ( dep ) ; } ) ; return deps ; } function extractRegistries ( pyprojectfile ) { const sources = pyprojectfile . tool ?. poetry ?. source ; if ( ! Array . isArray ( sources ) || sources . length === 0 ) { return null ; } const registryUrls = new Set < string > ( ) ; for ( const source of sources ) { if ( source . url ) { registryUrls . add ( source . url ) ; } } registryUrls . add ( process . env . PIP_INDEX_URL || 'https://pypi.org/pypi/' ) ; return Array . from ( registryUrls ) ; } export async function extractPackageFile ( content , fileName ) { logger . trace ( `poetry.extractPackageFile(${ fileName })` ) ; let pyprojectfile ; try { pyprojectfile = parse ( content ) ; } catch ( err ) { logger . debug ( { err } , 'Error parsing pyproject.toml file' ) ; return null ; } if ( ! pyprojectfile . tool ?. poetry ) { logger . debug ( `${ fileName } contains no poetry section` ) ; return null ; } const lockfileName = getSiblingFileName ( fileName , 'poetry.lock' ) ; const lockContents = await readLocalFile ( lockfileName , 'utf8' ) ; let poetryLockfile ; try { poetryLockfile = parse ( lockContents ) ; } catch ( err ) { logger . debug ( { err } , 'Error parsing pyproject.toml file' ) ; } const lockfileMapping = { } ; if ( poetryLockfile ?. package ) { for ( const poetryPackage of poetryLockfile . package ) { lockfileMapping [ poetryPackage . name ] = poetryPackage ; } } const deps = [ ... extractFromSection ( pyprojectfile , 'dependencies' , lockfileMapping ) , ... extractFromSection ( pyprojectfile , 'dev-dependencies' , lockfileMapping ) , ... extractFromSection ( pyprojectfile , 'extras' , lockfileMapping ) , ] ; if ( ! deps . length ) { return null ; } const constraints = { } ; if ( pyprojectfile [ 'build-system' ] ?. [ 'build-backend' ] === 'poetry.masonry.api' ) { constraints . poetry = pyprojectfile [ 'build-system' ] ?. requires . join ( ' ' ) ; } if ( is . nonEmptyString ( pyprojectfile . tool ?. poetry ?. dependencies ?. python ) ) { constraints . python = pyprojectfile . tool ?. poetry ?. dependencies ?. python ; } const res = { deps , registryUrls : extractRegistries ( pyprojectfile ) , constraints , } ; let lockFile = getSiblingFileName ( fileName , 'poetry.lock' ) ; if ( await localPathExists ( lockFile ) ) { res . lockFiles = [ lockFile ] ; } else { lockFile = getSiblingFileName ( fileName , 'pyproject.lock' ) ; if ( await localPathExists ( lockFile ) ) { res . lockFiles = [ lockFile ] ; } } return res ; }