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

Live Reloading #9

Merged
merged 4 commits into from
Jun 11, 2024
Merged

Live Reloading #9

merged 4 commits into from
Jun 11, 2024

Conversation

comhar
Copy link
Contributor

@comhar comhar commented Jun 9, 2024

This PR includes a basic implementation of live reloading.

How does the implementation work?
A subclass of FastHTML (FastHTMLWithLiveReload) does the following:

  • adds a websocket at /live-reload
  • injects a small javascript snippet LIVE_RELOAD_SCRIPT to each webpage
  • this snippet allows the browser and server to communicate with each other
  • as a result the browser can "detect" code changes on the server and reload

Here are some items to resolve:

  • more robust reloading
  • unit tests
  • documentation
  • test reloading with local static file

@comhar comhar marked this pull request as draft June 9, 2024 13:22
@comhar
Copy link
Contributor Author

comhar commented Jun 9, 2024

@jph00

settings.ini Outdated Show resolved Hide resolved
settings.ini Outdated Show resolved Hide resolved
@jph00
Copy link
Contributor

jph00 commented Jun 9, 2024

Looks like a nice approach! :) I can fix the routes TODO after merging, so don't worry about that for now.

- remove temporary routes fix (to be fixed in separate MR)
- remove unnecessary sqlite-utils requirement
@comhar
Copy link
Contributor Author

comhar commented Jun 10, 2024

reload logic

I've updated the reload logic so that it attempts a reload every 250ms up to 5 seconds before giving up. Hopefully that covers most use cases 🤞

local static reloads

I tested FastHTMLWithLiveReload with some local static files (js, css). To my pleasant surprise it handles this use-case as well 😄

Here's the setup I used for those tests:

  • create a static folder in the same directory as your app's main script with some example js and css files.
  • use the following uvicorn command
    uvicorn main:app --reload --reload-dir=../fasthtml --reload-include="*.py" --reload-include="*.js" --reload-include="*.css"
  • use the following script
app script
from fasthtml.all import *
import uvicorn

STATIC_DIR = Path(__file__).parent / "static"
routes = [
    Mount('/static', app=StaticFiles(directory=STATIC_DIR), name="static"),
]

hdrs = [
    Script(src="static/example.js"),
    Link(rel="stylesheet", type="text/css", href="static/example.css"),
]
app = FastHTMLWithLiveReload(hdrs=hdrs, routes=routes)
rt = app.route


@rt("/")
def get():
    return Title("FastHTML"), H1("Live reloading...")


if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=int(os.getenv("PORT", default=8000)))

⚠️ The same routes AttributeError that was thrown for WebSocketRoute is also thrown for Mount

tests & documentation

thoughts on what tests and docs should be included?

fasthtml/live_reload.py Outdated Show resolved Hide resolved
@jph00
Copy link
Contributor

jph00 commented Jun 10, 2024

I tested FastHTMLWithLiveReload with some local static files (js, css). To my pleasant surprise it handles this use-case as well 😄

Here's the setup I used for those tests:

You shouldn't need any of that, due to this:

https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/core.py#L254

Here's an example of use:

https://github.com/AnswerDotAI/fasthtml/blob/main/examples/db_app.py#L16

thoughts on what tests and docs should be included?

Might be hard to test automatically - but there's not much logic there, so maybe not needed?

I think just some basic docs in index.ipynb on how to use it would be sufficient. Sound OK?

- cleanup `FastHTMLWithLiveReload` init
- add live reloading section to docs
@comhar
Copy link
Contributor Author

comhar commented Jun 11, 2024

I think just some basic docs in index.ipynb on how to use it would be sufficient. Sound OK?

Sounds good. Added a Live Reloading section.

Might be hard to test automatically - but there's not much logic there, so maybe not needed?

True. As a sanity check I would like to add the basic test shown below. It doesn't cover everything but might catch some regressions in the future.

Test
# create a regular app with a "/hi" route
app = FastHTML()
cli = TestClient(app)
rt = app.route

@rt("/hi")
def get(): return 'Hi there'

# re-create the same app using live reloading
lr_app = FastHTMLWithLiveReload()
lr_cli = TestClient(app)
lr_rt = lr_app.route

@lr_rt("/hi")
def get(): return 'Hi there'

# check that live reloading does NOT change the underlying behvaiour
test_eq(cli.get('/hi').text, "Hi there")
test_eq(cli.get('/hi').text, lr_cli.get('/hi').text)

# check that live reloading adds the expected route and header script
hdrs, routes = app.router.hdrs, app.routes
lr_hdrs, lr_routes = lr_app.router.hdrs, lr_app.routes

test_eq(len(lr_hdrs), len(hdrs)+1)
test_eq(lr_hdrs[0], lr_app.LIVE_RELOAD_HEADER)
test_eq(len(lr_routes), len(routes)+1)
test_eq(lr_routes[0], lr_app.LIVE_RELOAD_ROUTE)

I didn't include the test in the latest commit because it will fail (due to the routes issue). Let me know if you would prefer to commit the failing test now or include it in the PR for the routes fix.

@comhar comhar marked this pull request as ready for review June 11, 2024 13:41
@comhar comhar changed the title Draft: Live Reloading Live Reloading Jun 11, 2024
@jph00 jph00 merged commit f654aef into AnswerDotAI:main Jun 11, 2024
@jph00
Copy link
Contributor

jph00 commented Jun 11, 2024

lgtm! thanks :D

@jph00 jph00 added the enhancement New feature or request label Jun 11, 2024
@jph00
Copy link
Contributor

jph00 commented Jun 11, 2024

@comhar I've fixed the routes and added the test so please take a look and check it looks OK.

@comhar
Copy link
Contributor Author

comhar commented Jun 11, 2024

@comhar I've fixed the routes and added the test so please take a look and check it looks OK.

Sweet! I ran the latest commits locally and the live reloading is working as expected 👍
The test looks good as well 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants