S
Solara•3mo ago
Alonso Silva

Webhooks

Hi 👋 , I saw this great example by @MaartenBreddels to add webhooks: https://github.com/widgetti/solara/issues/841#issuecomment-2539033646 It works like a charm. However, I wanted to send a code as such:
curl -X POST http://localhost:8765/webhook -d '{"message": "import matplotlib.pyplot as plt\nplt.bar([1,2],[1,2])\nplt.show()"}' -H "Content-Type: application/json"
curl -X POST http://localhost:8765/webhook -d '{"message": "import matplotlib.pyplot as plt\nplt.bar([1,2],[1,2])\nplt.show()"}' -H "Content-Type: application/json"
and execute it in the Page() by adding the last line:
if webhooks_result.value is None:
solara.Markdown("No data yet")
else:
solara.Markdown(f'{webhooks_result.value}')
exec(f'{webhooks_result.value}')
if webhooks_result.value is None:
solara.Markdown("No data yet")
else:
solara.Markdown(f'{webhooks_result.value}')
exec(f'{webhooks_result.value}')
The Markdown is shown but the plot is not. If I don't change anything in the code of the Page() but I save it again, then it displays it so it looks like it is executing the code but not displaying it. Any hint on how to display the executed code?
1 Reply
Monty Python
Monty Python•3mo ago
maartenbreddels
Seeing @Ben-Epstein also wanted to have this working, I've take another look at this. It appeared the problem was due to #919 which should be released soon. Together with this code:
from copy import deepcopy
import random
import string

import solara
import solara.lab
import solara.server.kernel_context
from starlette.responses import JSONResponse

from starlette.requests import Request
from anyio import to_thread



added = False

webhooks_result = solara.reactive(None)

def update_webhook_value(message):
# we update the reactive value for all kernels (i.e. connected browser pages)
for kernel_id, context in solara.server.kernel_context.contexts.items():
with context:
old_value = webhooks_result.value
message = deepcopy(message)
print(f'Updating webhook value from "{old_value}" to "{message}". Kernel: {kernel_id}')
webhooks_result.value = message


async def webhook_handler(request: Request):
try:
payload = await request.json()

print('Request received!')
print(payload.get('message', 'no message'))
payload = deepcopy(payload)
# if we call this directly, we run in the same thread as the uvicorn/starlette server (or anyio thread)
# that the websocket is related to, which causes issues (it will raise an exception in solara >= 1.43)
# using anyio, we run it in a separate thread
await to_thread.run_sync(update_webhook_value, payload.get('message', 'no message'))
return JSONResponse({
"status": "success",
"message": "Webhook received",
"data": payload
})
except Exception as e:
return JSONResponse({
"status": "error",
"message": str(e)
}, status_code=400)


def add_webhook_handler():
# workaround since we cannot import solara.server.starlette directly
global added
if not added:
import solara.server.starlette
solara.server.starlette.app.router.add_route("/webhook", webhook_handler, methods=["POST"])
added = True


@solara.component
def Page():
add_webhook_handler()

with solara.Column(align="center", style={"height": "100%", "justify-content": "center"}):
with solara.Card(style="width: 400px; margin: auto;"):
with solara.Column():
if webhooks_result.value is None:
solara.Markdown("No data yet")
else:
solara.Markdown(f'{webhooks_result.value}')
from copy import deepcopy
import random
import string

import solara
import solara.lab
import solara.server.kernel_context
from starlette.responses import JSONResponse

from starlette.requests import Request
from anyio import to_thread



added = False

webhooks_result = solara.reactive(None)

def update_webhook_value(message):
# we update the reactive value for all kernels (i.e. connected browser pages)
for kernel_id, context in solara.server.kernel_context.contexts.items():
with context:
old_value = webhooks_result.value
message = deepcopy(message)
print(f'Updating webhook value from "{old_value}" to "{message}". Kernel: {kernel_id}')
webhooks_result.value = message


async def webhook_handler(request: Request):
try:
payload = await request.json()

print('Request received!')
print(payload.get('message', 'no message'))
payload = deepcopy(payload)
# if we call this directly, we run in the same thread as the uvicorn/starlette server (or anyio thread)
# that the websocket is related to, which causes issues (it will raise an exception in solara >= 1.43)
# using anyio, we run it in a separate thread
await to_thread.run_sync(update_webhook_value, payload.get('message', 'no message'))
return JSONResponse({
"status": "success",
"message": "Webhook received",
"data": payload
})
except Exception as e:
return JSONResponse({
"status": "error",
"message": str(e)
}, status_code=400)


def add_webhook_handler():
# workaround since we cannot import solara.server.starlette directly
global added
if not added:
import solara.server.starlette
solara.server.starlette.app.router.add_route("/webhook", webhook_handler, methods=["POST"])
added = True


@solara.component
def Page():
add_webhook_handler()

with solara.Column(align="center", style={"height": "100%", "justify-content": "center"}):
with solara.Card(style="width: 400px; margin: auto;"):
with solara.Column():
if webhooks_result.value is None:
solara.Markdown("No data yet")
else:
solara.Markdown(f'{webhooks_result.value}')
And this curl command: ``` $ curl -X POST http://localhost:8877/webhook -d '{"message": "Hello from webhook!"}' -H "Content-Type: application...
Comment on widgetti/solara#841

Did you find this page helpful?