Esempio n. 1
0
 def check(self):
     ''' Internal consistency check.
 '''
     for tag in sorted(self._tag_up.keys()):
         ups = self._tag_up[tag]
         downs = self._tag_down.get(tag, 0)
         if ups != downs:
             warning("%s: ups=%d, downs=%d: tag %r", self, ups, downs, tag)
Esempio n. 2
0
def unrfc2047(s):
  ''' Accept a string `s` containing RFC2047 text encodings (or the whitespace
      littered varieties that come from some low quality mail clients) and
      decode them into flat Unicode.

      See http://tools.ietf.org/html/rfc2047 for the specification.
  '''
  if not isinstance(s, unicode):
    # TODO: should this come from the locale? that seems arbitrary as well
    s = unicode(s, FALLBACK_CHARSET)
  chunks = []
  sofar = 0
  for m in re_RFC2047.finditer(s):
    encoded_word = m.group()
    with Pfx(encoded_word):
      start = m.start()
      end = m.end()
      if start > sofar:
        chunks.append(s[sofar:start])
      charset = m.group(1)
      encoding = m.group(2).upper()
      encoded_text = m.group(3)
      decoded = None
      realtext = None
      if encoding == 'B':
        try:
          decoded = base64.b64decode(encoded_text)
        except (ValueError, TypeError) as e:
          warning("%r: %s", encoded_text, e)
          realtext = encoded_word
      elif encoding == 'Q':
        try:
          decoded = quopri.decodestring(encoded_text.replace('_', ' '))
        except (UnicodeEncodeError, ValueError) as e:
          warning("%r: %s", encoded_text, e)
          realtext = encoded_word
      else:
        warning("unhandled RFC2047 encoding %r, not decoding", encoding)
        realtext = encoded_word
      if realtext is None:
        try:
          realtext = decoded.decode(charset, 'replace')
        except LookupError as e:
          warning(
              "charset %r: %s; falling back to %r", charset, e,
              FALLBACK_CHARSET
          )
          try:
            realtext = decoded.decode(FALLBACK_CHARSET, 'replace')
          except LookupError:
            warning("fallback charset %r: %s; not decoding", charset, e)
            realtext = encoded_word
      chunks.append(realtext)
      sofar = end
  if sofar < len(s):
    chunks.append(s[sofar:])
  return unicode('').join(chunks)
Esempio n. 3
0
 def convert_sequence(sequence):
     ''' Convert a colour specification to an escape sequence.
 '''
     escs = []
     for colour_code in sequence.split(';'):
         try:
             escs.append(colour_escape(colour_code))
         except KeyError as e:
             warning("%r: %r: %e", sequence, colour_code, e)
     return ''.join(escs)
Esempio n. 4
0
    def __init__(
        self,
        util_name=None,
        term_name=None,
        type_name=None,
        colors_dirpath=None,
        envvar=None,
    ):
        ''' Initialise the `TerminalColors` instance.

        Parameters:
        * `util_name`: optional utility name, default from `sys.argv[0]`
        * `term_name`: optional terminal name, default from the `$TERM` envvar
        * `type_name`: optional type name, default `'enable'`
        * `colors_dirpath`: optional specification files directory path,
          default from `TerminalColors.TERMINAL_COLORS_D`
        * `envvar`: environment variable to override matches;
          the default `util_name+'_COLORS'`,
          thus `$LS_COLORS` if `util_name=='ls'`.
          That may be the value `False` if no environment variable should be an override.
    '''
        if util_name is None:
            try:
                util_name = basename(sys.argv[0])
            except KeyError:
                util_name = ''
        if term_name is None:
            term_name = os.environ.get('TERM', 'dumb')
        if type_name is None:
            type_name = 'enable'
        if colors_dirpath is None:
            colors_dirpath = self.TERMINAL_COLORS_D
        if envvar is None:
            envvar = self.envvar = util_name.upper() + '_COLORS'
        self.util_name = util_name
        self.term_name = term_name
        self.type_name = type_name
        self.colors_dirpath = colors_dirpath
        self.envvar = envvar
        self._mapping = None
        # prefill the mapping if the environment variable is present
        if envvar is not False:
            envval = os.environ.get(envvar)
            if envval:
                m = {}
                for field in envval.strip().split(':'):
                    if not field:
                        continue
                    try:
                        name, sequence = field.strip().split('=', 1)
                    except ValueError as e:
                        warning("$%s: %r: %e", envvar, field, e)
                    else:
                        m[name] = self.convert_sequence(sequence)
                self._mapping = m
Esempio n. 5
0
 def placeApp(self, appname, startpos=0, allowDupe=False):
     ''' Place the named application at the first free slot at startpos
     or beyond.
 '''
     if not allowDupe and self._byApp.get(appname, ()):
         warning("placeApp(%s, %d, allowDupe = %s): app exists at: %s",
                 appname, startpos, allowDupe, self._byApp[appname])
     pos = startpos
     while pos < len(self) and self[pos] is not None:
         pos += 1
     self.placeIcon({"displayIdentifier": appname}, pos)
Esempio n. 6
0
def stack_termios(fd=0, set_modes=None, clear_modes=None, strict=False):
  ''' Context manager to apply and restore changes to a tty.
      Yield the previous tty modes as from `termios.tcgetattr`
      or `None` if the changes could not be applied.
      If `strict`, raise an exception instead of yielding `None`.

      Parameters:
      * `fd`: optional tty file descriptor, default `0`.
      * `set_modes`: an optional  mapping of attribute name to new value
        for values to set
      * `clear_modes`: an optional  mapping of attribute name to new value
        for values to clear
      * `strict`: optional flag, default `False`;
        if true, raise exceptions from failed `tcgetattr` and `tcsetattr` calls
        otherwise issue a warning if the errno is not `ENOTTY` and proceed.
        This aims to provide ease of use in batch mode by default
        while providing a mode to fail overtly if required.

      The attribute names are from
      `iflag`, `oflag`, `cflag`, `lflag`, `ispeed`, `ospeed`, `cc`,
      corresponding to the list entries defined by the `termios.tcgetattr`
      call.

      For `set_modes`, the attributes `ispeed`, `ospeed` and `cc`
      are applied directly;
      the other attributes are binary ORed into the existing modes.

      For `clear_modes`, the attributes `ispeed`, `ospeed` and `cc`
      cannot be cleared;
      the other attributes are binary removed from the existing modes.

      For example, to turn off the terminal echo during some operation:

          with stack_termios(clear_modes={'lflag': termios.ECHO}):
              ... do something with tty echo disabled ...
  '''
  try:
    restore_modes = modify_termios(
        fd, set_modes=set_modes, clear_modes=clear_modes, strict=strict
    )
    yield restore_modes
  finally:
    if restore_modes:
      try:
        tcsetattr(fd, TCSANOW, restore_modes)
      except OSError as e:
        if strict:
          raise
        warning("tcsetattr(%d,TCSANOW,%r): %e", fd, restore_modes, e)
Esempio n. 7
0
def status(msg, *args, **kwargs):
  ''' Write a message to the terminal's status line.

      Parameters:
      * `msg`: message string
      * `args`: if not empty, the message is %-formatted with `args`
      * `file`: optional keyword argument specifying the output file.
        Default: `sys.stderr`.

      Hack: if there is no status line use the xterm title bar sequence :-(
  '''
  if args:
    msg = msg % args
  f = kwargs.pop('file', None)
  if kwargs:
    raise ValueError("unexpected keyword arguments: %r" % (kwargs,))
  if f is None:
    f = sys.stderr
  try:
    has_ansi_status = f.has_ansi_status
  except AttributeError:
    try:
      import curses  # pylint: disable=import-outside-toplevel
    except ImportError:
      has_ansi_status = None
    else:
      curses.setupterm()
      has_status = curses.tigetflag('hs')
      if has_status == -1:
        warning(
            'status: curses.tigetflag(hs): not a Boolean capability, presuming false'
        )
        has_ansi_status = None
      elif has_status > 0:
        has_ansi_status = (
            curses.tigetstr('to_status_line'),
            curses.tigetstr('from_status_line')
        )
      else:
        warning('status: hs=%s, presuming false', has_status)
        has_ansi_status = None
    f.has_ansi_status = has_ansi_status
  if has_ansi_status:
    msg = has_ansi_status[0] + msg + has_ansi_status[1]
  else:
    msg = '\033]0;' + msg + '\007'
  f.write(msg)
  f.flush()
