2121import functools
2222import re
2323import sys
24- from typing import Any , Callable , Dict , Optional , Tuple , Union
24+ from typing import Any , Callable , cast , Dict , Optional , Tuple , TypeVar , Union
2525
2626import gitlab .config # noqa: F401
2727
3535custom_actions : Dict [str , Dict [str , Tuple [Tuple [str , ...], Tuple [str , ...], bool ]]] = {}
3636
3737
38+ # For an explanation of how these type-hints work see:
39+ # https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
40+ #
41+ # The goal here is that functions which get decorated will retain their types.
42+ __F = TypeVar ("__F" , bound = Callable [..., Any ])
43+
44+
3845def register_custom_action (
3946 cls_names : Union [str , Tuple [str , ...]],
4047 mandatory : Tuple [str , ...] = tuple (),
4148 optional : Tuple [str , ...] = tuple (),
42- ) -> Callable :
43- def wrap (f : Callable ) -> Callable :
49+ ) -> Callable [[ __F ], __F ] :
50+ def wrap (f : __F ) -> __F :
4451 @functools .wraps (f )
45- def wrapped_f (* args , ** kwargs ) :
52+ def wrapped_f (* args : Any , ** kwargs : Any ) -> Any :
4653 return f (* args , ** kwargs )
4754
4855 # in_obj defines whether the method belongs to the obj or the manager
@@ -63,7 +70,7 @@ def wrapped_f(*args, **kwargs):
6370 action = f .__name__ .replace ("_" , "-" )
6471 custom_actions [final_name ][action ] = (mandatory , optional , in_obj )
6572
66- return wrapped_f
73+ return cast ( __F , wrapped_f )
6774
6875 return wrap
6976
@@ -135,12 +142,16 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
135142 return parser
136143
137144
138- def _get_parser (cli_module ):
145+ def _get_parser () -> argparse .ArgumentParser :
146+ # NOTE: We must delay import of gitlab.v4.cli until now or
147+ # otherwise it will cause circular import errors
148+ import gitlab .v4 .cli
149+
139150 parser = _get_base_parser ()
140- return cli_module .extend_parser (parser )
151+ return gitlab . v4 . cli .extend_parser (parser )
141152
142153
143- def _parse_value (v ) :
154+ def _parse_value (v : Any ) -> Any :
144155 if isinstance (v , str ) and v .startswith ("@" ):
145156 # If the user-provided value starts with @, we try to read the file
146157 # path provided after @ as the real value. Exit on any error.
@@ -162,18 +173,10 @@ def docs() -> argparse.ArgumentParser:
162173 if "sphinx" not in sys .modules :
163174 sys .exit ("Docs parser is only intended for build_sphinx" )
164175
165- # NOTE: We must delay import of gitlab.v4.cli until now or
166- # otherwise it will cause circular import errors
167- import gitlab .v4 .cli
168-
169- return _get_parser (gitlab .v4 .cli )
170-
176+ return _get_parser ()
171177
172- def main ():
173- # NOTE: We must delay import of gitlab.v4.cli until now or
174- # otherwise it will cause circular import errors
175- import gitlab .v4 .cli
176178
179+ def main () -> None :
177180 if "--version" in sys .argv :
178181 print (gitlab .__version__ )
179182 sys .exit (0 )
@@ -183,7 +186,7 @@ def main():
183186 # This first parsing step is used to find the gitlab config to use, and
184187 # load the propermodule (v3 or v4) accordingly. At that point we don't have
185188 # any subparser setup
186- (options , args ) = parser .parse_known_args (sys .argv )
189+ (options , _ ) = parser .parse_known_args (sys .argv )
187190 try :
188191 config = gitlab .config .GitlabConfigParser (options .gitlab , options .config_file )
189192 except gitlab .config .ConfigError as e :
@@ -196,14 +199,14 @@ def main():
196199 raise ModuleNotFoundError (name = "gitlab.v%s.cli" % config .api_version )
197200
198201 # Now we build the entire set of subcommands and do the complete parsing
199- parser = _get_parser (gitlab . v4 . cli )
202+ parser = _get_parser ()
200203 try :
201204 import argcomplete # type: ignore
202205
203206 argcomplete .autocomplete (parser )
204207 except Exception :
205208 pass
206- args = parser .parse_args (sys . argv [ 1 :] )
209+ args = parser .parse_args ()
207210
208211 config_files = args .config_file
209212 gitlab_id = args .gitlab
@@ -216,7 +219,7 @@ def main():
216219 action = args .whaction
217220 what = args .what
218221
219- args = args . __dict__
222+ args_dict = vars ( args )
220223 # Remove CLI behavior-related args
221224 for item in (
222225 "gitlab" ,
@@ -228,8 +231,8 @@ def main():
228231 "version" ,
229232 "output" ,
230233 ):
231- args .pop (item )
232- args = {k : _parse_value (v ) for k , v in args .items () if v is not None }
234+ args_dict .pop (item )
235+ args_dict = {k : _parse_value (v ) for k , v in args_dict .items () if v is not None }
233236
234237 try :
235238 gl = gitlab .Gitlab .from_config (gitlab_id , config_files )
@@ -241,6 +244,4 @@ def main():
241244 if debug :
242245 gl .enable_debug ()
243246
244- gitlab .v4 .cli .run (gl , what , action , args , verbose , output , fields )
245-
246- sys .exit (0 )
247+ gitlab .v4 .cli .run (gl , what , action , args_dict , verbose , output , fields )
0 commit comments