mirror of
https://github.com/badges/shields.git
synced 2025-04-18 19:44:04 +03:00
* [PyPI] Fix license for packages following PEP 639 PEP 639 states that the preferred way of documenting a Python project's license is an SPDX expression in a `License-Expression` metadata field. PyPI exposes this information in `info.license_expression` in its JSON data. Fixes: #11000 * add license_expression to pypi response schema * move comments inline into the relevant blocks * assign both license and license_expression to intermediate variables * always pass a license_expression in test input objects --------- Co-authored-by: chris48s <git@chris-shaw.dev>
121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
/*
|
|
Django versions will be specified in the form major.minor
|
|
trying to sort with `semver.compare` will throw e.g:
|
|
TypeError: Invalid Version: 1.11
|
|
because no patch release is specified, so we will define
|
|
our own functions to parse and sort django versions
|
|
*/
|
|
|
|
function parsePypiVersionString(str) {
|
|
if (typeof str !== 'string') {
|
|
return false
|
|
}
|
|
const x = str.split('.')
|
|
const maj = parseInt(x[0]) || 0
|
|
const min = parseInt(x[1]) || 0
|
|
return {
|
|
major: maj,
|
|
minor: min,
|
|
}
|
|
}
|
|
|
|
// Sort an array of django versions low to high.
|
|
function sortPypiVersions(versions) {
|
|
return versions.sort((a, b) => {
|
|
if (parsePypiVersionString(a).major === parsePypiVersionString(b).major) {
|
|
return parsePypiVersionString(a).minor - parsePypiVersionString(b).minor
|
|
} else {
|
|
return parsePypiVersionString(a).major - parsePypiVersionString(b).major
|
|
}
|
|
})
|
|
}
|
|
|
|
// Extract classifiers from a pypi json response based on a regex.
|
|
function parseClassifiers(parsedData, pattern, preserveCase = false) {
|
|
const results = []
|
|
for (let i = 0; i < parsedData.info.classifiers.length; i++) {
|
|
const matched = pattern.exec(parsedData.info.classifiers[i])
|
|
if (matched && matched[1]) {
|
|
if (preserveCase) {
|
|
results.push(matched[1])
|
|
} else {
|
|
results.push(matched[1].toLowerCase())
|
|
}
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
function getLicenses(packageData) {
|
|
const license = packageData.info.license
|
|
const licenseExpression = packageData.info.license_expression
|
|
|
|
if (licenseExpression) {
|
|
/*
|
|
The .license_expression field contains an SPDX expression, and it
|
|
is the preferred way of documenting a Python project's license.
|
|
See https://peps.python.org/pep-0639/
|
|
*/
|
|
return [licenseExpression]
|
|
} else if (license && license.length < 40) {
|
|
/*
|
|
The .license field may either contain
|
|
- a short license description (e.g: 'MIT' or 'GPL-3.0') or
|
|
- the full text of a license
|
|
but there is nothing in the response that tells us explicitly.
|
|
We have to make an assumption based on the length.
|
|
See https://github.com/badges/shields/issues/8689 and
|
|
https://github.com/badges/shields/pull/8690 for more info.
|
|
*/
|
|
return [license]
|
|
} else {
|
|
// else fall back to trove classifiers
|
|
const parenthesizedAcronymRegex = /\(([^)]+)\)/
|
|
const bareAcronymRegex = /^[a-z0-9]+$/
|
|
const spdxAliases = {
|
|
'OSI Approved :: Apache Software License': 'Apache-2.0',
|
|
'CC0 1.0 Universal (CC0 1.0) Public Domain Dedication': 'CC0-1.0',
|
|
'OSI Approved :: GNU Affero General Public License v3': 'AGPL-3.0',
|
|
'OSI Approved :: Zero-Clause BSD (0BSD)': '0BSD',
|
|
}
|
|
let licenses = parseClassifiers(packageData, /^License :: (.+)$/, true)
|
|
.map(classifier =>
|
|
classifier in spdxAliases ? spdxAliases[classifier] : classifier,
|
|
)
|
|
.map(classifier => classifier.split(' :: ').pop())
|
|
.map(license => license.replace(' License', ''))
|
|
.map(license => {
|
|
const match = license.match(parenthesizedAcronymRegex)
|
|
return match ? match[1].toUpperCase() : license
|
|
})
|
|
.map(license => {
|
|
const match = license.match(bareAcronymRegex)
|
|
return match ? license.toUpperCase() : license
|
|
})
|
|
if (licenses.length > 1) {
|
|
licenses = licenses.filter(l => l !== 'DFSG approved')
|
|
}
|
|
return licenses
|
|
}
|
|
}
|
|
|
|
function getPackageFormats(packageData) {
|
|
const { urls } = packageData
|
|
return {
|
|
hasWheel: urls.some(({ packagetype }) =>
|
|
['wheel', 'bdist_wheel'].includes(packagetype),
|
|
),
|
|
hasEgg: urls.some(({ packagetype }) =>
|
|
['egg', 'bdist_egg'].includes(packagetype),
|
|
),
|
|
}
|
|
}
|
|
|
|
export {
|
|
parseClassifiers,
|
|
parsePypiVersionString,
|
|
sortPypiVersions,
|
|
getLicenses,
|
|
getPackageFormats,
|
|
}
|