示例#1
0
def test_listeners():
    """`listeners()` returns a copied list of listeners."""

    call_me = Mock()
    ee = BaseEventEmitter()

    @ee.on('event')
    def event_handler():
        pass

    @ee.once('event')
    def once_handler():
        pass

    listeners = ee.listeners('event')

    assert listeners[0] == event_handler
    assert listeners[1] == once_handler

    # listeners is a copy, you can't mutate the innards this way
    listeners[0] = call_me

    ee.emit('event')

    call_me.assert_not_called()
示例#2
0
def test_properties_preserved():
    """Test that the properties of decorated functions are preserved."""

    call_me = Mock()
    call_me_also = Mock()
    ee = BaseEventEmitter()

    @ee.on('always')
    def always_event_handler():
        """An event handler."""
        call_me()

    @ee.once('once')
    def once_event_handler():
        """Another event handler."""
        call_me_also()

    assert always_event_handler.__doc__ == 'An event handler.'
    assert once_event_handler.__doc__ == 'Another event handler.'

    always_event_handler()
    call_me.assert_called_once()

    once_event_handler()
    call_me_also.assert_called_once()

    call_me_also.reset_mock()

    # Calling the event handler directly doesn't clear the handler
    ee.emit('once')
    call_me_also.assert_called_once()
示例#3
0
def test_new_listener_event():
    """The 'new_listener' event fires whenever a new listerner is added."""

    call_me = Mock()
    ee = BaseEventEmitter()

    ee.on('new_listener', call_me)

    # Should fire new_listener event
    @ee.on('event')
    def event_handler(data):
        pass

    call_me.assert_called_once()
示例#4
0
def test_emit_return():
    """Emit returns True when handlers are registered on an event, and false
    otherwise.
    """

    call_me = Mock()
    ee = BaseEventEmitter()

    # make sure emitting without a callback returns False
    assert not ee.emit('data')

    # add a callback
    ee.on('data')(call_me)

    # should return True now
    assert ee.emit('data')
示例#5
0
def test_once_removal():
    """Removal of once functions works
    """

    ee = BaseEventEmitter()

    def once_handler(data):
        pass

    handle = ee.once('event', once_handler)

    assert handle == once_handler

    ee.remove_listener('event', handle)

    assert ee._events['event'] == OrderedDict()
示例#6
0
def test_once():
    """Test that `once()` method works propers.
    """

    # very similar to "test_emit" but also makes sure that the event
    # gets removed afterwards

    call_me = Mock()
    ee = BaseEventEmitter()

    def once_handler(data):
        assert data == 'emitter is emitted!'
        call_me()

    # Tests to make sure that after event is emitted that it's gone.
    ee.once('event', once_handler)

    ee.emit('event', 'emitter is emitted!')

    call_me.assert_called_once()

    assert ee._events['event'] == OrderedDict()
示例#7
0
def test_listener_removal_on_emit():
    """Test that a listener removed during an emit is called inside the current
    emit cycle.
    """

    call_me = Mock()
    ee = BaseEventEmitter()

    def should_remove():
        ee.remove_listener('remove', call_me)

    ee.on('remove', should_remove)
    ee.on('remove', call_me)

    ee.emit('remove')

    call_me.assert_called_once()

    call_me.reset_mock()

    # Also test with the listeners added in the opposite order
    ee = BaseEventEmitter()
    ee.on('remove', call_me)
    ee.on('remove', should_remove)

    ee.emit('remove')

    call_me.assert_called_once()
