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

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

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#============================================================================# 

19 

20 

21''' Family of exceptions for package API. ''' 

22 

23 

24import contextlib as _contextlib 

25import logging as _logging 

26 

27import exceptiongroup as _exceptiongroup 

28 

29from . import __ 

30 

31 

32class Omniexception( __.immut.exceptions.Omniexception ): 

33 ''' Base for all exceptions raised by package API. ''' 

34 

35 

36class Omnierror( Omniexception, Exception ): 

37 ''' Base for error exceptions raised by package API. ''' 

38 

39 

40class ContentAcquireFailure( Omnierror ): 

41 ''' Failure to acquire content from location. ''' 

42 

43 def __init__( self, location: str | __.Path ): 

44 super( ).__init__( f"Could not acquire content from '{location}'." ) 

45 

46 

47class ContentDecodeFailure( Omnierror ): 

48 ''' Failure to decode content as character set from location. ''' 

49 

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}'." ) 

54 

55 

56class ContentUpdateFailure( Omnierror ): 

57 ''' Failure to update content at location. ''' 

58 

59 def __init__( self, location: str | __.Path ): 

60 super( ).__init__( f"Could not update content at '{location}'." ) 

61 

62 

63class DifferencesProcessFailure( Omnierror ): 

64 ''' Failure during diff processing. ''' 

65 

66 def __init__( self, reason: str ): 

67 super( ).__init__( f"Could not process changes. Reason: {reason}" ) 

68 

69 

70class EditorFailure( Omnierror ): 

71 ''' Failure while operating editor. ''' 

72 

73 def __init__( self, cause: str | Exception ): 

74 super( ).__init__( f"Could not edit content. Cause: {cause}" ) 

75 

76 

77class LocationInvalidity( Omnierror ): 

78 ''' Invalid location. ''' 

79 

80 def __init__( self, location: str | __.Path ): 

81 super( ).__init__( f"Invalid location '{location}'." ) 

82 

83 

84class MimeogramFormatEmpty( Omnierror ): 

85 ''' Attempt to format empty mimeogram. ''' 

86 

87 def __init__( self ): 

88 super( ).__init__( "Cannot format empty mimeogram." ) 

89 

90 

91class MimeogramParseFailure( Omnierror ): 

92 ''' Failure to parse mimeogram content. ''' 

93 

94 def __init__( self, reason: str ): 

95 super( ).__init__( f"Could not parse mimeogram. Reason: {reason}" ) 

96 

97 

98class PagerFailure( Omnierror ): 

99 ''' Failure while operating pager. ''' 

100 

101 def __init__( self, cause: str | Exception ): 

102 super( ).__init__( f"Could not display content. Cause: {cause}" ) 

103 

104 

105class ProgramAbsenceError( Omnierror ): 

106 ''' Could not discover valid editor. ''' 

107 

108 def __init__( self, species: str ): 

109 super( ).__init__( f"Could not discover valid {species}." ) 

110 

111 

112class TextualMimetypeInvalidity( Omnierror ): 

113 ''' Invalid textual MIME type for content at location. ''' 

114 

115 def __init__( self, location: str | __.Path, mimetype: str ): 

116 super( ).__init__( 

117 f"Invalid MIME type '{mimetype}' for content at '{location}'." ) 

118 

119 

120class TokenizerVariantInvalidity( Omnierror ): 

121 ''' Invalid tokenizer variant. ''' 

122 

123 def __init__( self, name: str, variant: str ): 

124 super( ).__init__( 

125 f"Invalid variant '{variant}' for tokenizer '{name}'." ) 

126 

127 

128class UrlSchemeNoSupport( Omnierror ): 

129 ''' Unsupported URL scheme. ''' 

130 

131 def __init__( self, url: str ): 

132 super( ).__init__( f"Scheme of URL '{url}' is not supported." ) 

133 

134 

135class UserOperateCancellation( Omniexception ): 

136 ''' Operation cancelled by user. ''' 

137 

138 def __init__( self, cause: BaseException ): 

139 super( ).__init__( f"Operation cancelled by user. Cause: {cause}" ) 

140 

141 

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. 

150 

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