Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cyclonedx/factory/license.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,5 @@ def make_with_license(self, name_or_spdx: str, *,
license_text: Optional[AttachedText] = None,
license_url: Optional[XsUri] = None) -> LicenseChoice:
"""Make a :class:`cyclonedx.model.LicenseChoice` with a license (name or SPDX-ID)."""
return LicenseChoice(license=self.license_factory.make_from_string(
name_or_spdx, license_text=license_text, license_url=license_url))
return LicenseChoice(licenses=[self.license_factory.make_from_string(
name_or_spdx, license_text=license_text, license_url=license_url)])
37 changes: 19 additions & 18 deletions cyclonedx/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import warnings
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Iterable, Optional, Tuple, TypeVar
from typing import Any, Iterable, List, Optional, Tuple, TypeVar

import serializable
from sortedcontainers import SortedSet
Expand Down Expand Up @@ -695,35 +695,36 @@ class LicenseChoice:
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_licenseChoiceType
"""

def __init__(self, *, license: Optional[License] = None, expression: Optional[str] = None) -> None:
if not license and not expression:
def __init__(self, *, licenses: Optional[List[License]] = None, expression: Optional[str] = None) -> None:
if not licenses and not expression:
raise NoPropertiesProvidedException(
'One of `license` or `expression` must be supplied - neither supplied'
'One of `licenses` or `expression` must be supplied - neither supplied'
)
if license and expression:
if licenses and expression:
warnings.warn(
'Both `license` and `expression` have been supplied - `license` will take precedence',
'Both `licenses` and `expression` have been supplied - `license` will take precedence',
RuntimeWarning
)
self.license = license
if not license:
self.licenses = licenses
if not licenses:
self.expression = expression
else:
self.expression = None

@property
def license(self) -> Optional[License]:
@property # type: ignore[misc]
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'license')
def licenses(self) -> Optional[List[License]]:
"""
License definition

Returns:
`License` or `None`
"""
return self._license
return self._licenses

@license.setter
def license(self, license: Optional[License]) -> None:
self._license = license
@licenses.setter
def licenses(self, licenses: Optional[List[License]] = None) -> None:
self._licenses = licenses

@property
def expression(self) -> Optional[str]:
Expand All @@ -748,15 +749,15 @@ def __eq__(self, other: object) -> bool:

def __lt__(self, other: Any) -> bool:
if isinstance(other, LicenseChoice):
return ComparableTuple((self.license, self.expression)) < ComparableTuple(
(other.license, other.expression))
return ComparableTuple((self.licenses or [], self.expression)) < ComparableTuple(
(other.licenses or [], other.expression))
return NotImplemented

def __hash__(self) -> int:
return hash((self.license, self.expression))
return hash((tuple(self.licenses) if self.licenses else (), self.expression))

def __repr__(self) -> str:
return f'<LicenseChoice license={self.license}, expression={self.expression}>'
return f'<LicenseChoice licenses={self.licenses}, expression={self.expression}>'


@serializable.serializable_class
Expand Down
14 changes: 7 additions & 7 deletions cyclonedx/model/bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(self, *, tools: Optional[Iterable[Tool]] = None,
authors: Optional[Iterable[OrganizationalContact]] = None, component: Optional[Component] = None,
manufacture: Optional[OrganizationalEntity] = None,
supplier: Optional[OrganizationalEntity] = None,
licenses: Optional[Iterable[LicenseChoice]] = None,
licenses: Optional[LicenseChoice] = None,
properties: Optional[Iterable[Property]] = None,
timestamp: Optional[datetime] = None) -> None:
self.timestamp = timestamp or get_now_utc()
Expand All @@ -77,7 +77,7 @@ def __init__(self, *, tools: Optional[Iterable[Tool]] = None,
self.component = component
self.manufacture = manufacture
self.supplier = supplier
self.licenses = licenses or [] # type: ignore
self.licenses = licenses
self.properties = properties or [] # type: ignore

if not tools:
Expand Down Expand Up @@ -195,9 +195,9 @@ def supplier(self, supplier: Optional[OrganizationalEntity]) -> None:
@property # type: ignore[misc]
@serializable.view(SchemaVersion1Dot3)
@serializable.view(SchemaVersion1Dot4)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'licenses')
# @serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'licenses')
@serializable.xml_sequence(7)
def licenses(self) -> "SortedSet[LicenseChoice]":
def licenses(self) -> Optional[LicenseChoice]:
"""
A optional list of statements about how this BOM is licensed.

Expand All @@ -207,8 +207,8 @@ def licenses(self) -> "SortedSet[LicenseChoice]":
return self._licenses

