Skip to content
Merged
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
52 changes: 35 additions & 17 deletions examples/prefect/hello_world.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
"""Dummy Prefect workflow that prints a message on an OSW page.

Usage (PowerShell):
$env:PREFECT_API_URL="https://osw.example.com:4200/api"
$env:PREFECT_PUBLIC_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
$env:OSW_USER="Bot@name"
$env:OSW_SERVER="osw.example.com"
$env:OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py

Usage (Bash):
export PREFECT_API_URL="https://osw.example.com:4200/api"
export PREFECT_PUBLIC_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
export OSW_USER="Bot@name"
export OSW_SERVER="osw.example.com"
export OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py
Option A — Direct Prefect access (worker can reach Prefect server directly):

PowerShell:
$env:PREFECT_API_URL="https://osw.example.com:4200/api"
$env:PREFECT_PUBLIC_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
$env:OSW_USER="Bot@name"; $env:OSW_SERVER="osw.example.com"
$env:OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py

Bash:
export PREFECT_API_URL="https://osw.example.com:4200/api"
export PREFECT_PUBLIC_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
export OSW_USER="Bot@name" OSW_SERVER="osw.example.com"
export OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py

Option B — ApiGateway only (Prefect server behind firewall, only reachable
through MediaWiki ApiGateway extension):

PowerShell:
$env:PREFECT_API_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
$env:OSW_USER="Bot@name"; $env:OSW_SERVER="osw.example.com"
$env:OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py

Bash:
export PREFECT_API_URL="https://osw.example.com/w/rest.php/apigateway/v1/prefect"
export OSW_USER="Bot@name" OSW_SERVER="osw.example.com"
export OSW_PASSWORD="bot-password"
python examples/prefect/hello_world.py

Environment variables:
PREFECT_API_URL Prefect server API URL (used by the worker)
PREFECT_API_URL Prefect server API URL (used by the worker).
Can be a direct URL or an ApiGateway URL.
PREFECT_PUBLIC_URL (optional) Public URL stored in PrefectFlow entity,
for use by browser clients (e.g. prefect.js).
for browser clients (e.g. prefect.js).
Falls back to PREFECT_API_URL if not set.
Only needed when PREFECT_API_URL differs from the
public gateway URL (Option A).
OSW_USER OSW bot username
OSW_SERVER OSW instance domain
OSW_PASSWORD OSW bot password. If not set, falls back to a
Expand Down
61 changes: 61 additions & 0 deletions src/osw/utils/_httpx_gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Auto-patches httpx for ApiGateway routing in Prefect subprocesses.

Activated by osw-httpx-gateway.pth in site-packages. Only patches if
PREFECT_API_URL contains an ApiGateway URL pattern. Login is lazy —
no network calls at import time. All ``osw`` imports are deferred to
first request so the hook works even before editable installs are on
sys.path.
"""

import os


def _install():
api_url = os.environ.get("PREFECT_API_URL", "")
if "/rest.php/apigateway/" not in api_url:
return

if not os.environ.get("OSW_SERVER"):
return

try:
import httpx
except ImportError:
return

class _LazyApiGatewayTransport(httpx.AsyncBaseTransport):
"""Wraps ApiGatewayTransport with lazy MW login on first request."""

def __init__(self, gateway_url):
self._gateway_url = gateway_url
self._inner = None

def _ensure_initialized(self):
if self._inner is not None:
return
# Deferred import — osw may not be on sys.path at .pth time
from osw.utils.workflow import ApiGatewayTransport, connect

osw_instance = connect()
self._inner = ApiGatewayTransport(
gateway_url=self._gateway_url,
mw_site=osw_instance.site.mw_site,
)

async def handle_async_request(self, request):
self._ensure_initialized()
return await self._inner.handle_async_request(request)

_transport = _LazyApiGatewayTransport(api_url)
_original_init = httpx.AsyncClient.__init__

def _patched_init(self, *args, **kwargs):
base = str(kwargs.get("base_url", ""))
if "/rest.php/apigateway/" in base and "transport" not in kwargs:
kwargs["transport"] = _transport
_original_init(self, *args, **kwargs)

httpx.AsyncClient.__init__ = _patched_init


_install()
Loading
Loading