8
8
import typing as ty
9
9
10
10
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
+
11
49
class SubDec :
12
50
"""
13
51
This class provides a way to decorate functions as subcommands for
@@ -75,37 +113,37 @@ def __init__(self,
75
113
replace the underscore character ("_") when converting the name of the
76
114
decorated function to a subcommand name.
77
115
"""
78
- self .__decorators_cache = {}
79
- self .__commands = {}
116
+ self .__decorators_cache : ty . Dict [ str , AnyDecoratorFactory ] = {}
117
+ self .__commands : ty . Dict [ AnyCallable , CommandDescriptor ] = {}
80
118
self .__name_prefix = name_prefix
81
119
self .__fn_dest = fn_dest
82
120
self .__sep = sep
83
121
84
- def create_parsers (self , subparsers ) :
122
+ def create_parsers (self , subparsers : SubparsersProtocol ) -> None :
85
123
"""
86
124
Create subparsers by calling ``subparsers.add_parser()`` for each
87
125
decorated function.
88
126
"""
89
127
for cmd in self .__commands .values ():
90
128
self .__create_parser (cmd , subparsers )
91
129
92
- def cmd (self , * k , ** kw ) :
130
+ def cmd (self , * k : ty . Any , ** kw : ty . Any ) -> DecoratorFactory [ F ] :
93
131
"""
94
132
Special decorator to register arguments to be passed do
95
133
``add_parser()``.
96
134
"""
97
- def decorator (fn ) :
135
+ def decorator (fn : F ) -> F :
98
136
cmd = self .__get_command (fn )
99
137
cmd ['add_parser_args' ] = (k , kw )
100
138
return fn
101
139
return decorator
102
140
103
- def __getattr__ (self , name : str ):
141
+ def __getattr__ (self , name : str ) -> AnyDecoratorFactory :
104
142
if name in self .__decorators_cache :
105
143
return self .__decorators_cache [name ]
106
144
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 :
109
147
cmd = self .__get_command (fn )
110
148
cmd ['subparser_call_stack' ].append ({
111
149
'method_name' : name ,
@@ -118,7 +156,7 @@ def decorator(fn):
118
156
self .__decorators_cache [name ] = decorator_wrapper
119
157
return decorator_wrapper
120
158
121
- def __get_command (self , fn : ty . Callable ) :
159
+ def __get_command (self , fn : AnyCallable ) -> CommandDescriptor :
122
160
if fn not in self .__commands :
123
161
self .__commands [fn ] = {
124
162
'name' : None ,
@@ -128,7 +166,10 @@ def __get_command(self, fn: ty.Callable):
128
166
}
129
167
return self .__commands [fn ]
130
168
131
- def __create_parser (self , cmd : dict , subparsers : ty .Any ):
169
+ def __create_parser (self ,
170
+ cmd : CommandDescriptor ,
171
+ subparsers : SubparsersProtocol ,
172
+ ) -> None :
132
173
name = cmd ['name' ]
133
174
if not name :
134
175
name = cmd ['fn' ].__name__
@@ -137,7 +178,7 @@ def __create_parser(self, cmd: dict, subparsers: ty.Any):
137
178
if self .__sep is not None :
138
179
name = name .replace ('_' , self .__sep )
139
180
140
- if cmd ['add_parser_args' ]:
181
+ if cmd ['add_parser_args' ] is not None :
141
182
add_parser_args , add_parser_kwargs = cmd ['add_parser_args' ]
142
183
if not add_parser_args :
143
184
add_parser_args = (name ,)
0 commit comments