Skip to content
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

Add ability to defer load until after page is rendered #3882

Merged
merged 6 commits into from
Sep 26, 2022
Merged

Conversation

philippjfr
Copy link
Member

@philippjfr philippjfr commented Sep 25, 2022

Makes it possible to defer the loading of components until after the page is rendered. Can be used explicitly with ParamMethod and ParamFunction (or via panel()) OR by setting config.defer_load.

import time
start = time.time()

import pandas as pd
import panel as pn
import hvplot.pandas

pn.extension(
    sizing_mode='stretch_width', template='fast', defer_load=True,
    loading_spinner='arc', loading_color='blue'
)

def duration():
    return time.time()-start

def big_load():
    time.sleep(2)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]})

def big_plot():
    time.sleep(2)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]}).hvplot()

pn.Column(big_load, big_plot, duration).servable()

defer_load

@philippjfr
Copy link
Member Author

@MarcSkovMadsen Give this one a go. For now it simply uses the loading parameter and therefore displays the globally configured loading_spinner.

@codecov
Copy link

codecov bot commented Sep 25, 2022

Codecov Report

Merging #3882 (a3abcae) into master (8ecb73b) will increase coverage by 0.00%.
The diff coverage is 87.64%.

@@           Coverage Diff           @@
##           master    #3882   +/-   ##
=======================================
  Coverage   82.83%   82.84%           
=======================================
  Files         215      216    +1     
  Lines       32385    32456   +71     
=======================================
+ Hits        26826    26888   +62     
- Misses       5559     5568    +9     
Flag Coverage Δ
ui-tests 34.46% <37.07%> (+0.06%) ⬆️
unitexamples-tests 74.91% <76.40%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
panel/io/server.py 74.65% <0.00%> (-0.15%) ⬇️
panel/io/state.py 69.48% <75.00%> (+0.13%) ⬆️
panel/param.py 87.08% <87.09%> (+0.29%) ⬆️
panel/tests/ui/test_param.py 88.23% <88.23%> (ø)
panel/config.py 60.37% <100.00%> (+0.10%) ⬆️
panel/tests/conftest.py 88.26% <100.00%> (+0.05%) ⬆️
panel/tests/test_param.py 99.68% <100.00%> (+<0.01%) ⬆️
panel/tests/util.py 87.37% <0.00%> (-1.95%) ⬇️
panel/io/cache.py 81.98% <0.00%> (-0.46%) ⬇️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 25, 2022

Thanks for taking this seriously @philippjfr . I actually believe its the most important missing piece of the Panel api to make it easy to create data apps with a great look and feel. There are still a few things around pn.bind. But this is actually a big step and its so easy to explain to users.

I totally agree that by default we should use the same loading spinner as we use elsewhere.

I will review now.

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 25, 2022

Feedback 1

Using the code example below I noticed a few thing.

panel-014-deferload.mp4
import time
start = time.time()

import pandas as pd
import panel as pn
import hvplot.pandas

pn.extension(
    sizing_mode='stretch_width', template='fast', defer_load=True,
    loading_spinner='arc', loading_color='#0072B5'
)

count = pn.widgets.IntSlider(value=3, start=1, end=10, name="Count").servable(target="sidebar")

def big_load():
    time.sleep(1)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]})

def big_plot(count=3):
    # time.sleep(2)
    return pd.DataFrame({"x": range(0,count), "y": range(0,count)}).hvplot(height=100, xlim=(0,10))

@pn.depends(count)
def big_plot1(count=3):
    return big_plot(count)

def duration():
    time.sleep(3)
    return time.time()-start

ibig_plot = pn.bind(big_plot, count=count)

layout= pn.Column(big_load, big_plot, big_plot1, ibig_plot, duration)

add = pn.widgets.Button(name="Add").servable(target="sidebar")

def add_panel(_):
    print("update")
    layout.append(pn.panel(ibig_plot))

add.on_click(add_panel)

layout.servable()

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 25, 2022

Feedback 2

In this example I would expect the value of the duration_no_sleep output to be shown immediately, but it is not.

not-defer-load.mp4
import time
start = time.time()

import pandas as pd
import panel as pn
import hvplot.pandas

pn.extension(
    sizing_mode='stretch_width', template='fast', defer_load=True,
    loading_spinner='arc', loading_color='#0072B5'
)

count = pn.widgets.IntSlider(value=3, start=1, end=10, name="Count").servable(target="sidebar")

