Пример #1
0
Файл: idom.py Проект: brl0/panel
 def _process_property_change(self, msg):
     if msg['msg'] is None:
         return {}
     from idom.core.layout import LayoutEvent
     dispatch = self._idom_layout.dispatch(LayoutEvent(**msg['msg']))
     asyncio.run_coroutine_threadsafe(dispatch, loop=self._idom_loop)
     for ref, (m, _) in self._models.items():
         m.msg = None
         push_on_root(ref)
     return {}
Пример #2
0
    async def recv_1():
        sent = await data_sent_1.get()

        element_id = sent["root"]
        element_data = sent["new"][element_id]
        if element_data["attributes"]["count"] == 4:
            done.set()
            raise asyncio.CancelledError()

        return LayoutEvent(target="an-event", data=[])
Пример #3
0
async def test_element_keys_inside_components_do_not_reset_state_of_component(
):
    """This is a regression test for a bug.

    You would not expect that calling `set_child_key_num` would trigger state to be
    reset in any `Child()` components but there was a bug where that happened.
    """

    effect_calls_without_state = []
    set_child_key_num = StaticEventHandler()
    did_call_effect = asyncio.Event()

    @component
    def Parent():
        state, set_state = use_state(0)
        return html.div(
            html.button(
                {
                    "onClick":
                    set_child_key_num.use(lambda: set_state(state + 1))
                },
                "click me",
            ),
            Child("some-key"),
            Child(f"key-{state}"),
        )

    @component
    def Child(child_key):
        state, set_state = use_state(0)

        @use_effect
        async def record_if_state_is_reset():
            if state:
                return
            effect_calls_without_state.append(child_key)
            set_state(1)
            did_call_effect.set()

        return html.div(
            child_key,
            key=child_key,
        )

    async with idom.Layout(Parent()) as layout:
        await layout.render()
        await did_call_effect.wait()
        assert effect_calls_without_state == ["some-key", "key-0"]
        did_call_effect.clear()

        for i in range(1, 5):
            await layout.deliver(LayoutEvent(set_child_key_num.target, []))
            await layout.render()
            assert effect_calls_without_state == ["some-key", "key-0"]
            did_call_effect.clear()
Пример #4
0
async def test_log_on_dispatch_to_missing_event_handler(caplog):
    @idom.component
    def SomeComponent():
        return idom.html.div()

    async with idom.Layout(SomeComponent()) as layout:
        await layout.dispatch(LayoutEvent(target="missing", data=[]))

    assert re.match(
        "Ignored event - handler 'missing' does not exist or its component unmounted",
        next(iter(caplog.records)).msg,
    )
Пример #5
0
async def test_model_key_preserves_callback_identity_for_common_elements(
        caplog):
    called_good_trigger = idom.Ref(False)
    good_handler = StaticEventHandler()
    bad_handler = StaticEventHandler()

    @idom.component
    def MyComponent():
        reverse_children, set_reverse_children = use_toggle()

        @good_handler.use
        def good_trigger():
            called_good_trigger.current = True
            set_reverse_children()

        @bad_handler.use
        def bad_trigger():
            raise ValueError("Called bad trigger")

        children = [
            idom.html.button({
                "onClick": good_trigger,
                "id": "good"
            },
                             "good",
                             key="good"),
            idom.html.button({
                "onClick": bad_trigger,
                "id": "bad"
            },
                             "bad",
                             key="bad"),
        ]

        if reverse_children:
            children.reverse()

        return idom.html.div(children)

    async with idom.Layout(MyComponent()) as layout:
        await layout.render()
        for i in range(3):
            event = LayoutEvent(good_handler.target, [])
            await layout.deliver(event)

            assert called_good_trigger.current
            # reset after checking
            called_good_trigger.current = False

            await layout.render()

    assert not caplog.records
