88import typing as ty
99
1010
11+ F = ty .TypeVar ('F' , bound = ty .Callable [..., ty .Any ])
12+
13+
14+ Decorator = ty .Callable [[F ], F ]
15+
16+
17+ DecoratorFactory = ty .Callable [..., Decorator [F ]]
18+
19+
20+ AnyCallable = ty .Callable [..., ty .Any ]
21+
22+
23+ AnyDecoratorFactory = DecoratorFactory [AnyCallable ]
24+
25+
26+ class CommandDescriptor (ty .TypedDict ):
27+ name : ty .Optional [str ]
28+ fn : AnyCallable
29+ subparser_call_stack : ty .List [SubparserCallDescriptor ]
30+ add_parser_args : ty .Optional [ty .Tuple [ty .Sequence [ty .Any ],
31+ ty .Dict [str , ty .Any ]]]
32+
33+ class SubparserCallDescriptor (ty .TypedDict ):
34+ method_name : str
35+ args : ty .Tuple [ty .Any , ...]
36+ kwargs : ty .Dict [str , ty .Any ]
37+
38+
39+ class SubparsersProtocol (ty .Protocol ):
40+ """
41+ A protocol class to describe the returned value of
42+ ``argparse.ArgumentParser.add_subparsers()``, since the official
43+ documentation does not point to a public type for the returned value.
44+ """
45+ def add_parser (self , name : str , ** kw : ty .Any ) -> argparse .ArgumentParser :
46+ ... # pragma: no cover
47+
48+
1149class SubDec :
1250 """
1351 This class provides a way to decorate functions as subcommands for
@@ -75,37 +113,37 @@ def __init__(self,
75113 replace the underscore character ("_") when converting the name of the
76114 decorated function to a subcommand name.
77115 """
78- self .__decorators_cache = {}
79- self .__commands = {}
116+ self .__decorators_cache : ty . Dict [ str , AnyDecoratorFactory ] = {}
117+ self .__commands : ty . Dict [ AnyCallable , CommandDescriptor ] = {}
80118 self .__name_prefix = name_prefix
81119 self .__fn_dest = fn_dest
82120 self .__sep = sep
83121
84- def create_parsers (self , subparsers ) :
122+ def create_parsers (self , subparsers : SubparsersProtocol ) -> None :
85123 """
86124 Create subparsers by calling ``subparsers.add_parser()`` for each
87125 decorated function.
88126 """
89127 for cmd in self .__commands .values ():
90128 self .__create_parser (cmd , subparsers )
91129
92- def cmd (self , * k , ** kw ) :
130+ def cmd (self , * k : ty . Any , ** kw : ty . Any ) -> DecoratorFactory [ F ] :
93131 """
94132 Special decorator to register arguments to be passed do
95133 ``add_parser()``.
96134 """
97- def decorator (fn ) :
135+ def decorator (fn : F ) -> F :
98136 cmd = self .__get_command (fn )
99137 cmd ['add_parser_args' ] = (k , kw )
100138 return fn
101139 return decorator
102140
103- def __getattr__ (self , name : str ):
141+ def __getattr__ (self , name : str ) -> AnyDecoratorFactory :
104142 if name in self .__decorators_cache :
105143 return self .__decorators_cache [name ]
106144
107- def decorator_wrapper (* k , ** kw ) :
108- def decorator (fn ) :
145+ def decorator_wrapper (* k : ty . Any , ** kw : ty . Any ) -> Decorator [ F ] :
146+ def decorator (fn : F ) -> F :
109147 cmd = self .__get_command (fn )
110148 cmd ['subparser_call_stack' ].append ({
111149 'method_name' : name ,
@@ -118,7 +156,7 @@ def decorator(fn):
118156 self .__decorators_cache [name ] = decorator_wrapper
119157 return decorator_wrapper
120158
121- def __get_command (self , fn : ty . Callable ) :
159+ def __get_command (self , fn : AnyCallable ) -> CommandDescriptor :
122160 if fn not in self .__commands :
123161 self .__commands [fn ] = {
124162 'name' : None ,
@@ -128,7 +166,10 @@ def __get_command(self, fn: ty.Callable):
128166 }
129167 return self .__commands [fn ]
130168
131- def __create_parser (self , cmd : dict , subparsers : ty .Any ):
169+ def __create_parser (self ,
170+ cmd : CommandDescriptor ,
171+ subparsers : SubparsersProtocol ,
172+ ) -> None :
132173 name = cmd ['name' ]
133174 if not name :
134175 name = cmd ['fn' ].__name__
@@ -137,7 +178,7 @@ def __create_parser(self, cmd: dict, subparsers: ty.Any):
137178 if self .__sep is not None :
138179 name = name .replace ('_' , self .__sep )
139180
140- if cmd ['add_parser_args' ]:
181+ if cmd ['add_parser_args' ] is not None :
141182 add_parser_args , add_parser_kwargs = cmd ['add_parser_args' ]
142183 if not add_parser_args :
143184 add_parser_args = (name ,)
0 commit comments