-
-
Notifications
You must be signed in to change notification settings - Fork 77
Improvements for synchronous and asychronous generators #908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
90d1483
to
2466c90
Compare
0571f84
to
3e2734c
Compare
ab9f9be
to
8476be8
Compare
from param.reactive import rx | ||
|
||
handle = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handle = None | |
handle: IPython.core.display_functions.DisplayHandle | None = None |
Was a bit confused when reading the code. The import could be done by a TYPE_HINTING
.
I'm still not a fan of the name Another thing is, do we need it to be an Exception? It almost seems like the user is doing something wrong each time they raise it. If it is an exception because it is easier to propagate the error to the right place for us as maintainers and does not have a real effect on the user if we change it to an object, I think we should change it. |
I still strongly prefer
|
Very happy to hear alternative naming suggestions. |
Let's start from the back (I would say I'm playing devil's advocate for some of the arguments) (3) A counterpoint would be if you do a heavy calculation in a function and then raise a (2) Why is this important? I think this code with sentinel is cleaner, # with sentinel value
value = func()
if value is Skip:
value = None
# with exception
try:
value = func()
except Skip:
value = None Another thing is that (1) I have a hard time coming up with an example that does not end with a The problem you are describing is supporting suboptimal code. This could lead to a side effect function raising Another reason for having it as a sentinel value is also that it will make it possible to type a hint that a function can return it; this is not possible with exceptions in Python. With that being said I generally don't have a problem with it being an exception. I just want to be sure that it is the best way to handle a skip, and if you feel that it is I have no objections to it. For naming maybe |
Seems like an anti-pattern to run some long calculation only to then skip, but I'll accept there will be (I would consider rare) cases where you don't know you want to skip until you get to the end of some long-running computation.
It isn't super important but it's not our code I'm talking about but user code, i.e. I like the fact that
I'm not really following your point here in particular the stuff about class DatePlotter(param.Parameterized):
date = param.CalendarDate()
update = param.Event()
...
@param.depends('date')
def get_daily_data(self):
if not self.update:
raise Skip()
return self.df[self.df.date==self.date]
@param.depends('date')
def plot_daily_data(self):
daily_df = self.get_daily_data()
return daily_df.hvplot()
dp = DailyPlotter()
pn.Row(dp.param, pn.widgets.Tabulator(dp), pn.pane.HoloViews(dp.plot_daily_data)) Here I can have a |
Then, let's say it is a lookup in a database, which is slow.
I really don't think that it needs to stand out. As I see it, a raised exception is usual when something goes wrong, and that is not what
I don't necessarily see this as a good thing. I think this could lead to weird side effects, like why a widget isn't updating because some weird nested function is raising In your example, there is no way to typehint that the If we take your example and want to send a notification to the user that there is no data in class DatePlotter(param.Parameterized):
date = param.CalendarDate()
update = param.Event()
...
@param.depends('date')
def get_daily_data(self) -> pd.DataFrame | Skip:
if not self.update:
return Skip
return self.df[self.df.date==self.date]
@param.depends('date')
def plot_daily_data(self) -> hvplot | Skip:
daily_df = self.get_daily_data()
# We want to inform the user there is no daily data
if daily_df is Skip:
pn.state.notifications.info("No daily data")
return Skip
return daily_df.hvplot()
dp = DailyPlotter()
pn.Row(dp.param, pn.widgets.Tabulator(dp), pn.pane.HoloViews(dp.plot_daily_data)) An option could be to support both methods, e.g., both returning and raising Another name for it could be |
Exceptions usually are for error conditions, but the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
The difference is that this is built into the language itself and is called each time you run a for loop without raising an exception yourself. I never think I have raised a Another point is that people can catch try:
# function which can both raise a ValueError and Skip
except Skip:
raise
except Exception:
# do something else Which users likely won't do, or even if they do, they can miss one. |
The last thing I want to say is that using |
Co-authored-by: Simon Høxbro Hansen <simon.hansen@me.com>
Co-authored-by: James A. Bednar <jbednar@continuum.io>
Co-authored-by: James A. Bednar <jbednar@continuum.io>
036939b
to
2472467
Compare
I can conceivably envision this being an issue but in practice I still consider this unlikely because:
A little dramatic maybe, but sure 😄 In any case I have now also handled |
If the database lookup fails that may well be because of a timeout error or other temporary issues in which case caching would be bad. As for your example above, I actually see the exception as a benefit here, because I can attach a reason in a straightforward and canonical way: class DatePlotter(param.Parameterized):
date = param.CalendarDate()
update = param.Event()
...
@param.depends('date')
def get_daily_data(self) -> pd.DataFrame | Skip:
if not self.update:
raise Skip("Not updating")
elif not self.date in self.df.date:
raise Skip('No daily data')
return self.df[self.df.date==self.date]
@param.depends('date')
def plot_daily_data(self) -> hvplot | Skip:
try:
daily_df = self.get_daily_data()
except Skip as e:
pn.state.notifications.info(str(e))
raise e
return daily_df.hvplot()
dp = DailyPlotter()
pn.Row(dp.param, pn.widgets.Tabulator(dp), pn.pane.HoloViews(dp.plot_daily_data)) |
4d9dc9b
to
d27f981
Compare
@hoxbro and I had another discussion. He still consider the For now I'd like to continue here and with one approval and having addressed the review comments I'm going to merge. |
This PR improves the support for synchronous and asychronous generators as references and dependencies. It also fixes a variety of bugs related to updating async generators. Specifically it implements the following:
Enhancements
Add ability to Skip reference updates of all kinds
Asy 10000 nchronous generators can now be annotated with
param.depends
In IPython contexts we now define a default
async_executor
meaning that async functionality can now be used by default without importing Panel or defining your own executor.Bug fixes
param.Number
to accept generators since they are not actually supported forDynamic
parametersToDo