示例#8
0
class SpeakingEyeApp(Gtk.Application):
    def __init__(
            self,
            app_id: str,
            # TODO: replace with config_reader
            config: Dict,
            config_reader: ConfigReader,
            logger: logging.Logger,
            application_info_matcher: ApplicationInfoMatcher,
            activity_reader: ActivityReader,
            files_provider: FilesProvider) -> None:
        super().__init__()
        self.logger = logger
        self.config_reader = config_reader

        self.connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
        self.screen_saver_bus_names = self.__dbus_get_screen_saver_bus_names()

        self.files_provider = files_provider

        language = get(config, 'language') or 'en'
        self.localizator = Localizator(self.files_provider.i18n_dir, language)

        self.theme = get(config, 'theme') or 'dark'
        self.active_icon = self.get_icon(IconState.ACTIVE)
        self.disabled_icon = self.get_icon(IconState.DISABLED)
        self.tray_icon = TrayIcon(app_id, self.disabled_icon,
                                  self.create_tray_menu())

        self.screen: Optional[Wnck.Screen] = None
        self.main_loop: Optional[GObject.MainLoop] = None
        self.name_changed_handler_id = None

        self.previous_active_window_name: Optional[str] = None
        self.previous_wm_class: Optional[str] = None

        self.overtime_timer = \
            Timer('overtime_timer', handler=self.overtime_timer_handler, interval_ms=1 * 60 * 1000, repeat=True)
        self.break_timer = \
            Timer('break_timer', handler=self.break_timer_handler, interval_ms=1 * 60 * 1000, repeat=True)
        self.distracting_app_timer = \
            Timer('distracting_app_timer', handler=self.distracting_app_timer_handler, interval_ms=1 * 60 * 1000,
                  repeat=True)

        self.event = BaseEventEmitter()

        self.event.on(ApplicationEvent.DISTRACTING_APP_OVERTIME.value,
                      self.show_distracting_app_overtime_notification)

        self.start_time = datetime.now()
        self.is_work_time = False
        self.is_work_time_update_time = self.start_time
        self.last_overtime_notification: Optional[Notification] = None
        self.last_break_notification: Optional[Notification] = None
        self.last_lock_screen_time: Optional[datetime] = None
        self.is_lock_screen_activated = False
        self.has_distracting_app_overtime_notification_shown = False
        self.is_overtime_notification_allowed_to_show = True
        self.is_break_notification_allowed_to_show = True

        self.user_work_time_hour_limit = config_reader.get_work_time_limit()
        self.user_breaks_interval_hours = get(
            config, 'time_limits.breaks_interval_hours') or 3
        self.user_distracting_apps_mins = config_reader.get_distracting_apps_mins(
        )

        self.writer = ActivityWriter(self.files_provider)

        self.app_info_matcher = application_info_matcher

        today_raw_data_file_path = self.files_provider.get_raw_data_file_path(
            date.today())
        self.holder = ActivityStatHolder(
            activity_reader.read(today_raw_data_file_path))

        self.holder.initialize_stats(self.app_info_matcher.detailed_app_infos)
        self.holder.initialize_stats(
            self.app_info_matcher.distracting_app_infos)

        self.current_activity: Optional[Activity] = None

        self.writer.event.on(ActivityWriter.NEW_DAY_EVENT,
                             self.on_new_day_started)

        self.logger.debug(
            f'Set user work time limit to [{self.user_work_time_hour_limit}] hours'
        )
        self.logger.debug(
            f'Set user user breaks interval to [{self.user_breaks_interval_hours}] hours'
        )

        Notify.init(app_id)
        self.__dbus_subscribe_to_screen_saver_signals()

        start_msg = self.localizator.get(
            'notification.start',
            start_time=self.start_time.strftime("%H:%M:%S"))
        self.logger.debug(start_msg)
        self.new_notification(msg=start_msg).show()

    def __dbus_method_call(self, bus_name: str, object_path: str,
                           interface_name: str, method_name: str) -> Any:
        if not self.connection:
            raise Exception('self.connection should be set!')

        no_parameters = None
        default_reply_type = None
        default_call_timeout = -1
        not_cancellable = None

        raw_result = self.connection.call_sync(
            bus_name, object_path, interface_name, method_name, no_parameters,
            default_reply_type, Gio.DBusCallFlags.NONE, default_call_timeout,
            not_cancellable)

        if raw_result:
            result, = raw_result

            return result

        return None

    def __dbus_get_all_bus_names(self) -> List[str]:
        return self.__dbus_method_call('org.freedesktop.DBus',
                                       '/org/freedesktop/DBus',
                                       'org.freedesktop.DBus', 'ListNames')

    def __dbus_lock_screen(self) -> None:
        for bus in self.screen_saver_bus_names:
            if bus == 'org.freedesktop.ScreenSaver':
                # NOTE: that server is just interface without implementation
                continue

            interface_name = f'/{bus.replace(".", "/")}'

            try:
                self.__dbus_method_call(bus, interface_name, bus, 'Lock')
            except Exception as e:
                self.logger.warning(
                    f'Please ignore it if lock screen works well. Lock screen error: [{e}]'
                )

    def __on_screen_saver_active_changed(self, connection: Gio.DBusConnection,
                                         sender_name: str, object_path: str,
                                         interface_name: str, signal_name: str,
                                         parameters: GLib.Variant) -> None:
        is_activated, = parameters
        self.is_lock_screen_activated = is_activated

        now = datetime.now()

        if self.is_lock_screen_activated:
            current_activity = Value.get_or_raise(self.current_activity,
                                                  'current_activity')

            self.previous_wm_class = current_activity.wm_class
            self.previous_active_window_name = current_activity.window_name

            wm_class = SpecialWmClass.LOCK_SCREEN.value
            window_name = ''
        else:
            if self.is_work_time:
                self.last_lock_screen_time = now

            wm_class = Value.get_or_raise(self.previous_wm_class,
                                          'previous_wm_class')
            window_name = Value.get_or_raise(self.previous_active_window_name,
                                             'previous_active_window_name')

        self.on_open_window(wm_class, window_name, now)

    def __dbus_get_screen_saver_bus_names(self) -> List[str]:
        bus_names = self.__dbus_get_all_bus_names()
        screen_saver_re = re.compile(r'^org\..*\.ScreenSaver$')

        return list(filter(screen_saver_re.match, bus_names))

    def __dbus_subscribe_to_screen_saver_signals(self) -> None:
        if not self.screen_saver_bus_names:
            raise Exception('self.screen_saver_bus_names should be set!')

        for bus_name in self.screen_saver_bus_names:
            self.connection.signal_subscribe(
                None, bus_name, 'ActiveChanged', None, None,
                Gio.DBusSignalFlags.NONE,
                self.__on_screen_saver_active_changed)

    def do_activate(self) -> None:
        signal.signal(signal.SIGTERM, self.handle_sigterm)
        self.screen = Wnck.Screen.get_default()
        self.screen.connect('active-window-changed',
                            self.on_active_window_changed)
        self.main_loop = GObject.MainLoop()
        self.overtime_timer.start()
        self.break_timer.start()
        self.distracting_app_timer.start()

    def on_active_window_changed(self, screen: Wnck.Screen,
                                 previously_active_window: Gtk.Window) -> None:
        now = datetime.now()

        # to prevent double handler connections
        if previously_active_window and self.name_changed_handler_id:
            previously_active_window.disconnect(self.name_changed_handler_id)

        active_window = screen.get_active_window()

        if active_window:
            self.name_changed_handler_id = active_window.connect(
                'name-changed', self.on_name_changed)
            wm_class = get_wm_class(active_window.get_xid())
            window_name = get_window_name(active_window)
        else:
            wm_class = SpecialWmClass.DESKTOP.value
            window_name = ''

        self.on_open_window(wm_class, window_name, now)

    def on_open_window(self, wm_class: str, window_name: str,
                       now: datetime) -> None:
        new_activity = Activity(wm_class, window_name, now, self.is_work_time)

        self.__on_activity_changed(self.current_activity, new_activity)

    def on_name_changed(self, window: Wnck.Window) -> None:
        now = datetime.now()

        current_activity = Value.get_or_raise(self.current_activity,
                                              'current_activity')
        window_name = get_window_name(window)

        new_activity = Activity(current_activity.wm_class, window_name, now,
                                current_activity.is_work_time)

        self.__on_activity_changed(current_activity, new_activity)

    def __on_activity_changed(self, previous_activity: Optional[Activity],
                              next_activity: Activity) -> None:
        now = datetime.now()

        if previous_activity is not None:
            previous_activity.set_end_time(now)
            self.writer.write(previous_activity)
            self.holder.update_stat(previous_activity)

        # NOTE: previous_activity is None when it is the first activity after starting
        previous_activity_app_name = \
            '' if previous_activity is None else f'{previous_activity.wm_class}|{previous_activity.window_name}'
        self.logger.debug(
            f'{now}: {previous_activity_app_name} -> '
            f'{next_activity.wm_class}|{next_activity.window_name}')

        self.app_info_matcher.set_if_matched(next_activity)

        self.current_activity = next_activity
        # NOTE: for reshowing notification when open distracting app once more time
        self.has_distracting_app_overtime_notification_shown = False

    def start_main_loop(self) -> None:
        try:
            self.main_loop.run()  # type: ignore[union-attr]
        except KeyboardInterrupt:
            self.stop()

    def stop(self) -> None:
        now = datetime.now()

        finish_time = now
        work_time = finish_time - self.start_time
        work_time -= timedelta(microseconds=work_time.microseconds)

        finish_msg = self.localizator.get(
            'notification.finish',
            finish_time=finish_time.strftime("%H:%M:%S"))
        work_time_msg = self.localizator.get('notification.work_time',
                                             work_time=work_time)

        self.logger.debug(f'{finish_msg}\n{work_time_msg}')

        self.logger.info(
            '              title |          work_time |            off_time')
        self.logger.info(
            '--------------------------------------------------------------')

        for holder_item in self.holder.items():
            # cast untyped title and stat to str and ActivityStat
            title, stat = cast(Tuple[str, ActivityStat], holder_item)

            padded_title = title.rjust(19, ' ')
            padded_work_time = f'{stat.work_time}'.rjust(19, ' ')
            padded_off_time = f'{stat.off_time}'.rjust(20, ' ')
            self.logger.info(
                f'{padded_title} |{padded_work_time} |{padded_off_time}')

        self.new_notification(msg=f'{finish_msg}; {work_time_msg}').show()
        Notify.uninit()

        self.main_loop.quit()  # type: ignore[union-attr]

    def on_close_item_click(self, menu_item: Gtk.MenuItem) -> None:
        self.stop()

    def on_open_report_item_click(self, menu_item: Gtk.MenuItem) -> None:
        browser = webbrowser.get(
            self.config_reader.get_report_server_browser())
        host = self.config_reader.get_report_server_host()
        port = self.config_reader.get_report_server_port()
        url = f'http://{host}:{port}'

        browser.open_new_tab(url)

    def set_work_time_state(self, value: bool) -> None:
        if value == self.is_work_time:
            self.logger.debug(
                'Trying to change is_work_time to the same value')
            return

        self.is_work_time = value

        current_activity = Value.get_or_raise(self.current_activity,
                                              'current_activity')
        now = datetime.now()
        new_activity = Activity(current_activity.wm_class,
                                current_activity.window_name, now,
                                self.is_work_time)

        self.__on_activity_changed(current_activity, new_activity)

        self.is_work_time_update_time = now

        self.logger.debug(f'Set Work Time to [{self.is_work_time}]')

        icon = self.active_icon if self.is_work_time else self.disabled_icon
        self.tray_icon.set_icon_if_exist(icon)

        if self.is_work_time:
            self.last_lock_screen_time = now
            self.last_break_notification = None
            self.last_overtime_notification = None

    def on_work_state_checkbox_item_click(self,
                                          menu_item: Gtk.MenuItem) -> None:
        self.set_work_time_state(not self.is_work_time)

    def create_tray_menu(self) -> Gtk.Menu:
        menu = Gtk.Menu()

        work_state_checkbox_item = Gtk.CheckMenuItem(
            self.localizator.get('tray.work_time'))
        work_state_checkbox_item.connect(
            'activate', self.on_work_state_checkbox_item_click)
        menu.append(work_state_checkbox_item)

        open_report_item = Gtk.MenuItem(
            self.localizator.get('tray.open_report'))
        open_report_item.connect('activate', self.on_open_report_item_click)
        menu.append(open_report_item)

        menu.append(Gtk.SeparatorMenuItem())

        close_item = Gtk.MenuItem(self.localizator.get('tray.close'))
        close_item.connect('activate', self.on_close_item_click)
        menu.append(close_item)

        menu.show_all()

        return menu

    def on_overtime_notification_closed(self) -> None:
        self.is_overtime_notification_allowed_to_show = True

    def on_finish_work_action_clicked(self) -> None:
        self.set_work_time_state(False)

    def on_take_break_clicked(self) -> None:
        self.__dbus_lock_screen()

    def new_notification(self,
                         msg: str,
                         urgency: Optional[Notify.Urgency] = None,
                         listen_closed_event: bool = False) -> Notification:
        # TODO: replace with NotificationFactory (Factory Pattern)
        return Notification('Speaking Eye', msg, self.active_icon, urgency,
                            listen_closed_event)

    def show_overtime_notification(self) -> None:
        msg = self.localizator.get('notification.overtime.text',
                                   hours=self.user_work_time_hour_limit)

        notification = self.new_notification(msg,
                                             Notify.Urgency.CRITICAL,
                                             listen_closed_event=True)

        notification.add_buttons((
            self.localizator.get(
                'notification.overtime.left_button'),  # finish_work
            self.localizator.get(
                'notification.overtime.right_button')  # remind_later
        ))

        notification.event.on(NotificationEvent.CLOSED.value,
                              self.on_overtime_notification_closed)
        notification.event.on(NotificationEvent.LEFT_BUTTON_CLICKED.value,
                              self.on_finish_work_action_clicked)

        notification.show()

        self.last_overtime_notification = notification
        self.is_overtime_notification_allowed_to_show = False

    def show_distracting_app_overtime_notification(
            self, title: str, total_time: timedelta) -> None:
        distracting_minutes = total_time.total_seconds() // 60
        text = self.localizator.get_random('notification.distracting_texts',
                                           10)
        emoji = choice(DISTRACTING_NOTIFICATION_EMOJIS)
        msg = self.localizator.get(
            'notification.distracting',
            app_title=title,
            distracting_minutes=int(distracting_minutes),
            text=text,
            emoji=emoji)

        self.new_notification(msg).show()

    def __on_break_notification_closed(self) -> None:
        self.is_break_notification_allowed_to_show = True

    def show_break_notification(self) -> None:
        emoji = choice(BREAK_TIME_EMOJIS)
        msg = self.localizator.get('notification.break.text', emoji=emoji)

        notification = self.new_notification(msg,
                                             Notify.Urgency.CRITICAL,
                                             listen_closed_event=True)

        notification.add_buttons((
            self.localizator.get(
                'notification.break.left_button'),  # take_break
            self.localizator.get(
                'notification.break.right_button')  # remind_later
        ))

        notification.event.on(NotificationEvent.CLOSED.value,
                              self.__on_break_notification_closed)
        notification.event.on(NotificationEvent.LEFT_BUTTON_CLICKED.value,
                              self.on_take_break_clicked)

        notification.show()

        self.last_break_notification = notification
        self.is_break_notification_allowed_to_show = False

    def on_new_day_started(self) -> None:
        open_new_file_msg = self.localizator.get('notification.new_day')

        self.logger.debug(open_new_file_msg)
        self.new_notification(msg=open_new_file_msg).show()

        self.set_work_time_state(False)

    def handle_sigterm(self, signal_number: int, frame: FrameType) -> None:
        self.stop()

    def __need_to_show_overtime_notification(self) -> bool:
        if not self.is_work_time:
            return False

        if self.is_lock_screen_activated:
            return False

        now = datetime.now()
        current_activity = Value.get_or_raise(self.current_activity,
                                              'current_activity')
        seconds_in_current_activity = (
            now - current_activity.start_time).total_seconds()
        total_work_time_seconds = seconds_in_current_activity + self.holder.total_work_time.total_seconds(
        )
        is_overtime_started = total_work_time_seconds >= self.user_work_time_hour_limit * 60 * 60

        if not is_overtime_started:
            return False

        if self.last_overtime_notification is None:
            return True

        if self.last_overtime_notification.last_shown is None:
            return True

        if not self.is_overtime_notification_allowed_to_show:
            return False

        seconds_from_last_notification = (
            now - self.last_overtime_notification.last_shown).total_seconds()
        interval_seconds = 15 * 60

        return seconds_from_last_notification >= interval_seconds

    def overtime_timer_handler(self) -> None:
        if not self.__need_to_show_overtime_notification():
            return

        self.show_overtime_notification()

    def __need_to_show_break_notification(self) -> bool:
        if not self.is_work_time:
            return False

        if self.is_lock_screen_activated:
            return False

        if not self.is_break_notification_allowed_to_show:
            return False

        now = datetime.now()
        start_work_time = self.is_work_time_update_time
        last_break_time = self.last_lock_screen_time if self.last_lock_screen_time else start_work_time

        if (now - last_break_time
            ).total_seconds() < self.user_breaks_interval_hours * 60 * 60:
            return False

        last_break_reminder_time = Value.get_or_default(
            lambda: self.last_break_notification.
            last_shown,  # type: ignore[union-attr]
            start_work_time)

        if (now - last_break_reminder_time).total_seconds() < 15 * 60:
            return False

        return True

    def break_timer_handler(self) -> None:
        if not self.__need_to_show_break_notification():
            return

        self.show_break_notification()

    def distracting_app_timer_handler(self) -> None:
        # TODO: ⚡️ run this timer only if work started
        if not self.is_work_time:
            return

        if self.has_distracting_app_overtime_notification_shown:
            return

        current_activity = Value.get_or_raise(self.current_activity,
                                              'current_activity')
        application_info = current_activity.application_info

        if application_info is None:
            # NOTE: It is None if detailed/distracting lists do not contain such activity
            #       See more in ApplicationInfoMatcher.set_if_matched()
            return

        if not application_info.is_distracting:
            return

        now = datetime.now()
        current_stats = self.holder[application_info.title]
        total_distracting_time = now - current_activity.start_time + current_stats.work_time

        if total_distracting_time.total_seconds(
        ) < self.user_distracting_apps_mins * 60:
            return

        self.event.emit(ApplicationEvent.DISTRACTING_APP_OVERTIME.value,
                        application_info.title, total_distracting_time)

        self.has_distracting_app_overtime_notification_shown = True

    def get_icon(self, icon_state: IconState) -> Path:
        if not self.theme:
            raise Exception('self.theme should be set!')

        path = self.files_provider.get_icon_file_path(self.theme, icon_state)

        if not path.exists():
            self.logger.warning(f'Icon [{path}] not found')

        return path
