justpy-org / justpy

An object oriented high-level Python Web Framework that requires no frontend programming
https://justpy.io
Apache License 2.0
1.2k stars 95 forks source link

hot reloading (refresh) of page when source is modified #236

Closed sandeep-gh closed 2 years ago

sandeep-gh commented 3 years ago

The current usual workflow is to a) modify the code, 2) stop the justpy service, 3) restart the service and finally reload the webpage. I was wondering if it is possible to bypass/short circuit this flow. Is it possible the page gets rendered automatically when the source is modified?

Also, thanks for the great package :).

elimintz commented 3 years ago

Thank you for using the package!

Please take a look at https://github.com/elimintz/justpy/issues/63 and let me know if this does not solve your problem

platinops commented 3 years ago

Any update on whether the next Justpy version will support this out of the box as mentioned in #151?

elimintz commented 3 years ago

It won't be in this upcoming version. Will try getting it into one of the next versions.

sandeep-gh commented 3 years ago

Great to know. This works well. One minor issue is that firefox keeps giving an alert "Page needs to be reloaded, click OK to reload" . The issue is mentioned here but I am not able to follow the fix. Suggestions?

elimintz commented 3 years ago

So this is really a feature, not a bug :). It was requested because after the developer restarts the server, users were sometimes unaware that their page was stale and not functioning because the websocket was disconnected.

I'll try to add a configuration variable but for now, you can get rid of this by getting rid of a few lines in https://github.com/elimintz/justpy/blob/master/justpy/templates/main.html#L65

Remove lines 67, 68, 75 and 76 and you should be fine.

sandeep-gh commented 3 years ago

I see. I am happy with this workaround. Great thing to have development cycle.

sandeep-gh commented 3 years ago

One final followup question: It seems we can send reload/refresh command to browser from command line . Is there a simple to way integrate this with uvicorn, so that I don't have to press ctrl-R post each refresh?

rodja commented 3 years ago

I'll try to add a configuration variable but for now, you can get rid of this by getting rid of a few lines in https://github.com/elimintz/justpy/blob/master/justpy/templates/main.html#L65

Remove lines 67, 68, 75 and 76 and you should be fine.

We found a solution without modifying the library code. Just set this on your WebPage:

wp.head_html = '<script>confirm = () => true;</script>'  # HACK: avoid confirmation dialog for reload
jasonjurotich commented 3 years ago

Ok so, I'm having a ton of problems with this now. If I comment out 67, 68, 75 and 76, the page does nothing, and if I leave them, it just constantly asks me to reload the page, without letting me do anything. I don't know if it's a new problem with uvicorn or the websockets, but I literally can't do anything. I am on Ubuntu 21 on GCP, everything up to date. Any help would be appreciated.

elimintz commented 3 years ago

Could you please post a small program that replicates this problem?

knoxvilledatabase commented 3 years ago

I've noticed this too, I think @jasonjurotich means that after pressing "OK" to reload the page, the page will begin to reload then prompt over and over again to reload.

Would it be possible to have the page automatically refresh say 3 seconds after losing the websocket connection, rather than prompt the user to click OK or Cancel?

Screen Shot 2021-08-05 at 12 32 06 AM

elimintz commented 3 years ago

I don't see this behavior in my setup, so I would like to replicate it. Is this something that didn't happen before and started happening now? I am trying to understand what changed. Does this happen with all browsers? Perhaps it is a browser specific issue.

I can implement the change you are suggesting, it seems like a good idea to me. Anyone can think of a problem the change may cause?

jasonjurotich commented 3 years ago

It is the super simple example. I am using gunicorn and nginx to run it. It is the same on Safari as on Chrome. If you click on OK it just keeps on coming back. I made a video showing it as well, if it helps. You can see it here. You might be able to replicate it if you start up a GCP VM instance, use Ubuntu 21 as the OS, install all the normal stuff, and have websockets updated as well to 9 (deleting pyppeteer). If you think that a specific venv would fix this, please just share a specific requirements.txt file and I will try that as well.

import justpy as jp
app = jp.app

def hello_world():
    wp = jp.WebPage()
    jp.Hello(a=wp)
    return wp

jp.justpy(hello_world, start_server=False)

gunicorn -k uvicorn.workers.UvicornWorker justpygit:app -b 0.0.0.0:5006 is what I am running.

If you need me to run a specific command to find out what the problem is, please let me know.

Screen Shot 2021-08-05 at 9 34 11 AM Screen Shot 2021-08-05 at 9 34 23 AM
elimintz commented 3 years ago

Thank you. If instead of clicking ok, you reload the page, does it work ok?

How do you feel about the proposed solution of reloading the page automatically a few seconds after the websocket is closed?

jasonjurotich commented 3 years ago

I tried the wp.head_html = '<script>confirm = () => true;</script>' but it just keeps refreshing, even though it no longer asks me to reload, you can see that it still tries to constantly reload on its own. Directly reloading the page does nothing to fix the problem.

elimintz commented 3 years ago