Пример #6
0
async def test_dispatcher_handles_more_than_one_event_at_a_time():
    block_and_never_set = asyncio.Event()
    will_block = asyncio.Event()
    second_event_did_execute = asyncio.Event()

    blocked_handler = StaticEventHandler()
    non_blocked_handler = StaticEventHandler()

    @idom.component
    def ComponentWithTwoEventHandlers():
        @blocked_handler.use
        async def block_forever():
            will_block.set()
            await block_and_never_set.wait()

        @non_blocked_handler.use
        async def handle_event():
            second_event_did_execute.set()

        return idom.html.div(
            idom.html.button({"onClick": block_forever}),
            idom.html.button({"onClick": handle_event}),
        )

    send_queue = asyncio.Queue()
    recv_queue = asyncio.Queue()

    asyncio.ensure_future(
        serve_json_patch(
            idom.Layout(ComponentWithTwoEventHandlers()),
            send_queue.put,
            recv_queue.get,
        ))

    await recv_queue.put(LayoutEvent(blocked_handler.target, []))
    await will_block.wait()

    await recv_queue.put(LayoutEvent(non_blocked_handler.target, []))
    await second_event_did_execute.wait()
Пример #7
0
async def test_changing_event_handlers_in_the_next_render():
    set_event_name = Ref()
    event_handler = StaticEventHandler()
    did_trigger = Ref(False)

    @component
    def Root():
        event_name, set_event_name.current = use_state("first")
        return html.button({
            event_name:
            event_handler.use(lambda: did_trigger.set_current(True))
        })

    async with Layout(Root()) as layout:
        await layout.render()
        await layout.deliver(LayoutEvent(event_handler.target, []))
        assert did_trigger.current
        did_trigger.current = False

        set_event_name.current("second")
        await layout.render()
        await layout.deliver(LayoutEvent(event_handler.target, []))
        assert did_trigger.current
        did_trigger.current = False
Пример #8
0
async def test_layout_cannot_be_used_outside_context_manager(caplog):
    @idom.component
    def Component():
        ...

    component = Component()
    layout = idom.Layout(component)

    with pytest.raises(Exception):
        await layout.deliver(LayoutEvent("something", []))

    with pytest.raises(Exception):
        layout.update(component)

    with pytest.raises(Exception):
        await layout.render()
Пример #9
0
async def test_model_key_preserves_callback_identity_for_components():
    called_good_trigger = idom.Ref(False)
    good_handler = StaticEventHandler()
    bad_handler = StaticEventHandler()

    @idom.component
    def RootComponent():
        reverse_children, set_reverse_children = use_toggle()

        children = [
            Trigger(set_reverse_children, name=name, key=name)
            for name in ["good", "bad"]
        ]

        if reverse_children:
            children.reverse()

        return idom.html.div(children)

    @idom.component
    def Trigger(set_reverse_children, name):
        if name == "good":

            @good_handler.use
            def callback():
                called_good_trigger.current = True
                set_reverse_children()

        else:

            @bad_handler.use
            def callback():
                raise ValueError("Called bad trigger")

        return idom.html.button({"onClick": callback, "id": "good"}, "good")

    async with idom.Layout(RootComponent()) as layout:
        await layout.render()
        for _ in range(3):
            event = LayoutEvent(good_handler.target, [])
            await layout.deliver(event)

            assert called_good_trigger.current
            # reset after checking
            called_good_trigger.current = False

            await layout.render()
Пример #10
0
async def test_log_error_on_bad_event_handler():
    bad_handler = StaticEventHandler()

    @idom.component
    def ComponentWithBadEventHandler():
        @bad_handler.use
        def raise_error():
            raise Exception("bad event handler")

        return idom.html.button({"onClick": raise_error})

    with assert_idom_did_log(match_error="bad event handler"):

        async with idom.Layout(ComponentWithBadEventHandler()) as layout:
            await layout.render()
            event = LayoutEvent(bad_handler.target, [])
            await layout.deliver(event)
