1   
  2   
  3  """ 
  4     Command-line interface tool. All scripts that require a command-line 
  5     interface should use this tool, replacing the Python getopt module. 
  6   
  7     The purpose of the command-line interface (CLI) tool is to make the 
  8     command-line interface both consistent and powerful for every VDFS script, 
  9     enabling the operator a lot of control without having to hack the script. 
 10     Furthermore, it automatically provides a lot of error checking to make the 
 11     script as robust as possible, as well as a --help/-h option documenting the 
 12     complete command-line interface for the script. 
 13   
 14     Usage 
 15     ===== 
 16   
 17     Import as:: 
 18         from wsatools.CLI import CLI 
 19   
 20     Define your list of program arguments and program options in a global part 
 21     of your module that is processed on import - this makes the options globally 
 22     accessible, and any module that imports this module will then automatically 
 23     inherit these command line options. For command-line options associated with 
 24     a class it may be clearer to define them within the class definition 
 25     following the declaration of class member variables (see e.g. 
 26     L{invocations.cu19.cu19.Cu19} class definition). To define the list program 
 27     arguments append L{CLI.Argument} objects to the CLI.progArgs list, and to 
 28     define the list of program options append L{CLI.Option} to the CLI.progOpts 
 29     list:: 
 30   
 31         CLI.progArgs.append(CLI.Argument('surveyID', example='1', 
 32                                          isValOK=lambda x: x.isdigit())) 
 33         CLI.progOpts += [ 
 34             # Example of a boolean option flag - default value is always false 
 35             CLI.Option('a', 'append', 
 36                        'append tables to any pre-existing release database ' 
 37                        'of the same name'), 
 38             # Example of an option with an argument that has a default value 
 39             CLI.Option('b', 'begin', 
 40                        'first observation date to release e.g. 2004-04-01', 
 41                        argument='DATE', 
 42                        default=str(ObsCal.earlyDefaultDateTime.date), 
 43                        isValOK=CLI.isDateOK), 
 44             # Example of an option with an argument with no default value 
 45             CLI.Option('r', 'rerun', 
 46                        'just apply to re-transfered files in this log', 
 47                        argument='FILE', isValOK=CLI.assertFileExists)] 
 48   
 49     For full details on command-line argument and option definitions please 
 50     to the L{CLI.Argument} and L{CLI.Option} class documentation respectively. 
 51   
 52     To change the default value of an inherited command-line argument:: 
 53   
 54         CLI.progArgs['comment'] = "Updating catalogue data" 
 55   
 56     To delete an inherited command-line argument/option:: 
 57   
 58         CLI.progArgs.remove('programmeID') 
 59   
 60     Then to process the command-line arguments/options, start your script with 
 61     this as the first line:: 
 62   
 63         cli = CLI("Script's name", '$Revision: 9505 $', 
 64                   "This script does stuff to the given survey.") 
 65   
 66     This line will cause the script to exit straight away if the user has 
 67     requested help, and the help screen will start with this information. It is 
 68     important to use this script documentation to document the script's 
 69     mandatory arguments, as shown in this example. The documentation can be 
 70     taken from the first sentence of a docstring by referencing the docstring 
 71     with the built-in Python C{__doc__} variable, or the docstring of a class 
 72     e.g. C{Cu19.__doc__}. This line also performs value tests on command-line 
 73     options so that errors are caught early. 
 74   
 75     To then access the command-line options/arguments use L{CLI.getArg()}, 
 76     L{CLI.getOpt()}, remembering that these functions will always return strings 
 77     (unless it is a boolean option, in which case it will be C{True}/C{False}) - 
 78     this is because the command-line can only pass strings as it has no 
 79     knowledge of Python types (and this module does no automatic conversion):: 
 80   
 81         # Example of a mandatory program argument 
 82         surveyID = int(cli.getArg('surveyID')) 
 83   
 84         # Example of a boolean flag option 
 85         appendDb = cli.getOpt('append') 
 86   
 87         # Example of an option with an argument 
 88         beginDate = utils.makeDateTime(cli.getOpt('begin')) 
 89   
 90         # Example of an option with an argument without default value 
 91         if cli.getOpt('rerun'): 
 92             filePathName = cli.getOpt('rerun') 
 93             contents = file(filePathName).readlines() 
 94   
 95     I{B{Note:}} 
 96        - Choose to add a program argument in favour of adding an option that 
 97          takes an argument, when that argument is mandatory in all use-cases of 
 98          the script. 
 99        - If an argument is not supplied for a L{CLI.Option}, it is assumed to be 
100          boolean with the default value of False. In designing boolean option 
101          flags please consider this, wording the option so that it turns 
102          something on, i.e. makes a condition true. 
103        - Default argument values must always be strings. This will ensure that 
104          calls to L{CLI.getArg()} and L{CLI.getOpt()} will always return strings 
105          for argument values (as opposed to boolean options), making the 
106          resulting type of a default value consistent with that of a 
107          user-supplied value. This also allows for consistent comparisons 
108          between a user value and a default value, as conversions may cause 
109          differences. 
110        - If a default value has no meaning for a certain program option that 
111          takes an argument, then do not supply a default value. If this option 
112          is not supplied then the argument value will be C{None}, which can be 
113          easily tested against. 
114        - Use the argument keyword when initialising an option 
115          (L{CLI.Option.__init__()}) to supply a hint as to what kind of data 
116          type the Python program is expecting, e.g. 'NUMBER' or 'FILE' etc., and 
117          supply the isValOK keyword with a function that takes a string argument 
118          and checks that supplied argument value is of the correct type and 
119          possibly value (e.g. a range check, file existance check). This check 
120          will be applied immediately upon the parsing of the command-line and so 
121          helps to spot errors early. 
122   
123     @group Errors & Exceptions: MalformedValueError, MissingArgumentError 
124   
125     @todo: Update design/documentation of classes in this module. 
126     @todo: Phase out use of getOptDef / getArgDef. This will result in a clear 
127            separation between classes that accept Python objects as arguments, 
128            and CLI objects return strings that are converted into Python objects 
129            by scripts. Would be nice if each argument could have a convertor 
130            as well as an isValOK method to perform the conversion on get. 
131     @todo: Tidy up indentation of option descriptions in help screen. 
132     @todo: Bring in an argument Type class heirachy (see trac ticket 42) to 
133            allow this module to perform automatic type conversions to Python 
134            types. 
135     @todo: Make consistent with optparse. 
136     @todo: Johann wants multiple examples on a help screen - this can be done 
137            but may lead to the development API becoming even more confusing! 
138            An alternative is to just initialise a CLI object with an additional 
139            text string containing whatever notes Johann wishes to add - only 
140            problem is it won't automatically be updated when arguments change 
141            which is the nice feature of the CLI module's help screen right now. 
142            Perhaps this is a good reason to leave these notes on a TWiki page? 
143   
144     @author: R.S. Collins 
145     @org:    WFAU, IfA, University of Edinburgh 
146  """ 
147   
148  from __future__      import division, print_function 
149  from future_builtins import map, zip 
150   
151  import getopt 
152  import mx.DateTime   
153  import readline      
154  from   operator import methodcaller 
155  import os 
156  import sys 
157  import time 
158   
159  import wsatools.ExternalProcess     as extp 
160  from   wsatools.SystemConstants import SystemConstants 
161  import wsatools.Utilities           as utils 
185   
189      """ An exception due to an incorrect number of supplied arguments. 
190      """ 
192          """ 
193          @param argList: List of argument values supplied. 
194          @type  argList: list(str) 
195   
196          """ 
197          super(MissingArgumentError, self).__init__( 
198            ' '.join(argList) + " - incorrect number of arguments") 
  199   