Esempio n. 8
0
 def dec(self, tag=None):
     ''' Decrement the counter.
     Wake up any threads waiting for its new value.
 '''
     with self._lock:
         self.value -= 1
         if tag is not None:
             tag = str(tag)
             self._tag_down.setdefault(tag, 0)
             self._tag_down[tag] += 1
             if self._tag_up.get(tag, 0) < self._tag_down[tag]:
                 warning("%s.dec: more .decs than .incs for tag %r", self,
                         tag)
         if self.value < 0:
             warning("%s.dec: value < 0!", self)
         self._notify()
Esempio n. 9
0
   def report_observation(self, attr):
       ''' Notify all the observers of the current value of `attr`.
 '''
       val_attr = '_' + attr
       value = getattr(self, val_attr, None)
       for observer in self._observable_class__observers[attr]:
           try:
               observer(self, attr, value)
           except Exception as e:  # pylint: disable=broad-except
               warning("%s.%s=%r: observer %s(...) raises: %s",
                       self,
                       val_attr,
                       value,
                       observer,
                       e,
                       exc_info=True)
Esempio n. 10
0
 def wrapped(*args, **kwargs):
     ''' Wrap `func` to emit an "OBSOLETE" warning before calling `func`.
 '''
     frame = traceback.extract_stack(None, 2)[0]
     caller = frame[0], frame[1]
     # pylint: disable=protected-access
     try:
         callers = func._OBSOLETE_callers
     except AttributeError:
         callers = func._OBSOLETE_callers = set()
     if caller not in callers:
         callers.add(caller)
         warning("OBSOLETE call to %s:%d %s(), called from %s:%d %s",
                 func.__code__.co_filename, func.__code__.co_firstlineno,
                 func.__name__, frame[0], frame[1], frame[2])
     return func(*args, **kwargs)
Esempio n. 11
0
 def scan(self, path=None):
     ''' Scan the colour specification in `path`
     and yield `(name,escape_sequence)` tuples.
 '''
     if path is None:
         path = self.find_specfile()
         if path is None:
             # no matching specfile
             return
     with open(path) as f:  # pylint: disable=unspecified-encoding
         for lineno, line in enumerate(f, 1):
             line = line.strip()
             if not line or line.startswith('#'):
                 continue
             try:
                 name, sequence = line.split()
             except ValueError as e:
                 warning("%s, %d: %s", path, lineno, e)
                 continue
             yield name, self.convert_sequence(sequence)
