11import argparse
22import inspect
3+ import os
34from collections .abc import Awaitable , Callable
45from typing import Annotated , Any , Literal , cast
56
@@ -47,24 +48,38 @@ def set_value(self, name: str, value: str) -> None:
4748class _ResolvableConfiguration (_Configuration ):
4849 value : str | None = None
4950 prompt : str | None = Field (default = None , exclude = True )
51+ env_var : str | None = Field (default = None , exclude = True )
52+ expose_as_arg : bool = Field (default = True , exclude = True )
5053
5154 def _get_question (self ) -> Question :
5255 raise NotImplementedError # pragma: no cover
5356
5457 def _on_answer (self ) -> None :
5558 pass
5659
60+ def _try_resolve_without_prompt (
61+ self , args : argparse .Namespace | None
62+ ) -> str | None :
63+ if self .expose_as_arg and args is not None :
64+ arg_value = getattr (args , self .name , None )
65+ if arg_value is not None :
66+ return str (arg_value )
67+ if self .env_var is not None :
68+ env_value = os .environ .get (self .env_var )
69+ if env_value is not None :
70+ return env_value
71+ return None
72+
5773 async def aconfigure (
5874 self ,
5975 get_value : GetValueFn | None = None ,
6076 args : argparse .Namespace | None = None ,
6177 ) -> None :
62- if args is not None :
63- arg_value = getattr (args , self .name , None )
64- if arg_value is not None :
65- self .value = arg_value
66- self ._on_answer ()
67- return
78+ resolved = self ._try_resolve_without_prompt (args )
79+ if resolved is not None :
80+ self .value = resolved
81+ self ._on_answer ()
82+ return
6883 self .value = await self ._get_question ().ask_async ()
6984 self ._on_answer ()
7085
@@ -125,6 +140,8 @@ def _resolve_validator(self, get_value: GetValueFn | None) -> ValidatorFn | None
125140 return cast (ValidatorFn , self .validator )
126141
127142 def add_to_parser (self , parser : argparse .ArgumentParser ) -> None :
143+ if not self .expose_as_arg :
144+ return
128145 parser .add_argument (
129146 f"--{ self .name .replace ('_' , '-' )} " ,
130147 dest = self .name ,
@@ -138,12 +155,11 @@ async def aconfigure(
138155 get_value : GetValueFn | None = None ,
139156 args : argparse .Namespace | None = None ,
140157 ) -> None :
141- if args is not None :
142- arg_value = getattr (args , self .name , None )
143- if arg_value is not None :
144- self .value = arg_value
145- self ._on_answer ()
146- return
158+ resolved = self ._try_resolve_without_prompt (args )
159+ if resolved is not None :
160+ self .value = resolved
161+ self ._on_answer ()
162+ return
147163 self .value = await questionary .text (
148164 self .prompt or self .name ,
149165 default = await self ._resolve_default (get_value ) or "" ,
@@ -166,6 +182,8 @@ def load_from_keyring(self) -> "PasswordConfiguration":
166182 return self
167183
168184 def add_to_parser (self , parser : argparse .ArgumentParser ) -> None :
185+ if not self .expose_as_arg :
186+ return
169187 parser .add_argument (
170188 f"--{ self .name .replace ('_' , '-' )} " ,
171189 dest = self .name ,
@@ -233,6 +251,8 @@ async def _resolve_options(
233251 return await cast (Callable [[], Awaitable [OptionsDict ]], self .options_factory )()
234252
235253 def add_to_parser (self , parser : argparse .ArgumentParser ) -> None :
254+ if not self .expose_as_arg :
255+ return
236256 kwargs : dict [str , Any ] = {
237257 "dest" : self .name ,
238258 "default" : None ,
@@ -249,14 +269,13 @@ async def aconfigure(
249269 get_value : GetValueFn | None = None ,
250270 args : argparse .Namespace | None = None ,
251271 ) -> None :
252- if args is not None :
253- arg_value = getattr (args , self .name , None )
254- if arg_value is not None :
255- options = await self ._resolve_options (get_value )
256- if not options or arg_value in options :
257- self .value = arg_value
258- self ._on_answer ()
259- return
272+ resolved = self ._try_resolve_without_prompt (args )
273+ if resolved is not None :
274+ options = await self ._resolve_options (get_value )
275+ if not options or resolved in options :
276+ self .value = resolved
277+ self ._on_answer ()
278+ return
260279 options = await self ._resolve_options (get_value )
261280 if len (options ) == 1 :
262281 self .value = next (iter (options ))
0 commit comments