示例#9
0
 def __init__(self, bus):
     BaseEventEmitter.__init__(self)
     self.path = '/'
     self.services = []
     dbus.service.Object.__init__(self, bus, self.path)
示例#10
0
 def __init__(self, files_provider: FilesProvider) -> None:
     self.__files_provider = files_provider
     self.__current_file: Optional[TextIO] = None
     self.__current_file_path: Optional[Path] = None
     self.event = BaseEventEmitter()
示例#11
0

def jpg_bytes(to_jpg):
    _, jpg = cv2.imencode('.jpg', to_jpg)
    return jpg.tobytes()


vs = VideoCaptureAsync(0)
time.sleep(2.0)

someState = State()
pt = ProcessingThread(vs, someState)
wt = WebsocketThread(someState)
sc = ServoControl(someState)

ee = BaseEventEmitter()
sendee = AsyncIOEventEmitter(loop=wt.sendLoop)


@ee.on('command_received')
def command_received(cmd):
    someState.handle_command(cmd)
    sc.handle_command(cmd)


@ee.on('ball_found')
def ball_found():
    sc.update_ball_camera()


app = Flask(__name__)
示例#12
0
def test_uplift_emit():
    call_me = Mock()

    base_ee = BaseEventEmitter()

    @base_ee.on('base_event')
    def base_handler():
        call_me('base event on base emitter')

    @base_ee.on('shared_event')
    def shared_base_handler():
        call_me('shared event on base emitter')

    uplifted_ee = uplift(UpliftedEventEmitter, base_ee)

    assert isinstance(uplifted_ee,
                      UpliftedEventEmitter), 'Returns an uplifted emitter'

    @uplifted_ee.on('uplifted_event')
    def uplifted_handler():
        call_me('uplifted event on uplifted emitter')

    @uplifted_ee.on('shared_event')
    def shared_uplifted_handler():
        call_me('shared event on uplifted emitter')

    # Events on uplifted proxy correctly
    assert uplifted_ee.emit('base_event')
    assert uplifted_ee.emit('shared_event')
    assert uplifted_ee.emit('uplifted_event')

    call_me.assert_has_calls([
        call('base event on base emitter'),
        call('shared event on uplifted emitter'),
        call('shared event on base emitter'),
        call('uplifted event on uplifted emitter')
    ])

    call_me.reset_mock()

    # Events on underlying proxy correctly
    assert base_ee.emit('base_event')
    assert base_ee.emit('shared_event')
    assert base_ee.emit('uplifted_event')

    call_me.assert_has_calls([
        call('base event on base emitter'),
        call('shared event on base emitter'),
        call('shared event on uplifted emitter'),
        call('uplifted event on uplifted emitter')
    ])

    call_me.reset_mock()

    # Quick check for unwrap
    uplifted_ee.unwrap()

    with pytest.raises(AttributeError):
        getattr(uplifted_ee, 'unwrap')

    with pytest.raises(AttributeError):
        getattr(base_ee, 'unwrap')

    assert not uplifted_ee.emit('base_event')
    assert uplifted_ee.emit('shared_event')
    assert uplifted_ee.emit('uplifted_event')

    assert base_ee.emit('base_event')
    assert base_ee.emit('shared_event')
    assert not base_ee.emit('uplifted_event')

    call_me.assert_has_calls([
        # No listener for base event on uplifted
        call('shared event on uplifted emitter'),
        call('uplifted event on uplifted emitter'),
        call('base event on base emitter'),
        call('shared event on base emitter')
        # No listener for uplifted event on uplifted
    ])