203      """ 
204      Container to store the specified set of arguments required by the program. 
205      It has separate lists for mandatory and optional argument definitions, and 
206      is a dictionary of argument example values referenced by name. Supplied 
207      argument values from getopt are passed through to processArgs() to provide 
208      a command-line argument look-up table for the program. 
209   
210      """ 
212          """ 
213          Initialise with the list of argument definitions for this program. 
214   
215          @param argList: List of argument definitions for this program. 
216          @type  argList: list(CLI.Argument) 
217   
218          """ 
219          self._manArgList = [arg for arg in argList if not arg.isOptional] 
220          self._optArgList = [arg for arg in argList if arg.isOptional] 
221          super(ArgGroup, self).__init__((arg.name, arg.example) 
222                                          for arg in self._optArgList) 
 223   
224       
225   
227          """ 
228          Create a new container by adding a new list of argument definitions to 
229          this container. 
230   
231          """ 
232          newArgGroup = ArgGroup() 
233          newArgGroup._manArgList = self._manArgList[:] 
234          newArgGroup._optArgList = self._optArgList[:] 
235          newArgGroup += argList 
236          return newArgGroup 
 237   
238       
239   
241          """ 
242          Increment the list of argument definitions with additional arguments. 
243   
244          """ 
245          self._manArgList += [arg for arg in argList if not arg.isOptional] 
246          self._optArgList += [arg for arg in argList if arg.isOptional] 
247          self.update(dict((arg.name, arg.example) for arg in self._optArgList)) 
248          return self 
 249   
