Example #1
0
async def main():
    logger.configure()
    win = MainWindow()

    async with qtrio.enter_emissions_channel(signals=[win.closed]) as emissions:
        win.show()
        await emissions.channel.receive()
Example #2
0
async def main(window=None):
    """Show the example window and iterate over the relevant signal emissions to respond
    to user interactions with the GUI.
    """
    if window is None:  # pragma: no cover
        window = Window.build()

    signals = [
        window.decrement.clicked,
        window.increment.clicked,
        window.widget.closed,
    ]

    async with qtrio.enter_emissions_channel(signals=signals) as emissions:
        window.show()

        async for emission in emissions.channel:
            if emission.is_from(window.decrement.clicked):
                window.decrement_count()
            elif emission.is_from(window.increment.clicked):
                window.increment_count()
            elif emission.is_from(window.widget.closed):
                break
            else:  # pragma: no cover
                raise qtrio.QTrioException(f"Unexpected emission: {emission}")
Example #3
0
    async def serve(
        self,
        *,
        task_status: trio_typing.TaskStatus[None] = trio.TASK_STATUS_IGNORED,
    ) -> None:
        signals = [
            self.decrement.clicked,
            self.increment.clicked,
            self.widget.closed,
        ]

        async with qtrio.enter_emissions_channel(signals=signals) as emissions:
            await self.show()
            task_status.started()
            self.serving_event.set()

            async for emission in emissions.channel:
                if emission.is_from(self.decrement.clicked):
                    self.decrement_count()
                elif emission.is_from(self.increment.clicked):
                    self.increment_count()
                elif emission.is_from(self.widget.closed):
                    break
                else:  # pragma: no cover
                    raise qtrio.QTrioException(
                        f"Unexpected emission: {emission}")
Example #4
0
    async def wait_send_task(self):
        async with qtrio.enter_emissions_channel(signals=[
            self.closed, self.w_edit_button.clicked, self.w_info_button.clicked
        ]) as emissions:
            async for emission in emissions.channel:
                if   emission.is_from(self.closed)               : break
                elif emission.is_from(self.w_edit_button.clicked): await self.on_send_to_lean()
                elif emission.is_from(self.w_info_button.clicked): await self.on_lean_info()

        self.log.info("Exit")
Example #5
0
async def main(button: typing.Optional[QtWidgets.QPushButton] = None):
    if button is None:  # pragma: no cover
        button = QtWidgets.QPushButton()

    button.setText("Exit")

    async with qtrio.enter_emissions_channel(signals=[button.clicked]) as emissions:
        button.show()

        await emissions.channel.receive()
Example #6
0
async def main():
    """
    This is the main loop. It opens a trio.nursery, instantiate a Container
    for signals and slots, and call the Container.choose_exercise method.
    Then it listens to signals emitted when windows are closed, and decides
    to quit when all windows are closed. Quitting implies stopping the lean
    server that may be running and closing the trio's nursery.
    """

    async with trio.open_nursery() as nursery:
        await site_installation_check(nursery)

        # Create container
        container = Container(nursery)

        # Choose first exercise
        exercise = exercise_from_argv()
        if not exercise:
            container.choose_exercise()
        else:
            container.start_exercise(exercise)
        # Main loop that just listen to closing windows signals,
        # and quit if there is no more open windows.
        signals = [
            container.close_chooser_window, container.close_exercise_window
        ]
        try:
            async with qtrio.enter_emissions_channel(signals=signals) as \
                    emissions:
                async for emission in emissions.channel:
                    log.debug("Signal received")
                    if emission.is_from(container.close_chooser_window):
                        # Remember that there is no more chooser window:
                        container.chooser_window = None
                        log.debug("No more chooser window")
                    elif emission.is_from(container.close_exercise_window):
                        # Remember that there is no more exercise window:
                        container.exercise_window = None
                        log.debug("No more exercise window")

                    # Quit if no more open window:
                    if not (container.chooser_window
                            or container.exercise_window):
                        log.debug("Closing d∃∀duction")
                        break
                # log.debug("Out of async for loop")
            # log.debug("Out of async with")
        finally:
            # Finally closing d∃∀duction
            if container.servint:
                await container.servint.file_invalidated.wait()
                container.servint.stop()  # Good job, buddy
                log.info("Lean server stopped!")
            if container.nursery:
                container.nursery.cancel_scope.cancel()
