Improve error reporting in smart Add code to check proper command line arguments for various smart commands. Exit with error if erroneous/additional arguments are given in the command line. Upstream-Status: Pending Signed-off-by: Bogdan Marinescu diff --git a/smart/commands/channel.py b/smart/commands/channel.py index aa76f91..63fbb35 100644 --- a/smart/commands/channel.py +++ b/smart/commands/channel.py @@ -157,7 +157,17 @@ def main(ctrl, opts): opts.show is None and opts.yaml is None): iface.warning(_("Can't edit channels information.")) raise Error, _("Configuration is in readonly mode.") - + + # Argument check + opts.check_args_of_option("set", -1) + opts.check_args_of_option("remove", -1) + opts.check_args_of_option("edit", 0) + opts.check_args_of_option("enable", -1) + opts.check_args_of_option("disable", -1) + opts.ensure_action("channel", ["add", "set", "remove", "remove_all", + "list", "show", "yaml", "enable", "disable"]) + opts.check_remaining_args() + if opts.add is not None: if not opts.add and opts.args == ["-"]: newchannels = [] diff --git a/smart/commands/check.py b/smart/commands/check.py index b08608a..506e852 100644 --- a/smart/commands/check.py +++ b/smart/commands/check.py @@ -72,6 +72,9 @@ def parse_options(argv): def main(ctrl, opts, reloadchannels=True): + # Argument check + opts.check_args_of_option("channels", 1) + if sysconf.get("auto-update"): from smart.commands import update updateopts = update.parse_options([]) diff --git a/smart/commands/config.py b/smart/commands/config.py index dd50dee..4fe4366 100644 --- a/smart/commands/config.py +++ b/smart/commands/config.py @@ -80,6 +80,12 @@ def main(ctrl, opts): globals["false"] = False globals["no"] = False + # Check arguments + opts.check_args_of_option("set", -1) + opts.check_args_of_option("remove", -1) + opts.ensure_action("config", ["set", "show", "yaml", "remove"]) + opts.check_remaining_args() + if opts.set: for opt in opts.set: m = SETRE.match(opt) diff --git a/smart/commands/download.py b/smart/commands/download.py index 6837993..b853c61 100644 --- a/smart/commands/download.py +++ b/smart/commands/download.py @@ -81,6 +81,14 @@ def parse_options(argv): def main(ctrl, opts): + # Argument check + opts.check_args_of_option("target", 1) + opts.check_args_of_option("output", 1) + opts.check_args_of_option("from_urls", -1) + opts.check_args_of_option("from_metalink", -1) + if not opts.args and not opts.from_metalink and not opts.from_urls: + raise Error, _("no package(s) given") + packages = [] if opts.args: if sysconf.get("auto-update"): diff --git a/smart/commands/info.py b/smart/commands/info.py index 12f74f0..59fbe98 100644 --- a/smart/commands/info.py +++ b/smart/commands/info.py @@ -58,6 +58,10 @@ def parse_options(argv): def main(ctrl, opts, reloadchannels=True): + # Argument check + if not opts.args: + raise Error, _("No package(s) given") + if sysconf.get("auto-update"): from smart.commands import update updateopts = update.parse_options([]) diff --git a/smart/commands/install.py b/smart/commands/install.py index 8a45954..590222c 100644 --- a/smart/commands/install.py +++ b/smart/commands/install.py @@ -76,6 +76,10 @@ def parse_options(argv): def main(ctrl, opts): + # Argument check + if not opts.args: + raise Error, _("no package(s) given") + if opts.explain: sysconf.set("explain-changesets", True, soft=True) diff --git a/smart/commands/reinstall.py b/smart/commands/reinstall.py index e59d896..32da3e6 100644 --- a/smart/commands/reinstall.py +++ b/smart/commands/reinstall.py @@ -68,7 +68,11 @@ def parse_options(argv): return opts def main(ctrl, opts): - + + # Argument check + if not opts.args: + raise Error, _("no package(s) given") + if opts.explain: sysconf.set("explain-changesets", True, soft=True) diff --git a/smart/commands/remove.py b/smart/commands/remove.py index b4823a6..acd3bbd 100644 --- a/smart/commands/remove.py +++ b/smart/commands/remove.py @@ -74,6 +74,10 @@ def parse_options(argv): def main(ctrl, opts): + # Argument check + if not opts.args: + raise Error, _("no package(s) given") + if opts.explain: sysconf.set("explain-changesets", True, soft=True) diff --git a/smart/commands/search.py b/smart/commands/search.py index 0d0b573..44806b8 100644 --- a/smart/commands/search.py +++ b/smart/commands/search.py @@ -44,6 +44,8 @@ def option_parser(): def parse_options(argv): opts = query.parse_options(argv, usage=USAGE, \ description=DESCRIPTION, examples=EXAMPLES) + if not argv: + raise Error, _("Search expression not specified") opts.name = opts.args opts.summary = opts.args opts.description = opts.args diff --git a/smart/commands/upgrade.py b/smart/commands/upgrade.py index ec86290..7e290d8 100644 --- a/smart/commands/upgrade.py +++ b/smart/commands/upgrade.py @@ -91,6 +91,9 @@ def parse_options(argv): def main(ctrl, opts): + # Argument check + opts.check_args_of_option("flag", 1) + if opts.explain: sysconf.set("explain-changesets", True, soft=True) diff --git a/smart/util/optparse.py b/smart/util/optparse.py index 4a3d3a8..279b0bf 100644 --- a/smart/util/optparse.py +++ b/smart/util/optparse.py @@ -70,6 +70,8 @@ import sys, os import types import textwrap from gettext import gettext as _ +from smart import Error +import re def _repr(self): return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) @@ -708,6 +710,12 @@ class Option: self.action, self.dest, opt, value, values, parser) def take_action(self, action, dest, opt, value, values, parser): + # Keep all the options in the command line in the '_given_opts' array + # This will be used later to validate the command line + given_opts = getattr(parser.values, "_given_opts", []) + user_opt = re.sub(r"^\-*", "", opt).replace("-", "_") + given_opts.append(user_opt) + setattr(parser.values, "_given_opts", given_opts) if action == "store": setattr(values, dest, value) elif action == "store_const": @@ -819,6 +827,54 @@ class Values: setattr(self, attr, value) return getattr(self, attr) + # Check if the given option has the specified number of arguments + # Raise an error if the option has an invalid number of arguments + # A negative number for 'nargs' means "at least |nargs| arguments are needed" + def check_args_of_option(self, opt, nargs, err=None): + given_opts = getattr(self, "_given_opts", []) + if not opt in given_opts: + return + values = getattr(self, opt, []) + if type(values) != type([]): + return + if nargs < 0: + nargs = -nargs + if len(values) >= nargs: + return + if not err: + if nargs == 1: + err = _("Option '%s' requires at least one argument") % opt + else: + err = _("Option '%s' requires at least %d arguments") % (opt, nargs) + raise Error, err + elif nargs == 0: + if len( values ) == 0: + return + raise Error, err + else: + if len(values) == nargs: + return + if not err: + if nargs == 1: + err = _("Option '%s' requires one argument") % opt + else: + err = _("Option '%s' requires %d arguments") % (opt, nargs) + raise Error, err + + # Check that at least one of the options in 'actlist' was given as an argument + # to the command 'cmdname' + def ensure_action(self, cmdname, actlist): + given_opts = getattr(self, "_given_opts", []) + for action in actlist: + if action in given_opts: + return + raise Error, _("No action specified for command '%s'") % cmdname + + # Check if there are any other arguments left after parsing the command line and + # raise an error if such arguments are found + def check_remaining_args(self): + if self.args: + raise Error, _("Invalid argument(s) '%s'" % str(self.args)) class OptionContainer: