[Avocado-devel] [RFC] Plugins and Interfaces (using avocado.core.output.View as example)

Lukáš Doktor ldoktor at redhat.com
Wed Sep 2 09:19:37 UTC 2015


Dne 1.9.2015 v 17:31 Cleber Rosa napsal(a):
> Hi folks,
>
> I've been doing some research and prototyping these last days, and I
> reached one specific point that I'd like to discuss early and openly.
>
> The point is about the Avocado UI, and how we have pretended that
> `avocado.core.output.View` follows some interface and could allow us
> to switch one View (or UI) for another one. I reckon that, by not
> having swappable Views (UIs), our design can be (actually is) broken.
>
> So the proposals here are:
>
> 1) We should document, and maybe enforce, the interface that different
>     components in Avocado expect and provide.
>
> By documenting, it could be as simple as:
>
>     # avocado/core/ui.py
>     class Base(object):
>         def setup(self):
>             raise NotImplementedError
>      def notify(self, event, msg='', **kwargs):
>             raise NotImplementedError
>      def teardown(self):
>             raise NotImplementedError
>
> Or we could enforce the implementation of these interfaces by using
> Abstract Base Classes:
>
>     # avocado/core/ui.py
>     import abc
>     class Base(object):
>         __metaclass__ = abc.ABCMeta
>
>          @abc.abstractmethod
>          def setup(self):
>
>          @abc.abstractmethod
>          def notify(self, event, msg='', **kwargs):
>
>          @abc.abstractmethod
>          def teardown(self):
>
> 2) Having these interfaces defined, there should be at least two solid
>     implementations that are tested in unittests and functional tests.
>
>     class Silent(Base):
>        def setup(self):
>            pass
>
>        def notify(self, event, msg='', **kwargs):
>            pass
>
>        def teardown(self):
>            pass
>
>      ---
>
>      class Curses(Base):
>         def setup(self):
>             import curses
>             window = curses.initscr()
>
>         def notify(self, event, msg='', **kwargs):
>             self.window.insstr(msg)
>
>         def teardown(self):
>             curses.endwin()
>
> The point is that a `--silent` option SHOULD be implemented by
> swapping the current UI "driver", and nothing else.
>
> The UI example given here is not a big deal code-wise or even
> functionality wise, but the same mentality and design should be taken
> to other areas. Running a test remotely or on a container or on a
> cloud instance should be a matter of swapping "test execution drivers"
> and that's all.
>
> My idea, given positive feedback, is to improve the current state of
> things in the UI layer first, and make guidelines/best practices for
> the other areas that should modular and extensible in Avocado.
>
> Cheers,
> Cleber Rosa.
>

Hello Cleber,

this actually correlates to what I proposed with logging. I like the 
idea of using `curses` to create main `avocado` screen.

The way I imagine this is (note I'm not sure if this is possible, I had 
no time to investigate it further):

1) We initialize logging, every stream get's special in-memory (or 
tmpfile) `early_print` handlers
2) When we parse the cmdline, we initialize screen (silent, simple or 
tail-like) The view would take care of reprinting the enabled 
`early_print` content and reconfiguring the logging accordingly to it's 
purpose:

- The silent view would simply remove all logs and leave the screen 
untouched
- The simple view would reprint the content of early_print (what's there 
in the in-memory handlers, then it'd replace the in-memory handlers with 
stdout handlers and voila, we have a simple output with configured content.
- The tail-like view would use eg. curses and periodically it'd check 
for new content of in-memory/tmpfile handlers. User would be able to 
move around using keyboard.

The benefit of this is, that by design we should have separated views. 
Silent should never display anything.

Simple one would have a minor overhead at the beginning as logs are only 
stored until we parse the configuration. Then we reconfigure the logging 
and voila, it's a simple python logging. (instead of major/minor we'd 
use info/debug)

The fancy tail-like should not be even that hard to develop. It's only a 
matter of separate thread, which reads the new content of logging 
handlers, filters them and displays them to the user.

For failures during early stage, we should use try/finally to print the 
left content of early_printers.

The biggest benefit I see in getting rid of the view. People would 
simply use `logging.getLogger(...)` and transparently based on the 
logger it'd either got logged, displayed on screen or ignored. We'd only 
chose which view to use with certain commands (eg. run usually uses 
simple, list uses the tail-like, ...).

For the future, we might consider using tail-like view even for 
(interactive) "avocado run" and extend the functionality of some special 
keys like "s" to enable/disable additional logging streams during the 
execution, context to see what's going on, switching between test/ui 
view, or even way to interrupt test and start debugging session...

Well, what do you think? It requires some effort at the beginning, but 
in the end it should be simpler to maintain and the users would not need 
to learn anything and simply use the python logging. And they need a 
simple interface, have a look at 
https://github.com/Andrei-Stepanov/avocado-vt/commit/a40ffb6500e6326d6c342706b2d1acb871cddbf9

Regards,
Lukáš




More information about the Avocado-devel mailing list