Source code for AutoArchive._ui.cmdline._cmdline_ui

# _cmdline_ui.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2014 Róbert Čerňanský



""":class:`CmdlineUi` class."""



__all__ = ["CmdlineUi"]



# {{{ INCLUDES

import sys
import os
import subprocess

from AutoArchive._infrastructure.py_additions import event
from AutoArchive._infrastructure.ui import VerbosityLevels, UiMessageKinds
from AutoArchive._infrastructure.configuration import Options


# }}} INCLUDES



# {{{ CLASSES

[docs]class CmdlineUi: """Implementation of the command-line user interface. Provides basic methods for showing messages of various importance to a user. It uses standard output and standard error as the user interface. :param appEnvironment: Application environment. :type appEnvironment: :class:`.AppEnvironment` :param configuration: Application configuration. :type configuration: :class:`.IConfiguration`""" #: Maximal length of the archive name in output messages. __MAX_NAME_LENGTH = 10 def __init__(self, appEnvironment, configuration): self.__configuration = configuration self.__appEnvironment = appEnvironment # currently processed archive specification file self.__processingArchSpec = None @event def messageShown(messageKind): """Fired after a user message was shown. Event is fired when one of the message kinds from :data:`UiMessageKinds` enum was shown or would be shown (but was suppressed due to the verbosity level). :param messageKind: Kind of the message that was shown. :type messageKind: :data:`UiMessageKinds`""" pass @property def verbosity(self): """Gets the verbosity level. If verbosity level is :data:`VerbosityLevels.Quiet` only messages of kind :data:`UiMessageKinds.Error` are shown. For level :data:`VerbosityLevels.Normal` all messages kinds except :data:`UiMessageKinds.Verbose` are shown. For level :data:`VerbosityLevels.Verbose` all message kinds are shown. :rtype: :data:`VerbosityLevels`""" if self.__configuration[Options.QUIET]: return VerbosityLevels.Quiet elif self.__configuration[Options.VERBOSE]: return VerbosityLevels.Verbose return VerbosityLevels.Normal
[docs] def setProcessingArchSpec(self, archSpec): """Sets currently processed :term:`archive specification file`. :param archSpec: The archive specification file name or path to it. :type archSpec: ``str``""" self.__processingArchSpec = archSpec
[docs] def showVerbose(self, msg): """Show a verbose-type message (:data:`UiMessageKinds.Verbose`) to the user. Verbose messages should be shown only if user enables it. Although this method can be called regardless of current verbosity level, the concrete implementation can decide whether it will be shown or not if verbosity level is 0. It is not recommended to call this method if verbosity level is 0 due to performance reasons. Current verbosity level can be obtained via :attr:`verbosity` property. See also: :attr:`verbosity`. :param msg: The message that should be shown to the user. :type msg: ``str``""" if self.verbosity == VerbosityLevels.Verbose: self.__printStandardMsg(msg) self.messageShown(UiMessageKinds.Verbose)
[docs] def showNotification(self, msg): """Show an unintrusive notification message (:data:`UiMessageKinds.Notification`) to the user. .. note:: If user interface implementation does not have means to support notifications then it should be presented to the user similarly as :meth:`showInfo`. See also: :attr:`verbosity`. :param msg: The message that should be shown to the user. :type msg: ``str``""" if self.verbosity != VerbosityLevels.Quiet: self.__printStandardMsg(msg) self.messageShown(UiMessageKinds.Notification)
[docs] def showInfo(self, msg): """Show an information message (:data:`UiMessageKinds.Info`) to the user. See also: :attr:`verbosity`. :param msg: The message that should be shown to the user. :type msg: ``str``""" if self.verbosity != VerbosityLevels.Quiet: self.__printStandardMsg(msg, self.__processingArchSpec) self.messageShown(UiMessageKinds.Info)
[docs] def showWarning(self, msg): """Show a warning message (:data:`UiMessageKinds.Warning`) to the user. See also: :attr:`verbosity`. :param msg: The message that should be shown to the user. :type msg: ``str``""" if self.verbosity != VerbosityLevels.Quiet: self.__printAttentionMsg("Warning", msg) self.messageShown(UiMessageKinds.Warning)
[docs] def showError(self, msg): """Show an error message to (:data:`UiMessageKinds.Error`) the user. See also: :attr:`verbosity`. :param msg: The message that should be shown to the user. :type msg: ``str``""" self.__printAttentionMsg("Error", msg) self.messageShown(UiMessageKinds.Error)
[docs] def presentLine(self, line): """Present a line of text to the user. .. note:: The verbosity level has no effect on presenting the line. :param line: The text that shall be presented to the user. :type line: ``str``""" self.__printStandardMsg(line)
[docs] def presentMultiFieldLine(self, multiFieldLine): """Present a line consisting of multiple fields of text to the user. .. note:: The verbosity level has no effect on presenting the line. :param multiFieldLine: Line that shall be presented. :type multiFieldLine: :class:`.MultiFieldLine`""" physicalWidth = self.__getTermWidth() widths = multiFieldLine.computeFieldWidths(physicalWidth) widthIdx = 0 line = "" for field in multiFieldLine.fields: width = widths[widthIdx] - 1 if widthIdx < (len(multiFieldLine.fields) - 1) else widths[widthIdx] paddedText = str.format("{text:<{width}} ", text = self._shortenString(field.text, width), width = width) line += paddedText widthIdx += 1 self.__printStandardMsg(line[:-1])
# {{{ helpers @classmethod def __printStandardMsg(cls, msg, currentArchSpec = None): archSpecToken = str.format( "[{}] ", cls.__getArchSpecToken(currentArchSpec)) if currentArchSpec is not None else "" print(str.format("{}{}", archSpecToken, msg)) def __printAttentionMsg(self, attentionString, msg): archSpecToken = str.format( " [{}] ", self.__getArchSpecToken(self.__processingArchSpec)) \ if self.__processingArchSpec is not None else " " sys.stderr.write(str.format( "{executableName}: {attentionString}!{archSpecToken}{msg}\n", executableName = self.__appEnvironment.executableName, attentionString = attentionString, archSpecToken = archSpecToken, msg = msg)) @classmethod def __getArchSpecToken(cls, currentArchSpec): if os.path.split(currentArchSpec)[0]: return os.path.join(cls._shortenString(os.path.split(currentArchSpec)[0], cls.__MAX_NAME_LENGTH), os.path.basename(currentArchSpec)) else: return currentArchSpec @staticmethod def _shortenString(token, length): halfLength = length // 2 return token[:halfLength] + "~" + token[-halfLength - ((length % 2) - 1):] if len(token) > length else token @staticmethod def __getTermWidth(): def getWinWidth(fd): try: import struct, fcntl, termios expectStruct = struct.pack("hh", 0, 0) width = struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, expectStruct))[1] except IOError: width = None return width # try get terminal width from standard file descriptors using ioctl for stdFd in (1, 2, 0): winWidth = getWinWidth(stdFd) if winWidth: return winWidth # try get terminal width from controlling terminal file descriptor using ioctl try: with open(os.ctermid()) as termFd: return getWinWidth(termFd) except IOError: pass # try environment variable if "COLUMNS" in os.environ.keys(): return os.environ["COLUMNS"] # try call external program stty status, output = subprocess.getstatusoutput("stty size") if status == 0: splitOutput = output.split() if len(splitOutput) == 2: try: return int(splitOutput[1]) except ValueError: pass return 80
# }}} helpers # }}} CLASSES