示例#13
0
def test_listener_removal():
    """Removing listeners removes the correct listener from an event."""

    ee = BaseEventEmitter()

    # Some functions to pass to the EE
    def first():
        return 1

    ee.on('event', first)

    @ee.on('event')
    def second():
        return 2

    @ee.on('event')
    def third():
        return 3

    def fourth():
        return 4

    ee.on('event', fourth)

    assert ee._events['event'] == OrderedDict([
        (first, first), (second, second), (third, third), (fourth, fourth)
    ])

    ee.remove_listener('event', second)

    assert ee._events['event'] == OrderedDict([(first, first), (third, third),
                                               (fourth, fourth)])

    ee.remove_listener('event', first)
    assert ee._events['event'] == OrderedDict([(third, third),
                                               (fourth, fourth)])

    ee.remove_all_listeners('event')
    assert ee._events['event'] == OrderedDict()
示例#14
0
def test_listener_removal_on_emit():
    """Test that a listener removed during an emit is called inside the current
    emit cycle.
    """

    call_me = Mock()
    ee = BaseEventEmitter()

    def should_remove():
        ee.remove_listener('remove', call_me)

    ee.on('remove', should_remove)
    ee.on('remove', call_me)

    ee.emit('remove')

    call_me.assert_called_once()

    call_me.reset_mock()

    # Also test with the listeners added in the opposite order
    ee = BaseEventEmitter()
    ee.on('remove', call_me)
    ee.on('remove', should_remove)

    ee.emit('remove')

    call_me.assert_called_once()