Example #7
0
async def test_main(
    qtbot: pytestqt.qtbot.QtBot,
    optional_hold_event: typing.Optional[trio.Event],
) -> None:
    message = "test world"

    async with trio.open_nursery() as nursery:
        start = functools.partial(
            qtrio.examples.buildingrespect.start_widget,
            message=message,
            hold_event=optional_hold_event,
        )
        widget: qtrio.examples.buildingrespect.Widget = await nursery.start(
            start)
        qtbot.addWidget(widget.widget)

        async with qtrio.enter_emissions_channel(signals=[widget.text_changed
                                                          ], ) as emissions:
            if optional_hold_event is not None:
                optional_hold_event.set()

            await trio.testing.wait_all_tasks_blocked(cushion=0.01)

            # lazily click the button and collect results in the memory channel rather
            # than starting concurrent tasks.

            # close the send channel so the receive channel knows when it is done
            async with emissions.send_channel:
                for _ in message:
                    widget.button.click()

                    # give Qt etc a chance to handle the clicks before closing the channel
                    await trio.testing.wait_all_tasks_blocked(cushion=0.01)

            results: typing.List[typing.Tuple[object]] = [
                emission.args async for emission in emissions.channel
            ]

            widget.button.click()

    assert results == [
        ("t", ),
        ("te", ),
        ("tes", ),
        ("test", ),
        ("test ", ),
        ("test w", ),
        ("test wo", ),
        ("test wor", ),
        ("test worl", ),
        ("test world", ),
    ]
Example #8
0
async def main():
    async with trio.open_nursery() as nursery:
        logger.configure()

        srv = SimpleServerInterface(nursery)
        win = MainWindow(nursery, srv)

        async with qtrio.enter_emissions_channel(signals=[win.closed]) as emissions:
            await srv.start()

            win.show()
            await emissions.channel.receive()

        srv.stop()
Example #9
0
    async def server_task(self):
        await self.server.start()
        await self.server.exercise_set(self.exercise)
        async with qtrio.enter_emissions_channel(
                signals=[self.closed, self.send.clicked, self.undo.clicked
                         ]) as emissions:
            async for emission in emissions.channel:
                if emission.is_from(self.closed):
                    break
                elif emission.is_from(self.send.clicked):
                    await self.go_send()
                elif emission.is_from(self.undo.clicked):
                    await self.go_undo()

        self.server.stop()
Example #10
0
async def site_installation_check(nursery):
    missing_packages = inst.check()

    if missing_packages:
        want_install_dialog = WantInstallMissingDependencies(
            map(lambda x: x[0], missing_packages))
        want_install_dialog.exec_()

        if want_install_dialog.yes:
            inst_stg = Install_Dependencies_Stage(nursery, missing_packages)
            async with qtrio.enter_emissions_channel(signals=[inst_stg.start_deaduction]) as \
                       emissions:

                inst_stg.start()
                async for emission in emissions.channel:
                    if emission.is_from(inst_stg.start_deaduction):
                        break
                inst_stg.stop()
Example #11
0
async def test_enter_emissions_channel_closes_both_channels():
    """Exiting enter_emissions_channel() closes send and receive channels on exit."""
    class MyQObject(QtCore.QObject):
        signal = QtCore.Signal(int)

    instance = MyQObject()
    max_buffer_size = 10

    async with qtrio.enter_emissions_channel(
            signals=[instance.signal],
            max_buffer_size=max_buffer_size,
    ) as emissions:
        pass

    with pytest.raises(trio.ClosedResourceError):
        emissions.channel.receive_nowait()

    with pytest.raises(trio.ClosedResourceError):
        emissions.send_channel.send_nowait(None)
Example #12
0
    async def serve(
        self,
        *,
        task_status: trio_typing.TaskStatus[None] = trio.TASK_STATUS_IGNORED,
    ) -> None:
        async with qtrio.enter_emissions_channel(
                signals=[self.button.clicked]) as emissions:
            i = 1
            await self.show()
            task_status.started()

            async for _ in emissions.channel:  # pragma: no branch
                self.set_text(self.message[:i])
                i += 1

                if i > len(self.message):
                    break

            # wait for another click to finish
            await emissions.channel.receive()
