Coverage for sources/agentsmgr/commands/base.py: 22%
45 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-13 00:43 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-13 00:43 +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''' Shared infrastructure for command implementations.
23 This module provides common utilities used across multiple command
24 implementations, including error handling, configuration management,
25 and data location resolution.
26'''
29import yaml as _yaml
31from . import __
34CoderConfiguration: __.typx.TypeAlias = __.cabc.Mapping[ str, __.typx.Any ]
37def intercept_errors( ) -> __.cabc.Callable[
38 [ __.cabc.Callable[
39 ..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ] ],
40 __.cabc.Callable[
41 ..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ]
42]:
43 ''' Decorator for CLI command handlers to intercept and render errors.
45 Provides clean separation between business logic and error handling:
47 **Purpose**: Enables command implementations to focus purely on
48 business logic while the decorator handles all error presentation
49 concerns.
51 **Responsibilities**:
53 - Intercepts Omnierror exceptions from command execution
54 - Renders errors in appropriate format (markdown for CLI)
55 - Ensures proper exit code handling (SystemExit with code 1)
57 **Pattern**: Commands implement business logic and raise exceptions;
58 decorator handles presentation and process termination. This
59 separation ensures commands remain testable and focused.
61 **Type narrowing note**: The isinstance(auxdata, _core.Globals)
62 checks in individual command execute methods serve type narrowing
63 purposes and must be retained for proper type checking.
64 '''
65 def decorator(
66 function: __.cabc.Callable[
67 ..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ]
68 ) -> __.cabc.Callable[
69 ..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ]
70 ]:
71 @__.funct.wraps( function )
72 async def wrapper(
73 self: __.typx.Any,
74 auxdata: __.typx.Any,
75 *posargs: __.typx.Any,
76 **nomargs: __.typx.Any,
77 ) -> None:
78 try: return await function( self, auxdata, *posargs, **nomargs )
79 except __.Omnierror as exception:
80 if isinstance( auxdata, __.Globals ):
81 await __.render_and_print_result(
82 exception, auxdata.display, auxdata.exits )
83 else:
84 for line in exception.render_as_markdown( ):
85 print( line, file = __.sys.stderr )
86 raise SystemExit( 1 ) from None
87 return wrapper
88 return decorator
91async def retrieve_configuration(
92 target: __.Path
93) -> __.cabc.Mapping[ str, __.typx.Any ]:
94 ''' Loads and validates configuration from Copier answers file.
96 Unified configuration loading used by multiple command
97 implementations. Reads from standard Copier answers location
98 and validates required fields.
99 '''
100 answers_file = (
101 target / ".auxiliary/configuration/copier-answers--agents.yaml" )
102 if not answers_file.exists( ):
103 raise __.ConfigurationAbsence( target )
104 try: content = answers_file.read_text( encoding = 'utf-8' )
105 except ( OSError, IOError ) as exception:
106 raise __.ConfigurationAbsence( ) from exception
107 try:
108 configuration: __.cabc.Mapping[ str, __.typx.Any ] = (
109 _yaml.safe_load( content ) )
110 except _yaml.YAMLError as exception:
111 raise __.ConfigurationInvalidity( exception ) from exception
112 if not isinstance( configuration, __.cabc.Mapping ):
113 raise __.ConfigurationInvalidity( )
114 await validate_configuration( configuration )
115 return configuration
118async def validate_configuration(
119 configuration: __.cabc.Mapping[ str, __.typx.Any ]
120) -> None:
121 ''' Validates required configuration fields are present and non-empty. '''
122 if not configuration.get( 'coders' ):
123 raise __.ConfigurationInvalidity( )
124 if not configuration.get( 'languages' ):
125 raise __.ConfigurationInvalidity( )
128def retrieve_data_location( source_spec: str ) -> __.Path:
129 ''' Resolves data source specification to local filesystem path.
131 Supports local paths, Git repositories, and remote sources through
132 pluggable source handlers. Uses registered handlers to resolve
133 various URL schemes to local filesystem paths.
134 '''
135 return __.resolve_source_location( source_spec )
138def retrieve_variant_answers_file(
139 auxdata: __.Globals, variant: str
140) -> __.Path:
141 ''' Retrieves path to variant answers file in test data directory.
143 Validates file existence and raises ConfigurationAbsence if not
144 found.
145 '''
146 data_directory = auxdata.provide_data_location( )
147 project_root = data_directory.parent
148 answers_file = (
149 project_root / 'tests' / 'data' / 'profiles'
150 / f"answers-{variant}.yaml" )
151 if not answers_file.exists( ):
152 raise __.ConfigurationAbsence( )
153 return answers_file