示例#15
0
    def __init__(
            self,
            app_id: str,
            # TODO: replace with config_reader
            config: Dict,
            config_reader: ConfigReader,
            logger: logging.Logger,
            application_info_matcher: ApplicationInfoMatcher,
            activity_reader: ActivityReader,
            files_provider: FilesProvider) -> None:
        super().__init__()
        self.logger = logger
        self.config_reader = config_reader

        self.connection = Gio.bus_get_sync(Gio.BusType.SESSION, None)
        self.screen_saver_bus_names = self.__dbus_get_screen_saver_bus_names()

        self.files_provider = files_provider

        language = get(config, 'language') or 'en'
        self.localizator = Localizator(self.files_provider.i18n_dir, language)

        self.theme = get(config, 'theme') or 'dark'
        self.active_icon = self.get_icon(IconState.ACTIVE)
        self.disabled_icon = self.get_icon(IconState.DISABLED)
        self.tray_icon = TrayIcon(app_id, self.disabled_icon,
                                  self.create_tray_menu())

        self.screen: Optional[Wnck.Screen] = None
        self.main_loop: Optional[GObject.MainLoop] = None
        self.name_changed_handler_id = None

        self.previous_active_window_name: Optional[str] = None
        self.previous_wm_class: Optional[str] = None

        self.overtime_timer = \
            Timer('overtime_timer', handler=self.overtime_timer_handler, interval_ms=1 * 60 * 1000, repeat=True)
        self.break_timer = \
            Timer('break_timer', handler=self.break_timer_handler, interval_ms=1 * 60 * 1000, repeat=True)
        self.distracting_app_timer = \
            Timer('distracting_app_timer', handler=self.distracting_app_timer_handler, interval_ms=1 * 60 * 1000,
                  repeat=True)

        self.event = BaseEventEmitter()

        self.event.on(ApplicationEvent.DISTRACTING_APP_OVERTIME.value,
                      self.show_distracting_app_overtime_notification)

        self.start_time = datetime.now()
        self.is_work_time = False
        self.is_work_time_update_time = self.start_time
        self.last_overtime_notification: Optional[Notification] = None
        self.last_break_notification: Optional[Notification] = None
        self.last_lock_screen_time: Optional[datetime] = None
        self.is_lock_screen_activated = False
        self.has_distracting_app_overtime_notification_shown = False
        self.is_overtime_notification_allowed_to_show = True
        self.is_break_notification_allowed_to_show = True

        self.user_work_time_hour_limit = config_reader.get_work_time_limit()
        self.user_breaks_interval_hours = get(
            config, 'time_limits.breaks_interval_hours') or 3
        self.user_distracting_apps_mins = config_reader.get_distracting_apps_mins(
        )

        self.writer = ActivityWriter(self.files_provider)

        self.app_info_matcher = application_info_matcher

        today_raw_data_file_path = self.files_provider.get_raw_data_file_path(
            date.today())
        self.holder = ActivityStatHolder(
            activity_reader.read(today_raw_data_file_path))

        self.holder.initialize_stats(self.app_info_matcher.detailed_app_infos)
        self.holder.initialize_stats(
            self.app_info_matcher.distracting_app_infos)

        self.current_activity: Optional[Activity] = None

        self.writer.event.on(ActivityWriter.NEW_DAY_EVENT,
                             self.on_new_day_started)

        self.logger.debug(
            f'Set user work time limit to [{self.user_work_time_hour_limit}] hours'
        )
        self.logger.debug(
            f'Set user user breaks interval to [{self.user_breaks_interval_hours}] hours'
        )

        Notify.init(app_id)
        self.__dbus_subscribe_to_screen_saver_signals()

        start_msg = self.localizator.get(
            'notification.start',
            start_time=self.start_time.strftime("%H:%M:%S"))
        self.logger.debug(start_msg)
        self.new_notification(msg=start_msg).show()
