Coverage for sources / mimeogram / exceptions.py: 78%
68 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 17:27 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-18 17:27 +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. '''
24import contextlib as _contextlib
25import logging as _logging
27import exceptiongroup as _exceptiongroup
29from . import __
32class Omniexception( __.immut.exceptions.Omniexception ):
33 ''' Base for all exceptions raised by package API. '''
36class Omnierror( Omniexception, Exception ):
37 ''' Base for error exceptions raised by package API. '''
40class ContentAcquireFailure( Omnierror ):
41 ''' Failure to acquire content from location. '''
43 def __init__( self, location: str | __.Path ):
44 super( ).__init__( f"Could not acquire content from '{location}'." )
47class ContentDecodeFailure( Omnierror ):
48 ''' Failure to decode content as character set from location. '''
50 def __init__( self, location: str | __.Path, charset: str ):
51 super( ).__init__(
52 f"Could not decode content at '{location}' "
53 f"as character set '{charset}'." )
56class ContentUpdateFailure( Omnierror ):
57 ''' Failure to update content at location. '''
59 def __init__( self, location: str | __.Path ):
60 super( ).__init__( f"Could not update content at '{location}'." )
63class DifferencesProcessFailure( Omnierror ):
64 ''' Failure during diff processing. '''
66 def __init__( self, reason: str ):
67 super( ).__init__( f"Could not process changes. Reason: {reason}" )
70class EditorFailure( Omnierror ):
71 ''' Failure while operating editor. '''
73 def __init__( self, cause: str | Exception ):
74 super( ).__init__( f"Could not edit content. Cause: {cause}" )
77class LocationInvalidity( Omnierror ):
78 ''' Invalid location. '''
80 def __init__( self, location: str | __.Path ):
81 super( ).__init__( f"Invalid location '{location}'." )
84class MimeogramFormatEmpty( Omnierror ):
85 ''' Attempt to format empty mimeogram. '''
87 def __init__( self ):
88 super( ).__init__( "Cannot format empty mimeogram." )
91class MimeogramParseFailure( Omnierror ):
92 ''' Failure to parse mimeogram content. '''
94 def __init__( self, reason: str ):
95 super( ).__init__( f"Could not parse mimeogram. Reason: {reason}" )
98class PagerFailure( Omnierror ):
99 ''' Failure while operating pager. '''
101 def __init__( self, cause: str | Exception ):
102 super( ).__init__( f"Could not display content. Cause: {cause}" )
105class ProgramAbsenceError( Omnierror ):
106 ''' Could not discover valid editor. '''
108 def __init__( self, species: str ):
109 super( ).__init__( f"Could not discover valid {species}." )
112class TextualMimetypeInvalidity( Omnierror ):
113 ''' Invalid textual MIME type for content at location. '''
115 def __init__( self, location: str | __.Path, mimetype: str ):
116 super( ).__init__(
117 f"Invalid MIME type '{mimetype}' for content at '{location}'." )
120class TokenizerVariantInvalidity( Omnierror ):
121 ''' Invalid tokenizer variant. '''
123 def __init__( self, name: str, variant: str ):
124 super( ).__init__(
125 f"Invalid variant '{variant}' for tokenizer '{name}'." )
128class UrlSchemeNoSupport( Omnierror ):
129 ''' Unsupported URL scheme. '''
131 def __init__( self, url: str ):
132 super( ).__init__( f"Scheme of URL '{url}' is not supported." )
135class UserOperateCancellation( Omniexception ):
136 ''' Operation cancelled by user. '''
138 def __init__( self, cause: BaseException ):
139 super( ).__init__( f"Operation cancelled by user. Cause: {cause}" )
142@_contextlib.contextmanager
143def report_exceptions(
144 scribe: _logging.Logger,
145 message: str,
146 eclass: type[ BaseException ] = SystemExit,
147 eposargs: __.cabc.Sequence[ __.typx.Any ] = ( 1, ),
148) -> __.cabc.Generator[ None, None, None ]:
149 """Intercept and report exceptions.
151 By default, raises ``SystemExit(1)``.
152 """
153 level = scribe.getEffectiveLevel( )
154 try:
155 yield
156 except _exceptiongroup.ExceptionGroup as excg: # pyright: ignore
157 scribe.error( message )
158 for exc in excg.exceptions: # pyright: ignore
159 if level <= _logging.DEBUG: # noqa: SIM108
160 nomargs = dict( exc_info = exc ) # pyright: ignore
161 else:
162 nomargs = { }
163 scribe.error( f"\tCause: {exc}", **nomargs ) # pyright: ignore
164 if eclass:
165 raise eclass( *eposargs ) from None
166 except Exception as exc:
167 if level <= _logging.DEBUG: 167 ↛ 168line 167 didn't jump to line 168 because the condition on line 167 was never true
168 scribe.exception( f"{message}" )
169 else:
170 scribe.error( f"{message} Cause: {exc}" )
171 if eclass: 171 ↛ exitline 171 didn't return from function 'report_exceptions' because the condition on line 171 was always true
172 raise eclass( *eposargs ) from None