Example #13
0
async def main():
    async with trio.open_nursery() as nursery:
        logger.configure()

        course = Course.from_file(
            Path("../../tests/lean_files/short_course/exercises.lean"))
        for counter, statement in enumerate(course.statements):
            print(f"Statement n°{counter:2d}: "
                  f"(exercise: {isinstance(statement, Exercise)}) "
                  f"{statement.lean_name}"
                  f" ({statement.pretty_name})")

        statement_id = int(input("Exercice n° ? "))
        exercise = course.statements[statement_id]

        win = ExerciseWindow(nursery, exercise)

        async with qtrio.enter_emissions_channel(
                signals=[win.closed]) as emissions:
            win.show()
            await emissions.channel.receive()
Example #14
0
async def test_main(qtbot: pytestqt.qtbot.QtBot,
                    optional_hold_event: typing.Optional[trio.Event]) -> None:
    message = "test world"

    async with trio.open_nursery() as nursery:
        start = functools.partial(
            qtrio.examples.crossingpaths.start_widget,
            message=message,
            change_delay=0.01,
            close_delay=0.01,
            hold_event=optional_hold_event,
        )
        widget: qtrio.examples.crossingpaths.Widget = await nursery.start(start
                                                                          )
        qtbot.addWidget(widget)

        async with qtrio.enter_emissions_channel(signals=[widget.text_changed
                                                          ], ) as emissions:
            if optional_hold_event is not None:
                optional_hold_event.set()

            async with emissions.send_channel:
                await widget.done_event.wait()

            results = [emission.args async for emission in emissions.channel]

    assert results == [
        ("t", ),
        ("te", ),
        ("tes", ),
        ("test", ),
        ("test ", ),
        ("test w", ),
        ("test wo", ),
        ("test wor", ),
        ("test worl", ),
        ("test world", ),
    ]
Example #15
0
async def test_file_open_set_path(tmp_path: pathlib.Path) -> None:
    file_path = tmp_path.joinpath("some_file")
    file_path.touch()

    dialog = qtrio.dialogs.create_file_open_dialog()

    trio_file_path = trio.Path(file_path)

    async def user():
        await emissions.channel.receive()

        await dialog.set_path(path=trio_file_path)

        assert dialog.accept_button is not None
        dialog.accept_button.click()

    async with qtrio.enter_emissions_channel(
            signals=[dialog.shown]) as emissions:
        async with trio.open_nursery() as nursery:
            nursery.start_soon(user)

            selected_path = await dialog.wait()

    assert selected_path == trio_file_path