Пример #11
0
 def _idom_on_msg(self, message, buffers):
     m_type = message.get("type")
     if m_type == "client-ready":
         v_id = message["viewID"]
         self._idom_views.add(v_id)
         update = LayoutUpdate("", None, self._idom_model)
         diff = VdomJsonPatch.create_from(update)
         self.send({"viewID": v_id, "data": diff})
     elif m_type == "dom-event":
         asyncio.run_coroutine_threadsafe(
             self._idom_layout.deliver(LayoutEvent(**message["data"])),
             loop=self._idom_loop,
         )
     elif m_type == "client-removed":
         v_id = message["viewID"]
         if v_id in self._idom_views:
             self._idom_views.remove(message["viewID"])
Пример #12
0
def make_events_and_expected_model():
    events = [LayoutEvent(STATIC_EVENT_HANDLER.target, [])] * 4
    expected_model = {
        "tagName":
        "",
        "children": [{
            "tagName": "div",
            "attributes": {
                "count": 4
            },
            "eventHandlers": {
                EVENT_NAME: {
                    "target": STATIC_EVENT_HANDLER.target,
                    "preventDefault": False,
                    "stopPropagation": False,
                }
            },
        }],
    }
    return events, expected_model
Пример #13
0
 async def sock_recv() -> LayoutEvent:
     data = await socket.recv()
     if data is None:
         raise Stop()
     return LayoutEvent(**json.loads(data))
Пример #14
0
 async def sock_recv() -> LayoutEvent:
     return LayoutEvent(**json.loads(await socket.receive_text()))
Пример #15
0
 async def recv():
     await asyncio.sleep(0)
     return LayoutEvent(target_id, [])
Пример #16
0
async def test_shared_state_dispatcher():
    done = asyncio.Event()
    changes_1 = []
    changes_2 = []
    target_id = "an-event"

    events_to_inject = [LayoutEvent(target=target_id, data=[])] * 4

    async def send_1(patch):
        changes_1.append(patch.changes)

    async def recv_1():
        await asyncio.sleep(0)
        try:
            return events_to_inject.pop(0)
        except IndexError:
            done.set()
            raise asyncio.CancelledError()

    async def send_2(patch):
        changes_2.append(patch.changes)

    async def recv_2():
        await done.wait()
        raise asyncio.CancelledError()

    @idom.component
    def Clickable():
        count, set_count = idom.hooks.use_state(0)

        @idom.event(target_id=target_id)
        async def an_event():
            set_count(count + 1)

        return idom.html.div({"anEvent": an_event, "count": count})

    async with SharedViewDispatcher(Layout(Clickable())) as dispatcher:
        await dispatcher.run(send_1, recv_1, "1")
        await dispatcher.run(send_2, recv_2, "2")

    expected_changes = [
        [
            {
                "op": "add",
                "path": "/eventHandlers",
                "value": {
                    "anEvent": {
                        "target": "an-event",
                        "preventDefault": False,
                        "stopPropagation": False,
                    }
                },
            },
            {
                "op": "add",
                "path": "/attributes",
                "value": {
                    "count": 0
                }
            },
            {
                "op": "add",
                "path": "/tagName",
                "value": "div"
            },
        ],
        [{
            "op": "replace",
            "path": "/attributes/count",
            "value": 1
        }],
        [{
            "op": "replace",
            "path": "/attributes/count",
            "value": 2
        }],
        [{
            "op": "replace",
            "path": "/attributes/count",
            "value": 3
        }],
    ]

    for c_2, expected_c in zip(changes_2, expected_changes):
        assert_same_items(c_2, expected_c)

    assert changes_1 == changes_2
Пример #17
0
 async def recv() -> LayoutEvent:
     return LayoutEvent(**json.loads(await message_queue.get()))
Пример #18
0
 def recv() -> LayoutEvent:
     return LayoutEvent(**json.loads(ws.receive()))
Пример #19
0
 async def sock_recv() -> LayoutEvent:
     message = json.loads(await socket.recv())
     event = message["body"]["event"]
     return LayoutEvent(event["target"], event["data"])
Пример #20
0
 def recv() -> Optional[LayoutEvent]:
     event = ws.receive()
     if event is not None:
         return LayoutEvent(**json.loads(event))
     else:
         return None