Skip to content

Instantly share code, notes, and snippets.

@rubenfiszel
Created March 25, 2026 11:46
Show Gist options
  • Select an option

  • Save rubenfiszel/199f236e9334f1ffcbf71c6cebf47f2b to your computer and use it in GitHub Desktop.

Select an option

Save rubenfiszel/199f236e9334f1ffcbf71c6cebf47f2b to your computer and use it in GitHub Desktop.
Windmill: relative imports and wmill.runScript examples (Python & TypeScript)
# Windmill: Relative Imports & wmill.runScript Examples
Two patterns for reusing scripts in Windmill — **relative imports** (inlined, same job) and **wmill.runScript** (separate child job).
---
## Python
### `f/common/send_email.py` — reusable unit
```python
from typing import Optional
def main(to: str, subject: str, body: str, html: Optional[bool] = False):
"""Send an email via SMTP resource."""
import wmill
import smtplib
from email.mime.text import MIMEText
smtp = wmill.get_resource("u/admin/smtp_server")
msg = MIMEText(body, "html" if html else "plain")
msg["Subject"] = subject
msg["From"] = smtp["from"]
msg["To"] = to
with smtplib.SMTP(smtp["host"], smtp["port"]) as server:
server.starttls()
server.login(smtp["user"], smtp["password"])
server.send_message(msg)
return {"sent_to": to, "subject": subject}
```
### `f/notifications/send_signup_welcome.py` — uses it via relative import
```python
import wmill
def main(user_email: str, user_name: str):
"""Send a welcome email after signup."""
# Option A: relative import (both scripts must be in the same workspace)
from f.common.send_email import main as send_email
send_email(
to=user_email,
subject=f"Welcome, {user_name}!",
body=f"<h1>Hey {user_name}</h1><p>Thanks for signing up.</p>",
html=True,
)
# Option B: wmill.runScript — runs it as a separate job (async, logged)
wmill.run_script("f/common/send_email", args={
"to": "admin@company.com",
"subject": f"New signup: {user_name}",
"body": f"{user_name} ({user_email}) just signed up.",
})
return {"notified": user_email}
```
---
## TypeScript
### `f/common/send_email.ts` — reusable unit
```typescript
import * as wmill from "windmill-client";
export async function main(to: string, subject: string, body: string) {
const smtp = await wmill.getResource("u/admin/smtp_server");
const res = await fetch(smtp.api_url, {
method: "POST",
headers: { Authorization: `Bearer ${smtp.api_key}`, "Content-Type": "application/json" },
body: JSON.stringify({ to, subject, html: body }),
});
return { sent_to: to, status: res.status };
}
```
### `f/notifications/send_order_confirmation.ts` — uses it both ways
```typescript
import * as wmill from "windmill-client";
// Option A: relative import — inlined, same job
import { main as sendEmail } from "/f/common/send_email.ts";
export async function main(order_id: string, customer_email: string, total: number) {
// Direct call via relative import (runs inline)
await sendEmail(
customer_email,
`Order #${order_id} confirmed`,
`<p>Your order of <b>$${total}</b> has been confirmed.</p>`
);
// Option B: wmill.runScript — runs as a separate child job
await wmill.runScript("f/common/send_email", {
to: "warehouse@company.com",
subject: `New order #${order_id}`,
body: `<p>Please fulfill order #${order_id} ($${total}) for ${customer_email}.</p>`,
});
return { order_id, notified: [customer_email, "warehouse@company.com"] };
}
```
---
## Key Differences
| | Relative import | `wmill.runScript` |
|---|---|---|
| **Execution** | Inlined in the same job | Spawns a separate child job |
| **Logging** | One combined log | Each call gets its own run in the UI |
| **Retries/timeout** | Shares parent's | Has its own |
| **Use when** | You want speed + simplicity | You want visibility, independent retries, or to call across languages |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment