diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | clcommands.py | 12 | ||||
-rw-r--r-- | configuration.py | 11 | ||||
-rw-r--r-- | optik_ext.py | 41 | ||||
-rw-r--r-- | testlib.py | 3 |
5 files changed, 53 insertions, 19 deletions
@@ -2,7 +2,10 @@ ChangeLog for logilab.common ============================ -- - + * new "date" otion type in optik_ext + * new clcommands module to handle commands based command line tool + (based on the configuration module) + * new AttrObject in testlib to create objects in test with arbitrary attributes * add pytest to run project's tests and get rid of all runtests.py * add pytest option to enable design-by-contract using aspects diff --git a/clcommands.py b/clcommands.py index c30e0a6..54bd6ff 100644 --- a/clcommands.py +++ b/clcommands.py @@ -40,7 +40,9 @@ class Command(Configuration): """base class for command line commands""" arguments = '' name = '' - + # max/min args, None meaning unspecified + min_args = None + max_args = None def __init__(self, __doc__=None, version=None): if __doc__: usage = __doc__ % (self.name, self.arguments, @@ -48,6 +50,13 @@ class Command(Configuration): else: usage = self.__doc__.replace(' ', '') Configuration.__init__(self, usage=usage, version=version) + + def check_args(self, args): + """check command's arguments are provided""" + if self.min_args is not None and len(args) < self.min_args: + raise BadCommandUsage('missing argument') + if self.max_args is not None and len(args) > self.max_args: + raise BadCommandUsage('too many arguments') def run(self, args): """run the command with its specific arguments""" @@ -96,6 +105,7 @@ def cmd_run(cmdname, *args): except KeyError: raise BadCommandUsage('no %s command' % cmdname) args = command.load_command_line_configuration(args) + command.check_args(args) try: command.run(args) except KeyboardInterrupt: diff --git a/configuration.py b/configuration.py index 76b1384..f513a94 100644 --- a/configuration.py +++ b/configuration.py @@ -88,7 +88,7 @@ from ConfigParser import ConfigParser, NoOptionError, NoSectionError from logilab.common.textutils import normalize_text, unquote from logilab.common.optik_ext import OptionParser, OptionGroup, Values, \ - OptionValueError, OptionError, HelpFormatter, generate_manpage, \ + OptionValueError, OptionError, HelpFormatter, generate_manpage, check_date, \ check_yn, check_csv, check_file, check_color, check_named, check_password,\ NO_DEFAULT, OPTPARSE_FORMAT_DEFAULT @@ -138,11 +138,15 @@ def file_validator(opt_dict, name, value): return check_file(None, name, value) def color_validator(opt_dict, name, value): - """validate and return a filepath for option of type 'file'""" + """validate and return a valid color for option of type 'color'""" return check_color(None, name, value) def password_validator(opt_dict, name, value): - """validate and return a filepath for option of type 'file'""" + """validate and return a string for option of type 'password'""" + return check_password(None, name, value) + +def date_validator(opt_dict, name, value): + """validate and return a mx DateTime object for option of type 'date'""" return check_password(None, name, value) @@ -158,6 +162,7 @@ VALIDATORS = {'string' : unquote, 'bool': yn_validator, 'named': named_validator, 'password': password_validator, + 'date': date_validator, 'choice': choice_validator, 'multiple_choice': multiple_choice_validator, } diff --git a/optik_ext.py b/optik_ext.py index fdaf9fd..17f0513 100644 --- a/optik_ext.py +++ b/optik_ext.py @@ -1,7 +1,7 @@ -# This program is free software; you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation; either version 2 of the License, or (at your option) any later -# version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS @@ -50,6 +50,8 @@ except ImportError: except: NO_DEFAULT = [] +from mx import DateTime + OPTPARSE_FORMAT_DEFAULT = sys.version_info >= (2, 4) from logilab.common.textutils import get_csv @@ -121,6 +123,16 @@ def check_file(option, opt, value): msg = "option %s: file %r does not exist" raise OptionValueError(msg % (opt, value)) +def check_date(option, opt, value): + """check a file value + return the filepath + """ + try: + return DateTime.strptime(value, "%Y/%m/%d") + except DateTime.Error : + raise OptionValueError( + "expected format of %s is yyyy/mm/dd" % opt) + def check_color(option, opt, value): """check a color value and returns it /!\ does *not* check color labels (like 'red', 'green'), only @@ -142,17 +154,18 @@ import types class Option(BaseOption): """override optik.Option to add some new option types """ - TYPES = BaseOption.TYPES + ("regexp", "csv", 'yn', 'named', "password", - "multiple_choice", "file", "font", "color") + TYPES = BaseOption.TYPES + ('regexp', 'csv', 'yn', 'date', 'named', 'password', + 'multiple_choice', 'file', 'font', 'color') TYPE_CHECKER = copy(BaseOption.TYPE_CHECKER) - TYPE_CHECKER["regexp"] = check_regexp - TYPE_CHECKER["csv"] = check_csv - TYPE_CHECKER["yn"] = check_yn - TYPE_CHECKER["named"] = check_named - TYPE_CHECKER["multiple_choice"] = check_csv - TYPE_CHECKER["file"] = check_file - TYPE_CHECKER["color"] = check_color - TYPE_CHECKER["password"] = check_password + TYPE_CHECKER['regexp'] = check_regexp + TYPE_CHECKER['csv'] = check_csv + TYPE_CHECKER['yn'] = check_yn + TYPE_CHECKER['named'] = check_named + TYPE_CHECKER['multiple_choice'] = check_csv + TYPE_CHECKER['file'] = check_file + TYPE_CHECKER['color'] = check_color + TYPE_CHECKER['password'] = check_password + TYPE_CHECKER['date'] = check_date def _check_choice(self): """FIXME: need to override this due to optik misdesign""" @@ -1063,3 +1063,6 @@ def enable_dbc(*args): return True +class AttrObject: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) |