Coverage for sources/librovore/xtnsmgr/configuration.py: 100%
48 statements
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 22:48 +0000
« prev ^ index » next coverage.py v7.10.4, created at 2025-08-20 22:48 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' Extension configuration loading and validation. '''
24from . import __
27ExtensionArguments: __.typx.TypeAlias = __.typx.Annotated[
28 __.cabc.Mapping[ str, __.typx.Any ],
29 __.ddoc.Doc( ''' Arguments to pass to extension/processor. ''' )
30]
31ExtensionConfig: __.typx.TypeAlias = __.typx.Annotated[
32 __.cabc.Mapping[ str, __.typx.Any ],
33 __.ddoc.Doc( ''' Configuration for a single extension/processor. ''' )
34]
37def validate_extension( config: ExtensionConfig ) -> None:
38 ''' Validates single extension configuration. '''
39 name = config.get( 'name' )
40 if not name or not isinstance( name, str ):
41 raise __.ExtensionConfigurationInvalidity(
42 name or '<unnamed>',
43 "Required field 'name' must be a non-empty string" )
44 enabled = config.get( 'enabled', True )
45 if not isinstance( enabled, bool ):
46 raise __.ExtensionConfigurationInvalidity(
47 name, "Field 'enabled' must be a boolean" )
48 package = config.get( 'package' )
49 if package is not None and not isinstance( package, str ):
50 raise __.ExtensionConfigurationInvalidity(
51 name, "Field 'package' must be a string" )
52 arguments = config.get( 'arguments', { } )
53 if not isinstance( arguments, dict ):
54 raise __.ExtensionConfigurationInvalidity(
55 name, "Field 'arguments' must be a dictionary" )
58def extract_inventory_extensions(
59 auxdata: __.Globals
60) -> tuple[ ExtensionConfig, ... ]:
61 ''' Loads and validates inventory extensions configuration. '''
62 return _extract_extension_type( auxdata, 'inventory-extensions' )
65def extract_structure_extensions(
66 auxdata: __.Globals
67) -> tuple[ ExtensionConfig, ... ]:
68 ''' Loads and validates structure extensions configuration. '''
69 return _extract_extension_type( auxdata, 'structure-extensions' )
72def extract_extensions(
73 auxdata: __.Globals
74) -> tuple[ ExtensionConfig, ... ]:
75 ''' Loads and validates all extensions configuration (legacy). '''
76 inventory_extensions = extract_inventory_extensions( auxdata )
77 structure_extensions = extract_structure_extensions( auxdata )
78 legacy_extensions = _extract_extension_type( auxdata, 'extensions' )
79 return inventory_extensions + structure_extensions + legacy_extensions
82def _extract_extension_type(
83 auxdata: __.Globals,
84 extension_type: str
85) -> tuple[ ExtensionConfig, ... ]:
86 ''' Loads and validates extensions of specific type. '''
87 configuration = auxdata.configuration
88 if not configuration: return ( )
89 raw = configuration.get( extension_type, [ ] )
90 if not isinstance( raw, list ):
91 raise __.ExtensionConfigurationInvalidity(
92 '<root>', f"Configuration '{extension_type}' must be a list" )
93 raw = __.typx.cast( list[ __.typx.Any ], raw )
94 extensions: list[ ExtensionConfig ] = [ ]
95 for i, config in enumerate( raw ):
96 if not isinstance( config, dict ):
97 raise __.ExtensionConfigurationInvalidity(
98 f'<{extension_type}[{i}]>',
99 f"{extension_type.title()} configuration must be dict" )
100 typed_config = __.typx.cast( ExtensionConfig, config )
101 validate_extension( typed_config )
102 extensions.append( typed_config )
103 return tuple( extensions )
106def select_active_extensions(
107 extensions: __.cabc.Sequence[ ExtensionConfig ]
108) -> tuple[ ExtensionConfig, ... ]:
109 ''' Filters extensions to only enabled ones. '''
110 return tuple( ext for ext in extensions if ext.get( 'enabled', True ) )
113def select_intrinsic_extensions(
114 extensions: __.cabc.Sequence[ ExtensionConfig ]
115) -> tuple[ ExtensionConfig, ... ]:
116 ''' Filters extensions to only built-in ones (no package field). '''
117 return tuple( ext for ext in extensions if ext.get( 'package' ) is None )
120def select_external_extensions(
121 extensions: __.cabc.Sequence[ ExtensionConfig ]
122) -> tuple[ ExtensionConfig, ... ]:
123 ''' Filters extensions to only external ones (has package field). '''
124 return tuple(
125 ext for ext in extensions if ext.get( 'package' ) is not None )
128def extract_extension_arguments(
129 extension: ExtensionConfig
130) -> ExtensionArguments:
131 ''' Extracts arguments dictionary from extension configuration. '''
132 return extension.get( 'arguments', { } )