classCli(Typer):""" Custom Typer object that: - validates a command parameters before executing it - accepts a configuration file describing the parameters - automatically instantiates parameters given a dictionary when type hinted """defcommand(# noqaself,name,*,cls:Optional[Type[TyperCommand]]=None,context_settings:Optional[Dict[Any,Any]]=None,help:Optional[str]=None,epilog:Optional[str]=None,short_help:Optional[str]=None,options_metavar:str="[OPTIONS]",add_help_option:bool=True,no_args_is_help:bool=False,hidden:bool=False,deprecated:bool=False,# Rich settingsrich_help_panel:Union[str,None]=Default(None),registry:Any=None,default_config:Optional[Config]=None,merge_with_default_config:bool=False,)->Callable[[CommandFunctionType],CommandFunctionType]:typer_command=super().command(name=name,cls=cls,help=help,epilog=epilog,short_help=short_help,options_metavar=options_metavar,add_help_option=add_help_option,no_args_is_help=no_args_is_help,hidden=hidden,deprecated=deprecated,rich_help_panel=rich_help_panel,context_settings={**(context_settingsor{}),"ignore_unknown_options":True,"allow_extra_args":True,},)defwrapper(fn):validated=validate_arguments(fn)@typer_commanddefcommand(ctx:Context,config:Optional[List[Path]]=None):config_path=confighas_meta=_fn_has_meta(fn)ifconfig_path:config,name_from_file=merge_from_disk(config_path)elifdefault_configisnotNone:config=default_configelse:config=Config({name:{}})ifdefault_configisnotNoneandmerge_with_default_config:config=Config(default_config).merge(config)model_fields=(validated.model.model_fieldsifhasattr(validated.model,"model_fields")elsevalidated.model.__fields__)fork,vinparse_overrides(ctx.args).items():if"."notink:parts=(name,k)else:parts=k.split(".")ifparts[0]inmodel_fieldsandparts[0]notinconfig:parts=(name,*parts)current=configifparts[0]notincurrent:raiseException(f"{k} does not match any existing section in config")forpartinparts[:-1]:current=current.setdefault(part,Config())current[parts[-1]]=vtry:default_seed=model_fields.get("seed")ifdefault_seedisnotNone:default_seed=default_seed.get_default()seed=Config.resolve(config.get(name,{}).get("seed",default_seed),registry=registry,root=config,)ifseedisnotNone:set_seed(seed)resolved_config=Config(config[name]).resolve(registry=registry,root=config)ifhas_meta:config_meta=dict(config_path=config_path,resolved_config=resolved_config,unresolved_config=config,)returnvalidated(**resolved_config,config_meta=config_meta,)else:returnvalidated(**resolved_config)except(LegacyValidationError,ConfitValidationError)ase:e.raw_errors=patch_errors(e.raw_errors,(name,),)ifis_debug()ore.__cause__isnotNone:raiseedefcprint(*args,style=None,**kw):# pragma: no coverreturnprint(*args,**kw)try:# pragma: no covertry:fromtyper.mainimportconsole_stderrexceptImportError:importrichconsole_stderr=rich.console.Console(stderr=True)cprint=console_stderr.printexceptException:# pragma: no coverpasscprint("Validation error:",style="red",end=" ")cprint(str(e))sys.exit(1)exceptKeyboardInterruptase:# pragma: no coverraiseException("Interrupted by user")fromereturnvalidatedreturnwrapper
parse_overrides(args)
Parse the overrides from the command line into a dictionary
of key-value pairs.
defparse_overrides(args:List[str])->Dict[str,Any]:""" Parse the overrides from the command line into a dictionary of key-value pairs. Parameters ---------- args: List[str] The arguments to parse Returns ------- Dict[str, Any] The parsed overrides as a dictionary """result={}whileargs:opt=args.pop(0)err=f"Invalid config override '{opt}'"ifopt.startswith("--"):# new argumentopt=opt.replace("--","")if"="inopt:# we have --opt=valueopt,value=opt.split("=",1)else:ifnotargsorargs[0].startswith("--"):# flag with no valuevalue="true"else:value=args.pop(0)opt=opt.replace("-","_")result[opt]=loads(value)else:secho(f"{err}: doesn't support shorthands",fg=colors.RED)exit(1)returnresult