1- from typing import Any , Dict , Iterable , List , Optional , Type , Union
1+ from typing import Any , Dict , Iterable , Iterator , List , Optional , Type , Union
22from xml .etree .ElementTree import Element # nosec B405
33
44import serializable
77from sortedcontainers import SortedSet
88
99from .._internal .compare import ComparableTuple as _ComparableTuple
10- from ..exception .serialization import CycloneDxDeserializationException , SerializationOfUnexpectedValueException
10+ from ..exception .model import MutuallyExclusivePropertiesException
11+ from ..exception .serialization import CycloneDxDeserializationException
1112from ..model import ExternalReference , HashType , _HashTypeRepositorySerializationHelper
1213from ..model .component import Component
1314from ..model .service import Service
@@ -152,15 +153,57 @@ def __init__(self, *, components: Optional[Iterable[Component]] = None,
152153 # Must use components/services or tools. Cannot use both
153154 self ._tools = tools or SortedSet ()
154155
156+ @property
157+ def components (self ) -> Iterable [Component ]:
158+ """
159+ Returns:
160+ A SortedSet of Components
161+ """
162+ return self ._components
163+
164+ @components .setter
165+ def components (self , components : Iterable [Component ]) -> None :
166+ if self ._tools :
167+ raise MutuallyExclusivePropertiesException (
168+ 'Cannot serialize both old (CycloneDX <= 1.4) and new '
169+ '(CycloneDX >= 1.5) format for tools.'
170+ )
171+
172+ self ._components = components
173+
174+ @property
175+ def services (self ) -> Iterable [Service ]:
176+ """
177+ Returns:
178+ A SortedSet of Services
179+ """
180+ return self ._services
181+
182+ @services .setter
183+ def services (self , services : Iterable [Service ]) -> None :
184+ if self ._tools :
185+ raise MutuallyExclusivePropertiesException (
186+ 'Cannot serialize both old (CycloneDX <= 1.4) and new '
187+ '(CycloneDX >= 1.5) format for tools: {o!r}'
188+ )
189+ self ._services = services
190+
155191 def __getattr__ (self , name : str ) -> Any :
156- if name == 'components' :
157- return self ._components
158- if name == 'services' :
159- return self ._services
192+ """
193+ Enables us to behave as list of tools to maintain
194+ backward compatibility.
160195
196+ Returns:
197+ An attribute of SortedSet
198+ """
161199 return getattr (self ._tools , name )
162200
163- def __iter__ (self ) -> Tool :
201+ def __iter__ (self ) -> Iterator [Tool ]:
202+ """
203+ Also part of acting as a list of tools
204+
205+ Returns Iterator[Tool]
206+ """
164207 for t in self ._tools :
165208 yield t
166209
@@ -173,12 +216,6 @@ def json_normalize(cls, o: ToolRepository, *,
173216 if not any ([o ._tools , o ._components , o ._services ]): # pylint: disable=protected-access
174217 return None
175218
176- if o ._tools and any ([o ._components , o ._services ]): # pylint: disable=protected-access
177- raise SerializationOfUnexpectedValueException (
178- 'Cannot serialize both old (CycloneDX <= 1.4) and new '
179- '(CycloneDX >= 1.5) format for tools: {o!r}'
180- )
181-
182219 if o ._tools : # pylint: disable=protected-access
183220 return [Tool .as_json (t ) for t in o ._tools ] # pylint: disable=protected-access
184221
0 commit comments