Example #16
0
async def auto_test(container: Container):
    """
    Test the Exercise's instance container.exercise by listening to
    deaduction signals and simulating user pressing buttons according to
    the instructions found in exercise.auto_test. The function assumes that
    the ExerciseMainWindow has been launched.

    Note that just one exercise is tested, this function is not in charge of
    processing to the next exercise to be tested.
    """

    # Auto-steps loop
    exercise = container.exercise
    log.info(f"Testing exercise {exercise.pretty_name}")
    auto_steps = exercise.refined_auto_steps
    log.debug('auto_steps:')
    total_string = 'AutoTest\n'
    for step in auto_steps:
        total_string += '    ' + step.raw_string + ',\n'
    log.debug(total_string)

    emw = container.exercise_window
    signals = [emw.proof_step_updated, emw.ui_updated]
    test_success = True
    steps_counter = 0
    async with qtrio.enter_emissions_channel(signals=signals) as \
            emissions:
        reports = [f'Exercise {exercise.pretty_name}']
        container.report.append(reports)
        async for emission in emissions.channel:
            # Check result #
            if emission.is_from(emw.proof_step_updated) and steps_counter:
                step = auto_steps[steps_counter - 1]
                report, step_success = emw.displayed_proof_step.compare(step)
                test_success = test_success and step_success
                if not report:
                    report = f'Success with {step.raw_string}'
                else:
                    report = f'{step.raw_string}' + report

                report = f"Step {steps_counter}: " + report
                if not emw.displayed_proof_step.success_msg \
                        and emw.displayed_proof_step.button \
                        and not emw.displayed_proof_step.button.is_cqfd() \
                        and not emw.displayed_proof_step.is_error():
                    report += "(no success msg)"
                reports.append(report)

            # Apply next step #
            elif emission.is_from(emw.ui_updated):
                log.debug("ui_updated received")

                step = auto_steps[steps_counter]
                steps_counter += 1
                log.debug(f"auto_step found: {step}")
                if not step:
                    log.debug("Found 'None' step, giving up")
                    emw.close()
                    break

                auto_selection, success = find_selection(step, emw)
                if not success:
                    # Quit this exercise
                    container.exercise_window.close()
                selection_names = [
                    item.display_name for item in auto_selection
                ]
                log.debug(f"Selection: {selection_names}")
                auto_user_input = [
                    int(item) if item.isdecimal() else item
                    for item in step.user_input
                ]

                if step.button:  # Auto step is a button step
                    if step.button.endswith('undo'):
                        await emw.process_async_signal(emw.servint.history_undo
                                                       )
                    elif step.button.endswith('redo'):
                        await emw.process_async_signal(emw.servint.history_redo
                                                       )
                    elif step.button.endswith('rewind'):
                        await emw.process_async_signal(
                            emw.servint.history_rewind)
                    else:
                        # e.g. Nouvel_Objet -> Nouvel Objet
                        step.button = step.button.replace('_', ' ')
                        action_btn = emw.ecw.action_button(step.button)
                        log.debug(f"Button: {action_btn}")
                        await emw.process_async_signal(
                            partial(emw.__server_call_action, action_btn,
                                    auto_selection, auto_user_input))

                elif step.statement:  # Auto step is a statement step
                    statement_widget = emw.ecw.statements_tree.from_name(
                        step.statement)
                    log.debug(f"Statement: {statement_widget}")
                    await emw.process_async_signal(
                        partial(emw.__server_call_statement, statement_widget,
                                auto_selection))
                else:
                    log.warning("Auto-step loop: empty step")

                if steps_counter == len(auto_steps):
                    break
    log.debug(f"Auto_test successfull: {test_success}")
    reports.insert(0, test_success)