250       
251   
253          """ 
254          Add a single argument. 
255   
256          @param arg: Argument to add to group of arguments. 
257          @type  arg: CLI.Argument 
258   
259          """ 
260          if arg.isOptional: 
261              self._optArgList.append(arg) 
262          else: 
263              self._manArgList.append(arg) 
264          self.update(dict((arg.name, arg.example) for arg in self._optArgList)) 
 265   
266       
267   
269          """ @return: The list of program arguments in order, mandatory first. 
270              @rtype:  str 
271          """ 
272          gap = ' ' 
273          argStr = gap 
274          if self._manArgList: 
275              argStr += gap.join(map(str, self._manArgList)) + gap 
276          argStr += gap.join('[%s]' % arg for arg in self._optArgList) 
277          return argStr 
 278   
279       
280   
282          """ @return: The example usage string for typical argument values. 
283              @rtype:  str 
284          """ 
285          examples = [] 
286          for arg in self._manArgList + self._optArgList: 
287              example = self.get(str(arg), arg.example) 
288              examples.append(repr(example) 
289                if type(example) is str and ' ' in example else str(example)) 
290          return ' '.join(examples) 
 291   
292       
293   
295          """ 
296          Processes the argument values supplied on the command-line, to return 
297          a look-up table to obtain argument values from their names. 
298   
299          @param argVals: List of argument values (e.g. from getopt) 
300          @type  argVals: list(str) 
301   
302          @return: Loop-up table for command-line argument values. 
303          @rtype:  dict{str : str} 
304   
305          """ 
306           
307          if len(argVals) < len(self._manArgList) or \ 
308             len(argVals) > len(self._manArgList) + len(self._optArgList): 
309              raise MissingArgumentError(argVals) 
310   
311           
312          numOptArgs = len(argVals) - len(self._manArgList) 
313   
314           
315          for arg, value in zip(self._manArgList + self._optArgList[:numOptArgs], 
316                                argVals): 
317              if not arg.isValOK(value): 
318                  raise MalformedValueError(str(arg), value, arg.example) 
319   
320           
321          if numOptArgs < len(self._optArgList): 
322              argVals += [self.get(arg.name) 
323                for arg in self._optArgList[numOptArgs - len(self._optArgList):]] 
324   
325           
326          return dict(zip( 
327            [arg.name for arg in self._manArgList + self._optArgList], argVals)) 
 328   
329       
330   
332          """ 
333          Removes specified argument from the list of supported arguments. 
334   
335          @param name: Argument name. 
336          @type  name: str 
337   
338          """ 
339          try: 
340              self._manArgList.remove(name) 
341          except ValueError: 
342              try: 
343                  self._optArgList.remove(name) 
344                  self.pop(name) 
345              except ValueError: 
346                  print("<Warning> Possible programming error: Cannot remove %s " 
347                        "from program's command-line argument list.\n" % name + 
348                        "Continuing...") 
  349   
353      """ 
354      Container to store the specified set of command-line options specified by 
355      the program. It is a dictionary of CLI.Option objects (option definitions) 
356      referenced by both their long and short (single character) names. Supplied 
357      options from getopt are passed through to processArgs() to provide a 
358      command-line option look-up table for the program. 
359   
360      """ 
362          """ 
363          Initialise with the list of option definitions for this program. 
364   
365          @param optList: List of option definitions for this program. 
366          @type  optList: list(CLI.Option) 
367   
368          """ 
369          super(OptGroup, self).__init__(self.__prepOptList(optList)) 
 370   
371       
372   
374          """ 
375          Create a new container by adding a new list of options definitions to 
376          this container. 
377   
378          """ 
379          newOptGroup = OptGroup().update(self) 
380          newOptGroup += optList 
381          return newOptGroup 
 382   
