Coverage for sources/mimeogram/__/exceptions.py: 71%
35 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-02 23:41 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-03-02 23:41 +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 internals.
23 * ``Omniexception``: Base for all internal exceptions
24 * ``Omnierror``: Base for all internals errors
25'''
28import contextlib as _contextlib
29import logging as _logging
31import exceptiongroup as _exceptiongroup
33from . import imports as __
36class Omniexception( BaseException, metaclass = __.ImmutableClass ):
37 ''' Base for all exceptions raised internally. '''
38 # TODO: Class attribute concealment.
39 # TODO: Instance attribute concealment and immutability.
41 _attribute_visibility_includes_: __.cabc.Collection[ str ] = (
42 frozenset( ( '__cause__', '__context__', ) ) )
45class Omnierror( Omniexception, Exception ):
46 ''' Base for error exceptions raised internally. '''
49class AddressLocateFailure( Omnierror, LookupError ):
50 ''' Failure to locate address. '''
52 def __init__(
53 self, subject: str, address: __.cabc.Sequence[ str ], part: str
54 ):
55 super( ).__init__(
56 f"Could not locate part '{part}' of address '{address}' "
57 f"in {subject}." )
60class AsyncAssertionFailure( Omnierror, AssertionError, TypeError ):
61 ''' Assertion of awaitability of entity failed. '''
63 def __init__( self, entity: __.typx.Any ):
64 super( ).__init__( f"Entity must be awaitable: {entity!r}" )
67class EntryAssertionFailure( Omnierror, AssertionError, KeyError ):
68 ''' Assertion of entry in dictionary failed. '''
70 def __init__( self, subject: str, name: str ):
71 super( ).__init__( f"Could not find entry '{name}' in {subject}." )
74class OperationInvalidity( Omnierror, RuntimeError ):
75 ''' Invalid operation. '''
77 def __init__( self, subject: str, name: str ):
78 super( ).__init__(
79 f"Could not perform operation '{name}' on {subject}." )
82@_contextlib.contextmanager
83def report_exceptions(
84 scribe: _logging.Logger,
85 message: str,
86 eclass: type[ BaseException ] = SystemExit,
87 eposargs: __.cabc.Sequence[ __.typx.Any ] = ( 1, ),
88) -> __.cabc.Generator[ None, None, None ]:
89 ''' Intercepts and reports exceptions.
91 By default, raises ``SystemExit( 1 )``.
92 '''
93 level = scribe.getEffectiveLevel( )
94 try: yield
95 except _exceptiongroup.ExceptionGroup as excg: # pyright: ignore
96 scribe.error( message ) # noqa: TRY400
97 for exc in excg.exceptions: # pyright: ignore
98 if level <= _logging.DEBUG:
99 nomargs = dict( exc_info = exc ) # pyright: ignore
100 else: nomargs = { }
101 scribe.error( # noqa: TRY400
102 f"\tCause: {exc}", **nomargs ) # pyright: ignore
103 if eclass: raise eclass( *eposargs ) from None
104 except Exception as exc: # pylint: disable=broad-exception-caught
105 if level <= _logging.DEBUG: scribe.exception( f"{message}" ) 105 ↛ 107line 105 didn't jump to line 107 because
106 else: scribe.error( f"{message} Cause: {exc}" ) # noqa: TRY400
107 if eclass: raise eclass( *eposargs ) from None