19
19
and according to different versions of the CycloneDX schema standard.
20
20
"""
21
21
22
- import importlib
23
22
import os
24
23
from abc import ABC , abstractmethod
25
- from typing import Iterable , Union , cast
24
+ from importlib import import_module
25
+ from typing import Iterable , Optional , Type , Union
26
26
27
27
from ..model .bom import Bom
28
28
from ..model .component import Component
@@ -46,7 +46,12 @@ def _chained_components(self, container: Union[Bom, Component]) -> Iterable[Comp
46
46
@property
47
47
@abstractmethod
48
48
def schema_version (self ) -> SchemaVersion :
49
- pass
49
+ ...
50
+
51
+ @property
52
+ @abstractmethod
53
+ def output_format (self ) -> OutputFormat :
54
+ ...
50
55
51
56
@property
52
57
def generated (self ) -> bool :
@@ -64,28 +69,23 @@ def set_bom(self, bom: Bom) -> None:
64
69
65
70
@abstractmethod
66
71
def generate (self , force_regeneration : bool = False ) -> None :
67
- pass
72
+ ...
68
73
69
74
@abstractmethod
70
75
def output_as_string (self ) -> str :
71
- pass
76
+ ...
72
77
73
78
def output_to_file (self , filename : str , allow_overwrite : bool = False ) -> None :
74
79
# Check directory writable
75
80
output_filename = os .path .realpath (filename )
76
81
output_directory = os .path .dirname (output_filename )
77
-
78
82
if not os .access (output_directory , os .W_OK ):
79
83
raise PermissionError (output_directory )
80
-
81
84
if os .path .exists (output_filename ) and not allow_overwrite :
82
85
raise FileExistsError (output_filename )
83
-
84
86
with open (output_filename , mode = 'wb' ) as f_out :
85
87
f_out .write (self .output_as_string ().encode ('utf-8' ))
86
88
87
- f_out .close ()
88
-
89
89
90
90
def get_instance (bom : Bom , output_format : OutputFormat = OutputFormat .XML ,
91
91
schema_version : SchemaVersion = LATEST_SUPPORTED_SCHEMA_VERSION ) -> BaseOutput :
@@ -99,10 +99,16 @@ def get_instance(bom: Bom, output_format: OutputFormat = OutputFormat.XML,
99
99
:param schema_version: SchemaVersion
100
100
:return:
101
101
"""
102
+ # all exceptions are undocumented, as they are pure functional, and should be prevented by correct typing...
103
+ if not isinstance (output_format , OutputFormat ):
104
+ raise TypeError (f"unexpected output_format: { output_format !r} " )
105
+ if not isinstance (schema_version , SchemaVersion ):
106
+ raise TypeError (f"unexpected schema_version: { schema_version !r} " )
102
107
try :
103
- module = importlib .import_module (f"cyclonedx.output.{ output_format .value .lower ()} " )
104
- output_klass = getattr (module , f"{ output_format .value } { schema_version .value } " )
105
- except (ImportError , AttributeError ) as e :
106
- raise ValueError (f"Unknown format { output_format .value .lower ()!r} : { e } " ) from None
107
-
108
- return cast (BaseOutput , output_klass (bom = bom ))
108
+ module = import_module (f'.{ output_format .name .lower ()} ' , __package__ )
109
+ except ImportError as error : # pragma: no cover
110
+ raise ValueError (f'Unknown output_format: { output_format .name } ' ) from error
111
+ output_klass : Optional [Type [BaseOutput ]] = module .BY_SCHEMA_VERSION .get (schema_version , None )
112
+ if output_klass is None : # pragma: no cover
113
+ raise ValueError (f'Unknown { output_format .name } /schema_version: { schema_version .name } ' )
114
+ return output_klass (bom = bom )
0 commit comments