Coverage for sources/appcore/cli/standard.py: 41%
43 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 00:58 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-19 00:58 +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''' Standard renderers for basic CLI display presentation modes. '''
24import json as _json
26from . import __
27from . import core as _core
30class Presentations( __.enum.Enum ): # TODO: Python 3.11: StrEnum
31 ''' Basic presentation modes (formats). '''
33 Json = 'json'
34 Markdown = 'markdown'
35 Toml = 'toml'
38class DisplayOptions( _core.DisplayOptions ):
39 ''' Display options with presentation modes. '''
41 compact: __.typx.Annotated[
42 bool, __.ddoc.Doc( ''' Display CLI results compactly? ''' ),
43 ] = False
44 presentation: __.typx.Annotated[
45 Presentations,
46 __.ddoc.Doc( ''' Presentation mode for CLI display. ''' ),
47 ] = Presentations.Markdown
50class Globals( __.Globals ):
51 ''' Application state with display options. '''
53 display: DisplayOptions = __.dcls.field( default_factory = DisplayOptions )
56class Renderable(
57 __.immut.DataclassProtocol, __.typx.Protocol,
58 decorators = ( __.typx.runtime_checkable, ),
59):
60 ''' Base class for objects which can render themselves. '''
62 @__.abc.abstractmethod
63 def render_dictionary( self ) -> dict[ str, __.typx.Any ]:
64 ''' Returns dictionary representation of object attributes. '''
65 raise NotImplementedError
67 def render_json( self, compact: bool = False, indent: int = 2 ) -> str:
68 ''' Renders object as JSON into string. '''
69 dictionary = self.render_dictionary( )
70 if compact:
71 return _json.dumps(
72 dictionary, ensure_ascii = False, separators = ( ',', ':' ) )
73 return _json.dumps( dictionary, ensure_ascii = False, indent = indent )
75 def render_markdown( self, display: DisplayOptions ) -> tuple[ str, ... ]:
76 ''' Renders object as Markdown into sequence of lines. '''
77 # TODO: Implement.
78 return ( )
80 def render_toml( self ) -> str:
81 ''' Renders object as TOML into string. '''
82 return __.tomli_w.dumps( self.render_dictionary( ) )
85@__.ctxl.asynccontextmanager
86async def intercept_errors(
87 auxdata: Globals
88) -> __.cabc.AsyncIterator[ None ]:
89 # TODO? Utilize mapping of exceptions to exit codes.
90 ''' Context manager which intercepts and renders exceptions. '''
91 try: yield
92 # TODO: Handle renderable exceptions.
93 except ( KeyboardInterrupt, SystemExit ): raise
94 except BaseException as exc:
95 # TODO: Implement.
96 raise SystemExit( 1 ) from exc
99async def render_and_print( auxdata: Globals, renderable: Renderable ) -> None:
100 ''' Renders and prints object according to display options. '''
101 display = auxdata.display
102 exits = auxdata.exits
103 stream = await display.provide_stream( exits )
104 match display.presentation:
105 case Presentations.Json:
106 text = renderable.render_json( compact = display.compact )
107 case Presentations.Markdown:
108 text = '\n'.join( renderable.render_markdown( display ) )
109 case Presentations.Toml:
110 text = renderable.render_toml( )
111 print( text, file = stream )