@@ -98,6 +98,7 @@ def init(self):
9898 A transparent attribute will also not have its init function called automatically, so you will need to do that on your own, as seen in the Outer init.
9999 The function is upper case on purpose, as it is supposed to be used in a Type context
100100 """
101+
101102 class Cloned (subclass ):
102103 __secret__ = getattr (subclass , "__secret__" , False )
103104 __transparent__ = True
@@ -129,46 +130,48 @@ def indent(level: int) -> str:
129130
130131@overload
131132def parameter (
132- * ,
133- desc : str ,
134- default : T = ...,
135- init : bool = True ,
136- repr : bool = True ,
137- hash : Optional [bool ] = None ,
138- compare : bool = True ,
139- metadata : Optional [Dict [str , Any ]] = ...,
140- kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
133+ * ,
134+ desc : str ,
135+ default : T = ...,
136+ init : bool = True ,
137+ repr : bool = True ,
138+ hash : Optional [bool ] = None ,
139+ compare : bool = True ,
140+ metadata : Optional [Dict [str , Any ]] = ...,
141+ kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
141142) -> T :
142143 ...
143144
145+
144146@overload
145147def parameter (
146- * ,
147- desc : str ,
148- default : T = ...,
149- init : bool = True ,
150- repr : bool = True ,
151- hash : Optional [bool ] = None ,
152- compare : bool = True ,
153- metadata : Optional [Dict [str , Any ]] = ...,
154- kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
148+ * ,
149+ desc : str ,
150+ default : T = ...,
151+ init : bool = True ,
152+ repr : bool = True ,
153+ hash : Optional [bool ] = None ,
154+ compare : bool = True ,
155+ metadata : Optional [Dict [str , Any ]] = ...,
156+ kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
155157) -> Field [T ]:
156158 ...
157159
160+
158161def parameter (
159- * ,
160- desc : str ,
161- secret : bool = False ,
162- global_parameter : bool = False ,
163- global_name : Optional [str ] = None ,
164- choices : Optional [dict [str , type ]] = None ,
165- default : T = MISSING ,
166- init : bool = True ,
167- repr : bool = True ,
168- hash : Optional [bool ] = None ,
169- compare : bool = True ,
170- metadata : Optional [Dict [str , Any ]] = None ,
171- kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
162+ * ,
163+ desc : str ,
164+ secret : bool = False ,
165+ global_parameter : bool = False ,
166+ global_name : Optional [str ] = None ,
167+ choices : Optional [dict [str , type ]] = None ,
168+ default : T = MISSING ,
169+ init : bool = True ,
170+ repr : bool = True ,
171+ hash : Optional [bool ] = None ,
172+ compare : bool = True ,
173+ metadata : Optional [Dict [str , Any ]] = None ,
174+ kw_only : Union [bool , _MISSING_TYPE ] = MISSING ,
172175) -> Field [T ]:
173176 if metadata is None :
174177 metadata = dict ()
@@ -202,7 +205,8 @@ def get_default(key, default):
202205InstanceResults = NestedCollection [Any ]
203206
204207
205- def get_at (collection : NestedCollection [C ], name : list [str ], at : int = 0 , * , meta : bool = False , no_raise : bool = False ) -> Optional [C ]:
208+ def get_at (collection : NestedCollection [C ], name : list [str ], at : int = 0 , * , meta : bool = False ,
209+ no_raise : bool = False ) -> Optional [C ]:
206210 if meta :
207211 name = name + ["$" ]
208212
@@ -244,7 +248,8 @@ def set_at(collection: NestedCollection[C], name: list[str], value: C, at: int =
244248 return set_at (collection [name [at ]], name , value , at + 1 , False )
245249
246250
247- def dfs_flatmap (collection : NestedCollection [C ], func : Callable [[list [str ], C ], Any ], basename : Optional [list [str ]] = None ):
251+ def dfs_flatmap (collection : NestedCollection [C ], func : Callable [[list [str ], C ], Any ],
252+ basename : Optional [list [str ]] = None ):
248253 if basename is None :
249254 basename = []
250255 output = []
@@ -363,7 +368,9 @@ def __call__(self, collection: ParsingResults) -> C:
363368 if value is None :
364369 raise ParameterError (f"Missing required parameter '--{ '.' .join (self .name )} '" , self .name )
365370 if value not in self .choices :
366- raise ParameterError (f"Invalid value for parameter '--{ '.' .join (self .name )} ': { value } (possible values are { ', ' .join (self .choices .keys ())} )" , self .name )
371+ raise ParameterError (
372+ f"Invalid value for parameter '--{ '.' .join (self .name )} ': { value } (possible values are { ', ' .join (self .choices .keys ())} )" ,
373+ self .name )
367374 choice , parameters = self .choices [value ]
368375 self ._instance = choice (** {
369376 name : parameter (collection )
@@ -374,15 +381,19 @@ def __call__(self, collection: ParsingResults) -> C:
374381 return self ._instance
375382
376383
377- def get_inspect_parameters_for_class (cls : type , basename : list [str ]) -> dict [str , tuple [inspect .Parameter , list [str ], Optional [dataclasses .Field ]]]:
384+ def get_inspect_parameters_for_class (cls : type , basename : list [str ]) -> dict [
385+ str , tuple [inspect .Parameter , list [str ], Optional [dataclasses .Field ]]]:
378386 fields = getattr (cls , "__dataclass_fields__" , {})
379387 return {
380388 name : (param , basename + [name ], fields .get (name ))
381389 for name , param in inspect .signature (cls .__init__ ).parameters .items ()
382390 if not (name == "self" or name .startswith ("_" ) or isinstance (name , NoneType ))
383391 }
384392
385- def get_type_description_default_for_parameter (parameter : inspect .Parameter , name : list [str ], field : Optional [dataclasses .Field ] = None ) -> tuple [Type , Optional [str ], Any ]:
393+
394+ def get_type_description_default_for_parameter (parameter : inspect .Parameter , name : list [str ],
395+ field : Optional [dataclasses .Field ] = None ) -> tuple [
396+ Type , Optional [str ], Any ]:
386397 parameter_type : Type = parameter .annotation
387398 description : Optional [str ] = None
388399
@@ -395,42 +406,53 @@ def get_type_description_default_for_parameter(parameter: inspect.Parameter, nam
395406 description = field .metadata .get ("desc" , None )
396407 if field .type is not None :
397408 if not (isinstance (field .type , type ) or get_origin (field .type ) is Union ):
398- raise ValueError (f"Parameter { '.' .join (name )} has an invalid type annotation: { field .type } ({ type (field .type )} )" )
409+ raise ValueError (
410+ f"Parameter { '.' .join (name )} has an invalid type annotation: { field .type } ({ type (field .type )} )" )
399411 parameter_type = field .type
400412
401413 # check if type is an Optional, and then get the actual type
402- if get_origin (parameter_type ) is Union and len (parameter_type .__args__ ) == 2 and parameter_type .__args__ [1 ] is NoneType :
414+ if get_origin (parameter_type ) is Union and len (parameter_type .__args__ ) == 2 and parameter_type .__args__ [
415+ 1 ] is NoneType :
403416 parameter_type = parameter_type .__args__ [0 ]
404417
405418 return parameter_type , description , default
406419
407420
408- def try_existing_parameter (parameter_collection : ParameterCollection , name : list [str ], typ : type , parameter_type : type , default : Any , description : str , secret_parameter : bool ) -> Optional [ParameterDefinition ]:
409- existing_parameter = get_at (parameter_collection , name , meta = (typ in (ComplexParameterDefinition , ChoiceParameterDefinition )))
421+ def try_existing_parameter (parameter_collection : ParameterCollection , name : list [str ], typ : type , parameter_type : type ,
422+ default : Any , description : str , secret_parameter : bool ) -> Optional [ParameterDefinition ]:
423+ existing_parameter = get_at (parameter_collection , name ,
424+ meta = (typ in (ComplexParameterDefinition , ChoiceParameterDefinition )))
410425 if not existing_parameter :
411426 return None
412427
413428 if existing_parameter .type != parameter_type :
414- raise ValueError (f"Parameter { '.' .join (name )} already exists with a different type ({ existing_parameter .type } != { parameter_type } )" )
429+ raise ValueError (
430+ f"Parameter { '.' .join (name )} already exists with a different type ({ existing_parameter .type } != { parameter_type } )" )
415431 if existing_parameter .default != default :
416432 if existing_parameter .default is None and isinstance (secret_parameter , no_default ) \
417- or existing_parameter .default is not None and not isinstance (secret_parameter , no_default ):
418- pass # syncing up "no defaults"
433+ or existing_parameter .default is not None and not isinstance (secret_parameter , no_default ):
434+ pass # syncing up "no defaults"
419435 else :
420- raise ValueError (f"Parameter { '.' .join (name )} already exists with a different default value ({ existing_parameter .default } != { default } )" )
436+ raise ValueError (
437+ f"Parameter { '.' .join (name )} already exists with a different default value ({ existing_parameter .default } != { default } )" )
421438 if existing_parameter .description != description :
422- raise ValueError (f"Parameter { '.' .join (name )} already exists with a different description ({ existing_parameter .description } != { description } )" )
439+ raise ValueError (
440+ f"Parameter { '.' .join (name )} already exists with a different description ({ existing_parameter .description } != { description } )" )
423441 if existing_parameter .secret != secret_parameter :
424- raise ValueError (f"Parameter { '.' .join (name )} already exists with a different secret status ({ existing_parameter .secret } != { secret_parameter } )" )
442+ raise ValueError (
443+ f"Parameter { '.' .join (name )} already exists with a different secret status ({ existing_parameter .secret } != { secret_parameter } )" )
425444
426445 return existing_parameter
427446
428447
429- def parameter_definitions_for_class (cls : type , name : list [str ], parameter_collection : ParameterCollection ) -> dict [str , ParameterDefinition ]:
430- return {name : parameter_definition_for (* metadata , parameter_collection = parameter_collection ) for name , metadata in get_inspect_parameters_for_class (cls , name ).items ()}
448+ def parameter_definitions_for_class (cls : type , name : list [str ], parameter_collection : ParameterCollection ) -> dict [
449+ str , ParameterDefinition ]:
450+ return {name : parameter_definition_for (* metadata , parameter_collection = parameter_collection ) for name , metadata in
451+ get_inspect_parameters_for_class (cls , name ).items ()}
431452
432453
433- def parameter_definition_for (param : inspect .Parameter , name : list [str ], field : Optional [dataclasses .Field ] = None , * , parameter_collection : ParameterCollection ) -> ParameterDefinition :
454+ def parameter_definition_for (param : inspect .Parameter , name : list [str ], field : Optional [dataclasses .Field ] = None , * ,
455+ parameter_collection : ParameterCollection ) -> ParameterDefinition :
434456 parameter_type , description , default = get_type_description_default_for_parameter (param , name , field )
435457 secret_parameter = (field and field .metadata .get ("secret" , False )) or getattr (parameter_type , "__secret__" , False )
436458
@@ -446,14 +468,18 @@ def parameter_definition_for(param: inspect.Parameter, name: list[str], field: O
446468 name = name [:- 1 ]
447469
448470 if parameter_type in (str , int , float , bool ):
449- existing_parameter = try_existing_parameter (parameter_collection , name , typ = ParameterDefinition , parameter_type = parameter_type , default = default , description = description , secret_parameter = secret_parameter )
471+ existing_parameter = try_existing_parameter (parameter_collection , name , typ = ParameterDefinition ,
472+ parameter_type = parameter_type , default = default ,
473+ description = description , secret_parameter = secret_parameter )
450474 if existing_parameter :
451475 return existing_parameter
452476 parameter = ParameterDefinition (name , parameter_type , default , description , secret_parameter )
453477 set_at (parameter_collection , name , parameter )
454478
455479 elif get_origin (parameter_type ) is Union :
456- existing_parameter = try_existing_parameter (parameter_collection , name , typ = ChoiceParameterDefinition , parameter_type = parameter_type , default = default , description = description , secret_parameter = secret_parameter )
480+ existing_parameter = try_existing_parameter (parameter_collection , name , typ = ChoiceParameterDefinition ,
481+ parameter_type = parameter_type , default = default ,
482+ description = description , secret_parameter = secret_parameter )
457483 if existing_parameter :
458484 return existing_parameter
459485
@@ -482,7 +508,9 @@ def parameter_definition_for(param: inspect.Parameter, name: list[str], field: O
482508 set_at (parameter_collection , name , parameter , meta = True )
483509
484510 else :
485- existing_parameter = try_existing_parameter (parameter_collection , name , typ = ComplexParameterDefinition , parameter_type = parameter_type , default = default , description = description , secret_parameter = secret_parameter )
511+ existing_parameter = try_existing_parameter (parameter_collection , name , typ = ComplexParameterDefinition ,
512+ parameter_type = parameter_type , default = default ,
513+ description = description , secret_parameter = secret_parameter )
486514 if existing_parameter :
487515 return existing_parameter
488516
@@ -499,8 +527,6 @@ def parameter_definition_for(param: inspect.Parameter, name: list[str], field: O
499527 return parameter
500528
501529
502-
503-
504530@dataclass
505531class Parseable (Generic [C ]):
506532 cls : Type [C ]
@@ -523,7 +549,9 @@ def __post_init__(self):
523549 )
524550
525551 def to_help (self , defaults : list [tuple [str , ParsingResults ]], level : int = 0 ) -> str :
526- return "\n " .join (dfs_flatmap (self ._parameter_collection , lambda _ , parameter : parameter .to_help (defaults , level + 1 ) if not isinstance (parameter , ComplexParameterDefinition ) else None ))
552+ return "\n " .join (dfs_flatmap (self ._parameter_collection ,
553+ lambda _ , parameter : parameter .to_help (defaults , level + 1 ) if not isinstance (
554+ parameter , ComplexParameterDefinition ) else None ))
527555
528556
529557CommandMap = dict [str , Union ["CommandMap[C]" , Parseable [C ]]]
@@ -532,10 +560,10 @@ def to_help(self, defaults: list[tuple[str, ParsingResults]], level: int = 0) ->
532560def _to_help (name : str , commands : Union [CommandMap [C ], Parseable [C ]], level : int = 0 , max_length : int = 0 ) -> str :
533561 h = ""
534562 if isinstance (commands , Parseable ):
535- h += f"{ indent (level )} { COMMAND_COLOR } { name } { COLOR_RESET } { ' ' * (max_length - len (name )+ 4 )} { commands .description } \n "
563+ h += f"{ indent (level )} { COMMAND_COLOR } { name } { COLOR_RESET } { ' ' * (max_length - len (name ) + 4 )} { commands .description } \n "
536564 elif isinstance (commands , dict ):
537565 h += f"{ indent (level )} { COMMAND_COLOR } { name } { COLOR_RESET } :\n "
538- max_length = max (max_length , level * INDENT_WIDTH + max (len (k ) for k in commands .keys ()))
566+ max_length = max (max_length , level * INDENT_WIDTH + max (len (k ) for k in commands .keys ()))
539567 for name , parser in commands .items ():
540568 h += _to_help (name , parser , level + 1 , max_length )
541569 return h
@@ -549,7 +577,8 @@ def to_help_for_commands(program: str, commands: CommandMap[C], command_chain: O
549577 return h
550578
551579
552- def to_help_for_command (program : str , command : list [str ], parseable : Parseable [C ], defaults : list [tuple [str , ParsingResults ]]) -> str :
580+ def to_help_for_command (program : str , command : list [str ], parseable : Parseable [C ],
581+ defaults : list [tuple [str , ParsingResults ]]) -> str :
553582 h = f"usage: { program } { COMMAND_COLOR } { ' ' .join (command )} { COLOR_RESET } { PARAMETER_COLOR } [--help] [--config config.json] [options...]{ COLOR_RESET } \n \n "
554583 h += parseable .to_help (defaults )
555584 h += "\n "
@@ -568,13 +597,16 @@ def instantiate(args: list[str], commands: CommandMap[C]) -> tuple[C, ParsingRes
568597 raise ValueError ("No arguments provided (this is probably a bug in the program)" )
569598 return _instantiate (args [0 ], args [1 :], commands , [])
570599
600+
571601def inner (cls ) -> Configurable :
572- cls .name = service_name
573- cls .host = service_desc
574- cls .__service__ = True
575- cls .__parameters__ = get_class_parameters (cls )
602+ cls .name = service_name
603+ cls .host = service_desc
604+ cls .__service__ = True
605+ cls .__parameters__ = get_class_parameters (cls )
606+
576607
577- def _instantiate (program : str , args : list [str ], commands : CommandMap [C ], command_chain : list [str ]) -> tuple [C , ParsingResults ]:
608+ def _instantiate (program : str , args : list [str ], commands : CommandMap [C ], command_chain : list [str ]) -> tuple [
609+ C , ParsingResults ]:
578610 if command_chain is None :
579611 command_chain = []
580612
@@ -597,7 +629,8 @@ def _instantiate(program: str, args: list[str], commands: CommandMap[C], command
597629 raise TypeError (f"Invalid command type { type (command )} " )
598630
599631
600- def get_environment_variables (parsing_results : ParsingResults , parameter_collection : ParameterCollection ) -> tuple [str , ParsingResults ]:
632+ def get_environment_variables (parsing_results : ParsingResults , parameter_collection : ParameterCollection ) -> tuple [
633+ str , ParsingResults ]:
601634 env_parsing_results = dict ()
602635 for key , value in os .environ .items ():
603636 # legacy support
@@ -615,7 +648,8 @@ def get_environment_variables(parsing_results: ParsingResults, parameter_collect
615648 return ("environment variables" , env_parsing_results )
616649
617650
618- def get_env_file_variables (parsing_results : ParsingResults , parameter_collection : ParameterCollection ) -> tuple [str , ParsingResults ]:
651+ def get_env_file_variables (parsing_results : ParsingResults , parameter_collection : ParameterCollection ) -> tuple [
652+ str , ParsingResults ]:
619653 env_file_parsing_results = dict ()
620654 for key , value in dotenv_values ().items ():
621655 key = key .split ("." )
@@ -626,13 +660,15 @@ def get_env_file_variables(parsing_results: ParsingResults, parameter_collection
626660 return (".env file" , env_file_parsing_results )
627661
628662
629- def get_config_file_variables (config_file_path : str , parsing_results : ParsingResults , parameter_collection : ParameterCollection ) -> tuple [str , ParsingResults ]:
663+ def get_config_file_variables (config_file_path : str , parsing_results : ParsingResults ,
664+ parameter_collection : ParameterCollection ) -> tuple [str , ParsingResults ]:
630665 with open (config_file_path , "r" ) as config_file :
631666 config_file_parsing_results = json .load (config_file )
632667 return (f"config file at '{ config_file_path } '" , config_file_parsing_results )
633668
634669
635- def filter_secret_values (parsing_results : ParsingResults , parameter_collection : ParameterCollection , basename : Optional [list [str ]] = None ) -> ParsingResults :
670+ def filter_secret_values (parsing_results : ParsingResults , parameter_collection : ParameterCollection ,
671+ basename : Optional [list [str ]] = None ) -> ParsingResults :
636672 if basename is None :
637673 basename = []
638674
@@ -645,7 +681,8 @@ def filter_secret_values(parsing_results: ParsingResults, parameter_collection:
645681 parsing_results [key ] = "<secret>"
646682
647683
648- def parse_args (program : str , command : list [str ], direct_args : list [str ], parseable : Parseable [C ], parse_env_file : bool = True , parse_environment : bool = True ) -> tuple [C , ParsingResults ]:
684+ def parse_args (program : str , command : list [str ], direct_args : list [str ], parseable : Parseable [C ],
685+ parse_env_file : bool = True , parse_environment : bool = True ) -> tuple [C , ParsingResults ]:
649686 parameter_collection = parseable ._parameter_collection
650687
651688 parsing_results : ParsingResults = dict ()
0 commit comments