Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions examples/sanic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Sanic Example

This example demonstrates how to use HMR with a Sanic application.

## How to Run

After installing dependencies, run the following command in this directory:

```sh
hmr app.py
```

This will start a Sanic development server with HOT-reloading enabled.

## What to Observe

Once the server is running, you can access the application at `http://localhost:8000`.

- Visit `http://localhost:8000/a` and `http://localhost:8000/b`.
- Try modifying `b.py` and refresh the browser to see the changes applied instantly (without rerunning `sleep(1)` in `a.py`).
- Everything else should work as expected too. You will find your development experience much smoother than just using `sanic app:app --dev`.

> [!NOTE]
> Unlike [the FastAPI example](../fastapi/), we haven't implement a separate integration for Sanic.
> If you know Sanic well, you are welcome to contribute an integration similar to `uvicorn-hmr`.

## Available Endpoints

- `/` - Main app endpoint with route information
- `/a` - Blueprint A endpoint
- `/b` - Blueprint B endpoint
- `/b/test` - Test endpoint from Blueprint B
15 changes: 15 additions & 0 deletions examples/sanic/a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from time import sleep

from sanic import Blueprint
from sanic.response import json

a = Blueprint("a", url_prefix="/a")


sleep(1)
print("slow module a.py imported")


@a.route("/")
async def index(_):
return json({"message": "Hello from a.py!"})
24 changes: 24 additions & 0 deletions examples/sanic/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import time

from a import a
from b import b
from sanic import Sanic
from sanic.response import json

# Create a dynamic app name to avoid conflicts during HMR
app_name = f"SanicExample_{int(time.time())}"
app = Sanic(app_name)

app.blueprint(a)
app.blueprint(b)


@app.route("/")
async def index(_):
return json({"message": "Hello from main Sanic app!", "routes": ["/a", "/b", "/b/test"]})


if __name__ == "__main__":
from start import start_server

start_server(app)
14 changes: 14 additions & 0 deletions examples/sanic/b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from sanic import Blueprint
from sanic.response import json

b = Blueprint("b", url_prefix="/b")


@b.route("/")
async def index(_):
return json({"message": "Hello from b.py! (FINAL HMR TEST)", "data": "HMR is working perfectly with Sanic!"})


@b.route("/test")
async def test(_):
return json({"message": "Test endpoint from b.py", "status": "working", "hmr": "fully operational"})
11 changes: 11 additions & 0 deletions examples/sanic/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[project]
name = "sanic-example"
version = "0"
requires-python = ">=3.12"
dependencies = [
"sanic~=24.6.0",
"hmr~=0.6.0",
]

[tool.uv]
package = false
60 changes: 60 additions & 0 deletions examples/sanic/start.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
You don't need to understand what this code does.

This file implements a simple function to start a Sanic server with HMR support.

To use it, just copy this file to your project and call the `start_server` function with your Sanic app instance.
And then run this file with `hmr path/to/this/file.py`.

We haven't implement a separate integration for Sanic.
So you need to use this function to start the server with HMR support.
"""

from atexit import register, unregister
from multiprocessing import Process
from typing import cast

from sanic import Sanic


class ServerProcess(Process):
def __init__(self, app: Sanic):
super().__init__(daemon=True)
self.app = app

def run(self):
# Run in a separate process to avoid signal conflicts
self.app.run(host="localhost", port=8000, debug=False, auto_reload=False, single_process=True)

def shutdown(self):
if self.is_alive():
self.terminate()
self.join(timeout=1)
if self.is_alive():
self.kill()


def start_server(app: Sanic):
global server

print("* Running on http://localhost:8000")

if server := cast("ServerProcess | None", globals().get("server")):
unregister(server.shutdown)
server.shutdown()

server = ServerProcess(app)
server.start()
register(server.shutdown)


if __name__ == "__main__":
from app import app

start_server(app)


if __name__ == "__main__":
from app import app

start_server(app)
Loading