Coverage for sources/mimeogram/cli.py: 31%

58 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-07 04:07 +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''' Command-line interface. ''' 

22 

23 

24from . import __ 

25from . import apply as _apply 

26from . import create as _create 

27from . import interfaces as _interfaces 

28from . import prompt as _prompt 

29 

30 

31_scribe = __.produce_scribe( __name__ ) 

32 

33 

34class VersionCommand( 

35 _interfaces.CliCommand, 

36 decorators = ( __.standard_dataclass, __.standard_tyro_class ), 

37): 

38 ''' Prints version information. ''' 

39 

40 async def __call__( self, auxdata: __.Globals ) -> None: 

41 ''' Executes command to print version information. ''' 

42 from . import __version__ 

43 print( f"{__package__} {__version__}" ) 

44 raise SystemExit( 0 ) 

45 

46 def provide_configuration_edits( self ) -> __.DictionaryEdits: 

47 ''' Provides edits against configuration from options. ''' 

48 return ( ) 

49 

50 

51class Cli( 

52 metaclass = __.ImmutableStandardDataclass, 

53 decorators = ( __.standard_dataclass, __.simple_tyro_class ), 

54): 

55 ''' Mimeogram: hierarchical data exchange between humans and LLMs. ''' 

56 

57 application: __.ApplicationInformation 

58 configfile: __.typx.Optional[ str ] = None 

59 # display: ConsoleDisplay 

60 inscription: __.InscriptionControl = ( 

61 __.InscriptionControl( mode = __.InscriptionModes.Rich ) ) 

62 command: __.typx.Union[ 

63 __.typx.Annotated[ 

64 _create.Command, 

65 __.tyro.conf.subcommand( 

66 'create', prefix_name = False ), 

67 ], 

68 __.typx.Annotated[ 

69 _apply.Command, 

70 __.tyro.conf.subcommand( 

71 'apply', prefix_name = False ), 

72 ], 

73 __.typx.Annotated[ 

74 _prompt.Command, 

75 __.tyro.conf.subcommand( 

76 'provide-prompt', prefix_name = False ), 

77 ], 

78 __.typx.Annotated[ 

79 VersionCommand, 

80 __.tyro.conf.subcommand( 

81 'version', prefix_name = False ), 

82 ], 

83 ] 

84 

85 async def __call__( self ): 

86 ''' Invokes command after library preparation. ''' 

87 nomargs = self.prepare_invocation_args( ) 

88 async with __.ExitsAsync( ) as exits: 

89 auxdata = await _prepare( exits = exits, **nomargs ) 

90 await self.command( auxdata = auxdata ) 

91 # await self.command( auxdata = auxdata, display = self.display ) 

92 

93 def prepare_invocation_args( 

94 self, 

95 ) -> __.cabc.Mapping[ str, __.typx.Any ]: 

96 ''' Prepares arguments for initial configuration. ''' 

97 configedits: __.DictionaryEdits = ( 

98 self.command.provide_configuration_edits( ) ) 

99 args: dict[ str, __.typx.Any ] = dict( 

100 application = self.application, 

101 configedits = configedits, 

102 environment = True, 

103 inscription = self.inscription, 

104 ) 

105 if self.configfile: args[ 'configfile' ] = self.configfile 

106 return args 

107 

108 

109def execute( ): 

110 ''' Entrypoint for CLI execution. ''' 

111 from asyncio import run 

112 config = ( 

113 __.tyro.conf.EnumChoicesFromValues, 

114 __.tyro.conf.HelptextFromCommentsOff, 

115 ) 

116 # default = Cli( 

117 # application = _application.Information( ), 

118 # display = ConsoleDisplay( ), 

119 # inscription = _inscription.Control( mode = _inscription.Modes.Rich ), 

120 # command = InspectCommand( ), 

121 # ) 

122 try: run( __.tyro.cli( Cli, config = config )( ) ) 

123 except SystemExit: raise 

124 except BaseException: 

125 _scribe.exception( 

126 "Program terminated from uncaught exception. " 

127 "Please file a bug report." ) 

128 raise SystemExit( 1 ) from None 

129 

130 

131def _discover_inscription_level_name( 

132 application: __.ApplicationInformation, 

133 control: __.InscriptionControl, 

134) -> str: 

135 if control.level is None: 

136 from os import environ 

137 for envvar_name_base in ( 'INSCRIPTION', 'LOG' ): 

138 envvar_name = ( 

139 "{name}_{base}_LEVEL".format( 

140 base = envvar_name_base, 

141 name = application.name.upper( ) ) ) 

142 if envvar_name not in environ: continue 

143 return environ[ envvar_name ] 

144 return 'INFO' 

145 return control.level 

146 

147 

148async def _prepare( 

149 application: __.ApplicationInformation, 

150 configedits: __.DictionaryEdits, 

151 environment: bool, 

152 exits: __.ExitsAsync, 

153 inscription: __.InscriptionControl, 

154) -> __.Globals: 

155 ''' Configures logging based on verbosity. ''' 

156 auxdata = await __.prepare( 

157 application = application, 

158 configedits = configedits, 

159 environment = environment, 

160 exits = exits, 

161 inscription = inscription ) 

162 _prepare_scribes( application, inscription ) 

163 return auxdata 

164 

165 

166def _prepare_scribes( 

167 application: __.ApplicationInformation, 

168 inscription: __.InscriptionControl, 

169) -> None: 

170 import logging 

171 from rich.console import Console 

172 from rich.logging import RichHandler 

173 level_name = _discover_inscription_level_name( application, inscription ) 

174 level = getattr( logging, level_name.upper( ) ) 

175 handler = RichHandler( 

176 console = Console( stderr = True ), 

177 rich_tracebacks = True, 

178 show_time = False ) 

179 logging.basicConfig( 

180 format = '%(name)s: %(message)s', 

181 level = level, 

182 handlers = [ handler ] ) 

183 logging.captureWarnings( True )