23
23
from enum import Enum
24
24
from typing import Iterable , Optional , Set
25
25
26
+ from sortedcontainers import SortedSet
27
+
26
28
from ..exception .model import (
27
29
InvalidLocaleTypeException ,
28
30
InvalidUriException ,
@@ -57,7 +59,41 @@ def sha1sum(filename: str) -> str:
57
59
return h .hexdigest ()
58
60
59
61
60
- class DataFlow (Enum ):
62
+ class ComparableTuple (tuple ):
63
+ """
64
+ Allows comparison of tuples, allowing for None values.
65
+ """
66
+
67
+ def __lt__ (self , other : object ) -> bool :
68
+ for s , o in zip (self , other ):
69
+ if s == o :
70
+ continue
71
+ if s is None :
72
+ return False
73
+ if o is None :
74
+ return True
75
+ if s < o :
76
+ return True
77
+ if s > o :
78
+ return False
79
+ return False
80
+
81
+ def __gt__ (self , other : object ) -> bool :
82
+ for s , o in zip (self , other ):
83
+ if s == o :
84
+ continue
85
+ if s is None :
86
+ return True
87
+ if o is None :
88
+ return False
89
+ if s < o :
90
+ return False
91
+ if s > o :
92
+ return True
93
+ return False
94
+
95
+
96
+ class DataFlow (str , Enum ):
61
97
"""
62
98
This is our internal representation of the dataFlowType simple type within the CycloneDX standard.
63
99
@@ -132,7 +168,7 @@ def __repr__(self) -> str:
132
168
return f'<DataClassification flow={ self .flow } >'
133
169
134
170
135
- class Encoding (Enum ):
171
+ class Encoding (str , Enum ):
136
172
"""
137
173
This is our internal representation of the encoding simple type within the CycloneDX standard.
138
174
@@ -215,7 +251,7 @@ def __repr__(self) -> str:
215
251
return f'<AttachedText content-type={ self .content_type } , encoding={ self .encoding } >'
216
252
217
253
218
- class HashAlgorithm (Enum ):
254
+ class HashAlgorithm (str , Enum ):
219
255
"""
220
256
This is our internal representation of the hashAlg simple type within the CycloneDX standard.
221
257
@@ -320,14 +356,19 @@ def __eq__(self, other: object) -> bool:
320
356
return hash (other ) == hash (self )
321
357
return False
322
358
359
+ def __lt__ (self , other : object ) -> bool :
360
+ if isinstance (other , HashType ):
361
+ return ComparableTuple ((self .alg , self .content )) < ComparableTuple ((other .alg , other .content ))
362
+ return NotImplemented
363
+
323
364
def __hash__ (self ) -> int :
324
365
return hash ((self .alg , self .content ))
325
366
326
367
def __repr__ (self ) -> str :
327
368
return f'<HashType { self .alg .name } :{ self .content } >'
328
369
329
370
330
- class ExternalReferenceType (Enum ):
371
+ class ExternalReferenceType (str , Enum ):
331
372
"""
332
373
Enum object that defines the permissible 'types' for an External Reference according to the CycloneDX schema.
333
374
@@ -378,6 +419,11 @@ def __eq__(self, other: object) -> bool:
378
419
return hash (other ) == hash (self )
379
420
return False
380
421
422
+ def __lt__ (self , other : object ) -> bool :
423
+ if isinstance (other , XsUri ):
424
+ return self ._uri < other ._uri
425
+ return NotImplemented
426
+
381
427
def __hash__ (self ) -> int :
382
428
return hash (self ._uri )
383
429
@@ -402,7 +448,7 @@ def __init__(self, *, reference_type: ExternalReferenceType, url: XsUri, comment
402
448
self .url = url
403
449
self .comment = comment
404
450
self .type = reference_type
405
- self .hashes = set (hashes or [])
451
+ self .hashes = SortedSet (hashes or [])
406
452
407
453
@property
408
454
def url (self ) -> XsUri :
@@ -461,13 +507,18 @@ def hashes(self) -> Set[HashType]:
461
507
462
508
@hashes .setter
463
509
def hashes (self , hashes : Iterable [HashType ]) -> None :
464
- self ._hashes = set (hashes )
510
+ self ._hashes = SortedSet (hashes )
465
511
466
512
def __eq__ (self , other : object ) -> bool :
467
513
if isinstance (other , ExternalReference ):
468
514
return hash (other ) == hash (self )
469
515
return False
470
516
517
+ def __lt__ (self , other : object ) -> bool :
518
+ if isinstance (other , ExternalReference ):
519
+ return ComparableTuple ((self ._type , self ._url , self ._comment )) < ComparableTuple ((other ._type , other ._url , other ._comment ))
520
+ return NotImplemented
521
+
471
522
def __hash__ (self ) -> int :
472
523
return hash ((
473
524
self ._type , self ._url , self ._comment ,
@@ -566,6 +617,11 @@ def __eq__(self, other: object) -> bool:
566
617
return hash (other ) == hash (self )
567
618
return False
568
619
620
+ def __lt__ (self , other : object ) -> bool :
621
+ if isinstance (other , License ):
622
+ return ComparableTuple ((self .id , self .name )) < ComparableTuple ((other .id , other .name ))
623
+ return NotImplemented
624
+
569
625
def __hash__ (self ) -> int :
570
626
return hash ((self .id , self .name , self .text , self .url ))
571
627
@@ -633,6 +689,11 @@ def __eq__(self, other: object) -> bool:
633
689
return hash (other ) == hash (self )
634
690
return False
635
691
692
+ def __lt__ (self , other : object ) -> bool :
693
+ if isinstance (other , LicenseChoice ):
694
+ return ComparableTuple ((self .license , self .expression )) < ComparableTuple ((other .license , other .expression ))
695
+ return NotImplemented
696
+
636
697
def __hash__ (self ) -> int :
637
698
return hash ((self .license , self .expression ))
638
699
@@ -690,6 +751,11 @@ def __eq__(self, other: object) -> bool:
690
751
return hash (other ) == hash (self )
691
752
return False
692
753
754
+ def __lt__ (self , other : object ) -> bool :
755
+ if isinstance (other , Property ):
756
+ return ComparableTuple ((self .name , self .value )) < ComparableTuple ((other .name , other .value ))
757
+ return NotImplemented
758
+
693
759
def __hash__ (self ) -> int :
694
760
return hash ((self .name , self .value ))
695
761
@@ -763,6 +829,11 @@ def __eq__(self, other: object) -> bool:
763
829
return hash (other ) == hash (self )
764
830
return False
765
831
832
+ def __lt__ (self , other : object ) -> bool :
833
+ if isinstance (other , NoteText ):
834
+ return ComparableTuple ((self .content , self .content_type , self .encoding )) < ComparableTuple ((other .content , other .content_type , other .encoding ))
835
+ return NotImplemented
836
+
766
837
def __hash__ (self ) -> int :
767
838
return hash ((self .content , self .content_type , self .encoding ))
768
839
@@ -830,6 +901,11 @@ def __eq__(self, other: object) -> bool:
830
901
return hash (other ) == hash (self )
831
902
return False
832
903
904
+ def __lt__ (self , other : object ) -> bool :
905
+ if isinstance (other , Note ):
906
+ return ComparableTuple ((self .locale , self .text )) < ComparableTuple ((other .locale , other .text ))
907
+ return NotImplemented
908
+
833
909
def __hash__ (self ) -> int :
834
910
return hash ((self .text , self .locale ))
835
911
@@ -902,11 +978,16 @@ def __eq__(self, other: object) -> bool:
902
978
return hash (other ) == hash (self )
903
979
return False
904
980
981
+ def __lt__ (self , other : object ) -> bool :
982
+ if isinstance (other , OrganizationalContact ):
983
+ return ComparableTuple ((self .name , self .email , self .phone )) < ComparableTuple ((other .name , other .email , other .phone ))
984
+ return NotImplemented
985
+
905
986
def __hash__ (self ) -> int :
906
987
return hash ((self .name , self .phone , self .email ))
907
988
908
989
def __repr__ (self ) -> str :
909
- return f'<OrganizationalContact name={ self .name } >'
990
+ return f'<OrganizationalContact name={ self .name } , email= { self . email } , phone= { self . phone } >'
910
991
911
992
912
993
class OrganizationalEntity :
@@ -925,8 +1006,8 @@ def __init__(self, *, name: Optional[str] = None, urls: Optional[Iterable[XsUri]
925
1006
'One of name, urls or contacts must be supplied for an OrganizationalEntity - none supplied.'
926
1007
)
927
1008
self .name = name
928
- self .url = set (urls or [])
929
- self .contact = set (contacts or [])
1009
+ self .url = SortedSet (urls or [])
1010
+ self .contact = SortedSet (contacts or [])
930
1011
931
1012
@property
932
1013
def name (self ) -> Optional [str ]:
@@ -954,7 +1035,7 @@ def url(self) -> Set[XsUri]:
954
1035
955
1036
@url .setter
956
1037
def url (self , urls : Iterable [XsUri ]) -> None :
957
- self ._url = set (urls )
1038
+ self ._url = SortedSet (urls )
958
1039
959
1040
@property
960
1041
def contact (self ) -> Set [OrganizationalContact ]:
@@ -968,7 +1049,7 @@ def contact(self) -> Set[OrganizationalContact]:
968
1049
969
1050
@contact .setter
970
1051
def contact (self , contacts : Iterable [OrganizationalContact ]) -> None :
971
- self ._contact = set (contacts )
1052
+ self ._contact = SortedSet (contacts )
972
1053
973
1054
def __eq__ (self , other : object ) -> bool :
974
1055
if isinstance (other , OrganizationalEntity ):
@@ -998,8 +1079,8 @@ def __init__(self, *, vendor: Optional[str] = None, name: Optional[str] = None,
998
1079
self .vendor = vendor
999
1080
self .name = name
1000
1081
self .version = version
1001
- self .hashes = set (hashes or [])
1002
- self .external_references = set (external_references or [])
1082
+ self .hashes = SortedSet (hashes or [])
1083
+ self .external_references = SortedSet (external_references or [])
1003
1084
1004
1085
@property
1005
1086
def vendor (self ) -> Optional [str ]:
@@ -1055,7 +1136,7 @@ def hashes(self) -> Set[HashType]:
1055
1136
1056
1137
@hashes .setter
1057
1138
def hashes (self , hashes : Iterable [HashType ]) -> None :
1058
- self ._hashes = set (hashes )
1139
+ self ._hashes = SortedSet (hashes )
1059
1140
1060
1141
@property
1061
1142
def external_references (self ) -> Set [ExternalReference ]:
@@ -1070,13 +1151,18 @@ def external_references(self) -> Set[ExternalReference]:
1070
1151
1071
1152
@external_references .setter
1072
1153
def external_references (self , external_references : Iterable [ExternalReference ]) -> None :
1073
- self ._external_references = set (external_references )
1154
+ self ._external_references = SortedSet (external_references )
1074
1155
1075
1156
def __eq__ (self , other : object ) -> bool :
1076
1157
if isinstance (other , Tool ):
1077
1158
return hash (other ) == hash (self )
1078
1159
return False
1079
1160
1161
+ def __lt__ (self , other : object ) -> bool :
1162
+ if isinstance (other , Tool ):
1163
+ return ComparableTuple ((self .vendor , self .name , self .version )) < ComparableTuple ((other .vendor , other .name , other .version ))
1164
+ return NotImplemented
1165
+
1080
1166
def __hash__ (self ) -> int :
1081
1167
return hash ((self .vendor , self .name , self .version , tuple (self .hashes ), tuple (self .external_references )))
1082
1168
@@ -1150,6 +1236,11 @@ def __eq__(self, other: object) -> bool:
1150
1236
return hash (other ) == hash (self )
1151
1237
return False
1152
1238
1239
+ def __lt__ (self , other : object ) -> bool :
1240
+ if isinstance (other , IdentifiableAction ):
1241
+ return ComparableTuple ((self .timestamp , self .name , self .email )) < ComparableTuple ((other .timestamp , other .name , other .email ))
1242
+ return NotImplemented
1243
+
1153
1244
def __hash__ (self ) -> int :
1154
1245
return hash ((self .timestamp , self .name , self .email ))
1155
1246
@@ -1187,6 +1278,11 @@ def __eq__(self, other: object) -> bool:
1187
1278
return hash (other ) == hash (self )
1188
1279
return False
1189
1280
1281
+ def __lt__ (self , other : object ) -> bool :
1282
+ if isinstance (other , Copyright ):
1283
+ return self .text < other .text
1284
+ return NotImplemented
1285
+
1190
1286
def __hash__ (self ) -> int :
1191
1287
return hash (self .text )
1192
1288
0 commit comments