Coverage for sources/agentsmgr/exceptions.py: 39%
88 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''' Family of exceptions for package API. '''
24from . import __
27class Omniexception( __.immut.exceptions.Omniexception ):
28 ''' Base for all exceptions raised by package API. '''
31class Omnierror( Omniexception, Exception ):
32 ''' Base for error exceptions raised by package API. '''
34 def render_as_markdown( self ) -> tuple[ str, ... ]:
35 ''' Renders exception as Markdown lines for display. '''
36 return ( f"❌ {self}", )
39class CoderAbsence( Omnierror, ValueError ):
40 ''' Coder absence in registry. '''
42 def __init__( self, coder: str ):
43 message = f"Coder not found in registry: {coder}"
44 super( ).__init__( message )
47class ConfigurationAbsence( Omnierror, FileNotFoundError ):
49 def __init__(
50 self, location: __.Absential[ __.Path ] = __.absent
51 ) -> None:
52 message = "Could not locate agents configuration"
53 if not __.is_absent( location ):
54 message = f"{message} at '{location}'"
55 super( ).__init__( f"{message}." )
57 def render_as_markdown( self ) -> tuple[ str, ... ]:
58 return (
59 f"❌ {self}",
60 "",
61 "Run 'copier copy gh:emcd/agents-common' to configure agents."
62 )
65class ConfigurationInvalidity( Omnierror, ValueError ):
66 ''' Base configuration data invalidity. '''
68 def __init__( self, reason: __.Absential[ str | Exception ] = __.absent ):
69 if __.is_absent( reason ): message = "Invalid configuration."
70 else: message = f"Invalid configuration: {reason}"
71 super( ).__init__( message )
75class ContentAbsence( Omnierror, FileNotFoundError ):
76 ''' Content file absence. '''
78 def __init__( self, content_type: str, content_name: str, coder: str ):
79 message = (
80 f"No {content_type} content found for {coder}: {content_name}" )
81 super( ).__init__( message )
84class FileOperationFailure( Omnierror, OSError ):
85 ''' File or directory operation failure. '''
87 def __init__( self, path: __.Path, operation: str = "access file" ):
88 message = f"Failed to {operation}: {path}"
89 super( ).__init__( message )
92class ContextInvalidity( Omnierror, TypeError ):
93 ''' Invalid execution context. '''
95 def __init__( self ):
96 message = "Invalid execution context: expected agentsmgr.cli.Globals"
97 super( ).__init__( message )
100class DataSourceNoSupport( Omnierror, ValueError ):
101 ''' Unsupported data source format error. '''
103 def __init__( self, source_spec: str ):
104 message = f"Unsupported source format: {source_spec}"
105 super( ).__init__( message )
110class GlobalsPopulationFailure( Omnierror, OSError ):
111 ''' Global settings population failure. '''
113 def __init__( self, source: __.Path, target: __.Path ):
114 message = f"Failed to populate global file from {source} to {target}"
115 super( ).__init__( message )
119class MemoryFileAbsence( Omnierror, FileNotFoundError ):
120 ''' Memory file absence.
122 Raised when project memory file (conventions.md) does not exist
123 but memory symlinks need to be created.
124 '''
126 def __init__( self, location: __.Path ) -> None:
127 self.location = location
128 super( ).__init__( f"Memory file not found: {location}" )
130 def render_as_markdown( self ) -> tuple[ str, ... ]:
131 ''' Renders memory file absence with helpful guidance. '''
132 lines = [ "## Error: Memory File Not Found" ]
133 lines.append( "" )
134 lines.append(
135 "The project memory file does not exist at the expected "
136 "location:" )
137 lines.append( "" )
138 lines.append( f" {self.location}" )
139 lines.append( "" )
140 lines.append(
141 "Memory files provide project-specific conventions and "
142 "context to AI coding assistants. Create this file before "
143 "running `agentsmgr populate`." )
144 lines.append( "" )
145 lines.append(
146 "**Suggested action**: Create "
147 "`.auxiliary/configuration/conventions.md` with "
148 "project-specific conventions, or copy from a template "
149 "project." )
150 return tuple( lines )
153class TargetModeNoSupport( Omnierror, ValueError ):
154 ''' Targeting mode lack of support. '''
156 def __init__( self, coder: str, mode: str, reason: str = '' ):
157 self.coder = coder
158 self.mode = mode
159 self.reason = reason
160 message = (
161 f"The {coder} coder does not support {mode} targeting mode." )
162 if reason: message = f"{message} {reason}"
163 super( ).__init__( message )
165 def render_as_markdown( self ) -> tuple[ str, ... ]:
166 ''' Renders targeting mode error with helpful guidance. '''
167 lines = [
168 "## Error: Unsupported Targeting Mode",
169 "",
170 f"The **{self.coder}** coder does not support "
171 f"**{self.mode}** targeting mode.",
172 ]
173 if self.reason:
174 lines.extend( [ "", self.reason ] )
175 return tuple( lines )
178class TemplateError( Omnierror, ValueError ):
179 ''' Template processing error. '''
181 def __init__( self, template_name: str ):
182 super( ).__init__( f"Template error: {template_name}" )
184 @classmethod
185 def for_missing_template(
186 cls, coder: str, item_type: str
187 ) -> __.typx.Self:
188 ''' Creates error for missing template. '''
189 return cls( f"no {item_type} template found for {coder}" )
191 @classmethod
192 def for_extension_parse( cls, template_name: str ) -> __.typx.Self:
193 ''' Creates error for extension parsing failure. '''
194 return cls( f"cannot determine output extension for {template_name}" )
197class ToolSpecificationInvalidity( ConfigurationInvalidity ):
198 ''' Tool specification invalidity. '''
200 def __init__( self, specification: __.typx.Any ):
201 message = f"Unrecognized tool specification: {specification}"
202 super( ).__init__( message )
205class ToolSpecificationTypeInvalidity( ConfigurationInvalidity ):
206 ''' Tool specification type invalidity. '''
208 def __init__( self, specification: __.typx.Any ):
209 specification_type = type( specification ).__name__
210 message = (
211 f"Tool specification must be string or dict, got: "
212 f"{specification_type}" )
213 super( ).__init__( message )