示例#16
0
from functools import partial
from pyee import BaseEventEmitter
event_emitter  = BaseEventEmitter()

class BasePlugin:
    
    def __init__(self, event_e):
        self._emitter = event_e
        self._listeners: list = []
        self.getListenersList()

    def getListenersList(self):
        self._listeners = [
            # means get all the methods that qualify as a listener
            method for item in dir(self) 
            if 
                callable((method:=getattr(self, item))
                and not item == "getMethodsList" 
                and not item.startswith("_")
                and     item.endswith("_listener"))
        ]
            
    def subscribeEvent(self, event_name, listener):
        self._emitter.on(event_name, listener)
        event_emitter.remove_listener
        return partial(self._emitter.remove_listener, event_name, listener)

    def loadPlugin(self):
        raise NotImplementedError(
            "this method should be implemented by a subclass"
        )
示例#17
0
def test_listener_removal():
    """Removing listeners removes the correct listener from an event."""

    ee = BaseEventEmitter()

    # Some functions to pass to the EE
    def first():
        return 1

    ee.on('event', first)

    @ee.on('event')
    def second():
        return 2

    @ee.on('event')
    def third():
        return 3

    def fourth():
        return 4

    ee.on('event', fourth)

    assert ee._events['event'] == OrderedDict([
        (first, first),
        (second, second),
        (third, third),
        (fourth, fourth)
    ])

    ee.remove_listener('event', second)

    assert ee._events['event'] == OrderedDict([
        (first, first),
        (third, third),
        (fourth, fourth)
    ])

    ee.remove_listener('event', first)
    assert ee._events['event'] == OrderedDict([
        (third, third),
        (fourth, fourth)
    ])

    ee.remove_all_listeners('event')
    assert ee._events['event'] == OrderedDict()
