TTY.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. # TTY UI
  2. # Copyright (C) 2002-2018 John Goerzen & contributors
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. import logging
  18. import sys
  19. import time
  20. from getpass import getpass
  21. from offlineimap import banner
  22. from offlineimap.ui.UIBase import UIBase
  23. class TTYFormatter(logging.Formatter):
  24. """Specific Formatter that adds thread information to the log output."""
  25. def __init__(self, *args, **kwargs):
  26. # super() doesn't work in py2.6 as 'logging' uses old-style class
  27. logging.Formatter.__init__(self, *args, **kwargs)
  28. self._last_log_thread = None
  29. def format(self, record):
  30. """Override format to add thread information."""
  31. # super() doesn't work in py2.6 as 'logging' uses old-style class
  32. log_str = logging.Formatter.format(self, record)
  33. # If msg comes from a different thread than our last, prepend
  34. # thread info. Most look like 'Account sync foo' or 'Folder
  35. # sync foo'.
  36. t_name = record.threadName
  37. if t_name == 'MainThread':
  38. return log_str # main thread doesn't get things prepended
  39. if t_name != self._last_log_thread:
  40. self._last_log_thread = t_name
  41. log_str = "%s:\n %s" % (t_name, log_str)
  42. else:
  43. log_str = " %s" % log_str
  44. return log_str
  45. class TTYUI(UIBase):
  46. def setup_consolehandler(self):
  47. """Backend specific console handler
  48. Sets up things and adds them to self.logger.
  49. :returns: The logging.Handler() for console output"""
  50. # create console handler with a higher log level
  51. ch = logging.StreamHandler()
  52. # ch.setLevel(logging.DEBUG)
  53. # create formatter and add it to the handlers
  54. self.formatter = TTYFormatter("%(message)s")
  55. ch.setFormatter(self.formatter)
  56. # add the handlers to the logger
  57. self.logger.addHandler(ch)
  58. self.logger.info(banner)
  59. # init lock for console output
  60. ch.createLock()
  61. return ch
  62. def isusable(self):
  63. """TTYUI is reported as usable when invoked on a terminal."""
  64. return sys.stdout.isatty() and sys.stdin.isatty()
  65. def getpass(self, username, config, errmsg=None):
  66. """TTYUI backend is capable of querying the password."""
  67. if errmsg:
  68. self.warn("%s: %s" % (username, errmsg))
  69. self._log_con_handler.acquire() # lock the console output
  70. try:
  71. return getpass("Enter password for user '%s': " % username)
  72. finally:
  73. self._log_con_handler.release()
  74. def mainException(self):
  75. if isinstance(sys.exc_info()[1], KeyboardInterrupt):
  76. self.logger.warn("Timer interrupted at user request; program "
  77. "terminating.\n")
  78. self.terminate()
  79. else:
  80. UIBase.mainException(self)
  81. def sleeping(self, sleepsecs, remainingsecs):
  82. """Sleep for sleepsecs, display remainingsecs to go.
  83. Does nothing if sleepsecs <= 0.
  84. Display a message on the screen if we pass a full minute.
  85. This implementation in UIBase does not support this, but some
  86. implementations return 0 for successful sleep and 1 for an
  87. 'abort', ie a request to sync immediately."""
  88. if sleepsecs > 0:
  89. if remainingsecs // 60 != (remainingsecs - sleepsecs) // 60:
  90. self.logger.info("Next refresh in %.1f minutes" % (
  91. remainingsecs / 60.0))
  92. time.sleep(sleepsecs)
  93. return 0