def big_load():
    time.sleep(3)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]})

def duration_no_sleep():
    return f"This text is important to show immediately {round(time.time()-start,2)}"

layout= pn.Column(big_load, pn.panel(duration_no_sleep, defer_load=False))

layout.servable()

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 25, 2022

Feedback 3

In this example I get the warnings below. I would not expect to get those.

import time
start = time.time()

import pandas as pd
import panel as pn
import hvplot.pandas

pn.extension(
    sizing_mode='stretch_width', template='fast',
    loading_spinner='arc', loading_color='#0072B5'
)

count = pn.widgets.IntSlider(value=3, start=1, end=10, name="Count").servable(target="sidebar")

def big_load():
    print("big_load")
    time.sleep(2)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]})

def duration():
    print("duration")
    time.sleep(2)
    return f"This text is important to show immediately {round(time.time()-start,2)}"

layout= pn.Column(pn.panel(big_load, defer_load=False), pn.panel(duration, defer_load=True))

layout.servable()

Warnings

WARNING:param.ParamFunction00953: The function 'big_load' does not have any dependencies and will never update. Are you sure you did not intend to depend on or bind a parameter or widget to this function? If not simply call the function before passing it to Panel. Otherwise, when passing a parameter as an argument, ensure you pass at least one parameter and reference the actual parameter object not the current value, i.e. use object.param.parameter not object.parameter.
2022-09-25 13:22:30,231 The function 'big_load' does not have any dependencies and will never update. Are you sure you did not intend to depend on or bind a parameter or widget to this function? If not simply call the function before passing it to Panel. Otherwise, when passing a parameter as an argument, ensure you pass at least one parameter and reference the actual parameter object not the current value, i.e. use object.param.parameter not object.parameter.

UPDATE: Maybe its ok to get?

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 25, 2022

Feedback 4

In this example I would expect the spinner to be shown for 4 seconds. But its only there for a split second.

spinner-only-shown-shortly.mp4
import time
start = time.time()

import pandas as pd
import panel as pn
import hvplot.pandas

pn.extension(
    sizing_mode='stretch_width', template='fast',
    loading_spinner='arc', loading_color='#0072B5'
)

count = pn.widgets.IntSlider(value=3, start=1, end=10, name="Count").servable(target="sidebar")

def big_load():
    print("big_load")
    time.sleep(2)
    return pd.DataFrame({"x": [1,2,3], "y": [1,2,3]})

def duration():
    print("duration")
    time.sleep(2)
    return f"This text is important to show immediately {round(time.time()-start,2)}"

layout= pn.Column(pn.panel(big_load, defer_load=True), duration)

layout.servable()

@philippjfr
Copy link
Member Author

Thanks for testing, will look into the issues tonight.

@philippjfr
Copy link
Member Author

philippjfr commented Sep 25, 2022

The plots update really slowly compared to usual (I believe) when I drag the slider ??

This is because the duration callback blocks updates while time.sleep is running. Running the app with multiple threads (using --num-threads) will fix this.

In this example I would expect the value of the duration_no_sleep output to be shown immediately, but it is not.

Again, ordering matters, the big_load callback is blocking so no other callbacks can execute. If you enable threading this works fine once again.

In this example I get the warnings below. I would not expect to get those.

The warning comes from the callback without defer_load. I'm also wavering on how useful these warnings are in practice.

In this example I would expect the spinner to be shown for 4 seconds. But its only there for a split second.

Looking into this one, it does seem like a bug.

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Sep 26, 2022

Comment 1 - referring to Feedback 1 above

image

This does not make sense to me: duration should not be executed when I drag the slider.

Comment 2 - referring to Feedback 2 above

image

This does not make sense to me: I would expect to be able to specify some functions to be deferred (big_load) and some to be executed before the page loads (duration_no_sleep). The implementation I had in the issue would have worked in that way.

BUT testing on the updated code I example in Feedback 2 it now works as I expect for the example from Feedback 2.

General Comment

This now works really great for me. Should probably be tested more by others as well. And some pytest tests added.

But its such an improvement for Panel makes it so much easier to make applications with a modern look and feel. Thanks.

@philippjfr
Copy link
Member Author

Yeah, clearly didn't study those examples enough. Thanks for double checking!

@philippjfr philippjfr merged commit e0e7db7 into master Sep 26, 2022
@philippjfr philippjfr deleted the defer_load branch September 26, 2022 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make it easier to defer the loading of panels
2 participants