Is this problem new, or was this always the case?

jasonjurotich commented 3 years ago

I don't have enough experience with websockets to be able to answer your question unfortunately. My only concern would be one, that doing what you mentioned would cause more activity than necessary on the VM instance, and two, the users would sense the lag at certain times. I suppose the real question is why is it disconnecting so much...

When I was working on justpy locally, this problem was fixed when we updated websockets on the Macbook I am using, but it began again as soon as I started trying to use justpy in the VM instance on GCP to use it in production. Again, my poor, humble suggestion would be just to start up a GCP VM instance (using the free tier), and maybe that might give you more of an idea of what might be causing this. If you have any command that I could run to see if an error comes out, I will gladly do so.

elimintz commented 3 years ago

I'll try to find some time to do as you suggest. In the mean time, please replace lines 65 to 77 in https://github.com/elimintz/justpy/blob/master/justpy/templates/main.html with:

        socket.addEventListener('error', function (event) {
            setTimeout(function(){ window.location.reload(); }, 3000);
        });

        var web_socket_closed = false;
        socket.addEventListener('close', function (event) {
            web_socket_closed = true;
            setTimeout(function(){ window.location.reload(); }, 3000);
        });

Does this work better for you?

jasonjurotich commented 3 years ago

I'm out running errands, but as soon as I get back, I will try it and report. Thanks!!!

jasonjurotich commented 3 years ago

ok, so... still no dice. So to make sure that it would work, I put the following in the script:

import justpy
app = jp.app

def my_click(self, msg):
    self.text = 'I was clicked'

def event_demo():
    wp = jp.WebPage()
    d = jp.Div(text='Not clicked yet', a=wp, classes='w-48 text-xl m-2 p-1 bg-blue-500 text-white')
    d.on('click', my_click)
    return wp

jp.justpy(event_demo, start_server=False)

It does nothing and again, it just tries to refresh every few seconds... It's hard to explain, so I'm linking a video inside Google Photos so you can see how it's trying to refresh itself constantly here.

elimintz commented 3 years ago

I'm sorry, I am out of ideas. If you just remove lines 65 to 77, the user would need to reload the page when you restart the application, but at least it will work.

jasonjurotich commented 3 years ago

Understood, sadly something is blocking the app, because when I take out the lines, the app does nothing, and when they are active, it just asks me to constantly reload. I suppose there is a package not working or something is wrong with Ubuntu 21, no idea. Hopefully someone finds something. I will keep on testing to see if it fixes itself somehow.

elimintz commented 3 years ago

Sorry, my bad. I just noticed that I made a mistake. Just erasing lines 65 to 77 would not work. You need to replace these lines with one line:

var web_socket_closed = false;
jasonjurotich commented 3 years ago

Sorry for the delay. Sadly that did not work as well. I guess I will just have to find a way of debugging your main.html file to find what is going on because I don't know much about websockets and I need to find out what is causing the problem because I'm not getting errors anywhere and it still won't go from "Not clicked yet" to "I was clicked". If you have any suggestion on how to find out where the error is, please let me know. Thanks.

jasonjurotich commented 3 years ago

Update. So I did a clean install of everything on a new Compute Engine VM instance. It worked when I used just the public IP with an open port. But, when I then tried to run it as a service with something like this below, it opened but would not react, and then when I tried to relate it to nginx, it just gave me a 502 bad gateway error. Would there be any way of sharing something here like a simple service + nginx config? If not, I will try and put it on stackoverflow, because I really have no idea how to find out what is going on. Thanks.

The service config I am using is something like this:

[Unit]
Description=Gunicorn instance daemon to serve FLTES1
After=network.target

[Service]
User=jj
Group=www-data
WorkingDirectory=/home/jj/flask1
Environment="PATH=/home/jj/flask1/flt/bin"
ExecStart=/home/jj/flask1/flt/bin/gunicorn -w 2 -b unix:fltes1.sock -m 007 -k uvicorn.workers.UvicornWorker fltes1:app 

[Install]
WantedBy=multi-user.target

The nginx config is something like this:

server {                                                                                                                   
        listen 80; 
        listen 443 ssl http2;
        server_name test1.domain.com;
        ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
    ssl_protocols TLSv1.3;

        location / {
            include proxy_params;
            proxy_pass http://unix:/home/jj/flask1/fltes1.sock;                                     
    }   
}
WolfgangFahl commented 2 years ago

Two questions:

  1. is #354 a solution for this
  2. should this be in the next milestone
WolfgangFahl commented 2 years ago

Pleae continue the discussion in #498

aalexei commented 1 year ago

Just hit a similar issue to above while running justly app behind nginx. Solution was to set up websocket proxying:

proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

Without it you get constant reloads.

WolfgangFahl commented 1 year ago

@aalexei thank you for the hint - would you please open a proper issue to improve the reloading code introduced with PR #354

Also you may supply a small pull request with your config code in a doc md file explaining what is does - that way you'll show up as a contributor :-)