示例#18
0
class Frame(ChannelOwner):
    def __init__(self, parent: ChannelOwner, type: str, guid: str,
                 initializer: Dict) -> None:
        super().__init__(parent, type, guid, initializer)
        self._parent_frame = from_nullable_channel(
            initializer.get("parentFrame"))
        if self._parent_frame:
            self._parent_frame._child_frames.append(self)
        self._name = initializer["name"]
        self._url = initializer["url"]
        self._detached = False
        self._child_frames: List[Frame] = []
        self._page: "Page"
        self._load_states: Set[str] = set(initializer["loadStates"])
        self._event_emitter = BaseEventEmitter()
        self._channel.on(
            "loadstate",
            lambda params: self._on_load_state(params.get("add"),
                                               params.get("remove")),
        )
        self._channel.on(
            "navigated",
            lambda params: self._on_frame_navigated(params),
        )

    def _on_load_state(self,
                       add: DocumentLoadState = None,
                       remove: DocumentLoadState = None) -> None:
        if add:
            self._load_states.add(add)
            self._event_emitter.emit("loadstate", add)
        elif remove and remove in self._load_states:
            self._load_states.remove(remove)

    def _on_frame_navigated(self, event: FrameNavigatedEvent) -> None:
        self._url = event["url"]
        self._name = event["name"]
        self._event_emitter.emit("navigated", event)
        if "error" not in event and hasattr(self, "_page") and self._page:
            self._page.emit("framenavigated", self)

    async def goto(
        self,
        url: str,
        timeout: int = None,
        waitUntil: DocumentLoadState = None,
        referer: str = None,
    ) -> Optional[Response]:
        return cast(
            Optional[Response],
            from_nullable_channel(await self._channel.send(
                "goto", locals_to_params(locals()))),
        )

    def _setup_navigation_wait_helper(self, timeout: int = None) -> WaitHelper:
        wait_helper = WaitHelper(self._loop)
        wait_helper.reject_on_event(
            self._page, "close",
            Error("Navigation failed because page was closed!"))
        wait_helper.reject_on_event(
            self._page, "crash",
            Error("Navigation failed because page crashed!"))
        wait_helper.reject_on_event(
            self._page,
            "framedetached",
            Error("Navigating frame was detached!"),
            lambda frame: frame == self,
        )
        if timeout is None:
            timeout = self._page._timeout_settings.navigation_timeout()
        wait_helper.reject_on_timeout(timeout,
                                      f"Timeout {timeout}ms exceeded.")
        return wait_helper

    async def waitForNavigation(
        self,
        url: URLMatch = None,
        waitUntil: DocumentLoadState = None,
        timeout: int = None,
    ) -> Optional[Response]:
        if not waitUntil:
            waitUntil = "load"

        if timeout is None:
            timeout = self._page._timeout_settings.navigation_timeout()
        deadline = monotonic_time() + timeout
        wait_helper = self._setup_navigation_wait_helper(timeout)
        matcher = URLMatcher(url) if url else None

        def predicate(event: Any) -> bool:
            # Any failed navigation results in a rejection.
            if event.get("error"):
                return True
            return not matcher or matcher.matches(event["url"])

        event = await wait_helper.wait_for_event(
            self._event_emitter,
            "navigated",
            predicate=predicate,
        )
        if "error" in event:
            raise Error(event["error"])

        if waitUntil not in self._load_states:
            timeout = deadline - monotonic_time()
            if timeout > 0:
                await self.waitForLoadState(state=waitUntil, timeout=timeout)

        if "newDocument" in event and "request" in event["newDocument"]:
            request = from_channel(event["newDocument"]["request"])
            return await request.response()
        return None

    async def waitForLoadState(self,
                               state: DocumentLoadState = None,
                               timeout: int = None) -> None:
        if not state:
            state = "load"
        if state not in ("load", "domcontentloaded", "networkidle"):
            raise Error(
                "state: expected one of (load|domcontentloaded|networkidle)")
        if state in self._load_states:
            return
        wait_helper = self._setup_navigation_wait_helper(timeout)
        await wait_helper.wait_for_event(self._event_emitter, "loadstate",
                                         lambda s: s == state)

    async def frameElement(self) -> ElementHandle:
        return from_channel(await self._channel.send("frameElement"))

    async def evaluate(self,
                       expression: str,
                       arg: Serializable = None,
                       force_expr: bool = None) -> Any:
        if not is_function_body(expression):
            force_expr = True
        return parse_result(await self._channel.send(
            "evaluateExpression",
            dict(
                expression=expression,
                isFunction=not (force_expr),
                arg=serialize_argument(arg),
            ),
        ))

    async def evaluateHandle(self,
                             expression: str,
                             arg: Serializable = None,
                             force_expr: bool = None) -> JSHandle:
        if not is_function_body(expression):
            force_expr = True
        return from_channel(await self._channel.send(
            "evaluateExpressionHandle",
            dict(
                expression=expression,
                isFunction=not (force_expr),
                arg=serialize_argument(arg),
            ),
        ))

    async def querySelector(self, selector: str) -> Optional[ElementHandle]:
        return from_nullable_channel(await self._channel.send(
            "querySelector", dict(selector=selector)))

    async def querySelectorAll(self, selector: str) -> List[ElementHandle]:
        return list(
            map(
                cast(ElementHandle, from_channel),
                await self._channel.send("querySelectorAll",
                                         dict(selector=selector)),
            ))

    async def waitForSelector(
        self,
        selector: str,
        timeout: int = None,
        state: Literal["attached", "detached", "visible", "hidden"] = None,
    ) -> Optional[ElementHandle]:
        return from_nullable_channel(await self._channel.send(
            "waitForSelector", locals_to_params(locals())))

    async def dispatchEvent(self,
                            selector: str,
                            type: str,
                            eventInit: Dict = None,
                            timeout: int = None) -> None:
        await self._channel.send(
            "dispatchEvent",
            dict(selector=selector,
                 type=type,
                 eventInit=serialize_argument(eventInit)),
        )

    async def evalOnSelector(
        self,
        selector: str,
        expression: str,
        arg: Serializable = None,
        force_expr: bool = None,
    ) -> Any:
        return parse_result(await self._channel.send(
            "evalOnSelector",
            dict(
                selector=selector,
                expression=expression,
                isFunction=not (force_expr),
                arg=serialize_argument(arg),
            ),
        ))

    async def evalOnSelectorAll(
        self,
        selector: str,
        expression: str,
        arg: Serializable = None,
        force_expr: bool = None,
    ) -> Any:
        return parse_result(await self._channel.send(
            "evalOnSelectorAll",
            dict(
                selector=selector,
                expression=expression,
                isFunction=not (force_expr),
                arg=serialize_argument(arg),
            ),
        ))

    async def content(self) -> str:
        return await self._channel.send("content")

    async def setContent(
        self,
        html: str,
        timeout: int = None,
        waitUntil: DocumentLoadState = None,
    ) -> None:
        await self._channel.send("setContent", locals_to_params(locals()))

    @property
    def name(self) -> str:
        return self._name or ""

    @property
    def url(self) -> str:
        return self._url or ""

    @property
    def parentFrame(self) -> Optional["Frame"]:
        return self._parent_frame

    @property
    def childFrames(self) -> List["Frame"]:
        return self._child_frames.copy()

    def isDetached(self) -> bool:
        return self._detached

    async def addScriptTag(
        self,
        url: str = None,
        path: str = None,
        content: str = None,
        type: str = None,
    ) -> ElementHandle:
        params = locals_to_params(locals())
        if path:
            with open(path, "r") as file:
                params["content"] = file.read() + "\n//# sourceURL=" + str(
                    Path(path))
                del params["path"]
        return from_channel(await self._channel.send("addScriptTag", params))

    async def addStyleTag(self,
                          url: str = None,
                          path: str = None,
                          content: str = None) -> ElementHandle:
        params = locals_to_params(locals())
        if path:
            with open(path, "r") as file:
                params["content"] = (file.read() + "\n/*# sourceURL=" +
                                     str(Path(path)) + "*/")
                del params["path"]
        return from_channel(await self._channel.send("addStyleTag", params))

    async def click(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: MousePosition = None,
        delay: int = None,
        button: MouseButton = None,
        clickCount: int = None,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        await self._channel.send("click", locals_to_params(locals()))

    async def dblclick(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: MousePosition = None,
        delay: int = None,
        button: MouseButton = None,
        timeout: int = None,
        force: bool = None,
    ) -> None:
        await self._channel.send("dblclick", locals_to_params(locals()))

    async def fill(self,
                   selector: str,
                   value: str,
                   timeout: int = None,
                   noWaitAfter: bool = None) -> None:
        await self._channel.send("fill", locals_to_params(locals()))

    async def focus(self, selector: str, timeout: int = None) -> None:
        await self._channel.send("focus", locals_to_params(locals()))

    async def textContent(self,
                          selector: str,
                          timeout: int = None) -> Optional[str]:
        return await self._channel.send("textContent",
                                        locals_to_params(locals()))

    async def innerText(self, selector: str, timeout: int = None) -> str:
        return await self._channel.send("innerText",
                                        locals_to_params(locals()))

    async def innerHTML(self, selector: str, timeout: int = None) -> str:
        return await self._channel.send("innerHTML",
                                        locals_to_params(locals()))

    async def getAttribute(self,
                           selector: str,
                           name: str,
                           timeout: int = None) -> Optional[str]:
        return await self._channel.send("getAttribute",
                                        locals_to_params(locals()))

    async def hover(
        self,
        selector: str,
        modifiers: List[KeyboardModifier] = None,
        position: MousePosition = None,
        timeout: int = None,
        force: bool = None,
    ) -> None:
        await self._channel.send("hover", locals_to_params(locals()))

    async def selectOption(
        self,
        selector: str,
        values: ValuesToSelect,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> List[str]:
        params = locals_to_params(locals())
        if "values" in params:
            values = params.pop("values")
            params = dict(**params, **convert_select_option_values(values))
        return await self._channel.send("selectOption", params)

    async def setInputFiles(
        self,
        selector: str,
        files: Union[str, Path, FilePayload, List[str], List[Path],
                     List[FilePayload]],
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        params = locals_to_params(locals())
        params["files"] = normalize_file_payloads(files)
        await self._channel.send("setInputFiles", params)

    async def type(
        self,
        selector: str,
        text: str,
        delay: int = None,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        await self._channel.send("type", locals_to_params(locals()))

    async def press(
        self,
        selector: str,
        key: str,
        delay: int = None,
        timeout: int = None,
        noWaitAfter: bool = None,
    ) -> None:
        await self._channel.send("press", locals_to_params(locals()))

    async def check(
        self,
        selector: str,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        await self._channel.send("check", locals_to_params(locals()))

    async def uncheck(
        self,
        selector: str,
        timeout: int = None,
        force: bool = None,
        noWaitAfter: bool = None,
    ) -> None:
        await self._channel.send("uncheck", locals_to_params(locals()))

    async def waitForTimeout(self, timeout: int) -> None:
        await self._connection._loop.create_task(asyncio.sleep(timeout / 1000))

    async def waitForFunction(
        self,
        expression: str,
        arg: Serializable = None,
        force_expr: bool = None,
        timeout: int = None,
        polling: Union[int, Literal["raf"]] = None,
    ) -> JSHandle:
        if not is_function_body(expression):
            force_expr = True
        params = locals_to_params(locals())
        params["isFunction"] = not (force_expr)
        params["arg"] = serialize_argument(arg)
        return from_channel(await self._channel.send("waitForFunction",
                                                     params))

    async def title(self) -> str:
        return await self._channel.send("title")

    def expect_load_state(
        self,
        state: DocumentLoadState = None,
        timeout: int = None,
    ) -> EventContextManagerImpl[Optional[Response]]:
        return EventContextManagerImpl(self.waitForLoadState(state, timeout))

    def expect_navigation(
        self,
        url: URLMatch = None,
        waitUntil: DocumentLoadState = None,
        timeout: int = None,
    ) -> EventContextManagerImpl[Optional[Response]]:
        return EventContextManagerImpl(
            self.waitForNavigation(url, waitUntil, timeout))