Skip to content

Commit b3c8d9a

Browse files
authored
BREAKING CHANGE: adopted PEP-3102 for model classes (#158)
Signed-off-by: Paul Horton <[email protected]>
1 parent 41a4be0 commit b3c8d9a

File tree

8 files changed

+40
-55
lines changed

8 files changed

+40
-55
lines changed

cyclonedx/model/__init__.py

+14-30
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,7 @@ class DataClassification:
7373
https://cyclonedx.org/docs/1.4/xml/#type_dataClassificationType
7474
"""
7575

76-
def __init__(self, flow: DataFlow, classification: str) -> None:
77-
if not flow and not classification:
78-
raise NoPropertiesProvidedException(
79-
'One of `flow` or `classification` must be supplied - neither supplied'
80-
)
81-
76+
def __init__(self, *, flow: DataFlow, classification: str) -> None:
8277
self.flow = flow
8378
self.classification = classification
8479

@@ -151,7 +146,7 @@ class AttachedText:
151146

152147
DEFAULT_CONTENT_TYPE = 'text/plain'
153148

154-
def __init__(self, content: str, content_type: str = DEFAULT_CONTENT_TYPE,
149+
def __init__(self, *, content: str, content_type: str = DEFAULT_CONTENT_TYPE,
155150
encoding: Optional[Encoding] = None) -> None:
156151
self.content_type = content_type
157152
self.encoding = encoding
@@ -282,7 +277,7 @@ def from_composite_str(composite_hash: str) -> 'HashType':
282277

283278
raise UnknownHashTypeException(f"Unable to determine hash type from '{composite_hash}'")
284279

285-
def __init__(self, algorithm: HashAlgorithm, hash_value: str) -> None:
280+
def __init__(self, *, algorithm: HashAlgorithm, hash_value: str) -> None:
286281
self._alg = algorithm
287282
self._content = hash_value
288283

@@ -329,17 +324,6 @@ class ExternalReferenceType(Enum):
329324
VCS = 'vcs'
330325
WEBSITE = 'website'
331326

332-
# def __eq__(self, other: object) -> bool:
333-
# if isinstance(other, ExternalReferenceType):
334-
# return hash(other) == hash(self)
335-
# return False
336-
#
337-
# def __hash__(self) -> int:
338-
# return hash(self.value)
339-
#
340-
# def __repr__(self) -> str:
341-
# return f'<ExternalReferenceType name={self.name}, value={self.value}>'
342-
343327

344328
class XsUri:
345329
"""
@@ -382,7 +366,7 @@ class ExternalReference:
382366
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.3/#type_externalReference
383367
"""
384368

385-
def __init__(self, reference_type: ExternalReferenceType, url: Union[str, XsUri], comment: str = '',
369+
def __init__(self, *, reference_type: ExternalReferenceType, url: Union[str, XsUri], comment: str = '',
386370
hashes: Optional[List[HashType]] = None) -> None:
387371
self._type: ExternalReferenceType = reference_type
388372
self._url = str(url)
@@ -459,7 +443,7 @@ class License:
459443
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_licenseType
460444
"""
461445

462-
def __init__(self, spxd_license_id: Optional[str] = None, license_name: Optional[str] = None,
446+
def __init__(self, *, spxd_license_id: Optional[str] = None, license_name: Optional[str] = None,
463447
license_text: Optional[AttachedText] = None, license_url: Optional[XsUri] = None) -> None:
464448
if not spxd_license_id and not license_name:
465449
raise MutuallyExclusivePropertiesException('Either `spxd_license_id` or `license_name` MUST be supplied')
@@ -554,7 +538,7 @@ class LicenseChoice:
554538
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_licenseChoiceType
555539
"""
556540

557-
def __init__(self, license: Optional[License] = None, license_expression: Optional[str] = None) -> None:
541+
def __init__(self, *, license: Optional[License] = None, license_expression: Optional[str] = None) -> None:
558542
if not license and not license_expression:
559543
raise NoPropertiesProvidedException(
560544
'One of `license` or `license_expression` must be supplied - neither supplied'
@@ -623,7 +607,7 @@ class Property:
623607
Specifies an individual property with a name and value.
624608
"""
625609

626-
def __init__(self, name: str, value: str) -> None:
610+
def __init__(self, *, name: str, value: str) -> None:
627611
self._name = name
628612
self._value = value
629613

@@ -668,7 +652,7 @@ class NoteText:
668652

669653
DEFAULT_CONTENT_TYPE: str = 'text/plain'
670654

671-
def __init__(self, content: str, content_type: Optional[str] = None,
655+
def __init__(self, *, content: str, content_type: Optional[str] = None,
672656
content_encoding: Optional[Encoding] = None) -> None:
673657
self.content = content
674658
self.content_type = content_type or NoteText.DEFAULT_CONTENT_TYPE
@@ -741,7 +725,7 @@ class Note:
741725

742726
_LOCALE_TYPE_REGEX = re.compile(r'^[a-z]{2}(?:\-[A-Z]{2})?$')
743727

744-
def __init__(self, text: NoteText, locale: Optional[str] = None) -> None:
728+
def __init__(self, *, text: NoteText, locale: Optional[str] = None) -> None:
745729
self.text = text
746730
self.locale = locale
747731

@@ -806,7 +790,7 @@ class OrganizationalContact:
806790
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_organizationalContact
807791
"""
808792

809-
def __init__(self, name: Optional[str] = None, phone: Optional[str] = None, email: Optional[str] = None) -> None:
793+
def __init__(self, *, name: Optional[str] = None, phone: Optional[str] = None, email: Optional[str] = None) -> None:
810794
if not name and not phone and not email:
811795
raise NoPropertiesProvidedException(
812796
'One of name, email or phone must be supplied for an OrganizationalContact - none supplied.'
@@ -866,7 +850,7 @@ class OrganizationalEntity:
866850
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_organizationalEntity
867851
"""
868852

869-
def __init__(self, name: Optional[str] = None, urls: Optional[List[XsUri]] = None,
853+
def __init__(self, *, name: Optional[str] = None, urls: Optional[List[XsUri]] = None,
870854
contacts: Optional[List[OrganizationalContact]] = None) -> None:
871855
if not name and not urls and not contacts:
872856
raise NoPropertiesProvidedException(
@@ -932,7 +916,7 @@ class Tool:
932916
See the CycloneDX Schema for toolType: https://cyclonedx.org/docs/1.3/#type_toolType
933917
"""
934918

935-
def __init__(self, vendor: Optional[str] = None, name: Optional[str] = None, version: Optional[str] = None,
919+
def __init__(self, *, vendor: Optional[str] = None, name: Optional[str] = None, version: Optional[str] = None,
936920
hashes: Optional[List[HashType]] = None,
937921
external_references: Optional[List[ExternalReference]] = None) -> None:
938922
self._vendor = vendor
@@ -1037,7 +1021,7 @@ class IdentifiableAction:
10371021
See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_identifiableActionType
10381022
"""
10391023

1040-
def __init__(self, timestamp: Optional[datetime] = None, name: Optional[str] = None,
1024+
def __init__(self, *, timestamp: Optional[datetime] = None, name: Optional[str] = None,
10411025
email: Optional[str] = None) -> None:
10421026
if not timestamp and not name and not email:
10431027
raise NoPropertiesProvidedException(
@@ -1110,7 +1094,7 @@ class Copyright:
11101094
See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_copyrightsType
11111095
"""
11121096

1113-
def __init__(self, text: str) -> None:
1097+
def __init__(self, *, text: str) -> None:
11141098
self.text = text
11151099

11161100
@property

cyclonedx/model/bom.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class BomMetaData:
3535
See the CycloneDX Schema for Bom metadata: https://cyclonedx.org/docs/1.3/#type_metadata
3636
"""
3737

38-
def __init__(self, tools: Optional[List[Tool]] = None) -> None:
38+
def __init__(self, *, tools: Optional[List[Tool]] = None) -> None:
3939
self.timestamp = datetime.now(tz=timezone.utc)
4040
self.tools = tools if tools else []
4141

@@ -149,7 +149,7 @@ def from_parser(parser: BaseParser) -> 'Bom':
149149
bom.add_components(parser.get_components())
150150
return bom
151151

152-
def __init__(self, components: Optional[List[Component]] = None, services: Optional[List[Service]] = None,
152+
def __init__(self, *, components: Optional[List[Component]] = None, services: Optional[List[Service]] = None,
153153
external_references: Optional[List[ExternalReference]] = None) -> None:
154154
"""
155155
Create a new Bom that you can manually/programmatically add data to later.

cyclonedx/model/component.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Commit:
4242
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_commitType
4343
"""
4444

45-
def __init__(self, uid: Optional[str] = None, url: Optional[XsUri] = None,
45+
def __init__(self, *, uid: Optional[str] = None, url: Optional[XsUri] = None,
4646
author: Optional[IdentifiableAction] = None, committer: Optional[IdentifiableAction] = None,
4747
message: Optional[str] = None) -> None:
4848
if not uid and not url and not author and not committer and not message:
@@ -149,7 +149,7 @@ class ComponentEvidence:
149149
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_componentEvidenceType
150150
"""
151151

152-
def __init__(self, licenses: Optional[List[LicenseChoice]] = None,
152+
def __init__(self, *, licenses: Optional[List[LicenseChoice]] = None,
153153
copyright_: Optional[List[Copyright]] = None) -> None:
154154
if not licenses and not copyright_:
155155
raise NoPropertiesProvidedException(
@@ -240,7 +240,7 @@ class Diff:
240240
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_diffType
241241
"""
242242

243-
def __init__(self, text: Optional[AttachedText] = None, url: Optional[XsUri] = None) -> None:
243+
def __init__(self, *, text: Optional[AttachedText] = None, url: Optional[XsUri] = None) -> None:
244244
if not text and not url:
245245
raise NoPropertiesProvidedException(
246246
'At least one of `text` or `url` must be provided for a `Diff`.'
@@ -310,7 +310,7 @@ class Patch:
310310
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_patchType
311311
"""
312312

313-
def __init__(self, type_: PatchClassification, diff: Optional[Diff] = None,
313+
def __init__(self, *, type_: PatchClassification, diff: Optional[Diff] = None,
314314
resolves: Optional[List[IssueType]] = None) -> None:
315315
self.type = type_
316316
self.diff = diff
@@ -400,9 +400,10 @@ class Pedigree:
400400
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_pedigreeType
401401
"""
402402

403-
def __init__(self, ancestors: Optional[List['Component']] = None, descendants: Optional[List['Component']] = None,
404-
variants: Optional[List['Component']] = None, commits: Optional[List[Commit]] = None,
405-
patches: Optional[List[Patch]] = None, notes: Optional[str] = None) -> None:
403+
def __init__(self, *, ancestors: Optional[List['Component']] = None,
404+
descendants: Optional[List['Component']] = None, variants: Optional[List['Component']] = None,
405+
commits: Optional[List[Commit]] = None, patches: Optional[List[Patch]] = None,
406+
notes: Optional[str] = None) -> None:
406407
if not ancestors and not descendants and not variants and not commits and not patches and not notes:
407408
raise NoPropertiesProvidedException(
408409
'At least one of `ancestors`, `descendants`, `variants`, `commits`, `patches` or `notes` must be '
@@ -589,7 +590,7 @@ class Swid:
589590
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_swidType
590591
"""
591592

592-
def __init__(self, tag_id: str, name: str, version: Optional[str] = None,
593+
def __init__(self, *, tag_id: str, name: str, version: Optional[str] = None,
593594
tag_version: Optional[int] = None, patch: Optional[bool] = None,
594595
text: Optional[AttachedText] = None, url: Optional[XsUri] = None) -> None:
595596
self.tag_id = tag_id
@@ -750,7 +751,7 @@ def for_file(absolute_file_path: str, path_for_bom: Optional[str]) -> 'Component
750751
)
751752
)
752753

753-
def __init__(self, name: str, component_type: ComponentType = ComponentType.LIBRARY,
754+
def __init__(self, *, name: str, component_type: ComponentType = ComponentType.LIBRARY,
754755
mime_type: Optional[str] = None, bom_ref: Optional[str] = None,
755756
supplier: Optional[OrganizationalEntity] = None, author: Optional[str] = None,
756757
publisher: Optional[str] = None, group: Optional[str] = None, version: Optional[str] = None,

cyclonedx/model/issue.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class IssueTypeSource:
4242
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_issueType
4343
"""
4444

45-
def __init__(self, name: Optional[str] = None, url: Optional[XsUri] = None) -> None:
45+
def __init__(self, *, name: Optional[str] = None, url: Optional[XsUri] = None) -> None:
4646
if not name and not url:
4747
raise NoPropertiesProvidedException(
4848
'Neither `name` nor `url` were provided - at least one must be provided.'
@@ -99,7 +99,7 @@ class IssueType:
9999
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/xml/#type_issueType
100100
"""
101101

102-
def __init__(self, classification: IssueClassification, id: Optional[str] = None, name: Optional[str] = None,
102+
def __init__(self, *, classification: IssueClassification, id: Optional[str] = None, name: Optional[str] = None,
103103
description: Optional[str] = None, source_name: Optional[str] = None,
104104
source_url: Optional[XsUri] = None, references: Optional[List[XsUri]] = None) -> None:
105105
self._type: IssueClassification = classification

cyclonedx/model/release_note.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class ReleaseNotes:
3232
See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.4/#type_releaseNotesType
3333
"""
3434

35-
def __init__(self, type: str, title: Optional[str] = None, featured_image: Optional[XsUri] = None,
35+
def __init__(self, *, type: str, title: Optional[str] = None, featured_image: Optional[XsUri] = None,
3636
social_image: Optional[XsUri] = None, description: Optional[str] = None,
3737
timestamp: Optional[datetime] = None, aliases: Optional[List[str]] = None,
3838
tags: Optional[List[str]] = None, resolves: Optional[List[IssueType]] = None,

cyclonedx/model/service.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Service:
3636
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/xml/#type_service
3737
"""
3838

39-
def __init__(self, name: str, bom_ref: Optional[str] = None, provider: Optional[OrganizationalEntity] = None,
39+
def __init__(self, *, name: str, bom_ref: Optional[str] = None, provider: Optional[OrganizationalEntity] = None,
4040
group: Optional[str] = None, version: Optional[str] = None, description: Optional[str] = None,
4141
endpoints: Optional[List[XsUri]] = None, authenticated: Optional[bool] = None,
4242
x_trust_boundary: Optional[bool] = None, data: Optional[List[DataClassification]] = None,

cyclonedx/model/vulnerability.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class BomTargetVersionRange:
5454
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
5555
"""
5656

57-
def __init__(self, version: Optional[str] = None, version_range: Optional[str] = None,
57+
def __init__(self, *, version: Optional[str] = None, version_range: Optional[str] = None,
5858
status: Optional[ImpactAnalysisAffectedStatus] = None) -> None:
5959
if not version and not version_range:
6060
raise NoPropertiesProvidedException(
@@ -131,7 +131,7 @@ class BomTarget:
131131
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
132132
"""
133133

134-
def __init__(self, ref: str, versions: Optional[List[BomTargetVersionRange]] = None) -> None:
134+
def __init__(self, *, ref: str, versions: Optional[List[BomTargetVersionRange]] = None) -> None:
135135
self.ref = ref
136136
self.versions = versions
137137

@@ -180,7 +180,7 @@ class VulnerabilityAnalysis:
180180
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
181181
"""
182182

183-
def __init__(self, state: Optional[ImpactAnalysisState] = None,
183+
def __init__(self, *, state: Optional[ImpactAnalysisState] = None,
184184
justification: Optional[ImpactAnalysisJustification] = None,
185185
responses: Optional[List[ImpactAnalysisResponse]] = None,
186186
detail: Optional[str] = None) -> None:
@@ -267,7 +267,7 @@ class VulnerabilityAdvisory:
267267
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_advisoryType
268268
"""
269269

270-
def __init__(self, url: XsUri, title: Optional[str] = None) -> None:
270+
def __init__(self, *, url: XsUri, title: Optional[str] = None) -> None:
271271
self.title = title
272272
self.url = url
273273

@@ -315,7 +315,7 @@ class VulnerabilitySource:
315315
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilitySourceType
316316
"""
317317

318-
def __init__(self, name: Optional[str] = None, url: Optional[XsUri] = None) -> None:
318+
def __init__(self, *, name: Optional[str] = None, url: Optional[XsUri] = None) -> None:
319319
if not name and not url:
320320
raise NoPropertiesProvidedException(
321321
'Either name or url must be provided for a VulnerabilitySource - neither provided'
@@ -370,7 +370,7 @@ class VulnerabilityReference:
370370
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
371371
"""
372372

373-
def __init__(self, id: Optional[str] = None, source: Optional[VulnerabilitySource] = None) -> None:
373+
def __init__(self, *, id: Optional[str] = None, source: Optional[VulnerabilitySource] = None) -> None:
374374
if not id and not source:
375375
raise NoPropertiesProvidedException(
376376
'Either id or source must be provided for a VulnerabilityReference - neither provided'
@@ -549,7 +549,7 @@ class VulnerabilityRating:
549549
they are redundant if you have the vector (the vector allows you to calculate the scores).
550550
"""
551551

552-
def __init__(self, source: Optional[VulnerabilitySource] = None, score: Optional[Decimal] = None,
552+
def __init__(self, *, source: Optional[VulnerabilitySource] = None, score: Optional[Decimal] = None,
553553
severity: Optional[VulnerabilitySeverity] = None,
554554
method: Optional[VulnerabilityScoreSource] = None, vector: Optional[str] = None,
555555
justification: Optional[str] = None,
@@ -669,7 +669,7 @@ class VulnerabilityCredits:
669669
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
670670
"""
671671

672-
def __init__(self, organizations: Optional[List[OrganizationalEntity]] = None,
672+
def __init__(self, *, organizations: Optional[List[OrganizationalEntity]] = None,
673673
individuals: Optional[List[OrganizationalContact]] = None) -> None:
674674
if not organizations and not individuals:
675675
raise NoPropertiesProvidedException(
@@ -732,7 +732,7 @@ class Vulnerability:
732732
See the CycloneDX schema: https://cyclonedx.org/docs/1.4/#type_vulnerabilityType
733733
"""
734734

735-
def __init__(self, bom_ref: Optional[str] = None, id: Optional[str] = None,
735+
def __init__(self, *, bom_ref: Optional[str] = None, id: Optional[str] = None,
736736
source: Optional[VulnerabilitySource] = None,
737737
references: Optional[List[VulnerabilityReference]] = None,
738738
ratings: Optional[List[VulnerabilityRating]] = None, cwes: Optional[List[int]] = None,

tests/test_model.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def test_issue_type(self) -> None:
177177
class TestModelNote(TestCase):
178178

179179
def test_note_plain_text(self) -> None:
180-
n = Note(text=NoteText('Some simple plain text'))
180+
n = Note(text=NoteText(content='Some simple plain text'))
181181
self.assertEqual(n.text.content, 'Some simple plain text')
182182
self.assertEqual(n.text.content_type, NoteText.DEFAULT_CONTENT_TYPE)
183183
self.assertIsNone(n.locale)

0 commit comments

Comments
 (0)