Michael Pasternak has uploaded a new change for review.

Change subject: cli: introducing event driven messaging
......................................................................

cli: introducing event driven messaging

Change-Id: I110a2b7d25a03ed1f256f24058f3c54535e27100
Signed-off-by: Michael pasternak <[email protected]>
---
A src/ovirtcli/annotations/__init__.py
A src/ovirtcli/annotations/requires.py
A src/ovirtcli/events/__init__.py
A src/ovirtcli/events/abstractevent.py
A src/ovirtcli/events/event.py
A src/ovirtcli/events/ievent.py
A src/ovirtcli/listeners/__init__.py
A src/ovirtcli/listeners/abstractlistener.py
A src/ovirtcli/listeners/errorlistener.py
A src/ovirtcli/listeners/ilistener.py
M src/ovirtcli/shell/cmdshell.py
M src/ovirtcli/shell/engineshell.py
12 files changed, 400 insertions(+), 31 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine-cli refs/changes/21/20321/1

diff --git a/src/ovirtcli/annotations/__init__.py 
b/src/ovirtcli/annotations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ovirtcli/annotations/__init__.py
diff --git a/src/ovirtcli/annotations/requires.py 
b/src/ovirtcli/annotations/requires.py
new file mode 100644
index 0000000..0f66499
--- /dev/null
+++ b/src/ovirtcli/annotations/requires.py
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2013 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 Requires(object):
+    """
+    Checks that method arg is of a given type
+    """
+
+    def __init__(self, typ):
+        """
+        Checks that method arg is of a given type
+
+        @param typ: the type to validate against
+        """
+        assert typ
+        self.typ = typ
+
+    def __call__(self, original_func):
+        decorator_self = self
+        def wrappee(*args, **kwargs):
+            self.__check_list(
+                      args[1],
+                      decorator_self.typ
+            )
+            return original_func(*args, **kwargs)
+        return wrappee
+
+    def __check_list(self, candidate, typ):
+        if not isinstance(candidate, typ):
+            raise TypeError(
+                    "%s instance is expected."
+                    %
+                    typ.__name__
+            )
diff --git a/src/ovirtcli/events/__init__.py b/src/ovirtcli/events/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ovirtcli/events/__init__.py
diff --git a/src/ovirtcli/events/abstractevent.py 
b/src/ovirtcli/events/abstractevent.py
new file mode 100644
index 0000000..f7bbb87
--- /dev/null
+++ b/src/ovirtcli/events/abstractevent.py
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2013 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 ovirtcli.events.ievent import IEvent
+from abc import ABCMeta
+
+class AbstractEvent(IEvent):
+    '''
+    The abstract event class
+    '''
+
+    __metaclass__ = ABCMeta
+
+    def __init__(self):
+        self.__id = id(self)
diff --git a/src/ovirtcli/events/event.py b/src/ovirtcli/events/event.py
new file mode 100644
index 0000000..ae3d2f4
--- /dev/null
+++ b/src/ovirtcli/events/event.py
@@ -0,0 +1,61 @@
+#
+# Copyright (c) 2013 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 ovirtcli.annotations.requires import Requires
+from ovirtcli.listeners.ilistener import IListener
+from ovirtcli.events.abstractevent import AbstractEvent
+
+class Event(AbstractEvent):
+    '''
+    The event class implementation
+    '''
+
+    def __init__(self):
+        self.__listeners = []
+        self.__id = id(self)
+
+    @Requires(IListener)
+    def __iadd__(self, listener):
+        """
+        adds event IListener
+        
+        @param listener: listener to register
+        """
+        assert listener != None
+        self.__listeners.append(listener)
+        return self
+
+    @Requires(IListener)
+    def __isub__(self, listener):
+        """
+        removes event IListener
+        
+        @param listener: listener to unregister
+        """
+        assert listener != None
+        self.__listeners.remove(listener)
+        return self
+
+    def fire(self, *args, **keywargs):
+        '''
+        event listeners notification
+        
+        @param args: a list o args
+        @param kwargs: a list o kwargs
+        '''
+        for listener in self.__listeners:
+            listener.onEvent(*args, **keywargs)
diff --git a/src/ovirtcli/events/ievent.py b/src/ovirtcli/events/ievent.py
new file mode 100644
index 0000000..3433147
--- /dev/null
+++ b/src/ovirtcli/events/ievent.py
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2013 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 ovirtcli.annotations.requires import Requires
+from ovirtcli.listeners.ilistener import IListener
+
+from abc import ABCMeta, abstractmethod
+
+class IEvent(object):
+    '''
+    The event interface
+    '''
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    @Requires(IListener)
+    def __iadd__(self, listener):
+        """
+        adds event IListener
+        
+        @param listener: listener to register
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    @Requires(IListener)
+    def __isub__(self, listener):
+        """
+        removes event IListener
+        
+        @param listener: listener to unregister
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def fire(self, *args, **keywargs):
+        '''
+        event listeners notification
+        
+        @param args: a list o args
+        @param kwargs: a list o kwargs
+        '''
+        raise NotImplementedError
diff --git a/src/ovirtcli/listeners/__init__.py 
b/src/ovirtcli/listeners/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/ovirtcli/listeners/__init__.py
diff --git a/src/ovirtcli/listeners/abstractlistener.py 
b/src/ovirtcli/listeners/abstractlistener.py
new file mode 100644
index 0000000..ad0f0bf
--- /dev/null
+++ b/src/ovirtcli/listeners/abstractlistener.py
@@ -0,0 +1,29 @@
+#
+# 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 ovirtcli.listeners.ilistener import IListener
+from abc import ABCMeta
+
+class AbstractListener(IListener):
+    '''
+    Abstract listener, listens for the events
+    '''
+
+    __metaclass__ = ABCMeta
+
+    def __init__(self):
+        self.__id = id(self)
diff --git a/src/ovirtcli/listeners/errorlistener.py 
b/src/ovirtcli/listeners/errorlistener.py
new file mode 100644
index 0000000..70e3197
--- /dev/null
+++ b/src/ovirtcli/listeners/errorlistener.py
@@ -0,0 +1,63 @@
+#
+# 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 ovirtcli.prompt import PromptMode
+
+from cli.context import ExecutionContext
+
+from ovirtcli.listeners.abstractlistener import AbstractListener
+
+class ErrorListener(AbstractListener):
+    '''
+    Listens for the error events
+    '''
+
+    def __init__(self, shell):
+        """
+        @param shell: EngineShell instance
+        """
+        assert shell != None
+        self.__shell = shell
+
+    def onEvent(self, *args, **kwargs):
+        '''
+        fired when error event is raised
+        
+        @param args: a list o args
+        @param kwargs: a list o kwargs
+        '''
+
+        if self.__shell.context.status == \
+           ExecutionContext.COMMUNICATION_ERROR:
+            self.__shell.owner._set_prompt(
+                       mode=PromptMode.Disconnected
+            )
+        elif self.__shell.context.status == \
+             ExecutionContext.AUTHENTICATION_ERROR:
+            self.__shell.owner._set_prompt(
+                       mode=PromptMode.Unauthorized
+            )
+        elif self.__shell._get_last_status() <> -1 and \
+             (
+              self.__shell._get_last_status() == \
+              ExecutionContext.COMMUNICATION_ERROR
+              or \
+              self.__shell._get_last_status() == \
+              ExecutionContext.AUTHENTICATION_ERROR
+             ):
+            self.__shell.owner._set_prompt(
+                       mode=PromptMode.Original
+            )
diff --git a/src/ovirtcli/listeners/ilistener.py 
b/src/ovirtcli/listeners/ilistener.py
new file mode 100644
index 0000000..78b0bc3
--- /dev/null
+++ b/src/ovirtcli/listeners/ilistener.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 abc import ABCMeta, abstractmethod
+
+class IListener(object):
+    '''
+    The listener interface
+    '''
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def onEvent(self, *args, **kwargs):
+        '''
+        raised when state change event occurs
+        
+        @param args: a list o args
+        @param kwargs: a list o kwargs
+        '''
+        raise NotImplementedError
diff --git a/src/ovirtcli/shell/cmdshell.py b/src/ovirtcli/shell/cmdshell.py
index 7ed5956..5a21781 100644
--- a/src/ovirtcli/shell/cmdshell.py
+++ b/src/ovirtcli/shell/cmdshell.py
@@ -15,9 +15,10 @@
 #
 import sys
 import os
