#!/usr/bin/env python # The command-line interface module creates a interface for # interacting with the python program (wikicrawl). This is an implementation # of the baker demo shown previously. The user can type in commands to # make the program do things. import baker import logging import readline # Needed for command history and arrows to work import sys from . import model from . import config # Problem pages: # Decision (from politics) # Malaysia (goes inside parenthesis) commander = baker.Baker() def main(): user_interface = InteractiveInterface() if len(sys.argv) > 1: # Command line arguments were passed in # command-line when invoking python user_interface.run(sys.argv) else: user_interface.start_command_loop() class InteractiveInterface: def __init__(self): self.model = model.Model() def run(self, args, main=True): """ Runs the command-line interface for a single command. If called by InteractiveInterface.run(sys.argv), this method will execute the commands and arguments specified on command line when running this program. Alternatively, the code could pass in a different set of arguments to specify what to do. See start_command_loop() for more information. """ try: commander.run(argv=args, main=True, help_on_error=True, instance=self) except baker.CommandError as ex: logging.warn('incorrect user input: %s' % ex) commander.usage() except baker.TopHelp as ex: commander.usage() except Exception as ex: logging.error('caught general exception!!') print(type(ex), ex) def start_command_loop(self): """ Repeatedly asks the user what command to run until they exit. This method calls InteractiveInterface.run(args) a little bit differently. Instead of passing the arguments from the command-line that were passed in when invoking the python wikicrawl app, this asks the user for a line of textual input and passes those strings to run() as the arguments. This way, the user can access an interactive shell and repeatedly issue different commands while the application is running. """ commander.usage() self.model.open_browser() while True: print('$ ', end = '') # Display to the user a command prompt # The dollar-sign is a common indication # of a shell that communicates to the user # that we are waiting for their textual # input. The end = '' indicates to python # to NOT drop to a newline after printing # in the terminal. Instead, let the user # type their command on the same line as # our printed '$ '. try: inp = input() except EOFError: # +D will send "End Line" and exit the command loop break # Note in arguments (mg): # Whenever a program is run in windows or *nix, the operating # system passes in the command string that was used to invoke # the program. You can append data in that command to configure # switches or values going into the program on the fly. For # example, you can invoke this wikicrawl app in more than one # way. You can of course run "python launcher.py" to run the # software but you can also pass in an argument. You can # alternatively run "python launcher.py ..." # and the operating system will provide the values into # the process that is running. # # In a real world use case, many commands provide switches to # adjust what the program does. For example, # # The command: # find music -iname "*justin*bieber*" # runs the "find" program and asks to find all the filenames that match the # pattern *justin*bieber* in the "music" directory. # (music, -iname, "*justin*biever*") are argument parameters # that are passed into the program. The program is coded to # parse and interpret these values and execute differently based # on the values passed in. This is one way to pass in information # into a running program. Some other ways are to read from a file # (such as how we read from settings.py to load the runtime # configuration), from something called environment variables # (won't get into but another set of values provided to programs # from the operating system), or they can be hard-coded into # the application. # # Side note: arguments are not unique to python (almost all # programming languages implement arguments), the functionality # is defined by the application (some programs require arguments, # some are optional, and the syntax for sending in argument # parameters are different and defined by the individual programs, # and lastly, the first argument sent in is the script name or # filename of the script. In our case, the first argument is # the string "launcher.py". If the user invoked the command # as C:\Users\mguest\launcher.py then the first argument # would be C:\Users\mguest\launcher.py. # What this method (start_command_loop()) does is provide a # REPL which is a # read-eval-print-loop. It repeatedly asks the user for an # input (read), evaluates that input into an action (evaluate), # give the user some feedback (print), and start the process # over again (loop). When you call "python", you are given a python # process that gives you a REPL interactive shell. The way # this wikicrawl app is implemented gives the user a REPL # that has commands to interact with wikipedia pages. args = [sys.argv[0], ] + inp.split() # The user can at any point in the command pass the argument # switch "--help". If doing this, the command line interface # will instead print out the inline documentation associated # with this command and quit after doing so. For example, # the user can type "python launcher.py do_random_page --help" # and the program will spit out the generated documentation # for the do_random_page command and run nothing. In our case, # this documentation is created by the baker library and will # print out the docstring associated with the method. Try it # out in your shell (cmd.exe or powershell.exe) by invoking # python launcher.py do_random_page --help # You will see the program spit out the heredoc below the # do_random_page method defined below. if '--help' in args: args.remove('--help') try: print('command usage:') commander.usage(args[1]) return except Exception as ex: print(type(ex), ex) continue self.run(args, main=False) @commander.command def do_random_page(self): """ Instructs the wikicrawl application to play the game on a random article. """ self.model.do_random_page() @commander.command def do_n_pages(self, n): """ Plays the wikicrawl game -times. """ try: n = int(n) except ValueError as ex: logging.warn('failed to process "%s" as a parameter' % n) return False for i in range(n): self.model.do_random_page() if __name__ == '__main__': main()