This release is a pre-release and may not be stable for production use.
PyWinUI
Build native WinUI 3 (Windows App SDK) desktop applications in idiomatic Python.
Not a themed look-alike and not a reimplementation — these are real WinUI 3 controls, wrapped so that you never have to touch WinRT unless you want to.
import pywinui as ui
class CounterApp(ui.App):
def build(self):
count = ui.TextBlock("0", font_size=32)
def bump(sender, args):
count.text = str(int(count.text) + 1)
return ui.Window(
title="PyWinUI Counter",
content=ui.StackPanel(
spacing=12, padding=24,
children=[
ui.TextBlock("Counter", font_size=20),
count,
ui.Button("Increment", on_click=bump),
],
),
)
CounterApp().run()
Status: early alpha. The architecture is verified end-to-end against the real bindings on Windows 11, but only five controls are wrapped so far. The API may change. See Current scope before depending on it.
Why now
The WinUI 3 projections for Python (PyWinRT) only landed in March 2025. Before that, a Python WinUI 3 app meant hand-rolling raw WinRT. The bindings now exist and work; PyWinUI is the ergonomics layer on top of them.
Requirements
- Windows 10/11 with the Windows App Runtime
- python.org Python 3.10+ — not the Microsoft Store build, which is a
packaged app and fails the runtime bootstrap with
ERROR_NOT_SUPPORTED
Install
pip install pywinui
The winui3-* dependencies are Windows-only and install automatically there.
On other platforms the package still installs and imports (the native layer is
loaded lazily), so the test suite and editor tooling work anywhere — but
anything that realizes a control needs Windows.
Two ways to build a tree
Both are first-class and they compose.
# Flutter style — the tree is a value. Best for reusable components.
ui.StackPanel(children=[ui.TextBlock("a"), ui.TextBlock("b")])
# With style — handles loops, conditionals and local references far better.
with ui.StackPanel() as panel:
ui.TextBlock("Items")
for name in items:
ui.Button(name, on_click=make_handler(name))
Async without the ceremony
Handlers may be async def. They're scheduled off the UI thread automatically,
WinRT async operations are awaited like any coroutine, and writes back to
widgets are marshalled onto the UI thread for you.
async def read_file(sender, args):
status.text = "Reading..."
file = await StorageFile.get_file_from_path_async(path)
text = await FileIO.read_text_async(file) # real WinRT I/O
status.text = f"{len(text)} chars" # marshalled back to the UI
Failures arrive as ordinary Python exceptions — a missing path raises
FileNotFoundError.
The escape hatch
The curated surface covers the common path with type hints, validation and value
conversions. Anything not wrapped still forwards by name, and widget.native
gives you the raw WinUI control:
btn = ui.Button("Save")
btn.native.background = some_brush # raw WinUI, fully supported
Current scope
| Working | Not yet |
|---|---|
TextBlock, TextBox, Button, StackPanel, Grid, Window |
The other ~100 WinUI controls |
| Both tree-building styles, attach/detach | Data binding, styles, resource dictionaries |
async def handlers, awaiting WinRT ops |
Control templates, virtualized lists |
| Off-thread writes auto-marshalled | Off-thread reads (wrap in run_on_ui) |
padding/margin, visible, content boxing |
Grid.Row/Grid.Column attached properties |
| Running from a Python environment | Packaging a double-clickable app (MSIX) |
Packaging is unproven. Shipping an app to end users means bundling Python and the Windows App Runtime, likely as MSIX, and that path has not been prototyped. Today this is a library for developers who already have Python installed.
Troubleshooting
DLL load failed ... The filename or extension is too long — your virtual
environment path is too deep. The winui3 extension modules have very long file
names (_winui3_microsoft_windows_applicationmodel_dynamicdependency_bootstrap),
and a nested venv can push them past Windows' 260-character MAX_PATH. Use a
shorter path, or enable long paths.
ERROR_NOT_SUPPORTED during bootstrap — you're on the Microsoft Store build
of Python, which is itself a packaged app. Use the python.org installer.
Examples
pip install -e ".[examples,dev]"
python examples/counter.py
python examples/async_file_read.py
Tests
pytest
The suite runs anywhere — no Windows required. It swaps in a fake native layer to lock down the tree model, both building styles, attach/detach, property forwarding, value conversions and thread marshalling. What it deliberately cannot cover is the binding boundary itself; that is verified by running the examples on Windows.
License
MIT
Metadata
Release files for pywinui 0.1.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pywinui-0.1.0a1.tar.gz | 21.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pywinui-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.9 kB
Release files / pywinui-0.1.0a1.tar.gz
| Download URL | pywinui-0.1.0a1.tar.gz |
|---|---|
| Size | 21.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8fd33c5e17a9dd44e6c22eebca6343d5b818fc67c7b98687891085673baca95f
|
|
BLAKE2b-256 checksum How to use checksums |
e353e7201b0911f5e5d650ea92e275589f662cdd67e2fc1e41bbe7987260db24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.11
|
Release files / pywinui-0.1.0a1-py3-none-any.whl
| Download URL | pywinui-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 13.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6d87ca6a574f13aa87a9eeb8a02365b0783af5480bc965f25a5d2c62e719254f
|
|
BLAKE2b-256 checksum How to use checksums |
32d1c0709b6ebb8261ffb6c1082239963cdf2070dfe8cf646f187283e91bfb49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.11
|