Esempio n. 12
0
def modify_termios(fd=0, set_modes=None, clear_modes=None, strict=False):
  ''' Apply mode changes to a tty.
      Return the previous tty modes as from `termios.tcgetattr`
      or `None` if the changes could not be applied.
      If `strict`, raise an exception instead of returning `None`.

      Parameters:
      * `fd`: optional tty file descriptor, default `0`.
      * `set_modes`: an optional  mapping of attribute name to new value
        for values to set
      * `clear_modes`: an optional  mapping of attribute name to new value
        for values to clear
      * `strict`: optional flag, default `False`;
        if true, raise exceptions from failed `tcgetattr` and `tcsetattr` calls
        otherwise issue a warning if the errno is not `ENOTTY` and proceed.
        This aims to provide ease of use in batch mode by default
        while providing a mode to fail overtly if required.

      The attribute names are from
      `iflag`, `oflag`, `cflag`, `lflag`, `ispeed`, `ospeed`, `cc`,
      corresponding to the list entries defined by the `termios.tcgetattr`
      call.

      For `set_modes`, the attributes `ispeed`, `ospeed` and `cc`
      are applied directly;
      the other attributes are binary ORed into the existing modes.

      For `clear_modes`, the attributes `ispeed`, `ospeed` and `cc`
      cannot be cleared;
      the other attributes are binary removed from the existing modes.

      For example, to turn off the terminal echo during some operation:

          old_modes = apply_termios(clear_modes={'lflag': termios.ECHO}):
              ... do something with tty echo disabled ...
          if old_modes:
              termios.tcsetattr(fd, termios.TCSANOW, old_modes)
  '''
  if set_modes:
    if not all(map(lambda k: k in _termios_modes_names, set_modes.keys())):
      raise ValueError(
          "set_modes: invalid mode keys: known=%r, supplied=%r" %
          (sorted(_termios_modes_names.keys()), set_modes)
      )
  if clear_modes:
    if not all(map(lambda k: k in _termios_modes_names, clear_modes.keys())):
      raise ValueError(
          "clear_modes: invalid mode keys: known=%r, supplied=%r" %
          (sorted(_termios_modes_names.keys()), clear_modes)
      )
    for k in 'ispeed', 'ospeed', 'cc':
      if k in clear_modes:
        raise ValueError("clear_modes: cannot clear %r" % (k,))
  try:
    original_modes = tcgetattr(fd)
  except OSError as e:
    if strict:
      raise
    if e.errno != errno.ENOTTY:
      warning("tcgetattr(%d): %s", fd, e)
    original_modes = None
  restore_modes = None
  if original_modes:
    new_modes = list(original_modes)
    if set_modes:
      for k, v in set_modes.items():
        i = _termios_modes_names[k]
        if k in ('ispeed', 'ospeed', 'cc'):
          new_modes[i] = v
        else:
          new_modes[i] |= v
    if clear_modes:
      for k, v in clear_modes.items():
        i = _termios_modes_names[k]
        new_modes[i] &= ~v
    if new_modes == original_modes:
      restore_modes = None
    else:
      try:
        tcsetattr(fd, TCSANOW, new_modes)
      except OSError as e:
        if strict:
          raise
        warning("tcsetattr(%d,TCSANOW,%r): %e", fd, new_modes, e)
      else:
        restore_modes = original_modes
  return restore_modes
Esempio n. 13
0
 def cachedmethod_wrapper(self, *a, **kw):
     with Pfx("%s.%s", self, attr):
         now = None
         value0 = getattr(self, val_attr, unset_value)
         sig0 = getattr(self, sig_attr, None)
         sig = getattr(self, sig_attr, None)
         if value0 is unset_value:
             # value unknown, needs compute
             pass
         # we have a cached value for return in the following logic
         elif poll_delay is None:
             return value0
         # see if the value is stale
         lastpoll = getattr(self, lastpoll_attr, None)
         now = time.time()
         if lastpoll is not None and now - lastpoll < poll_delay:
             # reuse cache
             return value0
         # update the poll time if we use it
         setattr(self, lastpoll_attr, now)
         # check the signature if provided
         # see if the signature is unchanged
         if sig_func is not None:
             try:
                 sig = sig_func(self)
             except Exception as e:  # pylint: disable=broad-except
                 # signature function fails, use the cache
                 warning("sig func %s(self): %s",
                         sig_func,
                         e,
                         exc_info=True)
                 return value0
             if sig0 is not None and sig0 == sig:
                 # signature unchanged
                 return value0
             # update signature
             setattr(self, sig_attr, sig)
         # compute the current value
         try:
             value = method(self, *a, **kw)
         except Exception as e:  # pylint: disable=broad-except
             # computation fails, return cached value
             if value0 is unset_value:
                 # no cached value
                 raise
             warning("exception calling %s(self): %s",
                     method,
                     e,
                     exc_info=True)
             return value0
         # update the cache
         setattr(self, val_attr, value)
         # bump revision if the value changes
         # noncomparable values are always presumed changed
         changed = value0 is unset_value or value0 is not value
         if not changed:
             try:
                 changed = value0 != value
             except TypeError:
                 changed = True
         if changed:
             setattr(self, rev_attr,
                     (getattr(self, rev_attr, None) or 0) + 1)
         return value