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