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