Example #17
0
async def main():
    """
    Select exercises to be tested, launch ExerciseMainWindow and Lean
    server, and then call auto_test successively on each exercise. The
    exercise in initialized by the Container.test_exercise method.

    The loop first collect a collection of exercises from arguments.
    Arguments may include
    1) a directory,
    2) a file path to a course, maybe with with the name of some exercise,
    3) or a file path to an individual exercise in a pkl file.

    - In the first case, it will collect all exercises with autotest data in
    all courses in the directory, as well ass all individual pkl
    test_exercises.
    - In the second case it will collect the individual exercise if
    specified, or all the exercises from this one, or all exercises in the
    specified course if no exercise is specified.
    - In the last case it will collect the specified exercise.
    """

    # ─────────────── Choose exercises ─────────────── #
    exercises = None
    dir_, course, exercise, all_from_this_one = coex_from_argv()
    # dir: Path
    # course : Course
    # exercise: Exercise
    # all_from_this_one: bool
    if dir_:
        exercises = get_exercises_from_dir(dir_)
    elif course:
        exercises = get_exercises_from_course(course, exercise,
                                              all_from_this_one)
    elif exercise:
        exercises = [exercise]

    if not exercises:
        quit()
    else:
        log.debug(f"Found {len(exercises)} with AutoTest metadata")

    # ─────────────── Testing exercises ─────────────── #
    async with trio.open_nursery() as nursery:
        # Create container and enter test mode
        container = Container(nursery)
        container.exercises = exercises

        # Main loop: quit if window is closed by user or if there is no more
        # exercise.
        signals = [container.test_complete, container.close_exercise_window]
        try:
            async with qtrio.enter_emissions_channel(signals=signals) as \
                    emissions:
                log.info("Entering main test loop")

                # Test first exercise
                container.exercise = container.exercises[0]
                container.exercises = container.exercises[1:]
                await container.test_exercise()
                container.nursery.start_soon(auto_test, container)

                async for emission in emissions.channel:
                    if emission.is_from(container.test_complete) \
                            and container.exercises:  # Test next exercise
                        log.debug("Test complete -> next exercise")
                        log.debug(f"{len(container.exercises)} exercises "
                                  f"remaining to test")
                        # Close window
                        container.exercise_window.window_closed.disconnect()
                        container.exercise_window.close()
                        if container.exercises:
                            # Test next exercise
                            container.exercise = container.exercises[0]
                            container.exercises = container.exercises[1:]
                            await container.test_exercise()
                            container.nursery.start_soon(auto_test, container)

                        else:
                            log.debug("No more exercises to test!")
                            break

                    elif emission.is_from(container.close_exercise_window):
                        log.info("Exercise window closed")
                        break

        finally:
            print("================================================")
            global_success = False not in [
                exo_report[0] for exo_report in container.report
            ]
            print(f"Global success : {global_success}")
            for exo_report in container.report:
                success = "success" if exo_report[0] else "FAILURE"
                if len(exo_report) > 1:
                    print(exo_report[1] + ": " + success)
                    for step_report in exo_report[2:]:
                        print(step_report)

            # Finally closing d∃∀duction
            if container.servint:
                await container.servint.file_invalidated.wait()
                container.servint.stop()  # Good job, buddy
                log.info("Lean server stopped!")
            if container.nursery:
                container.nursery.cancel_scope.cancel()
    async def server_task(self):
        """
        This method handles sending user data and actions to the server
        interface (self.servint). It listens to signals and calls
        specific methods for those signals accordingly. Async / await
        processes are used in accordance to what is done in the server
        interface. This method is called in self.__init__.
        The user actions are stored in self.proof_step.
        """

        self.freeze()
        await self.servint.exercise_set(self.exercise)
        self.freeze(False)

        async with qtrio.enter_emissions_channel(signals=[
                self.lean_editor.editor_send_lean, self.toolbar.redo_action.
                triggered, self.toolbar.undo_action.triggered,
                self.toolbar.rewind.triggered, self.__action_triggered,
                self.__statement_triggered, self.__apply_math_object_triggered
        ]) as emissions:
            async for emission in emissions.channel:
                self.statusBar.erase()

                if emission.is_from(self.lean_editor.editor_send_lean):
                    await self.process_async_signal(
                        self.__server_send_editor_lean)

                elif emission.is_from(self.toolbar.redo_action.triggered)\
                        and not self.lean_file.history_at_end:
                    # No need to call self.update_goal, this emits the
                    # signal proof_state_change of which
                    # self.update_goal is a slot.
                    # The UI simulate the redone step if possible.
                    self.proof_step.button = 'history_redo'
                    proof_step = self.lean_file.next_proof_step
                    if proof_step:
                        await self.simulate(proof_step)
                    await self.process_async_signal(self.servint.history_redo)

                elif emission.is_from(self.toolbar.undo_action.triggered):
                    self.proof_step.button = 'history_undo'
                    await self.process_async_signal(self.servint.history_undo)

                elif emission.is_from(self.toolbar.rewind.triggered):
                    self.proof_step.button = 'history_rewind'
                    await self.process_async_signal(self.servint.history_rewind
                                                    )

                elif emission.is_from(self.window_closed):
                    break

                elif emission.is_from(self.__action_triggered):
                    # emission.args[0] is the ActionButton triggered by user
                    button = emission.args[0]
                    self.proof_step.button = button
                    if button == self.ecw.action_apply_button \
                            and self.double_clicked_item:
                        # Make sure item is marked and added to selection
                        item = self.double_clicked_item
                        if item in self.current_selection:
                            self.current_selection.remove(item)
                        self.current_selection.append(item)  # Item is last
                        self.double_clicked_item = None
                    await self.process_async_signal(
                        partial(self.__server_call_action, emission.args[0]))

                elif emission.is_from(self.__statement_triggered):
                    # emission.args[0] is the StatementTreeWidgetItem
                    # triggered by user
                    if hasattr(emission.args[0], 'statement'):
                        self.proof_step.statement_item = emission.args[0]
                    await self.process_async_signal(
                        partial(self.__server_call_statement,
                                emission.args[0]))

                elif emission.is_from(self.__apply_math_object_triggered):
                    self.double_clicked_item = emission.args[0]
                    # Emulate click on 'apply' button:
                    self.ecw.action_apply_button.animateClick(msec=500)