Coverage for sources/ictruck/recipes/rich.py: 100%
56 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 05:21 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-05 05:21 +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''' Recipes for Rich formatters and printers.
23 .. note::
25 To use this module, you must have the ``rich`` package installed.
26'''
30from rich.console import Console as _Console
31from rich.pretty import pretty_repr as _pretty_repr
33from . import __
36_validate_arguments = (
37 __.validate_arguments(
38 globalvars = globals( ),
39 errorclass = __.exceptions.ArgumentClassInvalidity ) )
42class ConsoleTextIoInvalidity( __.exceptions.Omnierror, TypeError ):
43 ''' Text stream invalid for use with Rich console. '''
45 def __init__( self, stream: __.typx.Any ):
46 super( ).__init__( f"Invalid stream for Rich console: {stream!r}" )
49class Modes( __.enum.Enum ):
50 ''' Operation modes for Rich truck. '''
52 Formatter = 'formatter'
53 Printer = 'printer'
56ProduceTruckModeArgument: __.typx.TypeAlias = __.typx.Annotated[
57 Modes,
58 __.typx.Doc(
59 ''' Operation mode.
61 ``Formatter`` uses Rich to highlight and pretty text prior to
62 printing (output). Text from non-Rich formatters will be printed
63 as-is. Safer, but slightly more boring option.
64 ``Printer`` uses Rich to highlight and pretty text while printing
65 (output). Text from non-Rich formatters will be potentially be
66 highlighted and prettied. If the text already contains ANSI SGR
67 sequences (e.g., terminal colorization), then it might be
68 reprocessed by the printer, causing visual artifacts. Less safe,
69 but more vibrant option.
70 ''' ),
71]
72ProduceTruckStderrArgument: __.typx.TypeAlias = __.typx.Annotated[
73 bool, __.typx.Doc( ''' Output to standard diagnostic stream? ''' )
74]
77@_validate_arguments
78def install( # noqa: PLR0913
79 alias: __.InstallAliasArgument = __.builtins_alias_default,
80 flavors: __.ProduceTruckFlavorsArgument = __.absent,
81 active_flavors: __.ProduceTruckActiveFlavorsArgument = __.absent,
82 trace_levels: __.ProduceTruckTraceLevelsArgument = __.absent,
83 mode: ProduceTruckModeArgument = Modes.Formatter,
84 stderr: ProduceTruckStderrArgument = True,
85) -> __.Truck:
86 ''' Produces truck and installs it into builtins with alias.
88 Replaces an existing truck, preserving global module configurations.
90 Library developers should call :py:func:`__.register_module` instead.
91 '''
92 truck = produce_truck(
93 flavors = flavors,
94 active_flavors = active_flavors,
95 trace_levels = trace_levels,
96 mode = mode,
97 stderr = stderr )
98 return truck.install( alias = alias )
101@_validate_arguments
102def produce_console_formatter(
103 console: _Console,
104 control: __.FormatterControl,
105 mname: str,
106 flavor: int | str,
107) -> __.Formatter:
108 ''' Produces formatter which uses Rich highlighter and prettier. '''
109 return __.funct.partial( _console_format, console )
112@_validate_arguments
113def produce_console_printer(
114 console: _Console, mname: str, flavor: __.Flavor
115) -> __.Printer:
116 # TODO: Remove from recipe. Should always use simple printer.
117 ''' Produces a printer that uses Rich console printing.
119 .. note::
121 May reprocess ANSI SGR codes or markup from formatters, potentially
122 causing visual artifacts. Be careful to use this only with "safe"
123 formatters.
124 '''
125 return console.print
128@_validate_arguments
129def produce_pretty_formatter(
130 control: __.FormatterControl, mname: str, flavor: int | str
131) -> __.Formatter:
132 ''' Produces formatter which uses Rich prettier. '''
133 return _pretty_repr
136@_validate_arguments
137def produce_truck(
138 flavors: __.ProduceTruckFlavorsArgument = __.absent,
139 active_flavors: __.ProduceTruckActiveFlavorsArgument = __.absent,
140 trace_levels: __.ProduceTruckTraceLevelsArgument = __.absent,
141 mode: ProduceTruckModeArgument = Modes.Formatter,
142 stderr: ProduceTruckStderrArgument = True,
143) -> __.Truck:
144 ''' Produces icecream truck which integrates with Rich. '''
145 match mode:
146 case Modes.Formatter: factory = _produce_formatter_truck
147 case Modes.Printer: factory = _produce_printer_truck
148 return factory(
149 flavors = flavors,
150 active_flavors = active_flavors,
151 trace_levels = trace_levels,
152 stderr = stderr )
155@_validate_arguments
156def register_module(
157 name: __.RegisterModuleNameArgument = __.absent,
158 flavors: __.ProduceTruckFlavorsArgument = __.absent,
159 include_context: __.RegisterModuleIncludeContextArgument = __.absent,
160 prefix_emitter: __.RegisterModulePrefixEmitterArgument = __.absent,
161) -> None:
162 ''' Registers module with Rich prettier to format arguments.
164 Intended for library developers to configure debugging flavors without
165 overriding anything set by the application or other libraries.
166 '''
167 __.register_module(
168 name = name,
169 flavors = flavors,
170 formatter_factory = produce_pretty_formatter,
171 include_context = include_context,
172 prefix_emitter = prefix_emitter )
175def _console_format( console: _Console, value: __.typx.Any ) -> str:
176 with console.capture( ) as capture:
177 console.print( value )
178 return capture.get( )
181def _produce_formatter_truck(
182 flavors: __.ProduceTruckFlavorsArgument = __.absent,
183 active_flavors: __.ProduceTruckActiveFlavorsArgument = __.absent,
184 trace_levels: __.ProduceTruckTraceLevelsArgument = __.absent,
185 stderr: ProduceTruckStderrArgument = True,
186) -> __.Truck:
187 console = _Console( stderr = stderr )
188 gc_nomargs = { }
189 if not __.is_absent( flavors ): gc_nomargs[ 'flavors' ] = flavors
190 generalcfg = __.VehicleConfiguration(
191 formatter_factory = __.funct.partial(
192 produce_console_formatter, console ),
193 **gc_nomargs ) # pyright: ignore
194 target = __.sys.stderr if stderr else __.sys.stdout
195 if not isinstance( target, __.io.TextIOBase ):
196 raise ConsoleTextIoInvalidity( target )
197 nomargs: dict[ str, __.typx.Any ] = dict(
198 active_flavors = active_flavors,
199 generalcfg = generalcfg,
200 printer_factory = __.funct.partial(
201 __.produce_simple_printer, target ),
202 trace_levels = trace_levels )
203 return __.produce_truck( **nomargs )
206def _produce_printer_truck(
207 flavors: __.ProduceTruckFlavorsArgument = __.absent,
208 active_flavors: __.ProduceTruckActiveFlavorsArgument = __.absent,
209 trace_levels: __.ProduceTruckTraceLevelsArgument = __.absent,
210 stderr: ProduceTruckStderrArgument = True,
211) -> __.Truck:
212 console = _Console( stderr = stderr )
213 gc_nomargs = { }
214 if not __.is_absent( flavors ): gc_nomargs[ 'flavors' ] = flavors
215 generalcfg = __.VehicleConfiguration(
216 formatter_factory = produce_pretty_formatter,
217 **gc_nomargs ) # pyright: ignore
218 target = __.sys.stderr if stderr else __.sys.stdout
219 if not isinstance( target, __.io.TextIOBase ): # pragma: no cover
220 raise ConsoleTextIoInvalidity( target )
221 nomargs: dict[ str, __.typx.Any ] = dict(
222 active_flavors = active_flavors,
223 generalcfg = generalcfg,
224 printer_factory = __.funct.partial( produce_console_printer, console ),
225 trace_levels = trace_levels )
226 return __.produce_truck( **nomargs )
229# def _produce_prefix( console: _Console, mname: str, flavor: _Flavor ) -> str:
230# # TODO: Detect if terminal supports 256 colors or true color.
231# # Make spectrum of hues for trace depths, if so.
232# return _icecream.DEFAULT_PREFIX