Coverage for sources/agentsmgr/core.py: 84%

37 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-08 06:57 +0000

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

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#============================================================================# 

19 

20 

21''' Core types and interfaces for agentsmgr. ''' 

22 

23 

24from . import __ 

25 

26 

27class Renderable( __.typx.Protocol ): 

28 ''' Protocol for objects that can be rendered as Markdown. ''' 

29 

30 def render_as_markdown( self ) -> tuple[ str, ... ]: 

31 ''' Renders object as Markdown lines for display. ''' 

32 ... 

33 

34 

35class Presentations( __.enum.Enum ): 

36 ''' Enumeration for CLI display presentation formats. ''' 

37 

38 Markdown = 'markdown' 

39 

40 

41class DisplayOptions( __.appcore_cli.DisplayOptions ): 

42 ''' Consolidated display configuration for CLI output. ''' 

43 

44 presentation: Presentations = Presentations.Markdown 

45 

46 

47class Globals( __.appcore.state.Globals ): 

48 ''' Agentsmgr-specific global state container. 

49 

50 Extends appcore.state.Globals with agentsmgr-specific display 

51 configuration. 

52 ''' 

53 

54 display: DisplayOptions = __.dcls.field( default_factory = DisplayOptions ) 

55 

56 

57_ASCII_SYMBOL_REPLACEMENTS = __.immut.Dictionary( { 

58 '✅': '[OK]', 

59 '❌': '[ERROR]', 

60 '⚠️': '[WARN]', 

61 '⚠': '[WARN]', 

62 '🔍': '[INFO]', 

63 '🚀': '>>', 

64 '📁': '[FILES]', 

65 '🗑️': '[CLEANUP]', 

66 '🗑': '[CLEANUP]', 

67} ) 

68 

69 

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 ) 

77 

78 

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' ) 

84 

85 

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( '_', '' ) 

91 

92 

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 )