383       
384   
386          """ 
387          Increment the list of argument definitions with additional arguments. 
388   
389          """ 
390          self.update(dict(self.__prepOptList(optList))) 
391          return self 
 392   
393       
394   
396          """ Prepares the look-up table for both short and long name references. 
397          """ 
398          return [(opt.char, opt) for opt in optList] + \ 
399                 [(opt.longName, opt) for opt in optList] 
 400   
401       
402   
404          """ 
405          Add a single option. 
406   
407          @param option: Option to add to group of options. 
408          @type  option: CLI.Option 
409   
410          """ 
411          self.update(dict(self.__prepOptList([option]))) 
 412   
413       
414   
416          """ @return: The program option definitions for the --help mode. 
417              @rtype:  str 
418          """ 
419          return '\n'.join(option.getHelpLine() 
420                           for option in sorted(set(self.values()))) 
 421   
422       
423   
425          """ @return: Option long names in the format required by getopt. 
426              @rtype:  list(str) 
427          """ 
428           
429          return [key + '=' for key, option in self.items() 
430                  if option.argument and key == option.longName] + \ 
431                 [key for key, option in self.items() 
432                  if not option.argument and key == option.longName] 
 433   
434       
435   
437          """ @return: Option short names in the format required by getopt. 
438              @rtype:  str 
439          """ 
440           
441          optsWArgs = ':'.join(key for key, option in self.items() 
442                                    if option.argument and key == option.char) 
443          if optsWArgs: 
444               
445              optsWArgs = optsWArgs.lstrip(':') + ':' 
446   
447          return ''.join(key for key, option in self.items() 
448                              if not option.argument and key == option.char) \ 
449                 + optsWArgs 
 450   
451       
452   
454          """ 
455          Processes the options supplied on the command-line, to return a look-up 
456          table to obtain option values from their long names. 
457   
458          @param opts: List of options in format (name, value), where name maybe 
459                       short or long name. (e.g. from getopt) 
460          @type  opts: list(tuple(str, str)) 
461   
462          @return: Loop-up table for command-line option values. 
463          @rtype:  dict{str : str} 
464   
465          """ 
466           
467          procOpts = dict([(key, option.default) for key, option in self.items() 
468                                                 if key == option.longName]) 
469           
470          for supName, value in opts: 
471              nameStr = supName.lstrip('-') 
472               
473              longName = str(self.get(nameStr, nameStr)) 
474               
475              if not value: 
476                  procOpts[longName] = True 
477              else: 
478                  procOpts[longName] = value.strip() 
479   
480               
481              if not self.get(longName).isValOK(value) \ 
482                or self.get(longName).argument and not value: 
483                  raise MalformedValueError(longName, value, 
484                                            self.get(longName).default) 
485          return procOpts 
 486   
487       
488   
490          """ 
491          Removes specified option from the list of supported options. 
492   
493          @param name: Option long name. 
494          @type  name: str 
495   
496          """ 
497          try: 
498              option = self.pop(name) 
499          except KeyError: 
500              print("<Warning> Possible programming error: Cannot remove %s " 
501                    "from program's command-line option list.\n" % name + 
502                    "Continuing...") 
503          else: 
504              try: 
505                  self.pop(option.char) 
506              except KeyError: 
507                  pass 
  508   
509   
510   
511 -class CLI(object): 
 512      """ Command-line interface tool - manages command line arguments. """ 
513   
515          """ A program argument definition. """ 
516   
517 -        def __init__(self, name, example, isOptional=False, 
518                             isValOK=lambda _x: True): 
 519              """ 
520              Define this argument. 
521   
522              @param name:       Single word name of argument for look-up table, 
523                                 example usage help string. 
524              @type  name:       str 
525              @param example:    Example value for argument for help string, also 
526                                 default value if argument is optional. 
527              @type  example:    str 
528              @param isOptional: If True, then this argument does not need to 
529                                 be supplied and the example becomes the default. 
530              @type  isOptional: bool 
531              @param isValOK:    A function to test if the supplied value is 
532                                 formatted correctly, of the form "bool f(x)". 
533              @type  isValOK:    function 
534   
535              """ 
536              self.name = name 
537              self.example = example 
538              self.isOptional = isOptional 
539              self.isValOK = isValOK 
 540   
542              """ Compare on name (for removing etc.). """ 
543              return cmp(str(self), str(other)) 
 544   