-from ovirtcli.utils.typehelper import TypeHelper
-from ovirtsdk.infrastructure import brokers
+
 import itertools
+
+from ovirtcli.utils.typehelper import TypeHelper
 from ovirtcli.settings import OvirtCliSettings
 
 
diff --git a/src/ovirtcli/shell/engineshell.py 
b/src/ovirtcli/shell/engineshell.py
index 0efc20d..2a265cf 100644
--- a/src/ovirtcli/shell/engineshell.py
+++ b/src/ovirtcli/shell/engineshell.py
@@ -40,16 +40,17 @@
 from ovirtcli.shell.infocmdshell import InfoCmdShell
 from ovirtcli.shell.capabilitiescmdshell import CapabilitiesCmdShell
 
-from ovirtcli.settings import OvirtCliSettings
-from ovirtcli.prompt import PromptMode
-
 from cli.command.help import HelpCommand
 from cli.messages import Messages
+from cli.executionmode import ExecutionMode
 
 from urlparse import urlparse
 from ovirtcli.utils.colorhelper import ColorHelper
-from cli.executionmode import ExecutionMode
 
+from ovirtcli.events.event import Event
+from ovirtcli.listeners.errorlistener import ErrorListener
+from ovirtcli.settings import OvirtCliSettings
+from ovirtcli.prompt import PromptMode
 
 class EngineShell(cmd.Cmd, ConnectCmdShell, ActionCmdShell, \
                   ShowCmdShell, ListCmdShell, UpdateCmdShell, \
@@ -79,20 +80,31 @@
         SummaryCmdShell.__init__(self, context, parser)
         CapabilitiesCmdShell.__init__(self, context, parser)
 
-        self.last_output = ''
+        self.onError = Event()
+        self.onInit = Event()
+        self.onExit = Event()
+        self.onPromptChange = Event()
+        self.onSigInt = Event()
+
+        self.__last_output = ''
         self.__input_buffer = ''
         self.__org_prompt = ''
         self.__last_status = -1
 
-        self._set_prompt(mode=PromptMode.Disconnected)
+        self.__register_sys_listeners()
+        self.__init_promt()
+
         cmd.Cmd.doc_header = self.context.settings.get('ovirt-shell:commands')
         cmd.Cmd.undoc_header = 
self.context.settings.get('ovirt-shell:misc_commands')
         cmd.Cmd.intro = OvirtCliSettings.INTRO
 
         readline.set_completer_delims(' ')
-        signal.signal(signal.SIGINT, self.handler)
+        signal.signal(signal.SIGINT, self.__handler)
 
-    ########################### SYSTEM #################################
+        self.onInit.fire()
+
+    ############################ SHELL ##################################
+
     def cmdloop(self, intro=None, clear=True):
         try:
             if clear: self.do_clear('')
@@ -135,19 +147,45 @@
                         self.__input_buffer = ''
                         self._set_prompt(mode=PromptMode.Original)
                     return cmd.Cmd.onecmd(self, s)
-                finally:  # if communication error occurred, change prompt 
state
-                    if self.context.status == self.context.COMMUNICATION_ERROR:
+                finally:
+                    if self.context.status == \
+                       self.context.SYNTAX_ERROR \
+                       or \
+                       self.context.status == \
+                       self.context.COMMAND_ERROR \
+                       or \
+                       self.context.status == \
+                       self.context.UNKNOWN_ERROR:
+                        self.onError.fire()
+                    elif self.context.status == \
+                       self.context.COMMUNICATION_ERROR:
                         self.__last_status = self.context.status
-                        self.owner._set_prompt(mode=PromptMode.Disconnected)
-                    elif self.context.status == 
self.context.AUTHENTICATION_ERROR:
+                        self.onError.fire()
+                    elif self.context.status == \
+                       self.context.AUTHENTICATION_ERROR:
                         self.__last_status = self.context.status
-                        self.owner._set_prompt(mode=PromptMode.Unauthorized)
-                    elif self.__last_status == 
self.context.COMMUNICATION_ERROR or \
-                         self.__last_status == 
self.context.AUTHENTICATION_ERROR:
+                        self.onError.fire()
+                    elif self.__last_status <> -1 and \
+                         (
+                          self.__last_status == \
+                          self.context.COMMUNICATION_ERROR
+                          or \
+                          self.__last_status == \
+                          self.context.AUTHENTICATION_ERROR
+                         ):
+                        self.onError.fire()
                         self.__last_status = -1
-                        self.owner._set_prompt(mode=PromptMode.Original)
+
+    ########################### SYSTEM #################################
+
+    def __register_sys_listeners(self):
+        self.onError += ErrorListener(self)
+
+    def __init_promt(self):
+        self._set_prompt(mode=PromptMode.Disconnected)
 
     def _set_prompt(self, mode=PromptMode.Default):
+        self.onPromptChange.fire()
         if mode == PromptMode.Multiline:
             if not self.__org_prompt:
                 self.__org_prompt = self.prompt
@@ -225,7 +263,7 @@
         return cprompt
 
 
-    def __persistCmdOptions(self, opts):
+    def __persist_cmd_options(self, opts):
         """
         Overrides config file options with cmdline's.
         """
@@ -238,10 +276,13 @@
                         self.context.settings['cli:' + k] = v
 
 
+    def _get_last_status(self):
+        return self.__last_status
+
     def onecmd_loop(self, s):
         opts, args = self.parser.parse_args()
         del args
-        self.__persistCmdOptions(opts)
+        self.__persist_cmd_options(opts)
         if opts.connect or self.context.settings.get('cli:autoconnect'):
             self.do_clear('')
             self.do_connect(s)
@@ -348,10 +389,19 @@
             return None
 
     def _error(self, msg):
+        # this is a custom error, consumer responsibility
+        # to filter out duplicate calls on event
+        self.onError.fire()
         self.context._handle_exception(SyntaxError(msg))
 
     def _print(self, msg):
         self.context._pint_text(msg)
+
+    def __handler(self, signum, frame):
+        self.onSigInt.fire()
+        raise KeyboardInterrupt
+
+    ############################# COMMANDS #################################
 
     def do_EOF(self, line):
         """\
@@ -367,6 +417,7 @@
 
         Ctrl+D
         """
+        self.onExit.fire()
         self.emptyline(no_prompt=True)
         return True
 
@@ -384,7 +435,7 @@
 
         exit
         """
-
+        self.onExit.fire()
         sys.exit(0)
 
     def do_help(self, args):
@@ -401,7 +452,6 @@
 
         help show
         """
-
         if not args:
             self.context.execute_string('help\n')
         else:
@@ -413,7 +463,7 @@
                     self.context.terminal.stdout.write('\n' + getattr(self, 
'do_' + cmd).__doc__ + '\n')
                 else:
                     return self.context.execute_string('help ' + args + '\n')
-    ############################# SHELL #################################
+
     def do_shell(self, line):
         """\
         == Usage ==
@@ -432,10 +482,9 @@
 
         ! ls -la
         """
-
         output = os.popen(line).read()
         print output
-        self.last_output = output
+        self.__last_output = output
 
     def do_echo(self, line):
         """\
@@ -455,12 +504,8 @@
 
         echo str
         """
-
-        if self.last_output:
-            print line.replace('$out', self.last_output)
+        if self.__last_output:
+            print line.replace('$out', self.__last_output)
         elif line:
             print line
         else: print self.prompt
-    ############################## COMMON ################################
-    def handler(self, signum, frame):
-        raise KeyboardInterrupt


-- 
To view, visit http://gerrit.ovirt.org/20321
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I110a2b7d25a03ed1f256f24058f3c54535e27100
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

Reply via email to