14
14
#
15
15
# SPDX-License-Identifier: Apache-2.0
16
16
#
17
-
18
17
import hashlib
19
18
import re
20
19
import sys
21
20
import warnings
21
+ from datetime import datetime
22
22
from enum import Enum
23
23
from typing import List , Optional , Union
24
24
@@ -119,6 +119,17 @@ def classification(self) -> str:
119
119
def classification (self , classification : str ) -> None :
120
120
self ._classification = classification
121
121
122
+ def __eq__ (self , other : object ) -> bool :
123
+ if isinstance (other , DataClassification ):
124
+ return hash (other ) == hash (self )
125
+ return False
126
+
127
+ def __hash__ (self ) -> int :
128
+ return hash ((self .flow , self .classification ))
129
+
130
+ def __repr__ (self ) -> str :
131
+ return f'<DataClassification flow={ self .flow } >'
132
+
122
133
123
134
class Encoding (Enum ):
124
135
"""
@@ -191,6 +202,17 @@ def content(self) -> str:
191
202
def content (self , content : str ) -> None :
192
203
self ._content = content
193
204
205
+ def __eq__ (self , other : object ) -> bool :
206
+ if isinstance (other , AttachedText ):
207
+ return hash (other ) == hash (self )
208
+ return False
209
+
210
+ def __hash__ (self ) -> int :
211
+ return hash ((self .content , self .content_type , self .encoding ))
212
+
213
+ def __repr__ (self ) -> str :
214
+ return f'<AttachedText content-type={ self .content_type } , encoding={ self .encoding } >'
215
+
194
216
195
217
class HashAlgorithm (Enum ):
196
218
"""
@@ -270,8 +292,16 @@ def get_algorithm(self) -> HashAlgorithm:
270
292
def get_hash_value (self ) -> str :
271
293
return self ._content
272
294
295
+ def __eq__ (self , other : object ) -> bool :
296
+ if isinstance (other , HashType ):
297
+ return hash (other ) == hash (self )
298
+ return False
299
+
300
+ def __hash__ (self ) -> int :
301
+ return hash ((self ._alg , self ._content ))
302
+
273
303
def __repr__ (self ) -> str :
274
- return f'<Hash { self ._alg .value } :{ self ._content } >'
304
+ return f'<HashType { self ._alg .value } :{ self ._content } >'
275
305
276
306
277
307
class ExternalReferenceType (Enum ):
@@ -299,6 +329,17 @@ class ExternalReferenceType(Enum):
299
329
VCS = 'vcs'
300
330
WEBSITE = 'website'
301
331
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
+
302
343
303
344
class XsUri :
304
345
"""
@@ -322,9 +363,12 @@ def __init__(self, uri: str) -> None:
322
363
323
364
def __eq__ (self , other : object ) -> bool :
324
365
if isinstance (other , XsUri ):
325
- return str ( self ) == str ( other )
366
+ return hash ( other ) == hash ( self )
326
367
return False
327
368
369
+ def __hash__ (self ) -> int :
370
+ return hash (self ._uri )
371
+
328
372
def __repr__ (self ) -> str :
329
373
return self ._uri
330
374
@@ -391,8 +435,19 @@ def get_url(self) -> str:
391
435
"""
392
436
return self ._url
393
437
438
+ def __eq__ (self , other : object ) -> bool :
439
+ if isinstance (other , ExternalReference ):
440
+ return hash (other ) == hash (self )
441
+ return False
442
+
443
+ def __hash__ (self ) -> int :
444
+ return hash ((
445
+ self ._type , self ._url , self ._comment ,
446
+ tuple ([hash (hash_ ) for hash_ in set (sorted (self ._hashes , key = hash ))]) if self ._hashes else None
447
+ ))
448
+
394
449
def __repr__ (self ) -> str :
395
- return f'<ExternalReference { self ._type .name } , { self ._url } > { self . _hashes } '
450
+ return f'<ExternalReference { self ._type .name } , { self ._url } >'
396
451
397
452
398
453
class License :
@@ -478,6 +533,17 @@ def url(self) -> Optional[XsUri]:
478
533
def url (self , url : Optional [XsUri ]) -> None :
479
534
self ._url = url
480
535
536
+ def __eq__ (self , other : object ) -> bool :
537
+ if isinstance (other , License ):
538
+ return hash (other ) == hash (self )
539
+ return False
540
+
541
+ def __hash__ (self ) -> int :
542
+ return hash ((self .id , self .name , self .text , self .url ))
543
+
544
+ def __repr__ (self ) -> str :
545
+ return f'<License id={ self .id } , name={ self .name } >'
546
+
481
547
482
548
class LicenseChoice :
483
549
"""
@@ -534,6 +600,17 @@ def expression(self) -> Optional[str]:
534
600
def expression (self , expression : Optional [str ]) -> None :
535
601
self ._expression = expression
536
602
603
+ def __eq__ (self , other : object ) -> bool :
604
+ if isinstance (other , LicenseChoice ):
605
+ return hash (other ) == hash (self )
606
+ return False
607
+
608
+ def __hash__ (self ) -> int :
609
+ return hash ((self .license , self .expression ))
610
+
611
+ def __repr__ (self ) -> str :
612
+ return f'<LicenseChoice license={ self .license } , expression={ self .expression } >'
613
+
537
614
538
615
class Property :
539
616
"""
@@ -568,6 +645,17 @@ def get_value(self) -> str:
568
645
"""
569
646
return self ._value
570
647
648
+ def __eq__ (self , other : object ) -> bool :
649
+ if isinstance (other , Property ):
650
+ return hash (other ) == hash (self )
651
+ return False
652
+
653
+ def __hash__ (self ) -> int :
654
+ return hash ((self ._name , self ._value ))
655
+
656
+ def __repr__ (self ) -> str :
657
+ return f'<Property name={ self ._name } >'
658
+
571
659
572
660
class NoteText :
573
661
"""
@@ -630,6 +718,17 @@ def encoding(self) -> Optional[Encoding]:
630
718
def encoding (self , encoding : Optional [Encoding ]) -> None :
631
719
self ._encoding = encoding
632
720
721
+ def __eq__ (self , other : object ) -> bool :
722
+ if isinstance (other , NoteText ):
723
+ return hash (other ) == hash (self )
724
+ return False
725
+
726
+ def __hash__ (self ) -> int :
727
+ return hash ((self .content , self .content_type , self .encoding ))
728
+
729
+ def __repr__ (self ) -> str :
730
+ return f'<NoteText content_type={ self .content_type } , encoding={ self .encoding } >'
731
+
633
732
634
733
class Note :
635
734
"""
@@ -686,6 +785,17 @@ def locale(self, locale: Optional[str]) -> None:
686
785
f"ISO-3166 (or higher) country code. according to ISO-639 format. Examples include: 'en', 'en-US'."
687
786
)
688
787
788
+ def __eq__ (self , other : object ) -> bool :
789
+ if isinstance (other , Note ):
790
+ return hash (other ) == hash (self )
791
+ return False
792
+
793
+ def __hash__ (self ) -> int :
794
+ return hash ((hash (self .text ), self .locale ))
795
+
796
+ def __repr__ (self ) -> str :
797
+ return f'<Note id={ id (self )} , locale={ self .locale } >'
798
+
689
799
690
800
class OrganizationalContact :
691
801
"""
@@ -735,6 +845,17 @@ def phone(self) -> Optional[str]:
735
845
"""
736
846
return self ._phone
737
847
848
+ def __eq__ (self , other : object ) -> bool :
849
+ if isinstance (other , OrganizationalContact ):
850
+ return hash (other ) == hash (self )
851
+ return False
852
+
853
+ def __hash__ (self ) -> int :
854
+ return hash ((self .name , self .phone , self .email ))
855
+
856
+ def __repr__ (self ) -> str :
857
+ return f'<OrganizationalContact name={ self .name } >'
858
+
738
859
739
860
class OrganizationalEntity :
740
861
"""
@@ -785,6 +906,21 @@ def contacts(self) -> Optional[List[OrganizationalContact]]:
785
906
"""
786
907
return self ._contact
787
908
909
+ def __eq__ (self , other : object ) -> bool :
910
+ if isinstance (other , OrganizationalEntity ):
911
+ return hash (other ) == hash (self )
912
+ return False
913
+
914
+ def __hash__ (self ) -> int :
915
+ return hash ((
916
+ self .name ,
917
+ tuple ([hash (url ) for url in set (sorted (self .urls , key = hash ))]) if self .urls else None ,
918
+ tuple ([hash (contact ) for contact in set (sorted (self .contacts , key = hash ))]) if self .contacts else None
919
+ ))
920
+
921
+ def __repr__ (self ) -> str :
922
+ return f'<OrganizationalEntity name={ self .name } >'
923
+
788
924
789
925
class Tool :
790
926
"""
@@ -876,8 +1012,131 @@ def get_version(self) -> Optional[str]:
876
1012
"""
877
1013
return self ._version
878
1014
1015
+ def __eq__ (self , other : object ) -> bool :
1016
+ if isinstance (other , Tool ):
1017
+ return hash (other ) == hash (self )
1018
+ return False
1019
+
1020
+ def __hash__ (self ) -> int :
1021
+ return hash ((
1022
+ self ._vendor , self ._name , self ._version ,
1023
+ tuple ([hash (hash_ ) for hash_ in set (sorted (self ._hashes , key = hash ))]) if self ._hashes else None ,
1024
+ tuple ([hash (ref ) for ref in
1025
+ set (sorted (self ._external_references , key = hash ))]) if self ._external_references else None
1026
+ ))
1027
+
1028
+ def __repr__ (self ) -> str :
1029
+ return f'<Tool name={ self ._name } , version={ self ._version } , vendor={ self ._vendor } >'
1030
+
1031
+
1032
+ class IdentifiableAction :
1033
+ """
1034
+ This is out internal representation of the `identifiableActionType` complex type.
1035
+
1036
+ .. note::
1037
+ See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_identifiableActionType
1038
+ """
1039
+
1040
+ def __init__ (self , timestamp : Optional [datetime ] = None , name : Optional [str ] = None ,
1041
+ email : Optional [str ] = None ) -> None :
1042
+ if not timestamp and not name and not email :
1043
+ raise NoPropertiesProvidedException (
1044
+ 'At least one of `timestamp`, `name` or `email` must be provided for an `IdentifiableAction`.'
1045
+ )
1046
+
1047
+ self .timestamp = timestamp
1048
+ self .name = name
1049
+ self .email = email
1050
+
1051
+ @property
1052
+ def timestamp (self ) -> Optional [datetime ]:
1053
+ """
1054
+ The timestamp in which the action occurred.
1055
+
1056
+ Returns:
1057
+ `datetime` if set else `None`
1058
+ """
1059
+ return self ._timestamp
1060
+
1061
+ @timestamp .setter
1062
+ def timestamp (self , timestamp : Optional [datetime ]) -> None :
1063
+ self ._timestamp = timestamp
1064
+
1065
+ @property
1066
+ def name (self ) -> Optional [str ]:
1067
+ """
1068
+ The name of the individual who performed the action.
1069
+
1070
+ Returns:
1071
+ `str` if set else `None`
1072
+ """
1073
+ return self ._name
1074
+
1075
+ @name .setter
1076
+ def name (self , name : Optional [str ]) -> None :
1077
+ self ._name = name
1078
+
1079
+ @property
1080
+ def email (self ) -> Optional [str ]:
1081
+ """
1082
+ The email address of the individual who performed the action.
1083
+
1084
+ Returns:
1085
+ `str` if set else `None`
1086
+ """
1087
+ return self ._email
1088
+
1089
+ @email .setter
1090
+ def email (self , email : Optional [str ]) -> None :
1091
+ self ._email = email
1092
+
1093
+ def __eq__ (self , other : object ) -> bool :
1094
+ if isinstance (other , IdentifiableAction ):
1095
+ return hash (other ) == hash (self )
1096
+ return False
1097
+
1098
+ def __hash__ (self ) -> int :
1099
+ return hash ((hash (self .timestamp ), self .name , self .email ))
1100
+
1101
+ def __repr__ (self ) -> str :
1102
+ return f'<IdentifiableAction name={ self .name } , email={ self .email } >'
1103
+
1104
+
1105
+ class Copyright :
1106
+ """
1107
+ This is out internal representation of the `copyrightsType` complex type.
1108
+
1109
+ .. note::
1110
+ See the CycloneDX specification: https://cyclonedx.org/docs/1.4/xml/#type_copyrightsType
1111
+ """
1112
+
1113
+ def __init__ (self , text : str ) -> None :
1114
+ self .text = text
1115
+
1116
+ @property
1117
+ def text (self ) -> str :
1118
+ """
1119
+ Copyright statement.
1120
+
1121
+ Returns:
1122
+ `str` if set else `None`
1123
+ """
1124
+ return self ._text
1125
+
1126
+ @text .setter
1127
+ def text (self , text : str ) -> None :
1128
+ self ._text = text
1129
+
1130
+ def __eq__ (self , other : object ) -> bool :
1131
+ if isinstance (other , Copyright ):
1132
+ return hash (other ) == hash (self )
1133
+ return False
1134
+
1135
+ def __hash__ (self ) -> int :
1136
+ return hash (self .text )
1137
+
879
1138
def __repr__ (self ) -> str :
880
- return '<Tool {}:{}:{}>' . format ( self . _vendor , self . _name , self . _version )
1139
+ return f'<Copyright text= { self . text } >'
881
1140
882
1141
883
1142
if sys .version_info >= (3 , 8 ):
0 commit comments