# Path: sen/exceptions.py
# class NotifyError(Exception):
#     """
#     There was an error, we should notify user.
#     """
# 
# Path: sen/tui/commands/base.py
# class FrontendPriority(CommandPriority):
#     """ command needs to be executed ASAP """
# 
# class BackendPriority(CommandPriority):
#     """ command takes long to execute """
# 
# class SameThreadPriority(CommandPriority):
#     """ run the task in same thread as UI """
# 
# class KeyNotMapped(Exception):
#     pass
# 
# Path: sen/tui/constants.py
# CLEAR_NOTIF_BAR_MESSAGE_IN = 5
# 
# Path: sen/tui/widgets/util.py
# class ThreadSafeFrame(urwid.Frame):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.update_lock = threading.Lock()
# 
#     def set_body(self, body):
#         with self.update_lock:
#             return super().set_body(body=body)
# 
#     def set_footer(self, footer):
#         with self.update_lock:
#             return super().set_footer(footer=footer)
# 
#     def set_header(self, header):
#         with self.update_lock:
#             return super().set_header(header=header)
# 
#     def render(self, size, focus=False):
#         with self.update_lock:
#             return super().render(size=size, focus=focus)
# 
# Path: sen/util.py
# def log_traceback(func):
#     @functools.wraps(func)
#     def wrapper(*args, **kwargs):
#         logger.info("function %s is about to be started", func)
#         try:
#             response = func(*args, **kwargs)
#         except Exception:
#             log_last_traceback()
#         else:
#             logger.info("function %s finished", func)
#             # TODO: how long it took?
#             return response
#     return wrapper
# 
# class OrderedSet(list):
#     def append(self, p_object):
#         if p_object in self:
#             self.remove(p_object)
#         return super().append(p_object)


import logging
import threading
import urwid
from concurrent.futures.thread import ThreadPoolExecutor
from sen.exceptions import NotifyError
from sen.tui.commands.base import (
    FrontendPriority, BackendPriority,
    SameThreadPriority, KeyNotMapped
)
from sen.tui.constants import CLEAR_NOTIF_BAR_MESSAGE_IN
from sen.tui.widgets.util import ThreadSafeFrame
from sen.util import log_traceback, OrderedSet
                del self.message_widget_dict[message]
        self.reload_footer(rebuild_statusbar=False)

    def notify_message(self, message, level="info", clear_if_dupl=True,
                       clear_in=CLEAR_NOTIF_BAR_MESSAGE_IN):
        """
        :param message, str
        :param level: str, {info, error}
        :param clear_if_dupl: bool, if True, don't display the notification again
        :param clear_in: seconds, remove the notificantion after some time

        opens notification popup.
        """
        with self.notifications_lock:
            if clear_if_dupl and message in self.message_widget_dict.keys():
                logger.debug("notification %r is already displayed", message)
                return
            logger.debug("display notification %r", message)
            widget = urwid.AttrMap(urwid.Text(message), "notif_{}".format(level))
        return self.notify_widget(widget, message=message, clear_in=clear_in)

    def notify_widget(self, widget, message=None, clear_in=CLEAR_NOTIF_BAR_MESSAGE_IN):
        """
        opens notification popup.

        :param widget: instance of Widget, widget to display
        :param message: str, message to remove from list of notifications
        :param clear_in: int, time seconds when notification should be removed
        """


# Path: sen/exceptions.py
# class NotifyError(Exception):
#     """
#     There was an error, we should notify user.
#     """
# 
# Path: sen/tui/commands/base.py
# class FrontendPriority(CommandPriority):
#     """ command needs to be executed ASAP """
# 
# class BackendPriority(CommandPriority):
#     """ command takes long to execute """
# 
# class SameThreadPriority(CommandPriority):
#     """ run the task in same thread as UI """
# 
# class KeyNotMapped(Exception):
#     pass
# 
# Path: sen/tui/constants.py
# CLEAR_NOTIF_BAR_MESSAGE_IN = 5
# 
# Path: sen/tui/widgets/util.py
# class ThreadSafeFrame(urwid.Frame):
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.update_lock = threading.Lock()
# 
#     def set_body(self, body):
#         with self.update_lock:
#             return super().set_body(body=body)
# 
#     def set_footer(self, footer):
#         with self.update_lock:
#             return super().set_footer(footer=footer)
# 
#     def set_header(self, header):
#         with self.update_lock:
#             return super().set_header(header=header)
# 
#     def render(self, size, focus=False):
#         with self.update_lock:
#             return super().render(size=size, focus=focus)
# 
# Path: sen/util.py
# def log_traceback(func):
#     @functools.wraps(func)
#     def wrapper(*args, **kwargs):
#         logger.info("function %s is about to be started", func)
#         try:
#             response = func(*args, **kwargs)
#         except Exception:
#             log_last_traceback()
#         else:
#             logger.info("function %s finished", func)
#             # TODO: how long it took?
#             return response
#     return wrapper
# 
# class OrderedSet(list):
#     def append(self, p_object):
#         if p_object in self:
#             self.remove(p_object)
#         return super().append(p_object)


# Path: sen/tui/ui.py
import logging
import threading
import urwid
from concurrent.futures.thread import ThreadPoolExecutor
from sen.exceptions import NotifyError
from sen.tui.commands.base import (
    FrontendPriority, BackendPriority,
    SameThreadPriority, KeyNotMapped
)
from sen.tui.constants import CLEAR_NOTIF_BAR_MESSAGE_IN
from sen.tui.widgets.util import ThreadSafeFrame
from sen.util import log_traceback, OrderedSet

                del self.message_widget_dict[message]
        self.reload_footer(rebuild_statusbar=False)

    def notify_message(self, message, level="info", clear_if_dupl=True,
                       clear_in=CLEAR_NOTIF_BAR_MESSAGE_IN):
        """
        :param message, str
        :param level: str, {info, error}
        :param clear_if_dupl: bool, if True, don't display the notification again
        :param clear_in: seconds, remove the notificantion after some time

        opens notification popup.
        """
        with self.notifications_lock:
            if clear_if_dupl and message in self.message_widget_dict.keys():
                logger.debug("notification %r is already displayed", message)
                return
            logger.debug("display notification %r", message)
            widget = urwid.AttrMap(urwid.Text(message), "notif_{}".format(level))
        return self.notify_widget(widget, message=message, clear_in=clear_in)

    def notify_widget(self, widget, message=None, clear_in=CLEAR_NOTIF_BAR_MESSAGE_IN):
        """
        opens notification popup.

        :param widget: instance of Widget, widget to display
        :param message: str, message to remove from list of notifications
        :param clear_in: int, time seconds when notification should be removed
        """


        @log_traceback