Coverage for sources/agentsmgr/core.py: 84%
37 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-15 21:08 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-15 21:08 +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''' Core types and interfaces for agentsmgr. '''
24from . import __
27class Renderable( __.typx.Protocol ):
28 ''' Protocol for objects that can be rendered as Markdown. '''
30 def render_as_markdown( self ) -> tuple[ str, ... ]:
31 ''' Renders object as Markdown lines for display. '''
32 ...
35class Presentations( __.enum.Enum ):
36 ''' Enumeration for CLI display presentation formats. '''
38 Markdown = 'markdown'
41class DisplayOptions( __.appcore_cli.DisplayOptions ):
42 ''' Consolidated display configuration for CLI output. '''
44 presentation: Presentations = Presentations.Markdown
47class Globals( __.appcore.state.Globals ):
48 ''' Agentsmgr-specific global state container.
50 Extends appcore.state.Globals with agentsmgr-specific display
51 configuration.
52 '''
54 display: DisplayOptions = __.dcls.field( default_factory = DisplayOptions )
57_ASCII_SYMBOL_REPLACEMENTS = __.immut.Dictionary( {
58 '✅': '[OK]',
59 '❌': '[ERROR]',
60 '⚠️': '[WARN]',
61 '⚠': '[WARN]',
62 '🔍': '[INFO]',
63 '🚀': '>>',
64 '📁': '[FILES]',
65 '🗑️': '[CLEANUP]',
66 '🗑': '[CLEANUP]',
67} )
70def _adapt_output_lines_for_stream(
71 lines: tuple[ str, ... ],
72 stream: __.typx.Any,
73) -> tuple[ str, ... ]:
74 ''' Adapts rendered output to stream encoding capabilities. '''
75 if _stream_supports_unicode( stream ): return lines
76 return tuple( _render_ascii_line( line ) for line in lines )
79def _render_ascii_line( line: str ) -> str:
80 ''' Renders one output line as 7-bit ASCII. '''
81 for symbol, replacement in _ASCII_SYMBOL_REPLACEMENTS.items( ):
82 line = line.replace( symbol, replacement )
83 return line.encode( 'ascii', errors = 'ignore' ).decode( 'ascii' )
86def _stream_supports_unicode( stream: __.typx.Any ) -> bool:
87 ''' Returns true when stream encoding advertises UTF support. '''
88 encoding = getattr( stream, 'encoding', None )
89 if not encoding: return True 89 ↛ exitline 89 didn't return from function '_stream_supports_unicode' because the return on line 89 wasn't executed
90 return 'utf' in encoding.lower( ).replace( '-', '' ).replace( '_', '' )
93async def render_and_print_result(
94 result: Renderable,
95 display: DisplayOptions,
96 exits: __.ctxl.AsyncExitStack,
97) -> None:
98 ''' Centralizes result rendering logic with Rich formatting support. '''
99 stream = await display.provide_stream( exits )
100 match display.presentation:
101 case Presentations.Markdown: 101 ↛ exitline 101 didn't return from function 'render_and_print_result' because the pattern on line 101 always matched
102 lines = result.render_as_markdown( )
103 supports_unicode = _stream_supports_unicode( stream )
104 if supports_unicode and display.determine_colorization( stream ): 104 ↛ 105line 104 didn't jump to line 105 because the condition on line 104 was never true
105 from rich.console import Console
106 from rich.markdown import Markdown
107 console = Console( file = stream, force_terminal = True )
108 markdown_obj = Markdown( '\n'.join( lines ) )
109 console.print( markdown_obj )
110 else:
111 if not supports_unicode:
112 lines = _adapt_output_lines_for_stream( lines, stream )
113 output = '\n'.join( lines )
114 print( output, file = stream )