Replies: 11 comments 15 replies
-
The browser is a dynamic and ever changing place. We need a way for Python to easily define what to do when something changes. Until now, we have used a from pyscript import when
@when("click", "#my_button"):
def handle_click(click_event):
... or from pyscript import when
from pyscript.web import button
b = button("Click me")
@when("click", b)
def handle_click(click_event):
... We would like to generalise this approach to any sort of object found in PyScript... for example, objects or classes representing some underlying browser API. We have already informally discussed a "when-able" protocol handled by classes that implement a That's it! Discuss! |
Beta Was this translation helpful? Give feedback.
-
As we are discussing to refactor our API I'd like to make my point around the (personal) ideal @when(whenable[, options[, rest]]) Using previous examples that means that adding multiple listeners to a button, that currently looks like this: b = button("Click me")
@when("click", b)
@when("dblclick", b)
def handle_click(click_event):
... moving the target/subject before could instead like: @when(b, "click")
@when(b, "dblclick")
# or ...
@when(b, ["click", "dblclick"]) The protocol can easily be implemented in our DOM API or literally anything else and we could have explicit names that don't even require a string contract with the underlying logic: @when(geo_position)
def handle_coords():
... That could be triggerd for updates, one off results, or errors ... which are 3 different cases to handle ... writing this instead: @when("update", geolocation)
def handle_maybe(result):
.... requires geolocation whenable to understand the @when("update", geolocation)
@when("errored", geolocation)
@when("current", geolocation)
def handle_maybe(result):
if isinstance(result, Exception):
....
elif result.accuracy < threshold:
...
else:
.... The logic in that callback would remain identical if we had only: @when(geolocation)
def handle_geolocation(result):
# same logic as before Moreover, we could preserve the current state of affair for backward compatibility reasons by simply providing events name as references, instead of strings: @when(user.click, b)
def handle_click(event):
....
# or ....
@when(user.click, [b, c, d])
# keeping the "whenable" first In short, I really don't think strings are necessary or improve much, they actually create an obligatory understanding of such strings from the whenable reference or protocol so I think we could do better. Speaking strictly about DOM though, the rest of the JS world uses |
Beta Was this translation helpful? Give feedback.
-
This is such a fascinating feature. I was describing it to Damien last week, and he asked, "why not just await?" to which I replied, "the This brings up an interesting point: def do_something(result):
...
when(something.happens, do_something) Should be equivalent to: @when(something.happens):
def do_something(result):
... With my pedagogue's hat on: it's easier to explain the first example ("you're just calling a function to associate some future event with a function to handle the result"), rather than have to hand-wave over the use of a decorator ("it's just like that", is never a phrase a learner should have to accept). Rather, the use of I like the concept of just passing in a when-able, rather than (as happens now in the context of the DOM API) a string containing the name of an event, and an object in the DOM through which the event bubbles. But this requires thought about two things:
I think @WebReflection's proposed signature of, I guess the On that note... handling errors. I like @WebReflection's suggested pattern of checking if the result passed into the function is an instance of a Python I think we're getting close[r] now, and I like the simplicity of how things work. I wonder if we've overlooked anything? I'd like to bring this discussion to the community call tomorrow. My concern is the current cc/ @fpliger @mchilvers |
Beta Was this translation helpful? Give feedback.
-
Not sure if my concern with original
A non-functioning code example of how it would be used in several usecases would be great... |
Beta Was this translation helpful? Give feedback.
-
@Neon22 these are excellent points. Briefly (I'm about to step into a meeting), having def do_something(result):
...
# Dynamically create a thing.
b = pyscript.web.button("Click Me!")
# b.click is a "when-able"
when(b.click, do_something)
pyscript.web.page.append(b) Does this help..? |
Beta Was this translation helpful? Give feedback.
-
Hi folks, I've created a draft PR over on #2224 that currently just implements a couple of unit tests to reflect the current state of this conversation. This is a straw man. Please feel free to suggest changes. Thank you..! |
Beta Was this translation helpful? Give feedback.
-
Something else to ponder... We have been concentrating on how to link events and handlers via I suggest that when-ables should also have a For example, a numpty off-the-top-of-my-head example: class MyWhenable:
def __init__(self):
self.handlers = []
def trigger(self):
for handler in self.handlers:
handle({"result": "ok"})
def __when__(self, handler, *args, **kwargs):
self.handlers.append(handler)
def clear(self, *args):
if args:
for h in args:
self.handlers.remove(h)
else:
self.handlers = []
def my_handler(result):
... do stuff ...
whenable = MyWhenable()
when(whenable, my_handler)
... time passes ...
# No longer needed.
whenable.clear(my_handler) Thoughts..? |
Beta Was this translation helpful? Give feedback.
-
Another thought... perhaps we're over thinking this? We don't need a fancy-smansy "protocol" with a dunder-when. Perhaps we just need to provide a class Effect:
"""
Represents something (a side effect) that may happen at some point in the future.
"""
def __init__(self):
self._handlers = {} # key: id(handler), value: callable, args, kwargs
def trigger(self):
"""
Trigger the effect to create an event to pass into the handlers.
E.g.:
event = {"message": "ok"}
for handler in self._handlers.values():
handler["callable"](event, *handler["args"], **handler["kwargs"])
"""
raise NotImplementedError("Child class to override this method.")
def when(self, handler_function, *args, **kwargs):
self._handlers[id(handler)] = {
"callable": handler_function,
"args": args,
"kwargs": kwargs,
}
def clear(self, *args):
""""
Clear the specified handler functions in *args. If no handlers provided, clear all handlers.
""""
if args:
for handler in args:
self._handlers.pop(id(handler), None)
else:
self._handlers = {} But then, I can't help but wonder if we're not just re-inventing a very simple version of promises/futures/deferreds..? Thoughts? |
Beta Was this translation helpful? Give feedback.
-
OK folks, I've stacked some changes into a new branch that makes use of an b = button("Click me")
@when(b.onclick)
def handler(ev):
... handle the "click" event... Also, the
As always, this is an experiment and I'd welcome feedback, ideas and constructive critique! |
Beta Was this translation helpful? Give feedback.
-
Just listened to the youtube discussion. - https://youtu.be/_zbM5YtKrVA?t=2219 Good question from Martin about do we pass event arg through. IMHO we need to do this (have an event arg). Readable is important but making it impossible for me to implement apparent complexity easily is also important. |
Beta Was this translation helpful? Give feedback.
-
Fixed by #2239 |
Beta Was this translation helpful? Give feedback.
-
A discussion to flesh out the generic use of a
@when
decorator. To be decided by this discussion:@when
decorator.__when__
).Beta Was this translation helpful? Give feedback.
All reactions