546              """ @return: Argument's name. 
547                  @rtype:  str 
548              """ 
549              return self.name 
  550   
551       
552   
554          """ A program option definition. """ 
555   
556 -        def __init__(self, char, longName, desc, argument=None, default=None, 
557                             isValOK=lambda _x: True): 
 558              """ 
559              Define this option. 
560   
561              @param char:     The single character short name for the option, 
562                               will be preceded by '-' as command-line option. 
563              @type  char:     str 
564              @param longName: The long name of the option, single word, will be 
565                               preceded by '--' as command-line option. 
566              @type  longName: str 
567              @param desc:     Full description of option for the --help mode. 
568              @type  desc:     str 
569              @param argument: If option takes an argument, supply a hint here 
570                               e.g. 'NAME', for the --help mode. 
571              @type  argument: str 
572              @param default:  The default value for the option's argument. 
573              @type  default:  str 
574              @param isValOK:  A function to test if the supplied value is 
575                               formatted correctly, of the form "bool f(x)". 
576              @type  isValOK:  function 
577   
578              """ 
579              self.argument = argument 
580              self.char = char 
581              self.default = default or (None if argument else False) 
582              self.desc = desc 
583              self.isValOK = isValOK 
584              self.longName = longName 
 585   
587              """ Compare on option short name (for sorting etc.). """ 
588              return cmp(self.char, other.char) 
 589   
591              """ @return: Option's long name. 
592                  @rtype:  str 
593              """ 
594              return self.longName 
 595   
597              """ @return: Option details for the --help mode. 
598                  @rtype:  str 
599              """ 
600              if self.argument: 
601                  argStr = '=' + self.argument 
602              else: 
603                  argStr = '' 
604              if self.default: 
605                  if type(self.default) is str and ' ' in self.default: 
606                      defaultStr = ' (default: %r)' % self.default 
607                  else: 
608                      defaultStr = ' (default: %s)' % self.default 
609              else: 
610                  defaultStr = '' 
611              keyStr = '    -' + self.char + ', --' + self.longName + argStr 
612              gapStr = ' ' + ' ' * (27 - len(keyStr)) 
613              utils.WordWrapper.indent = 28 
614              return \ 
615                keyStr + gapStr + utils.WordWrapper.wrap(self.desc + defaultStr) 
  616   
617       
618   
619       
620      progArgs = ArgGroup() 
621       
622      progOpts = OptGroup([Option('h', 'help', 'display this help information')]) 
623   
624       
625   
626 -    def __init__(self, progName='', progVer='', progDesc='', checkSVN=True): 
 627          """ 
628          Parses command-line arguments. 
629   
630          @param progName: Name of the programme or just a class type. 
631          @type  progName: str or class 
632          @param progVer:  Version of the programme. (e.g. $Revision: 9505 $) 
633          @type  progVer:  str 
634          @param progDesc: Description of what the programme does, not necessary 
635                           if a class type was supplied as progName. 
636          @type  progDesc: str 
637          @param checkSVN: Check that SVN working copy is up-to-date. 
638          @type  checkSVN: bool 
639   
640          """ 
641          if not isinstance(progName, str): 
642              progDesc = progDesc or progName.__doc__ 
643              progName = progName.__name__ 
644   
645          self._progCmd = os.path.split(sys.argv[0])[-1] 
646           
647          self._progDesc = ' '.join(progDesc.split('.', 1)[0].split()) 
648          self._progName = progName or self._progCmd 
649          self._progVer = progVer.replace('$Revision', 'Version').rstrip('$') 
650          self._supArgs = None 
651          self._supOpts = None 
652          try: 
653              opts, args = getopt.getopt(sys.argv[1:], 
654                                         CLI.progOpts.getShortOpts(), 
655                                         CLI.progOpts.getLongNames()) 
656   
657              self._supOpts = CLI.progOpts.processOpts(opts) 
658   
659              if checkSVN and not self._supOpts['help']: 
660                  self._updateProgVer() 
661   
662              if self._supOpts['help']: 
663                  print(self.help()) 
664                  raise SystemExit 
665   
666              self._supArgs = CLI.progArgs.processArgs(args) 
667   
668          except (getopt.GetoptError, MissingArgumentError, 
669                  MalformedValueError) as error: 
670              msg = self._progCmd + ": %s\n\n" % error 
671              msg += self.usage() + "\n\n" 
672              msg += "Try `" + self._progCmd + " --help' for more information.\n" 
673              raise SystemExit(msg) 
 674   
