1- import argparse
1+ from __future__ import annotations
2+
23import logging
3- from typing import Optional , Callable , Union
4+ from argparse import (
5+ ArgumentParser ,
6+ _ArgumentGroup ,
7+ _MutuallyExclusiveGroup ,
8+ _SubParsersAction ,
9+ )
10+ from collections .abc import Generator , Iterable
411from copy import deepcopy
5-
12+ from typing import Any , Callable
613
714config : dict = {}
815logger = logging .getLogger (__name__ )
916logger .setLevel (logging .INFO )
1017
1118
12- def init_config ():
19+ def init_config () -> dict [ str , Any ] :
1320 return {"prefix_chars" : "-" }
1421
1522
16- def ensure_list (name : Union [ str , list ]) -> list :
23+ def ensure_list (name : str | list [ str ]) -> list [ str ] :
1724 if isinstance (name , str ):
18- name = [name ]
25+ return [name ]
1926 return name
2027
2128
22- def has_many_and_is_optional (names : list ) -> list :
29+ def has_many_and_is_optional (names : list [ str ] ) -> list [ str ] :
2330 """The arguments can have aliases only when they are optional.
2431
2532 If this is not the case, then it raises an error.
@@ -28,21 +35,18 @@ def has_many_and_is_optional(names: list) -> list:
2835 is_optional = all (name .startswith (tuple (prefix_chars )) for name in names )
2936
3037 if not is_optional and len (names ) > 1 :
31- msg = (
32- f"Only optional arguments (starting with { prefix_chars } ) "
33- "can have aliases"
38+ raise ValueError (
39+ f"Only optional arguments (starting with { prefix_chars } ) can have aliases"
3440 )
35- raise ValueError (msg )
3641 return names
3742
3843
39- def is_exclusive_group_or_not (arg : dict ):
44+ def is_exclusive_group_or_not (arg : dict ) -> None :
4045 if "exclusive_group" in arg and "group" in arg :
41- msg = "choose group or exclusive_group not both."
42- raise ValueError (msg )
46+ raise ValueError ("choose group or exclusive_group not both." )
4347
4448
45- def validate_args (args : list ) :
49+ def validate_args (args : Iterable [ dict ]) -> Generator [ dict , Any , None ] :
4650 for arg in args :
4751 arg ["name" ] = ensure_list (arg ["name" ])
4852 has_many_and_is_optional (arg ["name" ])
@@ -51,11 +55,11 @@ def validate_args(args: list):
5155
5256
5357def get_or_create_group (
54- parser ,
58+ parser : ArgumentParser ,
5559 groups : dict ,
56- title : Optional [ str ] = None ,
57- description : Optional [ str ] = None ,
58- ):
60+ title : str | None = None ,
61+ description : str | None = None ,
62+ ) -> _ArgumentGroup | Any :
5963 group_parser = groups .get (title )
6064 if group_parser is None :
6165 group_parser = parser .add_argument_group (title , description )
@@ -64,8 +68,11 @@ def get_or_create_group(
6468
6569
6670def get_or_create_exclusive_group (
67- parser , groups : dict , title : Optional [str ] = None , required = False
68- ):
71+ parser : ArgumentParser ,
72+ groups : dict ,
73+ title : str | None = None ,
74+ required : bool = False ,
75+ ) -> _MutuallyExclusiveGroup | Any :
6976 group_parser = groups .get (title )
7077 if group_parser is None :
7178 group_parser = parser .add_mutually_exclusive_group (required = required )
@@ -74,18 +81,18 @@ def get_or_create_exclusive_group(
7481 return group_parser
7582
7683
77- def add_arguments (parser , args : list ):
84+ def add_arguments (parser : ArgumentParser , args : list ) -> None :
7885 groups : dict = {}
7986 exclusive_groups : dict = {}
8087
8188 for arg in validate_args (args ):
8289 logger .debug ("arg: %s" , arg )
8390
8491 name : list = arg .pop ("name" )
85- group_title : Optional [ str ] = arg .pop ("group" , None )
86- exclusive_group_title : Optional [ str ] = arg .pop ("exclusive_group" , None )
92+ group_title : str | None = arg .pop ("group" , None )
93+ exclusive_group_title : str | None = arg .pop ("exclusive_group" , None )
8794
88- _parser = parser
95+ _parser : Any = parser
8996 if exclusive_group_title :
9097 logger .debug ("Exclusive group: %s" , exclusive_group_title )
9198 _parser = get_or_create_exclusive_group (
@@ -98,9 +105,9 @@ def add_arguments(parser, args: list):
98105 _parser .add_argument (* name , ** arg )
99106
100107
101- def add_subcommand (parser , command : dict ):
108+ def add_subcommand (parser : _SubParsersAction [ ArgumentParser ] , command : dict ) -> None :
102109 args : list = command .pop ("arguments" , None )
103- func : Optional [ Callable ] = command .pop ("func" , None )
110+ func : Callable | None = command .pop ("func" , None )
104111
105112 names : list = ensure_list (command .pop ("name" ))
106113 name : str = names .pop (0 )
@@ -116,7 +123,7 @@ def add_subcommand(parser, command: dict):
116123 add_arguments (command_parser , args )
117124
118125
119- def add_subparser (parser , subcommand : dict ):
126+ def add_subparser (parser : ArgumentParser , subcommand : dict ) -> None :
120127 commands : list = subcommand .pop ("commands" )
121128
122129 # This design is for python 3.6 compatibility
@@ -131,12 +138,14 @@ def add_subparser(parser, subcommand: dict):
131138 add_subcommand (subparser , command )
132139
133140
134- def add_parser (data : dict , parser_class : Callable , parents : Optional [list ]):
141+ def add_parser (
142+ data : dict , parser_class : Callable [..., ArgumentParser ], parents : list | None
143+ ) -> ArgumentParser :
135144 if parents is None :
136145 parents = []
137146
138- args : Optional [ list ] = data .pop ("arguments" , None )
139- subcommands : Optional [ dict ] = data .pop ("subcommands" , None )
147+ args : list | None = data .pop ("arguments" , None )
148+ subcommands : dict | None = data .pop ("subcommands" , None )
140149
141150 parser = parser_class (** data , parents = parents )
142151
@@ -153,9 +162,9 @@ def add_parser(data: dict, parser_class: Callable, parents: Optional[list]):
153162
154163def cli (
155164 data : dict ,
156- parser_class : Callable = argparse . ArgumentParser ,
157- parents : Optional [ list ] = None ,
158- ):
165+ parser_class : Callable [..., ArgumentParser ] = ArgumentParser ,
166+ parents : list | None = None ,
167+ ) -> ArgumentParser :
159168 """Create a cli application.
160169
161170 This is the entrypoint.
0 commit comments