Michael Pasternak has uploaded a new change for review. Change subject: cli: implement visualization effects ......................................................................
cli: implement visualization effects Change-Id: I074e6d03f46f42261f96a71024fe28f871bd0c65 Signed-off-by: Michael pasternak <[email protected]> --- M src/cli/command/command.py M src/cli/context.py M src/cli/error.py M src/cli/messages.py A src/cli/warning.py M src/ovirtcli/shell/engineshell.py A src/ovirtcli/utils/colorhelper.py 7 files changed, 182 insertions(+), 20 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine-cli refs/changes/47/20147/1 diff --git a/src/cli/command/command.py b/src/cli/command/command.py index f964ad2..b549e45 100644 --- a/src/cli/command/command.py +++ b/src/cli/command/command.py @@ -20,6 +20,7 @@ from ovirtcli.format.help import Help from ovirtcli.utils.methodhelper import MethodHelper from ovirtcli.utils.multivaluedict import MultiValueDict +from cli.warning import GenericWarning class Command(object): @@ -102,8 +103,8 @@ def execute(self, context): """Override this method in a subclass.""" - def warning(self, message): - self.context.terminal.stdout.write('warning: ' + message + '\n') + def warning(self, message, **kwargs): + raise GenericWarning(message, **kwargs) def write(self, message): self.context.terminal.stdout.write(message + '\n') diff --git a/src/cli/context.py b/src/cli/context.py index 3085b9b..33c3e5e 100644 --- a/src/cli/context.py +++ b/src/cli/context.py @@ -34,6 +34,7 @@ from cli.platform import Terminal from cli import platform from cli.executionmode import ExecutionMode +from ovirtcli.utils.colorhelper import ColorHelper class ExecutionContext(object): @@ -202,6 +203,15 @@ ] err_str = str(err) + err_str = ( + "\n" if not ( + err_str.startswith("\n") + or + err_str.startswith("\r\n") + ) + else "" + ) + err_str + for err_pattern in err_patterns: if err_str.find(err_pattern) <> -1: err_str = err_str.replace( @@ -221,25 +231,73 @@ elif isinstance(e, CommandError) or isinstance(e, RequestError) \ or isinstance(e, AmbiguousQueryError): self.status = getattr(e, 'status', self.COMMAND_ERROR) - sys.stderr.write('\nerror: %s\n\n' % self.__error_to_string(e)) + self.__pint_error(e) if hasattr(e, 'help'): sys.stderr.write('%s\n' % e.help) elif isinstance(e, SyntaxError): self.status = getattr(e, 'status', self.SYNTAX_ERROR) - sys.stderr.write('\nerror: %s\n\n' % self.__error_to_string(e)) + self.__pint_error(e) if hasattr(e, 'help'): sys.stderr.write('%s\n' % e.help) elif isinstance(e, ConnectionError): self.status = getattr(e, 'status', self.COMMUNICATION_ERROR) - sys.stderr.write('\nerror: %s\n\n' % self.__error_to_string(e)) + self.__pint_error(e) if hasattr(e, 'help'): sys.stderr.write('%s\n' % e.help) + elif isinstance(e, Warning): + self.__pint_warning(e) else: self.status = self.UNKNOWN_ERROR if self.settings['cli:debug']: sys.stderr.write(traceback.format_exc()) else: - sys.stderr.write('\nunknown error: %s\n\n' % self.__error_to_string(e)) + self.__pint_error(e, header='UNKNOWN ERROR') + + def __pint_error(self, e, header='ERROR'): + """ + prints error to stderr + + @param e: exception + @param header: the error header + """ + sys.stderr.write( + ColorHelper.color( + '\n++++++++++++++++++ %s ++++++++++++++++++\n%s\n\n' + % + ( + header, + self.__error_to_string(e) + ), + ColorHelper.RED if self.mode != ExecutionMode.SCRIPT + and self.interactive + else None + ) + ) + + def __pint_warning(self, e): + """ + prints warning to stdout + + @param e: exception + """ + sys.stdout.write( + ColorHelper.color( + '\n+++++++++++++++++ WARNING +++++++++++++++++\n%s\n\n' + % + self.__error_to_string(e), + ColorHelper.YELLOW if self.mode != ExecutionMode.SCRIPT + and self.interactive + else None + ) + ) + + def _pint_text(self, text): + """ + prints text to stdout + + @param text: text + """ + sys.stdout.write("\n" + text + "\n") def _read_command(self): """Parse input until we can parse at least one full command, and @@ -265,7 +323,7 @@ return command += line try: - parsed = self.parser.parse(command) + parsed = self.parser.parse(command) # @UnusedVariable except EOFError: prompt = self.settings['cli:ps2'] continue diff --git a/src/cli/error.py b/src/cli/error.py index 0529c68..27f4740 100644 --- a/src/cli/error.py +++ b/src/cli/error.py @@ -38,5 +38,5 @@ class CommandError(Error): """Illegal command.""" -class SyntaxError(Error): +class SyntaxError(Error): # @ReservedAssignment """Illegal syntax.""" diff --git a/src/cli/messages.py b/src/cli/messages.py index 55ec281..8da51de 100644 --- a/src/cli/messages.py +++ b/src/cli/messages.py @@ -41,7 +41,7 @@ CANNOT_CONNECT_TO_BACKEND = 'could NOT reach %s manager\n.' CANNOT_CONSTRUCT_COLLECTION_MEMBER_VIEW = 'cannot construct collection/member view, checked variants are:\n%s.\n' INVALID_DISPLAY_PROTOCOL = 'display protocol "%s" is not supported.' - INVALID_COMMAND = 'command "%s" not valid or not available while not connected.' + INVALID_COMMAND = 'command "%s" is not valid or not available while not connected.' INVALID_ENV_MODE_FOR_CONSOLE = 'not running in a GUI, cannot start a %s viewer.' INVALID_OPTION = 'option %s cannot be empty.' INVALID_COLLECTION_BASED_OPTION_SYNTAX = 'invalid syntax at "--%s", see help on collection based arguments for more details.' diff --git a/src/cli/warning.py b/src/cli/warning.py new file mode 100644 index 0000000..bb0652b --- /dev/null +++ b/src/cli/warning.py @@ -0,0 +1,35 @@ +# +# Copyright (c) 2010 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +from cli import compat + + +class Warning(Exception): # @ReservedAssignment + """Base class for python-cli warnings.""" + + def __init__(self, message=None, **kwargs): + if message is None: message = self.__doc__ + message = message + '\n' if not message.endswith('\n') \ + else message + compat.super(Warning, self).__init__(message) + + for key in kwargs: + setattr(self, key, kwargs[key]) + +class GenericWarning(Warning): + """Generic warning.""" + diff --git a/src/ovirtcli/shell/engineshell.py b/src/ovirtcli/shell/engineshell.py index ac6323e..e575961 100644 --- a/src/ovirtcli/shell/engineshell.py +++ b/src/ovirtcli/shell/engineshell.py @@ -47,6 +47,8 @@ from cli.messages import Messages from urlparse import urlparse +from ovirtcli.utils.colorhelper import ColorHelper +from cli.executionmode import ExecutionMode class EngineShell(cmd.Cmd, ConnectCmdShell, ActionCmdShell, \ @@ -156,21 +158,55 @@ elif mode == PromptMode.Disconnected or mode == PromptMode.Default: if not self.__org_prompt and self.prompt != "(Cmd) ": self.__org_prompt = self.prompt - self.prompt = self.context.settings.get('ovirt-shell:ps1.disconnected') + self.prompt = self.__get_disconnected_prompt() elif mode == PromptMode.Connected: self.prompt = self.__get_connected_prompt() + + def __get_disconnected_prompt(self): + dprompt = self.context.settings.get('ovirt-shell:ps1.disconnected') + if self.context.mode != ExecutionMode.SCRIPT \ + and self.context.interactive: + dprompt = dprompt.replace( + "disconnected", + ColorHelper.color( + "disconnected", + color_=ColorHelper.RED + ) + ) + return dprompt def __get_connected_prompt(self): if self.context.settings.get('ovirt-shell:extended_prompt'): url = self.context.settings.get('ovirt-shell:url') url_obj = urlparse(url) if url_obj and hasattr(url_obj, 'hostname'): - return self.context.settings.get( - 'ovirt-shell:ps3.connected' - ) % { - 'host':url_obj.hostname - } - return self.context.settings.get('ovirt-shell:ps2.connected') + cprompt = self.context.settings.get( + 'ovirt-shell:ps3.connected' + ) % { + 'host':url_obj.hostname + } + if self.context.mode != ExecutionMode.SCRIPT \ + and self.context.interactive: + cprompt = cprompt.replace( + "connected@" + url_obj.hostname, + ColorHelper.color( + 'connected@' + url_obj.hostname, + color_=ColorHelper.GREEN + ) + ) + return cprompt + + cprompt = self.context.settings.get('ovirt-shell:ps2.connected') + if self.context.mode != ExecutionMode.SCRIPT \ + and self.context.interactive: + cprompt = cprompt.replace( + "connected", + ColorHelper.color( + "connected", + color_=ColorHelper.GREEN + ) + ) + return cprompt def __persistCmdOptions(self, opts): @@ -296,12 +332,10 @@ return None def _error(self, msg): - sys.stderr.write("\nerror: " + msg + "\n") - sys.stdout.write("\n") + self.context._handle_exception(SyntaxError(msg)) def _print(self, msg): - sys.stdout.write("\n" + msg + "\n") - sys.stdout.write("\n") + self.context._pint_text(msg) def do_EOF(self, line): """\ diff --git a/src/ovirtcli/utils/colorhelper.py b/src/ovirtcli/utils/colorhelper.py new file mode 100644 index 0000000..0aab2d5 --- /dev/null +++ b/src/ovirtcli/utils/colorhelper.py @@ -0,0 +1,34 @@ +# +# Copyright (c) 2010 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +class ColorHelper(): + BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + + __NONE = None + __ENDC = '\033[0m' + + @staticmethod + def color(text, color_): + """ + Colors text + + @param text: text to color + @param color_: color to use (ColorHelper.RED|ColorHelper.BLUE...) + """ + if color_: + return "\x1b[1;%dm" % (30 + color_) + text + "\x1b[0m" + return text -- To view, visit http://gerrit.ovirt.org/20147 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I074e6d03f46f42261f96a71024fe28f871bd0c65 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine-cli Gerrit-Branch: master Gerrit-Owner: Michael Pasternak <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