675       
676   
678          """ Updates self._progVer to latest SVN version if possible. 
679          """ 
680          isOutdated = False 
681          modString = '' 
682          try: 
683              cmd = "svn status -u -q " + SystemConstants.srcPath 
684   
685               
686              for line in extp.run(cmd, stdIn='p', isVerbose=False): 
687   
688                   
689                  if line.startswith('M') and not line.endswith(".csv"): 
690                      modString = (" (Modified)" if self._progCmd in line else 
691                                   modString or " (modified)") 
692   
693                  if line and '*' in line.split(): 
694                      isOutdated = True 
695   
696                   
697                  if line.startswith("Status"): 
698                      progVer = line.strip().split()[-1] 
699                      verPrefix = "Version: " 
700                      if int(progVer) > int(self._progVer.replace(verPrefix, '')): 
701                          self._progVer = verPrefix + progVer 
702   
703          except Exception as error: 
704              print("<Warning> %s" % error) 
705   
706           
707          if isOutdated: 
708              modString = " (OUTDATED)" + modString 
709              if os.getenv("ALLOW_OUTDATED") != os.getenv("WSA_SANDBOX"): 
710                  try: 
711                      print("Your working copy is outdated. Press Ctrl-C now " 
712                            "and update your working copy before proceeding, " 
713                            "otherwise script will continue in...") 
714   
715                      step = 4   
716                      for i in reversed(xrange(0, step * 5, step)): 
717                          print("%s seconds" % (i + step)) 
718                          time.sleep(step) 
719   
720                  except KeyboardInterrupt: 
721                      exit() 
722   
723          self._progVer += modString 
 724   
725       
726   
727      @staticmethod 
729          """ 
730          A command-line test to ensure a file-exists. 
731   
732          @param filePathName: Full path to file. 
733          @type  filePathName: str 
734   
735          @return: True, if file exists, otherwise the program quits with an 
736                   error message. 
737          @rtype:  bool 
738   
739          """ 
740          try: 
741              assert os.path.exists(filePathName), \ 
742                     "File %s does not exist!" % filePathName 
743          except AssertionError as error: 
744              print(str(error)) 
745              raise SystemExit 
746          else: 
747              return True 
 748   
749       
750   
751      @staticmethod 
753          """ Prompt operator to check that they are using the server with the 
754              most free memory. 
755          """ 
756          import platform 
757          if "32bit" in platform.architecture() and not CLI.isConfirmed( 
758            "This script must be run on the 64-bit curation server with the " 
759            "most available system memory"): 
760              exit() 
 761   
762       
763   
765          """ 
766          @param name: Argument name. 
767          @type  name: str 
768   
769          @return: The argument value supplied via the command line. 
770          @rtype:  str or bool 
771   
772          """ 
773          return self._supArgs.get(name) 
 774   
775       
776   
777      @staticmethod 
779          """ 
780          @param name: Argument name. 
781          @type  name: str 
782   
783          @return: The example value for this argument. 
784          @rtype:  str or bool 
785   
786          """ 
787          return CLI.progArgs.get(name) 
 788   
789       
790   
792          """ 
793          @param name: Option long name. 
794          @type  name: str 
795   
796          @return: The option value supplied via the command line. 
797          @rtype:  str or bool 
798   
799          """ 
800          return self._supOpts.get(name) 
 801   
802       
803   
804      @staticmethod 
806          """ 
807          @param name: Option long name. 
808          @type  name: str 
809   
810          @return: The default value for this option. 
811          @rtype:  str or bool 
812   
813          """ 
814          return CLI.progOpts.get(name, CLI.Option('', '', '')).default 
 815   
816       
817   
819          """ 
820          Returns a string giving the command-line programme's name, version 
821          and parameters. 
822   
823          @param progName: Optional name of sub-programme being invoked by 
824                           this programme. 
825          @type  progName: str 
826   
827          @return: Programme name, current version, and invoked parameters. 
828          @rtype:  str 
829   
830          """ 
831          msg = self._progName + ' ' + self._progVer 
832   
833          if not self._supOpts['help']: 
834              utils.WordWrapper.indent = 26 
835              gap = utils.WordWrapper.indent * ' ' 
836              args = [] 
837              for arg in sys.argv[1:]: 
838                  if ' ' not in arg: 
839                      args.append(arg) 
840                  else: 
841                      args.append(repr(arg)) 
842              msg += '\n' + gap + \ 
843                utils.WordWrapper.wrap("Parameters: " + ' '.join(args)) 
844   
845              if progName: 
846                  msg = progName + " invoked from:\n" + gap + msg 
847   
848          return msg 
 849   
850       
851   
853          """ @return: A help string giving option definitions and command usage. 
854              @rtype:  str 
855          """ 
856          helpStr = self.getProgDetails() + '\n' 
857          helpStr += utils.WordWrapper.wrap(self._progDesc) + '\n\n' 
858          helpStr += self.usage() + '\n\n' 
859          helpStr += 'EXAMPLE:\n' 
860          helpStr += '    ' + self._progCmd + ' ' + \ 
861                     CLI.progArgs.getExampleStr() + '\n\n' 
862          helpStr += 'OPTIONS:\n' 
863          helpStr += CLI.progOpts.getHelpStr() + '\n' 
864          return helpStr 
 865   
866       
867   
868      @staticmethod 
870          """ 
871          Standard user command-line confirmation request. 
872   
873          @param message: Message to give to user explaining why confirmation is 
874                          required. 
875          @type  message: str 
876   
877          @return: True, if user gives confirmation. 
878          @rtype:  bool 
879   
880          """ 
881          message = "Warning: " + message.strip() 
882          if not message.endswith('.'): 
883              message += '.' 
884   
885           
886          readline.get_line_buffer() 
887          answer = \ 
888            raw_input(message + "\nAre you sure you wish to proceed? (Y/N) > ") 
889   
890          return answer.upper().startswith('Y') 
 891   
892       
893   
894      @staticmethod 
896          """ 
897          Checks to see if the supplied date string is in the correct format, 
898          either YYYY-MM-DD or YYYYMMDD or a semester date like 05A. 
899   
900          @param dateStr: Date string to check formatting. 
901          @type  dateStr: str 
902   
903          @return: True if the date string is correctly formatted. 
904          @rtype:  bool 
905   
906          """ 
907          dateStr = dateStr.strip().strip('"').strip("'") 
908          if '-' in dateStr:        
909              dateParts = dateStr.split('-') 
910              if len(dateParts) is not 3 or len(dateParts[0]) is not 4 or \ 
911                 len(dateParts[1]) is not 2 or len(dateParts[2]) is not 2: 
912                  return False 
913          elif len(dateStr) is 8:   
914              dateParts = (dateStr[:4], dateStr[4:6], dateStr[6:8]) 
915          elif len(dateStr) < 8:   
916              allSemesters = utils.unpackList(map(methodcaller("keys"), 
917                SystemConstants.obsCal.getAll("semDates"))) 
918   
919              return dateStr in (sem[2:] if sem.startswith("20") else sem 
920                                 for sem in allSemesters) 
921          else: 
922              return False 
923   
924          dateFormatOK = (all(part.isdigit() for part in dateParts) and 
925                          int(dateParts[0]) > 1900 and int(dateParts[0]) < 3000 
926                          and int(dateParts[1]) >= 1 and int(dateParts[1]) <= 12 
927                          and int(dateParts[2]) >= 1 and int(dateParts[2]) <= 31) 
928   
929           
930          if dateFormatOK: 
931              try: 
932                  utils.makeDateTime(dateStr) 
933              except mx.DateTime.RangeError as error: 
934                  print("<ERROR> %s" % error) 
935                  return False 
936          return dateFormatOK 
 937   
938       
939   
941          """ 
942          Resets an optional argument to the given value. 
943   
944          @param argName: Name of optional argument to reset. 
945          @type  argName: str 
946          @param value:   New value for the optional argument. 
947          @type  value:   str 
948   
949          @return: The original command-line supplied value. 
950          @rtype:  str 
951   
952          """ 
953          cliValue = self.getArg(argName) 
954          self._supArgs[argName] = value 
955          return cliValue 
 956   
957       
958   
960          """ @return: A command usage definition string for this program. 
961              @rtype:  str 
962          """ 
963          return 'usage: ' + self._progCmd + ' [options]' + \ 
964                 CLI.progArgs.getArgStr() 
  965   
966   
967