Repository navigation
feat: add official Python renderer package - #453
Merged
Mohamed Mansour (mohamedmansour) merged 10 commits intoAug 19, 2026
Merged
Mohamed Mansour (mohamedmansour) merged 10 commits into
Mohamed Mansour (mohamedmansour) merged 10 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot started reviewing on behalf of
Mohamed Mansour (mohamedmansour)
August 19, 2026 01:35
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a first-class Python integration for WebUI by introducing a new microsoft-webui PyPI package backed by native PyO3 bindings, along with the associated staging/validation logic, CI/CD automation, and documentation updates needed to ship and publish wheels/sdist as official release artifacts.
Changes:
- Introduces
crates/webui-python(PyO3 + maturin) with a typed Python facade (Renderer+StreamingSession) and a native_nativeextension module. - Extends release staging (
cargo xtask publish-stage) to build the Python sdist, preserve pre-staged wheels, and validate exact wheel/sdist naming and tags. - Adds CI/CD workflows and pipeline changes (GitHub Actions + Azure Pipelines) to build/test wheels, stage release artifacts, generate provenance, and publish to PyPI via Trusted Publishing, plus corresponding docs/spec updates.
Reviewed changes
Copilot reviewed 39 out of 42 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
xtask/src/publish.rs |
Adds publish/python staging, sdist packing via maturin, wheel+sdist validation, and publish-dir preservation rules. |
xtask/src/license_headers.rs |
Extends license-header enforcement to .py/.pyi with # comment style support. |
README.md |
Documents pip install microsoft-webui and notes Python runtime-only scope. |
docs/guide/integrations/python.md |
Adds a full Python integration guide (installation, buffered rendering, partials, streaming, API reference, safety notes). |
docs/guide/integrations/index.md |
Adds Python to the integrations index and reframes FFI as fallback for Python. |
docs/guide/integrations/ffi.md |
Updates FFI docs to position Python as preferring native package and adjusts streaming-session create signature narrative. |
docs/guide/installation.md |
Adds Python installation + minimal usage snippet and links to the Python guide. |
docs/guide/concepts/plugins/index.md |
Updates plugin trait docs to reflect HandlerPlugin: Send and documents threading contract. |
docs/ai/SKILL.md |
Updates docs-site AI skill content to reflect Python integration. |
docs/.webui-press/config.json |
Adds Python to docs navigation. |
DESIGN.md |
Updates spec for plugin factory/threading and documents Python binding + release artifact contract. |
deny.toml |
Updates advisory ignore list and license allowlist for new dependency set. |
crates/webui-python/src/lib.rs |
Implements the PyO3 native module, including renderer/session bindings and error mapping. |
crates/webui-python/Cargo.toml |
Adds the Rust crate manifest for the Python extension (cdylib + pyo3/serde_json/webui-handler deps). |
crates/webui-python/pyproject.toml |
Defines the PyPI project metadata and maturin/cibuildwheel/test/lint/typecheck configuration. |
crates/webui-python/README.md |
Adds Python-package README (install, usage, scope, build-from-source note). |
crates/webui-python/LICENSE |
Adds a package-local MIT license file for Python distributions. |
crates/webui-python/.gitignore |
Adds Python build/cache ignores for the new crate. |
crates/webui-python/python/microsoft_webui/__init__.py |
Exposes the typed public Python API surface and re-exports native exceptions/version. |
crates/webui-python/python/microsoft_webui/_api.py |
Provides the typed, Pythonic facade over the native module (state handling, enums, renderer/session APIs). |
crates/webui-python/python/microsoft_webui/_native.pyi |
Adds type stubs for the private native module. |
crates/webui-python/python/microsoft_webui/py.typed |
Marks the package as typed for type checkers. |
crates/webui-python/tests/conftest.py |
Adds shared fixtures (protocol bytes, renderer) for Python tests. |
crates/webui-python/tests/test_renderer.py |
Adds Python tests for renderer construction, state paths, plugins, tokens, threading, and error handling. |
crates/webui-python/tests/test_streaming.py |
Adds Python tests for streaming lifecycle, ordering, recoverability, and session lifetime. |
crates/webui-python/tests/test_package.py |
Adds packaging/API surface tests (versioning, __all__, exception pickling, py.typed marker, dependency checks). |
crates/webui-python/tests/validate_artifacts.py |
Adds validation tooling for PEP 639 license metadata/files in wheels/sdist. |
crates/webui-python/tests/fixtures/app/index.html |
Adds fixture templates used to generate/verify protocol rendering. |
crates/webui-python/tests/fixtures/app/greeting-card.html |
Adds fixture component template. |
crates/webui-python/tests/fixtures/app/greeting-card.css |
Adds fixture component CSS. |
crates/webui-python/tests/fixtures/greeting-card.css |
Adds fixture CSS output comparison baseline. |
crates/webui-python/benchmarks/ctypes_baseline.py |
Adds a ctypes/FFI baseline renderer for benchmarking overhead comparisons. |
crates/webui-python/benchmarks/benchmark_renderer.py |
Adds pytest-benchmark-based benchmarks for construction/rendering/partials/streaming and threaded throughput. |
crates/webui-handler/src/streaming/owned.rs |
Adds a test asserting owned streaming session Send and synchronized-host patterns. |
crates/webui-handler/src/plugin/mod.rs |
Updates HandlerPlugin to require Send, documents threading rules, and adds a test for Send-not-Sync plugin viability. |
Cargo.toml |
Adds pyo3 to [workspace.dependencies] with abi3-py311 and import-lib support. |
Cargo.lock |
Updates lockfile for new dependencies and transitive updates. |
.github/workflows/pr-python.yml |
Adds PR CI to build/test wheels (incl. manylinux), validate tags/digests, run tests, and enforce lint/mypy on a quality leg. |
.github/workflows/publish-pypi.yml |
Adds release-driven PyPI publication workflow with strict provenance, checksum, license, and asset validation + Trusted Publishing. |
.ado/pipelines/azure-pipelines-build.yml |
Extends build pipeline to build/test Python wheels (incl. enforced native ARM64 smoke tests via self-hosted pools) and stage Python artifacts. |
.ado/pipelines/azure-pipelines-cd.yml |
Extends CD pipeline to stage Python artifacts, generate provenance, include Python assets in GitHub Release, and coordinate publish ordering. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Add a typed HandlerError::InvalidState variant so bindings classify caller state errors by variant instead of matching on error prose, cross-build the three ARM64 wheels in PR CI, and anchor every CI restatement of the release target contract to xtask. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cross-compiled ARM64 wheels now ship on the same terms as the ARM64 npm, NuGet, FFI, and CLI binaries this pipeline has always produced. Restore the main branch trigger and downgrade the three native ARM64 smoke tests from release-blocking failures to optional coverage that warns when skipped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move wheel build policy into cargo xtask publish-python so each target leg uses the same single-command pattern as the native artifacts, and drop the PyPI publication path along with the provenance, digest re-validation, and ARM64 pool gating that only existed to support it. Wheels and the sdist are still built, validated, and attached to the GitHub Release; publishing to PyPI is deferred until package ownership and signing policy are settled. Net effect is 2428 fewer lines of pipeline YAML and inline shell. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
publish-python duplicated a per-target command that publish-build already owns. Build the wheel as part of publish-build instead, with --native-only and --python-only mirroring the existing --native-only/--pack-only flags on publish-stage. Only Linux needs the split, because its wheel must link an old glibc inside a manylinux container while the natives build on the host. Export is now mode-aware so the container run adds publish/python/ without erasing the natives the host run staged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…-renderer-package # Conflicts: # deny.toml
The Python checks duplicated toolchain setup and cache configuration that .github/actions/build already encapsulates, and ran without the lint gate. Move them into pr.yml as python-wheel, python-test, and python-fixture jobs so they reuse the shared composite action, the ubuntu-build cache, and the fail-fast lint phase. Trim the interpreter matrix while consolidating: the Linux wheel is installed on CPython 3.11 through 3.14 to prove the abi3 claim, while macOS and Windows only re-verify that their wheel loads. That drops the added job count from 18 to 12. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The FFI workflow duplicated the toolchain setup and cache configuration that .github/actions/build already encapsulates and ran without the lint gate. Move it into pr.yml as an ffi job that depends on build-linux and shares the ubuntu-build cache key, since it exercises the same debug workspace crates that job already compiled. Also drop the three TestArm64PythonWheel entries left in AssembleRelease's dependsOn when those jobs were removed. Azure validates the job graph at queue time, so the dangling references would have failed the release build. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The Linux ARM64 leg failed because the manylinux cross image pre-sets CARGO_BUILD_TARGET, so cargo cross-compiled xtask itself and could not execute it. Clear that variable when invoking the host build tool. The Windows ARM64 leg failed because maturin was told to use the host interpreter, which it skips when the target architecture differs and then rejects by name. abi3 needs no target interpreter at all, so stop passing --interpreter and let maturin derive the ABI from the feature and the platform from --target. Locked in with a test asserting no target ever pins one. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Mohamed Mansour (mohamedmansour)
requested review from
Bang Lee (Qusic),
Jane Chu (janechu) and
mcritzjam
August 19, 2026 05:06
Branch protection can only require per-job contexts, so the required-check list had to change every time a job was added, renamed, or removed - three times in this pull request alone. Add a pr-checks job that depends on every other job and fails unless all of them succeeded, so exactly one stable context needs protecting. Rename the FFI job from 'FFI Integration Tests' to 'FFI' to match the terse sibling names. Preserving the longer name bought nothing, because folding the workflow in had already changed the full context. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
8 tasks
Jane Chu (janechu)
approved these changes
Aug 19, 2026
Mohamed Mansour (mohamedmansour)
merged commit Aug 19, 2026
7f2d9d1
into
microsoft:main
24 checks passed
Mohamed Mansour (mohamedmansour)
deleted the
mohamedmansour-python-renderer-package
branch
August 19, 2026 18:08
Mohamed Mansour (mohamedmansour)
added a commit
that referenced
this pull request
Aug 26, 2026
## Release Bumps WebUI to `0.0.26`. Previous release tag: `v0.0.25` ## Changes since `v0.0.25` Features: - Python hosts now have an official typed PyO3 renderer package with buffered, partial, template, token, and host-driven streaming APIs (feat: add official Python renderer package #453 by @mohamedmansour). - Progressive SSR can place transport-flushed streaming boundaries inside reusable components while preserving component ownership and continuation state (feat: support component-local streaming boundaries #460 by @mohamedmansour). - Components can defer compiler-owned hydration until interaction, with router preload handoff and a combined lazy-render policy (feat: add compiler-driven interaction hydration #485 by @mohamedmansour). - Builds can opt into global Light DOM CSS while preserving explicit authored Shadow roots and deterministic style closures (feat: make Light DOM a global CSS opt-in #429 by @mohamedmansour). - State projection supports application-owned TypeScript 7.0.2 while retaining the TypeScript 6 migration path (feat: support TypeScript 7 projection compilation #452 by @mohamedmansour). - WebUI Press supports layout-scoped compile-time named regions with fallback HTML, state, components, and scripts (feat(press): add compile-time named regions #484 by @mohamedmansour). - FAST v2 and v3 gain plugin-owned local and npm component discovery with validated FAST template transformation (feat: add plugin-owned FAST component discovery #378 by @janechu). Fixes: - Missing condition identifiers are treated as falsy before negation and logical evaluation, aligning SSR with the browser runtime (fix: negate missing condition paths correctly #449 by @mohamedmansour). - FAST route state now scales through escaped scalar kebab-case attributes shared by FAST v2 and v3 (fix: scale FAST route state with scalar attributes #450 by @janechu). - Dynamic Link-mode components wait for native stylesheet readiness across navigation and component assets, preventing unstyled flashes (fix: prevent dynamic component stylesheet flashes #454 by @mohamedmansour). - Authored component definitions defer whenever compiled template metadata has not arrived, including ordinary router navigation (fix: defer authored define() whenever template metadata is missing #461 by @mohamedmansour). - Client structural updates preserve authored order and sibling ownership for shared slots and raw HTML ranges (fix: preserve source order for shared structural slots #465 by @mohamedmansour, fix: preserve siblings around raw HTML updates #466 by @mohamedmansour). - Native-element attributes no longer leak into the local state of a later component (perf(handler): stop native attributes leaking into component state #469 by @mohamedmansour). - Templates-and-state-only SSR bootstrap payloads no longer require component style metadata (fix: allow SSR bootstrap without component styles #475 by @mohamedmansour). - The high-level Rust `serve_request` path now preserves complete render options, including CSP nonces, while sharing the same entry and request path with partial rendering (fix: forward CSP nonces through serve_request #488 by @mohamedmansour). - Projection compilation bounds source reads, excludes binary and unsupported-loader inputs from semantic analysis, and preserves deterministic cleanup under large esbuild graphs (fix: bound projection adapter source reads #489 by @mohamedmansour). - Router pending UI remains mounted through pre-commit work and settles at the synchronous DOM commit, with stale, aborted, and re-entrant navigation cleanup kept generation-safe (fix: settle router pending UI during navigation commits #491 by @mohamedmansour). Docs: - The documentation site adds accessible responsive navigation, improved reading layouts, stronger search behavior, and Playground recovery states (feat: polish WebUI documentation experience #459 by @mohamedmansour). - Slot-resolution implementation guidance now documents pre-order lookup, pending placement ownership, and marker handling (chore: clarify slot resolution comments #471 by @mohamedmansour). - Repeated `w-ref` behavior is now explicit: refs are scalar and the last wired occurrence wins, while stable authored IDs or item components provide identity-based lookup (chore: clarify repeated w-ref behavior #490 by @mohamedmansour). Maintenance: - Release and package policy metadata now constrains the transitive h2 advisory, uses SPDX NuGet licensing, and classifies publishing jobs correctly (chore: allow constrained h2 advisory #451 by @janechu, fix: use modern NuGet license metadata #457 by @janechu, chore: mark publishing jobs as release jobs #458 by @janechu). - The development and CI Rust toolchain is updated to 1.98 with the resulting warnings resolved (Update rust toolchain version and fix clippy warning #462 by @telecos). - Release builds use Thin LTO to retain cross-crate optimization with faster linking (perf: switch release builds to Thin LTO #464 by @mohamedmansour). - Handler and expression hot paths reduce attribute vtable calls, render lookup and scope allocations, and single-term condition overhead (perf(handler): centralize HTML attribute writing in ResponseWriter #467 by @mohamedmansour, perf(expressions): fast-path single-term conditions #470 by @mohamedmansour, perf(handler): reduce render lookup and scope allocations #472 by @mohamedmansour). - Node rendering can reuse immutable prepared-state snapshots and bounded per-route output capacity hints (perf(node): reuse prepared state across renders #477 by @mohamedmansour, perf: reuse Node render output capacity #478 by @mohamedmansour). - Framework hydration releases bootstrap data sooner and reduces allocations for bindings, empty hosts, and visible conditionals (perf: reduce framework hydration allocations #479 by @mohamedmansour, perf: release SSR bootstrap memory after hydration #480 by @mohamedmansour, perf: reduce empty template host overhead #482 by @mohamedmansour, perf: remove visible conditional anchors #483 by @mohamedmansour). - Benchmark tooling now renders the full contact workload, runs Criterion baselines per target, and restores the streaming hydration fixture (fix(bench): render contacts in contact-book benchmark #468 by @mohamedmansour, fix(xtask): run Criterion benchmarks per target #473 by @mohamedmansour, fix: repair streaming hydration benchmark fixture #476 by @mohamedmansour). ## Validation - `cargo xtask check` --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WebUI needs a first-class Python integration that ships native binaries instead of requiring users to clone the repository and compile the C FFI library themselves. This adds an official
microsoft-webuipackage with a Pythonic, typed API and a performance-first native implementation.Scope
This PR builds the Python packages; it does not publish them. Wheels and the sdist are validated and attached to each GitHub Release. Publishing to PyPI is deferred until package ownership and ESRP/signing policy for a Microsoft-owned PyPI project are settled.
Approach
abi3-py311stable ABI, with reusable renderer state, GIL-detached rendering, buffered/partial/template APIs, and host-driven streaming.cargo xtask publish-build, the same single command that already produces the native artifacts.--native-only/--python-onlycover the one split case, Linux, where the wheel must link an old glibc inside amanylinuxcontainer while the natives build on the host.Performance
The direct PyO3 path measured 5.30 us median versus 7.65 us for the matching
ctypesFFI baseline, approximately 1.44x faster.Error contract
webui-handlergains a typedHandlerError::InvalidStatevariant so host bindings can distinguish a caller state-JSON error from a render failure without matching on message text. Python maps that one variant toStateError.Build coverage
All six wheels are cross-compiled on Microsoft-hosted x64 agents, exactly like the ARM64 npm, NuGet, FFI, and CLI binaries this pipeline already ships. Linux wheels build inside a digest-pinned
manylinux2014cross image so they link an old glibc.PR CI builds all six wheels. The Linux wheel is then installed on CPython 3.11 through 3.14 to prove the abi3 claim, while macOS and Windows re-verify that their own wheel loads. Lint, strict typing, stubtest, and a protocol-fixture drift check run alongside. A drift checker anchors every CI restatement of the platform tag list to the contract in
xtask/src/publish.rs.CI consolidation
pr-python.ymlandpr-ffi.ymlare folded intopr.yml, so every PR check shares onelintgate, the./.github/actions/buildcomposite, and theubuntu-buildcache instead of duplicating toolchain setup.A new
PR Checksjob depends on every other job and fails unless all of them succeeded.