11import inspect
22import re
33import traceback
4+ import typing as ty
45import warnings
56
67import click
78from docutils import nodes
9+ from docutils import statemachine
10+ from docutils .nodes import section
811from docutils .parsers import rst
912from docutils .parsers .rst import directives
10- from docutils import statemachine
1113from sphinx .util import logging
1214from sphinx .util import nodes as sphinx_nodes
15+ from sphinx import application
1316
1417LOG = logging .getLogger (__name__ )
1518
2023ANSI_ESC_SEQ_RE = re .compile (r'\x1B\[\d+(;\d+){0,2}m' , flags = re .MULTILINE )
2124
2225
23- def _indent (text , level = 1 ) :
26+ def _indent (text : str , level : int = 1 ) -> str :
2427 prefix = ' ' * (4 * level )
2528
26- def prefixed_lines ():
29+ def prefixed_lines () -> ty . Generator [ str , None , None ] :
2730 for line in text .splitlines (True ):
2831 yield (prefix + line if line .strip () else line )
2932
3033 return '' .join (prefixed_lines ())
3134
3235
33- def _get_usage (ctx ) :
36+ def _get_usage (ctx : click . Context ) -> str :
3437 """Alternative, non-prefixed version of 'get_usage'."""
3538 formatter = ctx .make_formatter ()
3639 pieces = ctx .command .collect_usage_pieces (ctx )
3740 formatter .write_usage (ctx .command_path , ' ' .join (pieces ), prefix = '' )
38- return formatter .getvalue ().rstrip ('\n ' )
41+ return formatter .getvalue ().rstrip ('\n ' ) # type: ignore
3942
4043
41- def _get_help_record (opt ) :
44+ def _get_help_record (opt : click . Option ) -> ty . Tuple [ str , str ] :
4245 """Re-implementation of click.Opt.get_help_record.
4346
4447 The variant of 'get_help_record' found in Click makes uses of slashes to
@@ -49,14 +52,14 @@ def _get_help_record(opt):
4952 [1] http://www.sphinx-doc.org/en/stable/domains.html#directive-option
5053 """
5154
52- def _write_opts (opts ) :
55+ def _write_opts (opts : ty . List [ str ]) -> str :
5356 rv , _ = click .formatting .join_options (opts )
5457 if not opt .is_flag and not opt .count :
5558 name = opt .name
5659 if opt .metavar :
5760 name = opt .metavar .lstrip ('<[{($' ).rstrip ('>]})$' )
5861 rv += ' <{}>' .format (name )
59- return rv
62+ return rv # type: ignore
6063
6164 rv = [_write_opts (opt .opts )]
6265 if opt .secondary_opts :
@@ -101,7 +104,7 @@ def _write_opts(opts):
101104 return ', ' .join (rv ), '\n ' .join (out )
102105
103106
104- def _format_help (help_string ) :
107+ def _format_help (help_string : str ) -> ty . Generator [ str , None , None ] :
105108 help_string = inspect .cleandoc (ANSI_ESC_SEQ_RE .sub ('' , help_string ))
106109
107110 bar_enabled = False
@@ -118,7 +121,7 @@ def _format_help(help_string):
118121 yield ''
119122
120123
121- def _format_description (ctx ) :
124+ def _format_description (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
122125 """Format the description for a given `click.Command`.
123126
124127 We parse this as reStructuredText, allowing users to embed rich
@@ -129,7 +132,7 @@ def _format_description(ctx):
129132 yield from _format_help (help_string )
130133
131134
132- def _format_usage (ctx ) :
135+ def _format_usage (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
133136 """Format the usage for a `click.Command`."""
134137 yield '.. code-block:: shell'
135138 yield ''
@@ -138,20 +141,20 @@ def _format_usage(ctx):
138141 yield ''
139142
140143
141- def _format_option (opt ) :
144+ def _format_option (opt : click . Option ) -> ty . Generator [ str , None , None ] :
142145 """Format the output for a `click.Option`."""
143- opt = _get_help_record (opt )
146+ opt_help = _get_help_record (opt )
144147
145- yield '.. option:: {}' .format (opt [0 ])
146- if opt [1 ]:
148+ yield '.. option:: {}' .format (opt_help [0 ])
149+ if opt_help [1 ]:
147150 yield ''
148151 for line in statemachine .string2lines (
149- ANSI_ESC_SEQ_RE .sub ('' , opt [1 ]), tab_width = 4 , convert_whitespace = True
152+ ANSI_ESC_SEQ_RE .sub ('' , opt_help [1 ]), tab_width = 4 , convert_whitespace = True
150153 ):
151154 yield _indent (line )
152155
153156
154- def _format_options (ctx ) :
157+ def _format_options (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
155158 """Format all `click.Option` for a `click.Command`."""
156159 # the hidden attribute is part of click 7.x only hence use of getattr
157160 params = [
@@ -166,7 +169,7 @@ def _format_options(ctx):
166169 yield ''
167170
168171
169- def _format_argument (arg ) :
172+ def _format_argument (arg : click . Argument ) -> ty . Generator [ str , None , None ] :
170173 """Format the output of a `click.Argument`."""
171174 yield '.. option:: {}' .format (arg .human_readable_name )
172175 yield ''
@@ -177,7 +180,7 @@ def _format_argument(arg):
177180 )
178181
179182
180- def _format_arguments (ctx ) :
183+ def _format_arguments (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
181184 """Format all `click.Argument` for a `click.Command`."""
182185 params = [x for x in ctx .command .params if isinstance (x , click .Argument )]
183186
@@ -187,7 +190,9 @@ def _format_arguments(ctx):
187190 yield ''
188191
189192
190- def _format_envvar (param ):
193+ def _format_envvar (
194+ param : ty .Union [click .Option , click .Argument ]
195+ ) -> ty .Generator [str , None , None ]:
191196 """Format the envvars of a `click.Option` or `click.Argument`."""
192197 yield '.. envvar:: {}' .format (param .envvar )
193198 yield ' :noindex:'
@@ -202,9 +207,9 @@ def _format_envvar(param):
202207 yield _indent ('Provide a default for :option:`{}`' .format (param_ref ))
203208
204209
205- def _format_envvars (ctx ) :
210+ def _format_envvars (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
206211 """Format all envvars for a `click.Command`."""
207- params = [x for x in ctx .command .params if getattr ( x , ' envvar' ) ]
212+ params = [x for x in ctx .command .params if x . envvar ]
208213
209214 for param in params :
210215 yield '.. _{command_name}-{param_name}-{envvar}:' .format (
@@ -218,7 +223,7 @@ def _format_envvars(ctx):
218223 yield ''
219224
220225
221- def _format_subcommand (command ) :
226+ def _format_subcommand (command : click . Command ) -> ty . Generator [ str , None , None ] :
222227 """Format a sub-command of a `click.Command` or `click.Group`."""
223228 yield '.. object:: {}' .format (command .name )
224229
@@ -232,7 +237,7 @@ def _format_subcommand(command):
232237 yield _indent (line )
233238
234239
235- def _format_epilog (ctx ) :
240+ def _format_epilog (ctx : click . Context ) -> ty . Generator [ str , None , None ] :
236241 """Format the epilog for a given `click.Command`.
237242
238243 We parse this as reStructuredText, allowing users to embed rich
@@ -242,15 +247,18 @@ def _format_epilog(ctx):
242247 yield from _format_help (ctx .command .epilog )
243248
244249
245- def _get_lazyload_commands (ctx ) :
250+ def _get_lazyload_commands (ctx : click . Context ) -> ty . Dict [ str , click . Command ] :
246251 commands = {}
247252 for command in ctx .command .list_commands (ctx ):
248253 commands [command ] = ctx .command .get_command (ctx .command , command )
249254
250255 return commands
251256
252257
253- def _filter_commands (ctx , commands = None ):
258+ def _filter_commands (
259+ ctx : click .Context ,
260+ commands : ty .Optional [ty .List [str ]] = None ,
261+ ) -> ty .List [click .Command ]:
254262 """Return list of used commands."""
255263 lookup = getattr (ctx .command , 'commands' , {})
256264 if not lookup and isinstance (ctx .command , click .MultiCommand ):
@@ -259,14 +267,17 @@ def _filter_commands(ctx, commands=None):
259267 if commands is None :
260268 return sorted (lookup .values (), key = lambda item : item .name )
261269
262- names = [name .strip () for name in commands .split (',' )]
263- return [lookup [name ] for name in names if name in lookup ]
270+ return [lookup [command ] for command in commands if command in lookup ]
264271
265272
266- def _format_command (ctx , nested , commands = None ):
273+ def _format_command (
274+ ctx : click .Context ,
275+ nested : str ,
276+ commands : ty .Optional [ty .List [str ]] = None ,
277+ ) -> ty .Generator [str , None , None ]:
267278 """Format the output of `click.Command`."""
268279 if ctx .command .hidden :
269- return
280+ return None
270281
271282 # description
272283
@@ -321,26 +332,24 @@ def _format_command(ctx, nested, commands=None):
321332 if nested in (NESTED_FULL , NESTED_NONE ):
322333 return
323334
324- commands = _filter_commands (ctx , commands )
335+ command_objs = _filter_commands (ctx , commands )
325336
326- if commands :
337+ if command_objs :
327338 yield '.. rubric:: Commands'
328339 yield ''
329340
330- for command in commands :
341+ for command_obj in command_objs :
331342 # Don't show hidden subcommands
332- if command .hidden :
343+ if command_obj .hidden :
333344 continue
334345
335- for line in _format_subcommand (command ):
346+ for line in _format_subcommand (command_obj ):
336347 yield line
337348 yield ''
338349
339350
340- def nested (argument ):
341- values = (NESTED_FULL , NESTED_SHORT , NESTED_NONE )
342- if not argument :
343- return None
351+ def nested (argument : ty .Optional [str ]) -> ty .Optional [str ]:
352+ values = (NESTED_FULL , NESTED_SHORT , NESTED_NONE , None )
344353
345354 if argument not in values :
346355 raise ValueError (
@@ -362,11 +371,8 @@ class ClickDirective(rst.Directive):
362371 'show-nested' : directives .flag ,
363372 }
364373
365- def _load_module (self , module_path ) :
374+ def _load_module (self , module_path : str ) -> ty . Union [ click . Command , click . Group ] :
366375 """Load the module."""
367- # __import__ will fail on unicode,
368- # so we ensure module path is a string here.
369- module_path = str (module_path )
370376
371377 try :
372378 module_name , attr_name = module_path .split (':' , 1 )
@@ -395,16 +401,22 @@ def _load_module(self, module_path):
395401
396402 parser = getattr (mod , attr_name )
397403
398- if not isinstance (parser , click .BaseCommand ):
404+ if not isinstance (parser , ( click .Command , click . Group ) ):
399405 raise self .error (
400- '"{}" of type "{}" is not derived from '
406+ '"{}" of type "{}" is not click.Command or click.Group. '
401407 '"click.BaseCommand"' .format (type (parser ), module_path )
402408 )
403409 return parser
404410
405411 def _generate_nodes (
406- self , name , command , parent , nested , commands = None , semantic_group = False
407- ):
412+ self ,
413+ name : str ,
414+ command : click .Command ,
415+ parent : ty .Optional [click .Context ],
416+ nested : str ,
417+ commands : ty .Optional [ty .List [str ]] = None ,
418+ semantic_group : bool = False ,
419+ ) -> ty .List [section ]:
408420 """Generate the relevant Sphinx nodes.
409421
410422 Format a `click.Group` or `click.Command`.
@@ -416,7 +428,7 @@ def _generate_nodes(
416428 :param commands: Display only listed commands or skip the section if
417429 empty
418430 :param semantic_group: Display command as title and description for
419- CommandCollection.
431+ `click. CommandCollection` .
420432 :returns: A list of nested docutil nodes
421433 """
422434 ctx = click .Context (command , info_name = name , parent = parent )
@@ -474,7 +486,7 @@ def _generate_nodes(
474486
475487 return [section ]
476488
477- def run (self ):
489+ def run (self ) -> ty . Iterable [ section ] :
478490 self .env = self .state .document .settings .env
479491
480492 command = self ._load_module (self .arguments [0 ])
@@ -498,12 +510,14 @@ def run(self):
498510 )
499511 nested = NESTED_FULL if show_nested else NESTED_SHORT
500512
501- commands = self .options .get ('commands' )
513+ commands = [
514+ command .strip () for command in self .options .get ('commands' , '' ).split (',' )
515+ ]
502516
503517 return self ._generate_nodes (prog_name , command , None , nested , commands )
504518
505519
506- def setup (app ) :
520+ def setup (app : application . Sphinx ) -> ty . Dict [ str , ty . Any ] :
507521 app .add_directive ('click' , ClickDirective )
508522
509523 return {
0 commit comments