Coverage for sources/mimeogram/cli.py: 30%
51 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-17 22:34 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-17 22:34 +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''' Command-line interface. '''
24from __future__ import annotations
26from . import __
27from . import apply as _apply
28from . import create as _create
29from . import prompt as _prompt
32_scribe = __.produce_scribe( __name__ )
35class Cli(
36 metaclass = __.ImmutableStandardDataclass,
37 decorators = ( __.standard_dataclass, __.simple_tyro_class ),
38):
39 ''' Mimeogram: hierarchical data exchange between humans and LLMs. '''
41 application: __.ApplicationInformation
42 configfile: __.typx.Optional[ str ] = None
43 # display: ConsoleDisplay
44 inscription: __.InscriptionControl = (
45 __.InscriptionControl( mode = __.InscriptionModes.Rich ) )
46 command: __.typx.Union[
47 __.typx.Annotated[
48 _create.Command,
49 __.tyro.conf.subcommand( # pyright: ignore
50 'create', prefix_name = False ),
51 ],
52 __.typx.Annotated[
53 _apply.Command,
54 __.tyro.conf.subcommand( # pyright: ignore
55 'apply', prefix_name = False ),
56 ],
57 __.typx.Annotated[
58 _prompt.Command,
59 __.tyro.conf.subcommand( # pyright: ignore
60 'provide-prompt', prefix_name = False ),
61 ],
62 ]
64 async def __call__( self ):
65 ''' Invokes command after library preparation. '''
66 nomargs = self.prepare_invocation_args( )
67 async with __.ExitsAsync( ) as exits:
68 auxdata = await _prepare( exits = exits, **nomargs )
69 await self.command( auxdata = auxdata )
70 # await self.command( auxdata = auxdata, display = self.display )
72 def prepare_invocation_args(
73 self,
74 ) -> __.cabc.Mapping[ str, __.typx.Any ]:
75 ''' Prepares arguments for initial configuration. '''
76 configedits: __.DictionaryEdits = (
77 self.command.provide_configuration_edits( ) )
78 args: dict[ str, __.typx.Any ] = dict(
79 application = self.application,
80 configedits = configedits,
81 environment = True,
82 inscription = self.inscription,
83 )
84 if self.configfile: args[ 'configfile' ] = self.configfile
85 return args
88def execute( ):
89 ''' Entrypoint for CLI execution. '''
90 from asyncio import run
91 config = (
92 __.tyro.conf.EnumChoicesFromValues,
93 __.tyro.conf.HelptextFromCommentsOff,
94 )
95 # default = Cli(
96 # application = _application.Information( ),
97 # display = ConsoleDisplay( ),
98 # inscription = _inscription.Control( mode = _inscription.Modes.Rich ),
99 # command = InspectCommand( ),
100 # )
101 try: run( __.tyro.cli( Cli, config = config )( ) )
102 except SystemExit: raise
103 except BaseException:
104 _scribe.exception(
105 "Program terminated from uncaught exception. "
106 "Please file a bug report." )
107 raise SystemExit( 1 ) from None
110def _discover_inscription_level_name(
111 application: __.ApplicationInformation,
112 control: __.InscriptionControl,
113) -> str:
114 if control.level is None:
115 from os import environ
116 for envvar_name_base in ( 'INSCRIPTION', 'LOG' ):
117 envvar_name = (
118 "{name}_{base}_LEVEL".format(
119 base = envvar_name_base,
120 name = application.name.upper( ) ) )
121 if envvar_name not in environ: continue
122 return environ[ envvar_name ]
123 return 'INFO'
124 return control.level
127async def _prepare(
128 application: __.ApplicationInformation,
129 configedits: __.DictionaryEdits,
130 environment: bool,
131 exits: __.ExitsAsync,
132 inscription: __.InscriptionControl,
133) -> __.Globals:
134 ''' Configures logging based on verbosity. '''
135 auxdata = await __.prepare(
136 application = application,
137 configedits = configedits,
138 environment = environment,
139 exits = exits,
140 inscription = inscription )
141 _prepare_scribes( application, inscription )
142 return auxdata
145def _prepare_scribes(
146 application: __.ApplicationInformation,
147 inscription: __.InscriptionControl,
148) -> None:
149 import logging
150 from rich.console import Console
151 from rich.logging import RichHandler
152 level_name = _discover_inscription_level_name( application, inscription )
153 level = getattr( logging, level_name.upper( ) )
154 handler = RichHandler(
155 console = Console( stderr = True ),
156 rich_tracebacks = True,
157 show_time = False )
158 logging.basicConfig(
159 format = '%(name)s: %(message)s',
160 level = level,
161 handlers = [ handler ] )
162 logging.captureWarnings( True )