Coverage for sources/mimeogram/__/inscription.py: 100%
62 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-16 02:11 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-16 02:11 +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''' Scribes for debugging and logging. '''
24from . import imports as __
27class Modes( __.enum.Enum ): # TODO: Python 3.11: StrEnum
28 ''' Possible modes for logging output. '''
30 Null = 'null' # suppress library logs
31 Pass = 'pass' # pass library logs to root logger # nosec
32 Rich = 'rich' # print rich library logs to stderr
35class Control(
36 metaclass = __.ImmutableStandardDataclass,
37 decorators = ( __.standard_dataclass, ),
38):
39 ''' Logging and debug printing behavior. '''
41 mode: Modes = Modes.Null
42 level: __.typx.Optional[ __.typx.Literal[
43 'debug', 'info', 'warn', 'error', 'critical' # noqa: F821
44 ] ] = None
46 # TODO? Support capture file and stream choice.
49def prepare( control: Control ) -> None:
50 ''' Prepares various scribes in a sensible manner. '''
51 prepare_scribe_icecream( control = control )
52 prepare_scribe_logging( control = control )
55def prepare_scribe_icecream( control: Control ) -> None:
56 ''' Prepares Icecream debug printing. '''
57 from os import environ
58 match environ.get( '_DEVELOPMENT_MODE_', 'FALSE' ).upper( ):
59 case '1' | 'ON' | 'T' | 'TRUE' | 'Y' | 'YES': pass
60 case _:
61 import builtins
62 setattr( builtins, 'ic', _passthrough )
63 return
64 from icecream import ic, install
65 nomargs: dict[ str, __.typx.Any ] = dict(
66 includeContext = True, prefix = 'DEBUG ' )
67 match control.mode:
68 case Modes.Null:
69 ic.configureOutput( **nomargs )
70 ic.disable( )
71 case Modes.Pass:
72 ic.configureOutput( **nomargs )
73 case Modes.Rich: # pragma: no branch
74 from rich.pretty import pretty_repr
75 ic.configureOutput( argToStringFunction = pretty_repr, **nomargs )
76 install( )
79def prepare_scribe_logging( control: Control ) -> None:
80 ''' Prepares standard Python logging. '''
81 # https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
82 import logging
83 level_name = _discover_inscription_level_name( control )
84 level = getattr( logging, level_name.upper( ) )
85 scribe = __.produce_scribe( __.package_name )
86 scribe.propagate = False # prevent double-logging
87 scribe.setLevel( level )
88 match control.mode:
89 case Modes.Null:
90 scribe.addHandler( logging.NullHandler( ) )
91 case Modes.Pass:
92 scribe.propagate = True
93 case Modes.Rich: # pragma: no branch
94 from rich.console import Console
95 from rich.logging import RichHandler
96 formatter = logging.Formatter( "%(name)s: %(message)s" )
97 handler = RichHandler(
98 console = Console( stderr = True ),
99 rich_tracebacks = True,
100 show_time = False )
101 handler.setFormatter( formatter )
102 scribe.addHandler( handler )
103 scribe.debug( "Logging initialized." )
106def _discover_inscription_level_name( control: Control ) -> str:
107 if control.level is None:
108 from os import environ
109 for envvar_name_base in ( 'INSCRIPTION', 'LOG' ):
110 envvar_name = (
111 "{name}_{base}_LEVEL".format(
112 base = envvar_name_base,
113 name = __.package_name.upper( ) ) )
114 if envvar_name not in environ: continue
115 return environ[ envvar_name ]
116 return 'INFO'
117 return control.level
120def _passthrough( *args: __.typx.Any ) -> __.cabc.Sequence[ __.typx.Any ]:
121 return args