44"""
55import argparse
66import AutoCompleter
7+ import itertools
78from typing import List
89
910import cmd2
@@ -20,6 +21,7 @@ def __init__(self):
2021
2122 # For mocking a data source for the example commands
2223 ratings_types = ['G' , 'PG' , 'PG-13' , 'R' , 'NC-17' ]
24+ show_ratings = ['TV-Y' , 'TV-Y7' , 'TV-G' , 'TV-PG' , 'TV-14' , 'TV-MA' ]
2325 static_list_directors = ['J. J. Abrams' , 'Irvin Kershner' , 'George Lucas' , 'Richard Marquand' ,
2426 'Rian Johnson' , 'Gareth Edwards' ]
2527 actors = ['Mark Hamill' , 'Harrison Ford' , 'Carrie Fisher' , 'Alec Guinness' , 'Peter Mayhew' ,
@@ -66,6 +68,24 @@ def __init__(self):
6668 },
6769
6870 }
71+ USER_SHOW_LIBRARY = {'SW_REB' : ['S01E01' , 'S02E02' ]}
72+ SHOW_DATABASE_IDS = ['SW_CW' , 'SW_TCW' , 'SW_REB' ]
73+ SHOW_DATABASE = {'SW_CW' : {'title' : 'Star Wars: Clone Wars' ,
74+ 'rating' : 'TV-Y7' ,
75+ 'seasons' : {1 : ['S01E01' , 'S01E02' , 'S01E03' ],
76+ 2 : ['S02E01' , 'S02E02' , 'S02E03' ]}
77+ },
78+ 'SW_TCW' : {'title' : 'Star Wars: The Clone Wars' ,
79+ 'rating' : 'TV-PG' ,
80+ 'seasons' : {1 : ['S01E01' , 'S01E02' , 'S01E03' ],
81+ 2 : ['S02E01' , 'S02E02' , 'S02E03' ]}
82+ },
83+ 'SW_REB' : {'title' : 'Star Wars: Rebels' ,
84+ 'rating' : 'TV-Y7' ,
85+ 'seasons' : {1 : ['S01E01' , 'S01E02' , 'S01E03' ],
86+ 2 : ['S02E01' , 'S02E02' , 'S02E03' ]}
87+ },
88+ }
6989
7090 # This demonstrates a number of customizations of the AutoCompleter version of ArgumentParser
7191 # - The help output will separately group required vs optional flags
@@ -179,6 +199,19 @@ def _do_media_shows(self, args) -> None:
179199 if not args .command :
180200 self .do_help ('media shows' )
181201
202+ elif args .command == 'list' :
203+ for show_id in TabCompleteExample .SHOW_DATABASE :
204+ show = TabCompleteExample .SHOW_DATABASE [show_id ]
205+ print ('{}\n -----------------------------\n {} ID: {}'
206+ .format (show ['title' ], show ['rating' ], show_id ))
207+ for season in show ['seasons' ]:
208+ ep_list = show ['seasons' ][season ]
209+ print (' Season {}:\n {}'
210+ .format (season ,
211+ '\n ' .join (ep_list )))
212+ print ()
213+
214+
182215 media_parser = AutoCompleter .ACArgumentParser (prog = 'media' )
183216
184217 media_types_subparsers = media_parser .add_subparsers (title = 'Media Types' , dest = 'type' )
@@ -207,6 +240,10 @@ def _do_media_shows(self, args) -> None:
207240 shows_parser = media_types_subparsers .add_parser ('shows' )
208241 shows_parser .set_defaults (func = _do_media_shows )
209242
243+ shows_commands_subparsers = shows_parser .add_subparsers (title = 'Commands' , dest = 'command' )
244+
245+ shows_list_parser = shows_commands_subparsers .add_parser ('list' )
246+
210247 @with_category (CAT_AUTOCOMPLETE )
211248 @with_argparser (media_parser )
212249 def do_media (self , args ):
@@ -257,6 +294,10 @@ def _query_movie_database(self):
257294 def _query_movie_user_library (self ):
258295 return TabCompleteExample .USER_MOVIE_LIBRARY
259296
297+ def _filter_library (self , text , line , begidx , endidx , full , exclude = []):
298+ candidates = list (set (full ).difference (set (exclude )))
299+ return [entry for entry in candidates if entry .startswith (text )]
300+
260301 library_parser = AutoCompleter .ACArgumentParser (prog = 'library' )
261302
262303 library_subcommands = library_parser .add_subparsers (title = 'Media Types' , dest = 'type' )
@@ -276,6 +317,32 @@ def _query_movie_user_library(self):
276317 library_show_parser = library_subcommands .add_parser ('show' )
277318 library_show_parser .set_defaults (func = _do_library_show )
278319
320+ library_show_subcommands = library_show_parser .add_subparsers (title = 'Command' , dest = 'command' )
321+
322+ library_show_add_parser = library_show_subcommands .add_parser ('add' )
323+ library_show_add_parser .add_argument ('show_id' , help = 'Show IDs to add' )
324+ library_show_add_parser .add_argument ('episode_id' , nargs = '*' , help = 'Show IDs to add' )
325+
326+ library_show_rmv_parser = library_show_subcommands .add_parser ('remove' )
327+
328+ # Demonstrates a custom completion function that does more with the command line than is
329+ # allowed by the standard completion functions
330+ def _filter_episodes (self , text , line , begidx , endidx , show_db , user_lib ):
331+ tokens , _ = self .tokens_for_completion (line , begidx , endidx )
332+ show_id = tokens [3 ]
333+ if show_id :
334+ if show_id in show_db :
335+ show = show_db [show_id ]
336+ all_episodes = itertools .chain (* (show ['seasons' ].values ()))
337+
338+ if show_id in user_lib :
339+ user_eps = user_lib [show_id ]
340+ else :
341+ user_eps = []
342+
343+ return self ._filter_library (text , line , begidx , endidx , all_episodes , user_eps )
344+ return []
345+
279346 @with_category (CAT_AUTOCOMPLETE )
280347 @with_argparser (library_parser )
281348 def do_library (self , args ):
@@ -300,21 +367,42 @@ def complete_library(self, text, line, begidx, endidx):
300367 movie_add_choices = {'movie_id' : self ._query_movie_database }
301368 movie_remove_choices = {'movie_id' : self ._query_movie_user_library }
302369
370+ # This demonstrates the ability to mix custom completion functions with argparse completion.
371+ # By specifying a tuple for a completer, AutoCompleter expects a custom completion function
372+ # with optional index-based as well as keyword based arguments. This is an alternative to using
373+ # a partial function.
374+
375+ show_add_choices = {'show_id' : (self ._filter_library , # This is a custom completion function
376+ # This tuple represents index-based args to append to the function call
377+ (list (TabCompleteExample .SHOW_DATABASE .keys ()),)
378+ ),
379+ 'episode_id' : (self ._filter_episodes , # this is a custom completion function
380+ # this list represents index-based args to append to the function call
381+ [TabCompleteExample .SHOW_DATABASE ],
382+ # this dict contains keyword-based args to append to the function call
383+ {'user_lib' : TabCompleteExample .USER_SHOW_LIBRARY })}
384+ show_remove_choices = {}
385+
303386 # The library movie sub-parser group 'command' has 2 sub-parsers:
304387 # 'add' and 'remove'
305388 library_movie_command_params = \
306389 {'add' : (movie_add_choices , None ),
307390 'remove' : (movie_remove_choices , None )}
308391
392+ library_show_command_params = \
393+ {'add' : (show_add_choices , None ),
394+ 'remove' : (show_remove_choices , None )}
395+
309396 # The 'library movie' command has a sub-parser group called 'command'
310397 library_movie_subcommand_groups = {'command' : library_movie_command_params }
398+ library_show_subcommand_groups = {'command' : library_show_command_params }
311399
312400 # Mapping of a specific sub-parser of the 'type' group to a tuple. Each
313401 # tuple has 2 values corresponding what's passed to the constructor
314402 # parameters (arg_choices,subcmd_args_lookup) of the nested
315403 # instance of AutoCompleter
316404 library_type_params = {'movie' : (None , library_movie_subcommand_groups ),
317- 'show' : (None , None )}
405+ 'show' : (None , library_show_subcommand_groups )}
318406
319407 # maps the a subcommand group to a dictionary mapping a specific
320408 # sub-command to a tuple of (arg_choices, subcmd_args_lookup)
0 commit comments