Source code for vdat.command_interpreter.relay
"""Except for the logging mechanism, the :class:`.CommandInterpreter` uses
relays-like objects to communicate with the external word. This module defines
a few classes with an ``emit`` method, that mimic `PyQt signals
<http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html>`_
The names of the ``emit`` method arguments are the type of the parameter
followed by an underscore and optionally by an explanatory name.
Available relays:
* ``command_string``: accept an int and a string
.. automethod:: _CommandString.emit
:noindex:
* ``progress``: accept three numbers, the total expected number, the number of
successes and of failures;
.. automethod:: _Progress.emit
:noindex:
* ``logger``: accept a two strings
.. automethod:: _Logger.emit
:noindex:
"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import logging
import sys
__all__ = ['override_emit']
relays = {}
def register(name):
"""Initialise the decorated class and save it into the ``relays``
dictionary with ``name`` as key
Parameters
----------
name : string
name under which the class or instance must be registered
Returns
-------
decorator : function
class decorator
"""
def decorator(cls):
"save the class into the ``_registered`` dictionary"
relays[name] = cls()
return cls
return decorator
@register("command_string")
class _CommandString(object):
"""Receive the string of the command to execute after substituting the
keywords"""
[docs] def emit(self, int_, string_):
"""Default implementation:
print the ``string_`` for when ``int_ == 0``"""
if int_ == 0:
print(string_)
@register("progress")
class _Progress(object):
"""Receive total number of jobs and the number successful and failed
ones"""
[docs] def emit(self, int_tot, int_done, int_fail):
"""Default implementation:
Print the percentages of finished, successful and failed jobs,
overwriting the line"""
line = "Progress: {0:.0%}, success: {1:.0%}, failures: {2:.0%}"
line = line.format(int_done / int_tot,
(int_done - int_fail) / int_tot,
int_fail / int_tot)
sys.stdout.write("\r\x1b[K" + line)
sys.stdout.flush()
@register("logger")
class _Logger(object):
"""Receive the level of severity of the message and a string to print.
"""
[docs] def emit(self, int_level, string_msg):
"""Default implementation:
print ``int_level: string_msg``
The ``int_level`` values are chosen to be the standard `logging levels
<https://docs.python.org/2/library/logging.html#logging-levels>`_ and
are converted to strings accordingly
The idea behind this relay is to bind it to the main logger if needed.
We decided not to do this directly as we don't know the name of the
logger. We implement it in this way to have a more coherent interface.
"""
print(logging.getLevelName(int_level) + ": " + string_msg)
def get_relay(name):
"""Get the relay associated with ``name``
Parameters
----------
name : string
name of the requested relay
Returns
-------
one of the relay instances defined in this module and registered into the
``relays`` dictionary
"""
return relays[name]
[docs]def override_emit(name, new_emit):
"""Replace the emit method in the relay instance called ``name`` with
``new_emit``. The original class is not modified.
Parameters
----------
name : string
name of the relay
new_emit : callable
function to use to replace the default ``emit`` method. This function
must have at least one argument, ``self`` and it's signature must match
the original one to avoid errors at runtime
"""
import types
relays[name].emit = types.MethodType(new_emit, relays[name])