@licenses.setter
def licenses(self, licenses: Iterable[LicenseChoice]) -> None:
self._licenses = SortedSet(licenses)
def licenses(self, licenses: Optional[LicenseChoice]) -> None:
self._licenses = licenses

@property # type: ignore[misc]
@serializable.view(SchemaVersion1Dot3)
Expand Down Expand Up @@ -239,7 +239,7 @@ def __eq__(self, other: object) -> bool:

def __hash__(self) -> int:
return hash((
tuple(self.authors), self.component, tuple(self.licenses), self.manufacture, tuple(self.properties),
tuple(self.authors), self.component, self.licenses, self.manufacture, tuple(self.properties),
self.supplier, self.timestamp, tuple(self.tools)
))

Expand Down
14 changes: 7 additions & 7 deletions cyclonedx/model/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import warnings
from enum import Enum
from os.path import exists
from typing import Any, Iterable, Optional, Set, Union
from typing import Any, Iterable, List, Optional, Set, Union
from uuid import uuid4

# See https://github.com/package-url/packageurl-python/issues/65
Expand Down Expand Up @@ -756,7 +756,7 @@ def __init__(self, *, name: str, type: ComponentType = ComponentType.LIBRARY,
supplier: Optional[OrganizationalEntity] = None, author: Optional[str] = None,
publisher: Optional[str] = None, group: Optional[str] = None, version: Optional[str] = None,
description: Optional[str] = None, scope: Optional[ComponentScope] = None,
hashes: Optional[Iterable[HashType]] = None, licenses: Optional[Iterable[LicenseChoice]] = None,
hashes: Optional[Iterable[HashType]] = None, licenses: Optional[LicenseChoice] = None,
copyright: Optional[str] = None, purl: Optional[PackageURL] = None,
external_references: Optional[Iterable[ExternalReference]] = None,
properties: Optional[Iterable[Property]] = None, release_notes: Optional[ReleaseNotes] = None,
Expand All @@ -781,7 +781,7 @@ def __init__(self, *, name: str, type: ComponentType = ComponentType.LIBRARY,
self.description = description
self.scope = scope
self.hashes = hashes or [] # type: ignore
self.licenses = licenses or [] # type: ignore
self.licenses = licenses
self.copyright = copyright
self.cpe = cpe
self.purl = purl
Expand Down Expand Up @@ -1034,7 +1034,7 @@ def hashes(self, hashes: Iterable[HashType]) -> None:
@serializable.view(SchemaVersion1Dot4)
@serializable.xml_array(serializable.XmlArraySerializationType.FLAT, 'licenses')
@serializable.xml_sequence(10)
def licenses(self) -> "SortedSet[LicenseChoice]":
def licenses(self) -> Optional[LicenseChoice]:
"""
A optional list of statements about how this Component is licensed.

Expand All @@ -1044,8 +1044,8 @@ def licenses(self) -> "SortedSet[LicenseChoice]":
return self._licenses

@licenses.setter
def licenses(self, licenses: Iterable[LicenseChoice]) -> None:
self._licenses = SortedSet(licenses)
def licenses(self, licenses: Optional[LicenseChoice]) -> None:
self._licenses = licenses

@property # type: ignore[misc]
@serializable.xml_sequence(11)
Expand Down Expand Up @@ -1267,7 +1267,7 @@ def __lt__(self, other: Any) -> bool:
def __hash__(self) -> int:
return hash((
self.type, self.mime_type, self.supplier, self.author, self.publisher, self.group, self.name,
self.version, self.description, self.scope, tuple(self.hashes), tuple(self.licenses), self.copyright,
self.version, self.description, self.scope, tuple(self.hashes), self.licenses, self.copyright,
self.cpe, self.purl, self.swid, self.pedigree, tuple(self.external_references), tuple(self.properties),
tuple(self.components), self.evidence, self.release_notes, self.modified
))
Expand Down
2 changes: 1 addition & 1 deletion cyclonedx/model/vulnerability.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ class VulnerabilitySeverity(str, Enum):
UNKNOWN = 'unknown'

@staticmethod
def get_from_cvss_scores(scores: Union[Tuple[float], float, None]) -> 'VulnerabilitySeverity':
def get_from_cvss_scores(scores: Union[Tuple[float, ...], float, None]) -> 'VulnerabilitySeverity':
"""
Derives the Severity of a Vulnerability from it's declared CVSS scores.

Expand Down
2 changes: 1 addition & 1 deletion cyclonedx/serialization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def serialize(cls, o: object) -> str:
raise ValueError(f'Attempt to serialize a non-UUID: {o.__class__}')

@classmethod
def deserialize(cls, o: object) -> PackageURL:
def deserialize(cls, o: object) -> UUID:
try:
return UUID(str(o))
except ValueError:
Expand Down
Loading