PyWinRT 4.0¶
PyWinRT 4.0 changes how the projection is built and published more than any
release before it, and changes less about the Python you write than that might
suggest. A projection package is no longer compiled code, each package is
versioned after the upstream release it projects, and the Windows App SDK and
WebView2 packages are back under winrt. This page describes those changes
and the others that a user of 3.x will meet. Each package's own changelog,
linked from PyPI, lists what changed in that package one line at a time.
Coming from 3.x, start with Moving from 3.x.
Projection packages are data¶
A projection package such as winrt-Windows.Storage no longer contains a
compiled extension module. It is one py3-none-any wheel holding the type
stubs, a small __init__.py and a table that describes the namespace: its
types, their members and the call that each member makes. winrt-runtime
reads the table and makes the calls, so it and the interop packages are the
only parts of PyWinRT that are still compiled.
What this means in practice:
- One wheel serves every supported version of Python and every architecture,
so a new version of Python works with the projection packages as soon as
winrt-runtimehas wheels for it. - Installing a package from its source distribution needs neither a C++ compiler nor the Windows SDK.
- The 349 projection packages are 6.7 MB of wheels in all. The wheels of v3.2.1 were 49.2 MB for each version of Python and each architecture.
- A call into WinRT costs a little more. When the table projection was
measured against v3.2.1 on one machine, most kinds of member cost 10 to 45 %
more per call, which is tens of nanoseconds on calls that took 60 to 400 ns.
Passing a
dictwhere anIMapis expected and callingget()on an async operation that has already finished cost about twice as much. What the member itself does, such as reaching a device, a file or the display, still takes far longer. The table projection has had one round of tuning so far. - A frozen application has to carry each namespace's table, the
_table.pywinrtbeside its__init__.py.winrt-runtimeships a PyInstaller hook that does this, and Freezing an application gives the options for cx_Freeze and Nuitka.
Versioning¶
A projection package is versioned by the upstream release it projects, rather
than by the version of the code generator that read it.
winrt-Windows.Storage is 4!10.0.28000.2705, the version of the Windows SDK
it was generated from, and the Windows App SDK, WinUI 2 and WebView2 packages
are numbered after their own releases in the same way. The 4! is a PEP 440
epoch. It says which generation of PyWinRT a package belongs to and makes the
new numbers sort above every 3.x release.
The packages written by hand, winrt-runtime, winrt-table-compiler and the
Windows SDK interop packages, are versioned on their own from 4.0.0 and
released when they change. The two Windows App SDK interop packages are
released with the App SDK packages and carry their version.
A package requires winrt-runtime>=4.0.0,<5 rather than one ~= release of
it, and every other package it names with a floor, so pip is free to install
newer ones. The exception is the Windows App SDK, which Microsoft releases as
one set and PyWinRT publishes as one package per component: a component
requires the other components whose types it hands back with ~=, so that
all of them come from the same release. Versioning
explains the scheme, the upper bounds and how to pin.
Package names¶
The Windows App SDK and WebView2 packages carry the winrt- prefix again and
are imported from the winrt top-level package, as they were before v3.0:
winui3.microsoft.ui.xamliswinrt.microsoft.ui.xaml, and every otherwinui3.module likewise.webview2.microsoft.web.webview2.coreiswinrt.microsoft.web.webview2.core, and its distribution iswinrt-Microsoft.Web.WebView2.- The Windows App SDK is published as 7 packages, one per NuGet component such
as
winrt-Microsoft.WindowsAppSDK.WinUI, rather than one per namespace. - The App SDK's bootstrapper is
winrt-Microsoft.WindowsAppRuntime.Bootstrap, named after the.dllit carries. Its module is stillwinrt.microsoft.windows.applicationmodel.dynamicdependency.bootstrap. - WinUI 2 is one package,
winui2-Microsoft.UI.Xaml, which keeps its own prefix and top-level package, since it defines the same namespaces as the App SDK's WinUI. winrt-Microsoft.Web.WebView2.Dllis new. It redistributes the.dllthat the WebView2 projection needs, and that projection requires it.
Which module a namespace is imported from changes only for the two renamed prefixes. What changes for the regrouped families is which package to install. The v3 to v4 migration guide has the full tables, and its script reports what in your code and your requirements needs to change.
An overloaded method has one name again and is called by the number of
arguments, where v3.x gave each overload a name of its own. In the Windows SDK
packages the v3.x names still work and raise a DeprecationWarning. In the
others they are gone, along with the prefixes.
The runtime¶
- Wheels are published for Python 3.11 to 3.15. Python 3.9 and 3.10 are no longer supported.
- Wheels are published for the free-threaded Python 3.14t and 3.15t, which keep the GIL disabled. Threads and apartments says what to watch for.
- The runtime can no longer be imported into a subinterpreter. It raises
ImportError, since a callback from WinRT reaches the main interpreter only. winrt-runtimeno longer shipsMSVCP140.dll, and nothing in PyWinRT needs the Visual C++ redistributable.- An interop module reaches the runtime through the Python functions of
winrt.runtime.interoprather than a C API, so a module of your own can use them the same way.winrt-sdk, which shipped the C++ headers that a compiled module used to need, is no longer published. - A member is no longer checked against
ApiInformationbefore every call. A member that this version of Windows lacks fails when it is called, with anAttributeErrorthat names it. - Several conversions raise the exception that Python itself would:
ValueErrorfor a negative unsigned integer or a character aChar16cannot hold,ModuleNotFoundErrorfor a type whose package is not installed, andTypeErrorfor anasync defhandler or forNoneas an event handler.
The Python surface¶
winrt.system.Arrayis aSequencewhose items can be assigned. It has noinsert()ordel, a slice is a newArray, two arrays compare by value, and itsrepr()shows its items. It takes the type of its elements, such asInt32orbool, in place of a format string, which is deprecated. See Arrays.- A projected vector counts a negative index from the end and raises
IndexErrorout of range, and its slices with a step of 1 can be assigned and deleted. A projected map compares by content, so it is not hashable, like adict. - Structs are hashable by their fields and have
__match_args__, so a class pattern matches one by position. - Calling an interface type such as
IStringable()raisesTypeError.isinstance()with an interface asks the object for it, asas_()does, andissubclass()answers for a projected class and the interfaces it implements. Type._from(obj)is deprecated and warns. Writeobj.as_(Type)instead. See Query interface.HResultandEventRegistrationTokenare subclasses ofint. Theirvaluefield is deprecated. See Error codes and event tokens.- The structs of
Windows.Foundation.Numericsexport a buffer that NumPy reads without a copy, and@is the matrix product.Matrix * MatrixraisesTypeError. See Numerics. - An
IMemoryBuffersupports the buffer protocol, andlen()of anIBufferis its length. A buffer that WinRT writes into must be writable, so abytesis refused there. See Buffers. - Importing
Bufferfromwinrt.systemis deprecated and warns. Usecollections.abc.Buffer, orReadableBufferorWriteableBufferto say whether WinRT reads the buffer or writes into it. SeeBuffer. - Awaiting an async operation that has finished returns its result again, and an error from an operation that no task waits for any more is reported to the event loop. See Awaitables.
- A Python class can derive from a composable class with protected
constructors, such as
Panel, and its protected and overridable members are projected with a leading underscore. A Python implementation of an interface is awinrt.system.Object. See Implementing an interface. - A Python subclass of a composable class derives from one class, as in C#,
and can add only WinRT interfaces as further bases. A mixin, a second
runtime class or
typing.Genericas a base raisesTypeError. See Objects.
Type hints¶
The stubs use modern annotation syntax and the conventions type checkers
expect of a stub. The projected collections derive from their
collections.abc counterparts, winrt.system.Object is the base of every
interface, members that WinRT marks deprecated carry @deprecated, and more
return types include None, mostly in Windows.Storage and the Bluetooth
namespaces. Constructors and many functions are positional-only, an integer
parameter takes any typing.SupportsIndex, and an IBuffer parameter is
typed as ReadableBuffer or WriteableBuffer. Code that type-checked against
3.x may need a narrowing or a cast here and there.
Projecting your own components¶
The PyWinRT tool on NuGet writes a table and type stubs instead of C++, so
projecting a component of your own needs no compiler, and the package it makes
is built with winrt-table-compiler. The tool needs the .NET 10 runtime and
ships the ABI call shape census of the winrt-runtime it goes with. A
component whose members need a call shape the census lacks is reported when it
is generated. Projections of your own
components describes the
packaging.
Moving from 3.x¶
A 3.x projection package cannot be loaded by the 4.0 runtime, so upgrade every
package together. After pip install -U winrt-runtime on its own, importing a
3.x package fails with:
The v3 to v4 migration guide lists the renamed and regrouped packages and the code changes, and the script beside it finds them in your code. Then run a type checker over your code with the 4.0 stubs, which reports most of what is left. Versioning says more about the two generations.