-
-
Notifications
You must be signed in to change notification settings - Fork 93
Expand file tree
/
Copy pathpackaging.py
More file actions
110 lines (96 loc) · 4.98 KB
/
packaging.py
File metadata and controls
110 lines (96 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# This file is part of CycloneDX Python
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
from collections.abc import Generator
from re import compile as re_compile
from typing import TYPE_CHECKING
from cyclonedx.exception.model import InvalidUriException
from cyclonedx.model import AttachedText, ExternalReference, ExternalReferenceType, XsUri
from cyclonedx.model.license import DisjunctiveLicense, LicenseAcknowledgement
from .cdx import url_label_to_ert
from .pep621 import classifiers2licenses as pep621_classifiers2licenses
if TYPE_CHECKING: # pragma: no cover
from cyclonedx.factory.license import LicenseFactory
from cyclonedx.model.license import License
from ..py_interop.packagemetadata import PackageMetadata
def metadata2licenses(metadata: 'PackageMetadata', lfac: 'LicenseFactory',
gather_texts: bool
) -> Generator['License', None, None]:
lack = LicenseAcknowledgement.DECLARED
if (lexp := metadata.get('License-Expression')) is not None:
# see https://packaging.python.org/en/latest/specifications/license-expression/
# see spec: https://peps.python.org/pep-0639/#add-license-expression-field
# Use `make_from_string` to have simple SPDX expressions that are just one disjunctive license
# to be simplified to a CycloneDX license ID. And we get error resilence for free, as invalid
# expressions are converted to CycloneDX named licenses.
yield lfac.make_from_string(lexp,
license_acknowledgement=lack)
# Per PEP 639: if License-Expression exists, the deprecated declarations MUST be ignored
else:
if 'Classifier' in metadata:
# see spec: https://packaging.python.org/en/latest/specifications/core-metadata/#classifier-multiple-use
classifiers: list[str] = metadata.get_all('Classifier') # type:ignore[assignment]
yield from pep621_classifiers2licenses(classifiers, lfac, lack)
for mlicense in set(metadata.get_all('License', ())):
# see spec: https://packaging.python.org/en/latest/specifications/core-metadata/#license
if len(mlicense) <= 0:
continue
license = lfac.make_from_string(mlicense, license_acknowledgement=lack)
if isinstance(license, DisjunctiveLicense) and license.id is None:
if gather_texts:
# per spec, `License` is either a SPDX ID/Expression, or a license text(not name!)
yield DisjunctiveLicense(name=f"declared license of '{metadata['Name']}'",
acknowledgement=lack,
text=AttachedText(content=mlicense))
else:
yield license
def metadata2extrefs(metadata: 'PackageMetadata') -> Generator['ExternalReference', None, None]:
for meta_key, extref_typet in (
# see https://packaging.python.org/en/latest/specifications/core-metadata/#home-page
('Home-page', ExternalReferenceType.WEBSITE),
# see https://packaging.python.org/en/latest/specifications/core-metadata/#download-url
('Download-URL', ExternalReferenceType.DISTRIBUTION),
):
if meta_key in metadata:
try:
yield ExternalReference(
comment=f'from packaging metadata: {meta_key}',
type=extref_typet,
url=XsUri(metadata[meta_key]))
except InvalidUriException: # pragma: nocover
pass
for label_url in metadata.get_all('Project-URL', ()):
# see https://packaging.python.org/en/latest/specifications/core-metadata/#project-url-multiple-use
label, url = label_url.split(',', maxsplit=1)
try:
yield ExternalReference(
comment=f'from packaging metadata Project-URL: {label}',
type=url_label_to_ert(label),
url=XsUri(url.strip()))
except InvalidUriException: # pragma: nocover
pass
_NORMALIZE_PN_MATCHER = re_compile(r'[-_.]+')
_NORMALIZE_PN_REPLACE = '-'
def normalize_packagename(name: str) -> str:
"""
Normalize package names.
Also applies to names of package extras.
see https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
"""
return _NORMALIZE_PN_MATCHER.sub(
_NORMALIZE_PN_REPLACE,
name.lower()
)