4343
4444@serializable .serializable_enum
4545class LifecyclePhase (str , Enum ):
46+ """
47+ Enum object that defines the permissible 'phase' for a Lifecycle according to the CycloneDX schema.
48+
49+ .. note::
50+ See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.3/#type_classification
51+ """
4652 DESIGN = 'design'
47- PREBUILD = 'pre-build'
53+ PRE_BUILD = 'pre-build'
4854 BUILD = 'build'
49- POSTBUILD = 'post-build'
55+ POST_BUILD = 'post-build'
5056 OPERATIONS = 'operations'
5157 DISCOVERY = 'discovery'
52- DECOMISSION = 'decommission'
58+ DECOMMISSION = 'decommission'
5359
5460
5561@serializable .serializable_class
@@ -88,11 +94,18 @@ def __lt__(self, other: Any) -> bool:
8894 return NotImplemented
8995
9096 def __repr__ (self ) -> str :
91- return f'<PredefinedLifecycle name ={ self ._phase } >'
97+ return f'<PredefinedLifecycle phase ={ self ._phase } >'
9298
9399
94100@serializable .serializable_class
95101class NamedLifecycle :
102+ """
103+ Object that defines custom state in the product lifecycle.
104+
105+ .. note::
106+ See the CycloneDX Schema definition: https://cyclonedx.org/docs/1.5/#metadata_lifecycles
107+ """
108+
96109 def __init__ (self , name : str , * , description : Optional [str ] = None ) -> None :
97110 self ._name = name
98111 self ._description = description
@@ -165,9 +178,7 @@ class LifecycleRepository(SortedSet[Lifecycle]):
165178
166179 This is a `set`, not a `list`. Order MUST NOT matter here.
167180 """
168-
169181else :
170-
171182 class LifecycleRepository (SortedSet ):
172183 """Collection of :class:`Lifecycle`.
173184
@@ -182,7 +193,6 @@ def json_normalize(cls, o: LifecycleRepository, *,
182193 ** __ : Any ) -> Any :
183194 if len (o ) == 0 :
184195 return None
185-
186196 return [json_loads (li .as_json ( # type:ignore[union-attr]
187197 view_ = view )) for li in o ]
188198
@@ -192,12 +202,13 @@ def json_denormalize(cls, o: List[Dict[str, Any]],
192202 repo = LifecycleRepository ()
193203 for li in o :
194204 if 'phase' in li :
195- repo .add (PredefinedLifecycle .from_json (li )) # type:ignore[attr-defined]
205+ repo .add (PredefinedLifecycle .from_json ( # type:ignore[attr-defined]
206+ li ))
196207 elif 'name' in li :
197- repo .add (NamedLifecycle .from_json (li )) # type:ignore[attr-defined]
208+ repo .add (NamedLifecycle .from_json ( # type:ignore[attr-defined]
209+ li ))
198210 else :
199211 raise CycloneDxDeserializationException (f'unexpected: { li !r} ' )
200-
201212 return repo
202213
203214 @classmethod
@@ -208,33 +219,27 @@ def xml_normalize(cls, o: LifecycleRepository, *,
208219 ** __ : Any ) -> Optional [Element ]:
209220 if len (o ) == 0 :
210221 return None
211-
212222 elem = Element (element_name )
213223 for li in o :
214224 elem .append (li .as_xml ( # type:ignore[union-attr]
215225 view_ = view , as_string = False , element_name = 'lifecycle' , xmlns = xmlns ))
216-
217226 return elem
218227
219228 @classmethod
220229 def xml_denormalize (cls , o : Element ,
221230 default_ns : Optional [str ],
222231 ** __ : Any ) -> LifecycleRepository :
223232 repo = LifecycleRepository ()
224-
225- for li in o :
226- tag = li .tag if default_ns is None else li .tag .replace (f'{{{ default_ns } }}' , '' )
227-
228- if tag == 'lifecycle' :
229- stages = list (li )
230-
231- predefined_lifecycle = next ((el for el in stages if 'phase' in el .tag ), None )
232- named_lifecycle = next ((el for el in stages if 'name' in el .tag ), None )
233- if predefined_lifecycle is not None :
234- repo .add (PredefinedLifecycle .from_xml (li , default_ns )) # type:ignore[attr-defined]
235- elif named_lifecycle is not None :
236- repo .add (NamedLifecycle .from_xml (li , default_ns )) # type:ignore[attr-defined]
233+ ns_map = {'bom' : default_ns or '' }
234+ # Do not iterate over `o` and do not check for expected `.tag` of items.
235+ # This check could have been done by schema validators before even deserializing.
236+ for li in o .iterfind ('bom:lifecycle' , ns_map ):
237+ if li .find ('bom:phase' , ns_map ) is not None :
238+ repo .add (PredefinedLifecycle .from_xml ( # type:ignore[attr-defined]
239+ li , default_ns ))
240+ elif li .find ('bom:name' , ns_map ) is not None :
241+ repo .add (NamedLifecycle .from_xml ( # type:ignore[attr-defined]
242+ li , default_ns ))
237243 else :
238- raise CycloneDxDeserializationException (f'unexpected: { li !r} ' )
239-
244+ raise CycloneDxDeserializationException (f'unexpected content: { li !r} ' )
240245 return repo
0 commit comments