pywinui 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ # Normalize to LF in the repo; Git checks out native line endings on Windows.
2
+ * text=auto eol=lf
@@ -0,0 +1,41 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ # The suite is designed to run without Windows or the winui3 wheels, so we
11
+ # prove that claim on Linux as well as running it where the library ships.
12
+ test:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [windows-latest, ubuntu-latest]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+ runs-on: ${{ matrix.os }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ - run: python -m pip install --upgrade pip
25
+ - run: pip install -e ".[dev]"
26
+ - run: pytest -v
27
+
28
+ # Realizing native controls needs a UI thread and the Windows App Runtime,
29
+ # which GitHub's runners don't provide, so the examples are NOT run here.
30
+ # Run them manually on a real Windows box after touching _Native — see
31
+ # AGENTS.md section 5.1.
32
+ build:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.13"
39
+ - run: pip install build twine
40
+ - run: python -m build
41
+ - run: twine check dist/*
@@ -0,0 +1,19 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .venv/
9
+ venv/
10
+
11
+ # Editors
12
+ .idea/
13
+ .vscode/
14
+ *.swp
15
+
16
+ # OS
17
+ Thumbs.db
18
+ Desktop.ini
19
+ /.pypirc
@@ -0,0 +1,283 @@
1
+ # PyWinUI — Agent Briefing
2
+
3
+ You are picking up **PyWinUI**, a Pythonic wrapper library that lets developers
4
+ build **native WinUI 3 (Windows App SDK) desktop applications in Python**. This
5
+ document explains the goal, the design decisions already made (and *why*), the
6
+ current state of the code, the constraints you must respect, and what remains.
7
+ Read it fully before changing anything.
8
+
9
+ ---
10
+
11
+ ## 1. The goal
12
+
13
+ Let someone write a modern Windows 11 desktop app — real native WinUI 3 controls,
14
+ not a themed look-alike — using ordinary, idiomatic Python.
15
+
16
+ The guiding principle: **a user should not know they are using WinUI/WinRT
17
+ underneath, except when they reach for advanced features.** The common path feels
18
+ like a normal Python UI library (Flet/Flutter/nicegui-flavored). The WinRT
19
+ machinery only surfaces when someone deliberately goes past the curated surface.
20
+
21
+ This is a wrapper, not a reimplementation. Underneath sit the **PyWinRT `winui3`
22
+ projection packages** (auto-generated Python bindings for the Windows App SDK,
23
+ e.g. `winui3-Microsoft.UI.Xaml`). We wrap those to make them pleasant.
24
+
25
+ ### Why this is feasible now (and wasn't before)
26
+ The `winui3` namespace projections were only added to PyWinRT around **March
27
+ 2025**. An earlier same-named attempt (github.com/bornacheck/PyWinUI) was archived
28
+ in **May 2023** with essentially no code — at that time no WinUI projections
29
+ existed, so it would have meant hand-rolling raw WinRT. That foundation now exists;
30
+ the remaining work is the ergonomics layer, which is what this project is.
31
+
32
+ ---
33
+
34
+ ## 2. Non-negotiable design principles
35
+
36
+ These are the spine of the project. Preserve them unless you have a strong,
37
+ explicit reason and you update this document.
38
+
39
+ ### 2.1 Isolate all native calls behind one glue layer
40
+ Every `winui3` import and every WinRT-specific call lives in the `_Native` class
41
+ (and a couple of clearly marked helpers). The rest of the library is pure Python
42
+ and never mentions WinRT. This is what makes the code testable off-Windows and
43
+ lets us fix binding changes in one place.
44
+
45
+ ### 2.2 The `# VERIFY` convention
46
+ Any line that mirrors the C#/WinRT object model but has **not** been confirmed
47
+ against the real packages on Windows is tagged `# VERIFY`. These are the calls an
48
+ agent on a Windows machine must validate. Do not silently remove the tags; remove
49
+ one only after confirming that specific call works, and say so in the commit.
50
+
51
+ ### 2.3 Declaration is separate from realization
52
+ Constructing widgets builds a cheap pure-Python tree. A later `_realize()` pass
53
+ walks that tree and creates the native controls. Native objects can only be
54
+ created after the runtime is bootstrapped and on the UI thread, so this split is
55
+ deliberate — keep it. Never create native controls in `__init__`.
56
+
57
+ ### 2.4 Curated surface + escape hatch
58
+ Common properties/events get explicit, Pythonic, validated wrappers. Anything not
59
+ wrapped still works via attribute forwarding, and `widget.native` is the blessed
60
+ way to reach the raw WinUI control for advanced use. The curated layer is where
61
+ type hints, validation, and value conversions live; forwarding covers the long
62
+ tail. Keep both tiers.
63
+
64
+ ### 2.5 Two tree-building styles, both first-class
65
+ - **Flutter style:** `Panel(children=[A(), B()])` — the tree is a value.
66
+ - **With style:** `with Panel(): A(); B()` — widgets constructed inside a block
67
+ auto-attach to it (nicegui-style, via a `contextvars` parent stack).
68
+
69
+ They compose (a panel can be seeded with `children=[...]` and extended inside a
70
+ `with`). **With-style is the idiomatic default** (it handles loops, conditionals,
71
+ and local child references far better); **Flutter style is preferred for reusable
72
+ components**, where a function returning a widget is referentially transparent.
73
+ Do not remove either.
74
+
75
+ ### 2.6 Structure vs rendering are different axes
76
+ - **Attachment** (structural: "is this widget in the tree?") is a one-time,
77
+ construct-time decision. Controlled by auto-parenting, `attached=False`,
78
+ `detached()`, and manual `panel.add(...)`.
79
+ - **Visibility** (rendering: "is this widget shown?") is a live property,
80
+ `visible: bool`, mapped to WinUI's `Visibility` enum.
81
+
82
+ Never conflate them. Critically: **WinUI enforces one parent per element** —
83
+ adding an already-parented control elsewhere throws. So "hide it and move it
84
+ later" does not work; you must detach/re-add. This constraint is why `attached`
85
+ and `visible` are separate and must stay separate.
86
+
87
+ ### 2.7 Pythonic naming, curated conversions
88
+ PyWinRT already projects properties as `snake_case`, so forwarding is mostly a
89
+ `setattr`. A small number of properties need conversion; these live in one place
90
+ (a `_Native` helper + a branch in `_set_native`). Current examples:
91
+ `padding`/`margin` → `Thickness`, `visible` → `Visibility`. New conversions
92
+ (`enabled`, `opacity`, colors, alignment enums) follow the identical pattern.
93
+
94
+ ### 2.8 Threads and async are hidden
95
+ WinUI has one UI thread; touching a control from another thread throws, and WinRT
96
+ I/O returns `IAsyncOperation` objects. The library hides this:
97
+ - Event handlers may be `async def`; they are auto-scheduled.
98
+ - Widget writes made off the UI thread are auto-marshalled onto it.
99
+ - WinRT async ops are awaited directly — **PyWinRT 3.2 projects `__await__`**, so
100
+ `await picker.pick_single_file_async()` just works and WinRT failures arrive as
101
+ ordinary Python exceptions (a missing path raises `FileNotFoundError`).
102
+ `as_future(op)` is now a thin `ensure_future` wrapper, kept for when you need a
103
+ real Future (`gather`, `wait_for`, cancellation) — not a hand-rolled bridge.
104
+ - `run_on_ui(fn)` is the explicit escape hatch.
105
+
106
+ Current architecture (v1, simple + robust): asyncio runs on a **separate daemon
107
+ thread** because `Application.start` owns the UI thread's message pump; UI
108
+ mutations hop back via the `DispatcherQueue`. Known limitation: only *writes* are
109
+ auto-marshalled, not *reads*.
110
+
111
+ ---
112
+
113
+ ## 3. Current state
114
+
115
+ Files:
116
+
117
+ | File | What it is |
118
+ |------|-----------|
119
+ | `src/pywinui/__init__.py` | The library. Sole source file. |
120
+ | `examples/counter.py` | Minimal counter app — runs for real on Windows. |
121
+ | `examples/async_file_read.py` | Async handler awaiting real WinRT I/O — also runs. |
122
+ | `tests/test_pywinui.py` | Pytest suite — 31 tests, all passing. |
123
+ | `pyproject.toml` | Packaging + the full winui3 dependency list. |
124
+ | `README.md` / `LICENSE` | Public-facing docs; MIT. |
125
+ | `AGENTS.md` | This document (project root). |
126
+
127
+ It's a package directory holding one module so that splitting `_Native` or the
128
+ controls into submodules later doesn't change the import path or the layout.
129
+
130
+ Both examples have been executed against the real bindings, not just written.
131
+
132
+ What exists in `pywinui.py`: the `_Native` glue layer; a `Widget` base with
133
+ lazy realization, attribute forwarding, the `.native` escape hatch, and the
134
+ container/context-manager protocol; controls `TextBlock`, `TextBox`, `Button`,
135
+ `StackPanel`, `Grid`; `Window`; an `App` lifecycle with bootstrap + background
136
+ asyncio loop; and the dispatcher/async bridge (`run_on_ui`, `as_future`,
137
+ `detached`).
138
+
139
+ ### What the tests cover vs. don't
140
+ The suite runs **anywhere** (no Windows needed) by swapping a fake native layer
141
+ in via a fixture. It locks in: both tree-building styles and their composition,
142
+ nesting and context cleanup (including on exceptions), the Window single-content
143
+ slot and leaf rejection, attach/detach/`add`, property queueing and forwarding,
144
+ the `padding→Thickness`, `visible→Visibility` and `content→boxed` conversions,
145
+ `as_future` adaptation and error propagation, and async/thread marshalling in
146
+ both directions.
147
+
148
+ The tests still **cannot** cover the native calls themselves — the fake layer
149
+ mirrors the binding contract, it doesn't prove it. That split is correct, but it
150
+ does mean the fake must be kept honest: when a real binding turns out to behave
151
+ differently (as boxing did), update `FakeNative` to match, or the suite will
152
+ happily keep certifying the wrong contract. Re-run the two examples on Windows
153
+ after any change to `_Native`.
154
+
155
+ ---
156
+
157
+ ## 4. Hard constraints (read before running or editing)
158
+
159
+ 1. **Native code only runs on Windows** with the **Windows App Runtime**
160
+ installed. The library imports `winui3` lazily so it can be imported (and
161
+ tested) elsewhere, but anything that realizes controls needs Windows.
162
+ 2. **Use python.org Python, not the Microsoft Store build.** The Store build is a
163
+ packaged app and fails the runtime bootstrap with `ERROR_NOT_SUPPORTED`.
164
+ 3. **Import paths (resolved).** `winui3.microsoft.ui.xaml` is correct — the docs'
165
+ `winui3.microsoft.windows.ui.xaml` was a typo. **Every WinRT namespace is a
166
+ separate wheel**, and parents do not pull in children: `...Xaml.Controls` and
167
+ `...DynamicDependency.Bootstrap` must be installed explicitly, and
168
+ `winrt-Windows.Foundation` is required for value boxing and event delegates.
169
+ The full list is in the install hint inside `_Native.load()`.
170
+ 4. **Keep the pure-Python / native split.** Do not scatter `winui3` imports
171
+ through the codebase. New native touchpoints go in `_Native` and get a
172
+ `# VERIFY` tag.
173
+ 5. **Respect the one-parent rule** (see 2.6) in any layout/reparenting code.
174
+ 6. **Don't break the two marshalling tests** without intent — they encode the
175
+ current threading contract.
176
+
177
+ ---
178
+
179
+ ## 5. Open work (roughly prioritized)
180
+
181
+ ### 5.1 Verify the `# VERIFY` calls on Windows — mostly DONE
182
+ Verified on Windows 11, winui3 3.2.1, python.org Python 3.13, by launching a
183
+ real window (counter app: native controls realized, click handler mutated them,
184
+ off-thread write marshalled back). Confirmed as originally guessed:
185
+ - **App lifecycle:** `Application.start(callback)` is the right unpackaged entry
186
+ point — no `Application` subclass needed. `Window.activate()`, `.title`,
187
+ `.content` all correct.
188
+ - **Bootstrap:** `bootstrap.initialize(options=...)` as a context manager, with
189
+ `InitializeOptions.ON_NO_MATCH_SHOW_UI`.
190
+ - **Dispatcher:** `get_for_current_thread()` and `try_enqueue(fn)` both work, and
191
+ a plain Python callable *does* satisfy the delegate.
192
+ - **Layout:** `Panel.children.append(...)` works.
193
+ - **Values:** `Thickness(l,t,r,b)` correct; `Visibility.VISIBLE`/`COLLAPSED`
194
+ correct. Ints are coerced to double automatically (`font_size=20` → `20.0`).
195
+ - **Events:** `add_<event>` / `remove_<event>` with `(sender, args)` is correct.
196
+
197
+ Two things were **not** as assumed, and are now fixed in `_Native`:
198
+ - **Object-typed properties need boxing.** `Button.content = "Go"` raises
199
+ `TypeError: not a System.Object`. Strings/numbers must go through
200
+ `PropertyValue.create_*`; this is the new `_Native.box()` helper, wired into
201
+ `_set_native` for `content`. Any future object-typed property (`Tag`,
202
+ `ContentControl.Content`, ...) needs the same treatment.
203
+ - **Namespaces are per-wheel** (see §4.3) — the missing `winrt-Windows.Foundation`
204
+ package was what made event subscription look broken.
205
+
206
+ The **async bridge is now verified too** (`example_async.py`, driven end-to-end):
207
+ an `async def` handler returned in 0.1ms without blocking the UI thread, awaited
208
+ two chained WinRT async ops, and its result landed in a native `TextBlock` via
209
+ the DispatcherQueue. The originally-assumed mechanism (settable `Completed` +
210
+ `get_results()`) does work — it was verified directly — but it turned out to be
211
+ unnecessary, hence the `as_future` simplification in §2.8.
212
+
213
+ **No `# VERIFY` tags remain.** `TextBox` and `Grid` are the only controls never
214
+ instantiated; they follow the same pattern as verified ones.
215
+
216
+ Also observed: queued `run_on_ui` work cannot run while a handler is blocking the
217
+ UI thread — it only drains once the handler returns and the pump resumes. Obvious
218
+ in hindsight, but it makes any blocking wait inside a handler a deadlock risk.
219
+
220
+ ### 5.2 DispatcherQueue-driven single event loop
221
+ Retire the two-thread split by running one event loop driven by the
222
+ `DispatcherQueue`, so handlers run *on* the UI thread and marshalling largely
223
+ disappears (this also fixes the read-marshalling gap). Bigger job. When you do it,
224
+ the two marshalling tests are the contract to preserve or deliberately update.
225
+
226
+ ### 5.3 More controls + attached properties
227
+ Add the common controls. **Grid** needs a helper for attached properties
228
+ (`Grid.Row`/`Grid.Column`) — they don't fit the plain-property model and are
229
+ currently stubbed. Design that helper before adding grid-heavy layouts.
230
+
231
+ ### 5.4 More curated conversions
232
+ `enabled`, `opacity`, colors/brushes, alignment enums — each follows the
233
+ `padding`/`visible` template.
234
+
235
+ ### 5.5 Packaging
236
+ Shipping a double-clickable app means bundling Python + the Windows App Runtime,
237
+ ideally as MSIX, and reconciling that with the dynamic-dependency bootstrap. This
238
+ is the least-trodden path and worth prototyping early.
239
+
240
+ ### 5.6 No declarative XAML (by design, for now)
241
+ The tree is built imperatively. There is no XAML markup path, hot-reload, or
242
+ designer. A declarative DSL over the bindings would be future work, not a current
243
+ goal.
244
+
245
+ ### 5.7 Naming / PyPI
246
+ `pywinui` is currently unclaimed on PyPI — register a placeholder to hold the name.
247
+ Note the archived GPL-3.0 repo of the same name exists (no code); pick a license
248
+ deliberately.
249
+
250
+ ---
251
+
252
+ ## 6. Reference notes for translating WinUI docs
253
+
254
+ There are no Python API docs for the bindings (they're generated). Use Microsoft's
255
+ Windows App SDK / WinRT reference and translate names with PyWinRT conventions:
256
+ - Namespaces → lowercase, no underscores.
257
+ - Type names → stay `CapitalizedWords`.
258
+ - Methods/properties/fields/events → `snake_case`.
259
+ - Enum members → `UPPER_CASE`.
260
+ - Many methods are async and return `IAsyncOperation`-family types (see 2.8).
261
+
262
+ ---
263
+
264
+ ## 7. How to run the tests
265
+
266
+ ```
267
+ pip install -e ".[dev]"
268
+ pytest
269
+ ```
270
+
271
+ `pythonpath = ["src"]` in `pyproject.toml` means the suite also runs straight
272
+ from a checkout without installing. To exercise the native layer you must be on
273
+ Windows and run the examples by hand:
274
+
275
+ ```
276
+ pip install -e ".[examples,dev]"
277
+ python examples/counter.py
278
+ python examples/async_file_read.py
279
+ ```
280
+
281
+ They pass on any OS. If you touch the tree model, attach/detach, property
282
+ forwarding, conversions, or marshalling, keep them green — that suite is the
283
+ guardrail protecting the design decisions above.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Israel Dryer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.4
2
+ Name: pywinui
3
+ Version: 0.1.0a1
4
+ Summary: Build native WinUI 3 (Windows App SDK) desktop apps in idiomatic Python
5
+ Project-URL: Homepage, https://github.com/israel-dryer/pywinui
6
+ Project-URL: Repository, https://github.com/israel-dryer/pywinui
7
+ Project-URL: Issues, https://github.com/israel-dryer/pywinui/issues
8
+ Author-email: Israel Dryer <israel.dryer@gmail.com>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Israel Dryer
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: desktop,gui,windows,windows-app-sdk,winrt,winui,winui3
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Environment :: Win32 (MS Windows)
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
37
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Software Development :: User Interfaces
44
+ Classifier: Typing :: Typed
45
+ Requires-Python: >=3.10
46
+ Requires-Dist: winrt-windows-foundation<4,>=3.2; sys_platform == 'win32'
47
+ Requires-Dist: winui3-microsoft-ui-dispatching<4,>=3.2; sys_platform == 'win32'
48
+ Requires-Dist: winui3-microsoft-ui-xaml-controls<4,>=3.2; sys_platform == 'win32'
49
+ Requires-Dist: winui3-microsoft-ui-xaml<4,>=3.2; sys_platform == 'win32'
50
+ Requires-Dist: winui3-microsoft-windows-applicationmodel-dynamicdependency-bootstrap<4,>=3.2; sys_platform == 'win32'
51
+ Requires-Dist: winui3-microsoft-windows-applicationmodel-dynamicdependency<4,>=3.2; sys_platform == 'win32'
52
+ Provides-Extra: dev
53
+ Requires-Dist: pytest>=8.0; extra == 'dev'
54
+ Provides-Extra: examples
55
+ Requires-Dist: winrt-windows-storage<4,>=3.2; (sys_platform == 'win32') and extra == 'examples'
56
+ Description-Content-Type: text/markdown
57
+
58
+ # PyWinUI
59
+
60
+ Build **native WinUI 3** (Windows App SDK) desktop applications in idiomatic Python.
61
+
62
+ Not a themed look-alike and not a reimplementation — these are real WinUI 3
63
+ controls, wrapped so that you never have to touch WinRT unless you want to.
64
+
65
+ ```python
66
+ import pywinui as ui
67
+
68
+
69
+ class CounterApp(ui.App):
70
+ def build(self):
71
+ count = ui.TextBlock("0", font_size=32)
72
+
73
+ def bump(sender, args):
74
+ count.text = str(int(count.text) + 1)
75
+
76
+ return ui.Window(
77
+ title="PyWinUI Counter",
78
+ content=ui.StackPanel(
79
+ spacing=12, padding=24,
80
+ children=[
81
+ ui.TextBlock("Counter", font_size=20),
82
+ count,
83
+ ui.Button("Increment", on_click=bump),
84
+ ],
85
+ ),
86
+ )
87
+
88
+
89
+ CounterApp().run()
90
+ ```
91
+
92
+ > **Status: early alpha.** The architecture is verified end-to-end against the
93
+ > real bindings on Windows 11, but only five controls are wrapped so far. The API
94
+ > may change. See [Current scope](#current-scope) before depending on it.
95
+
96
+ ## Why now
97
+
98
+ The WinUI 3 projections for Python ([PyWinRT](https://github.com/pywinrt/pywinrt))
99
+ only landed in March 2025. Before that, a Python WinUI 3 app meant hand-rolling
100
+ raw WinRT. The bindings now exist and work; PyWinUI is the ergonomics layer on
101
+ top of them.
102
+
103
+ ## Requirements
104
+
105
+ - **Windows 10/11** with the [Windows App Runtime](https://aka.ms/windowsappsdk/runtime)
106
+ - **python.org Python 3.10+** — *not* the Microsoft Store build, which is a
107
+ packaged app and fails the runtime bootstrap with `ERROR_NOT_SUPPORTED`
108
+
109
+ ## Install
110
+
111
+ ```
112
+ pip install pywinui
113
+ ```
114
+
115
+ The `winui3-*` dependencies are Windows-only and install automatically there.
116
+ On other platforms the package still installs and imports (the native layer is
117
+ loaded lazily), so the test suite and editor tooling work anywhere — but
118
+ anything that realizes a control needs Windows.
119
+
120
+ ## Two ways to build a tree
121
+
122
+ Both are first-class and they compose.
123
+
124
+ ```python
125
+ # Flutter style — the tree is a value. Best for reusable components.
126
+ ui.StackPanel(children=[ui.TextBlock("a"), ui.TextBlock("b")])
127
+
128
+ # With style — handles loops, conditionals and local references far better.
129
+ with ui.StackPanel() as panel:
130
+ ui.TextBlock("Items")
131
+ for name in items:
132
+ ui.Button(name, on_click=make_handler(name))
133
+ ```
134
+
135
+ ## Async without the ceremony
136
+
137
+ Handlers may be `async def`. They're scheduled off the UI thread automatically,
138
+ WinRT async operations are awaited like any coroutine, and writes back to
139
+ widgets are marshalled onto the UI thread for you.
140
+
141
+ ```python
142
+ async def read_file(sender, args):
143
+ status.text = "Reading..."
144
+ file = await StorageFile.get_file_from_path_async(path)
145
+ text = await FileIO.read_text_async(file) # real WinRT I/O
146
+ status.text = f"{len(text)} chars" # marshalled back to the UI
147
+ ```
148
+
149
+ Failures arrive as ordinary Python exceptions — a missing path raises
150
+ `FileNotFoundError`.
151
+
152
+ ## The escape hatch
153
+
154
+ The curated surface covers the common path with type hints, validation and value
155
+ conversions. Anything not wrapped still forwards by name, and `widget.native`
156
+ gives you the raw WinUI control:
157
+
158
+ ```python
159
+ btn = ui.Button("Save")
160
+ btn.native.background = some_brush # raw WinUI, fully supported
161
+ ```
162
+
163
+ ## Current scope
164
+
165
+ | Working | Not yet |
166
+ |---|---|
167
+ | `TextBlock`, `TextBox`, `Button`, `StackPanel`, `Grid`, `Window` | The other ~100 WinUI controls |
168
+ | Both tree-building styles, attach/detach | Data binding, styles, resource dictionaries |
169
+ | `async def` handlers, awaiting WinRT ops | Control templates, virtualized lists |
170
+ | Off-thread writes auto-marshalled | Off-thread *reads* (wrap in `run_on_ui`) |
171
+ | `padding`/`margin`, `visible`, content boxing | `Grid.Row`/`Grid.Column` attached properties |
172
+ | Running from a Python environment | Packaging a double-clickable app (MSIX) |
173
+
174
+ **Packaging is unproven.** Shipping an app to end users means bundling Python and
175
+ the Windows App Runtime, likely as MSIX, and that path has not been prototyped.
176
+ Today this is a library for developers who already have Python installed.
177
+
178
+ ## Troubleshooting
179
+
180
+ **`DLL load failed ... The filename or extension is too long`** — your virtual
181
+ environment path is too deep. The winui3 extension modules have very long file
182
+ names (`_winui3_microsoft_windows_applicationmodel_dynamicdependency_bootstrap`),
183
+ and a nested venv can push them past Windows' 260-character `MAX_PATH`. Use a
184
+ shorter path, or [enable long paths](https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation).
185
+
186
+ **`ERROR_NOT_SUPPORTED` during bootstrap** — you're on the Microsoft Store build
187
+ of Python, which is itself a packaged app. Use the python.org installer.
188
+
189
+ ## Examples
190
+
191
+ ```
192
+ pip install -e ".[examples,dev]"
193
+ python examples/counter.py
194
+ python examples/async_file_read.py
195
+ ```
196
+
197
+ ## Tests
198
+
199
+ ```
200
+ pytest
201
+ ```
202
+
203
+ The suite runs **anywhere** — no Windows required. It swaps in a fake native
204
+ layer to lock down the tree model, both building styles, attach/detach, property
205
+ forwarding, value conversions and thread marshalling. What it deliberately
206
+ cannot cover is the binding boundary itself; that is verified by running the
207
+ examples on Windows.
208
+
209
+ ## License
210
+
211
+ MIT