Metadata-Version: 2.4
Name: dynwinrt
Version: 0.1.0rc22
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Summary: Python bindings for dynamic WinRT API invocation
Author: Microsoft
License: MIT
Requires-Python: >=3.11, <3.15
Description-Content-Type: text/markdown
Project-URL: Homepage, https://github.com/microsoft/dynwinrt
Project-URL: Issues, https://github.com/microsoft/dynwinrt/issues
Project-URL: Repository, https://github.com/microsoft/dynwinrt

# dynwinrt

`dynwinrt` is the native CPython runtime for Python projections generated by
[`dynwinrt-codegen`](https://pypi.org/project/dynwinrt-codegen/). It supports
CPython 3.11–3.14 on Windows x64 and ARM64.

## Install

```powershell
python -m pip install --pre dynwinrt dynwinrt-codegen
dynwinrt-codegen generate --namespace Windows.Foundation --class-name Uri `
  --lang py --output generated_uri
```

Generated package manifests pin `dynwinrt` to the exact version of
`dynwinrt-codegen` that produced them. The runtime wheel includes
`__init__.pyi` and `py.typed` for static type checking.

Generated `IReference<T>` values are projected as `T | None`; native values,
`None`, and generated `IReference_*` wrappers are accepted as inputs.

## Async WinRT operations

Generated async methods return typed, asyncio-compatible operation objects:

```python
operation = writer.store_async()
stored_bytes = await operation

task = asyncio.create_task(writer.store_async())
stored_bytes = await task

async with asyncio.TaskGroup() as group:
    task = group.create_task(writer.store_async())
```

Generated methods return `WinRTCoroutine[T]` and
`WinRTCoroutineWithProgress[T, P]`, which retain the structural
`WinRTAsync[T]` and `WinRTAsyncWithProgress[T, P]` contracts while also being
typed as coroutines for `asyncio.create_task()`. The concrete runtime wrappers
remain private.

Regenerated bindings no longer block inside async methods. Existing code that
expects an immediate result must use `await operation` or `operation.wait()`.

`asyncio` task cancellation calls `IAsyncInfo.Cancel()` on the underlying
WinRT operation. Operations with supported progress values also expose
`operation.progress(callback)`. Fast operations can finish before registration;
in that case no future progress exists and registration is a no-op.

An operation can be awaited directly more than once; every direct await observes
the same converted completion, failure, or cancellation. Its coroutine driver is
one-shot like a native Python coroutine, so pass a given operation to
`create_task()`, `TaskGroup.create_task()`, or `ensure_future()` only once.
Directly awaiting that operation after its task completes remains supported.
Calling `close()` cancels pending WinRT work and permanently closes the coroutine
driver.

For scripts without an event loop, `operation.wait()` remains available as an
explicit blocking API. It rejects started operations when called from a running
asyncio loop or an STA thread, where blocking could freeze or deadlock the
caller.

WinRT HRESULT failures raise `OSError` (or a standard `OSError` subclass) with
the signed HRESULT in `error.winerror`. The exception message preserves
restricted WinRT error information when Windows provides it.

## Python-native values

Generated collection projections implement the standard `collections.abc`
protocols: vectors behave as sequences, maps as mappings, and WinRT iterables
and iterators work with `iter()` and `next()`. Mutable vectors support indexing,
slicing, assignment, insertion, and deletion; mutable maps support standard
mapping assignment and deletion.

Method inputs accept normal Python sequences and mappings in place of compatible
WinRT collection interfaces, subject to the producer limits below.
Byte arrays accept `bytes` and `bytearray`; GUID,
`DateTime`, and `TimeSpan` values use `uuid.UUID`, `datetime.datetime`, and
`datetime.timedelta`.

Generated `Windows.Storage.Streams.Buffer` and `IBuffer` projections also
provide copied byte conversion:

```python
winrt_buffer = IBuffer.from_bytes(bytearray(b"\x00\x01\xff"))
data: bytes = winrt_buffer.to_bytes()
```

Both directions copy exactly `Length` bytes. Mutating the input or releasing
the WinRT object does not change the returned `bytes`, and no native buffer
pointer is exposed.

Exceptions raised by Python event or delegate callbacks are reported through
`sys.unraisablehook`. The originating WinRT invocation receives
`0xA0EE4005` (`PYWINRT_E_UNRAISABLE_PYTHON_EXCEPTION`) instead of unconditional
success. Generated delegate parameters accept normal Python callables. WinRT
chooses the callback thread, so callbacks must not assume they run on the
registration thread or an asyncio event-loop thread. Keep each token returned by
`on_*` and pass it to the matching `off_*` when the subscription is no longer
needed. For callback-style cleanup, `subscribe_*` returns an idempotent
unsubscribe function. `once_*` subscribes for at most one callback invocation.

WinRT flags enums are projected as `enum.IntFlag`. Overloaded methods share one
Python name with runtime type/arity dispatch and `typing.overload` declarations.
Activatable runtime classes use normal constructors, for example
`Uri("https://example.com")`. Constructor overloads come only from WinMD
`ActivatableAttribute` and public `ComposableAttribute` declarations. Classes
without that metadata, including system-returned classes and protected-only
composition, raise a class-named `TypeError` on normal construction and their
stubs expose no public constructor. Native return values still use the internal
`_from_native`/`DynWinRTValue` wrapping path.

### Creating WinRT collections

`DynWinRTValue.create_vector(items, element_type)` and
`DynWinRTValue.create_map(keys, values, key_type, value_type)` keep their public
signatures. They validate element identity, native layout, ownership, and the
complete set of closed collection IIDs before creating a native object.
These limits apply to **producing** collections, including conversion of Python
sequences/mappings, not to consuming collections returned by Windows.

| Element / key / value | x64 | ARM64 | i686 |
|---|---|---|---|
| Boolean, integers, Char16, enum, HRESULT | Supported | Supported | Up to 4 bytes; I64/U64 rejected |
| HSTRING; owned nullable interface/object references | Supported | Supported | Supported |
| POD structs in populated vectors or maps | Sizes 1, 2, 4, 8 | Up to 8 bytes, non-HFA only | Up to 4 bytes |
| Additional POD structs in **empty vectors only** | Larger than 8 bytes | Larger than 16 bytes, non-HFA only | None |
| Top-level F32/F64/GUID; structs recursively containing HSTRING or references | Rejected, even empty | Rejected, even empty | Rejected, even empty |

POD means a validated, reference-free native layout. HFA means an aggregate of
one to four same-width floating-point values, including nested aggregates.
Thus `Point`/`Size` work on x64 but are rejected on ARM64 even when empty;
`PointInt32` works on x64 and ARM64. ARM64 also rejects empty `RectInt32`,
`Rect`, and `BasicGeoposition` vectors. `ManipulationDelta` (20 bytes, five floats,
non-HFA) supports empty
vectors on x64 and ARM64. Large map keys/values are rejected even for empty maps.

Structs and typed enums require exact type identity, not a matching byte size
or shape. Scalars require the matching value variant, except U16 inputs for
Char16, I32 inputs for signed enum/HRESULT, U32 inputs for unsigned enum, and
range-checked I32 inputs for I8/U8/Char16. For example, `from_i32(255)` is
accepted for U8; `from_i32(257)` raises instead of wrapping. Reference inputs
are retained and queried for the declared IID; an incompatible interface raises, while
`DynWinRTValue.null_value()` remains a null reference.

WinRT enums preserve their metadata-declared `Int32` or `UInt32` backing type.
`DynWinRTType.enum_type(name, names, values)` remains signed by default. Pass
`DynWinRTType.u32_type()` as its fourth argument for a `UInt32` flags enum;
generated wrappers supply this automatically. Enum member lookup,
`enum_value()`, `get_enum_int()`, `to_number()`, and `to_int()` preserve values
through `0xffffffff`. Unsigned enum arrays and struct fields use the U32
accessors, and their generic interface IIDs include the `u4` signature.
Update the runtime and regenerate wrappers together.

For admitted POD structs, vector `IndexOf` and map key operations compare
metadata-declared fields by value, including nested fields, and ignore padding.
Floating fields use numerical equality: `+0` and `-0` match, while NaN does not
match even the same NaN bits. This also applies to collection views; a
NaN-containing struct map key is not found by lookup. Stored field bits are
not normalized.
Map construction uses the same comparison as `Insert`: the last value for an
equal key wins, retaining the first key's field bits and iteration position.

The Python helpers use validated typed core factories. Rust callers can use
the safe `create_vector_from_values` / `create_map_from_values` factories for
the same checks. The metadata-free Rust `create_value_vector`, `create_vector`,
and `create_map` constructors require `unsafe`: callers must prove the native
ABI, ownership, element types, and complete IID set themselves.
The metadata-free `create_value_vector` retains packed-byte equality, including
padding, rather than metadata-directed field equality.
Unsupported layouts raise
`RuntimeError`; HRESULT-backed type, range, or QueryInterface failures raise
`OSError`. Complete native struct collection support is tracked in
[microsoft/dynwinrt#161](https://github.com/microsoft/dynwinrt/issues/161).

## Raw object projection

Use `project_as(value, Type)` when metadata returns `Object`/`IInspectable` but
the application knows the concrete generated type. This is common with XAML
APIs such as `XamlReader.load()` and `FrameworkElement.find_name()`:

```python
from dynwinrt import project_as
from generated.microsoft.ui.xaml.controls import Button, StackPanel
from generated.microsoft.ui.xaml.markup import XamlReader

raw_panel = XamlReader.load(XAML)
if raw_panel is None:
    raise RuntimeError("XamlReader returned no value")
panel = project_as(raw_panel, StackPanel)

raw_button = panel.find_name("Submit")
if raw_button is None:
    raise RuntimeError("Submit was not found")
button = project_as(raw_button, Button)
```

`project_as()` accepts generated runtime classes only and borrows its input:
the raw value or source wrapper remains valid. The returned wrapper owns the
QueryInterface result, participates in the active
`projected_lifetime_scope()`, and preserves the projection identity cache.
Classes with a verifiable default-interface IID remain valid projection
targets even when metadata exposes them only through `Object`/`IInspectable`.
Projection always performs QueryInterface, so a static-only declaration cannot
produce a wrapper unless the input actually implements that default interface.
Incompatible types raise the ordinary WinRT `OSError`. Static-only metadata
classes with no instance surface are not projection targets.

### Explicit boxed-value unboxing

Generic WinRT `Object`/`IInspectable` results remain raw `DynWinRTValue`
instances. Use `unbox_object()` only where the application expects a boxed
`Windows.Foundation.IPropertyValue`, such as values from
`DeviceInformation.properties`:

```python
from dynwinrt import unbox_object

raw = device_information.properties["System.Devices.DeviceInstanceId"]
instance_id = unbox_object(raw)
```

The helper borrows its argument. It maps supported numeric, Boolean, string,
character, GUID, and corresponding array property types to Python values;
64-bit integers use `int`, GUIDs use `uuid.UUID`, and `UInt8Array` uses `bytes`.
`None` stays `None`. If the object does not implement `IPropertyValue`, the
exact same Python object is returned, so identity and later projection remain
intact. Unsupported property types (including `DateTime`, `TimeSpan`, geometry,
inspectable, and other types) and native getter failures raise an exception.

Use `wrapper.as_interface(InterfaceClass)` when converting an existing
wrapper to an interface view. Use `InterfaceClass.from_value(raw)` for a raw
`DynWinRTValue`. Do not call the internal `_from_native()` method from
application code.

## COM apartments and cleanup

Use `RoApartment` to initialize COM for a thread and balance every successful
initialization:

```python
with RoApartment(0):  # RO_INIT_SINGLETHREADED
    use_winrt()
```

Use `RoApartment(1)` for `RO_INIT_MULTITHREADED`. Nested contexts using the same
model are supported. Requesting a conflicting model raises `OSError` with
`RPC_E_CHANGED_MODE`. The low-level `ro_initialize()` API remains available, but
each successful call, including `S_FALSE`, must be paired with one
`ro_uninitialize()` call on the same thread.

Generated runtime classes that implement `IClosable` support `with` and an
idempotent `close()` method. Prefer deterministic cleanup instead of relying on
Python garbage collection.

## Standalone WinRT interface implementations

Generated, supported non-generic WinRT interfaces provide `.implementation()`
and `.implement()` factories. They create standalone, `IInspectable`-rooted
objects; they do not activate or register an OS class, compose a WinUI control,
or implement Classic COM interfaces. `.implement()` returns a generic management
handle with a stable typed `.value`, not a type cast or the raw handler:

```python
from generated.windows.foundation import IClosable, IStringable

class Label:
    def to_string(self) -> str:
        return "Python-backed WinRT object"

    def close(self) -> None:
        pass

handler = Label()
with IStringable.implement(
    handler, interfaces=[(IClosable, handler)]
) as impl:
    print(impl.value.to_string())
```

The generated handler protocols describe snake-case methods and their input
and output types. Additional interface descriptors provide separate native
views sharing the same object identity. Every required interface must be
included; an incomplete implementation is rejected before publication.
`release_projected(view)` releases a view independently without disposing or
releasing its controller. The advanced `from_implementation(impl)` path accepts
both new handles and low-level native owners; positional descriptors remain
supported. The primary `.value` needs no `release_projected` in the common
pattern. It is created lazily once, and `release()`/`dispose()` release that
view along with the handle's owner reference. `.value` raises after either
operation and never recreates a released view.
Heterogeneous convenience lists have per-pair static checking for interfaces
from the same generated package. For heterogeneous combinations across
generated packages, use positional typed `.implementation(...)` descriptors.
Runtime list acceptance and the generic homogeneous-list overload are unchanged.

The management implementation ships as `dynwinrt/_implementation.py` beside the
native extension. Tracebacks and coverage use this installed source file; no
build checkout is needed. The native module's public class identities and
exports are unchanged.

The low-level runtime surface is:

- `DynWinRTImplementationMethod(name, vtable_index, signature)`: an immutable
  method definition using the existing `DynWinRTMethodSig`.
- `DynWinRTInterfacePlan.create(name, interface_type, methods,
  required_iids=())`: an immutable, validated interface definition. Methods
  occupy every slot starting at slot 6; required-interface membership is
  checked when composing the complete implementation.
- `DynWinRTImplementationDescriptor(plan, dispatch)`: a generated interface
  plan and synchronous `dispatch(vtable_index, args)` callable.
- `DynWinRTImplementation.create(interfaces, callback,
  runtime_class_name=None)`: the native owner. Its callback receives
  `(interface_index, vtable_index, list[DynWinRTValue])` and must return a
  `list[DynWinRTValue]` in signature output order, with any logical return last.
  A void callback returns `[]`, not `None`.

Prefer generated plans rather than hand-authoring native signatures. The
runtime validates scalar, GUID, HSTRING, complete struct, managed-reference,
and pass/receive/fill-array contracts. For fill arrays the **callback** receives
a `UInt32` capacity and must return an array of exactly that length. Existing
outbound `DynWinRTMethodHandle` calls still take a preallocated array value for
a fill-array argument. `DynWinRTValue.from_hresult()` constructs an exact
signed HRESULT value, including for typed HRESULT arrays.

Callbacks are synchronous and run only on the creating native thread, with a
fresh copy of the `contextvars` context captured at creation. Reentrant calls
are supported. Async callables and coroutine/awaitable results are rejected;
no event loop is scheduled or blocked. Already-created native WinRT async
values can be passed through like other managed native values. Arbitrary
generic interface implementations, cross-thread dispatch, and Python
subinterpreters are not supported.

Callable wrappers for received native delegates can prepare a reusable
`DynWinRTDelegateMethod.create(iid, signature)` and call
`method.invoke(value, args)`. The convenience
`value.invoke_delegate(iid, signature, args)` remains available and uses the
same invocation path. The IID and signature must describe the same WinRT
delegate's `Invoke` method, including computed closed-generic IIDs. This calls
slot 3 on the delegate's `IUnknown` root and returns all outputs as a list
(`[]` for void). It borrows the Python value, owns a native pin through the
call, and propagates failed HRESULTs. Arguments use the existing outbound
`invoke_all()` contract, including preallocated fill-array values; these
helpers do not infer signatures or turn arbitrary objects into delegates.
Generated received-delegate callables use positional-only arguments in both
their Protocol declarations and runtime wrappers; ordinary projected methods
continue to accept their existing keyword arguments.

### Implementation lifetime and callback failures

`to_value()` returns an independently owned native reference. `release()`
drops only the owner's reference; callbacks remain alive while native
consumers retain other references, even after the Python owner wrapper is
gone. `disconnect()` disables all views and releases captured Python handlers
without dropping the owner's native reference. `dispose()` disconnects and
releases, and is repeat-safe. The owner context manager calls `dispose()`.
An in-flight callback can finish safely when it calls any of these methods;
subsequent calls after disconnection fail with `RO_E_CLOSED`.

GC tracing is deliberately conservative: captured Python references are
reported as owner edges only when that owner is the sole native reference.
This collects ordinary handler/self/owner cycles without treating native
consumers as collectable Python references. Cycles that retain a native alias,
or that have requested native weak references, may require **explicit
`dispose()`**, including a handler capturing a typed handle whose primary view
has already been created. Callback invocation and `to_value()` require the creating thread;
foreign-thread `to_value()` raises `OSError` with `RPC_E_WRONG_THREAD`.
Owner release, disposal, and garbage collection can run on another Python
thread without leaking the owner's native reference. This does not make
arbitrary wrapped Windows objects agile. Prefer deterministic cleanup;
unexpected GC cleanup failures are reported to `sys.unraisablehook`.
Interpreter shutdown separately closes the callback gate and drops
captured handlers; late native calls cannot enter a finalized interpreter.

Python callback exceptions and invalid Python output containers are reported
to `sys.unraisablehook` and fail the native call with
`PYWINRT_E_UNRAISABLE_PYTHON_EXCEPTION` (`0xA0EE4005`). Native output-contract
mismatches also fail rather than returning fabricated values. `take_error()`
returns and clears the latest native callback diagnostic; `is_closed` is a
read-only property.

## Experimental WinUI support

When the required WinUI metadata is generated, `Application.create()` installs
`XamlControlsResources` and configures unpackaged resource resolution.
`Application.create_with_metadata_provider(...)` is available when the
application supplies its own provider.

Python retains the validated WinUI implementation DLL after its first actual
WinUI activation until the process terminates. One named native owner keeps
one ordinary loader reference to `Microsoft.UI.Xaml.dll`; repeated activation
does not accumulate references. Retained native module handles determine
identity, so legal differences in path spelling or casing do not imply a
different runtime. If WinUI activation requires the DLL-probing
fallback, its successful loader reference is transferred to the same owner
(one reference per required implementation DLL), rather than leaked per call.
Imports, ordinary WinRT activation, and COM apartment initialization do not
load or retain WinUI or CoreMessaging.
When a WinUI metadata provider loads Controls before XAML, the runtime resolves
the actual `Application` activation factory to establish this lifetime before
returning the provider's factory; it does not create an Application or start
its message loop.

This is a process-lifetime module policy, not an object cache. Continue to
close windows, unsubscribe events, release projected/native objects, and
balance COM initialization on each thread as shown below. WinUI module-level
static state can remain after those application-owned resources are released.
The retained XAML mapping keeps its code valid when Controls static destruction
runs during COM cleanup, even when another MTA exits after the UI STA.

No new context manager or async executor is required. The Python runtime does
not release the published module reference from `RoApartment.close()`, Python
`atexit`, or wrapper finalizers: the operating system reclaims it at process
termination. Consequently, in-process unloading/hot replacement of that WinUI
implementation is not supported. This does not prohibit WinUI's documented
thread-level XAML/DispatcherQueue shutdown, guarantee repeated
`Application.start()` in one process, or imply that all SDK static resources
are gone when a window closes. The JavaScript and independent WinRT/COM
lifetime policies are unchanged.

Python subclasses of public composable controls preserve one COM identity for
inherited properties and methods. Metadata-supported
`measure_override`, `arrange_override`, and `on_apply_template` callbacks run
synchronously on the creating UI apartment with the `contextvars` context
captured during construction. Unsupported native override shapes fail during
construction instead of falling back to an unsafe ABI.

After creating the generated application, publicly composable controls can
register a Python subclass for activation by `XamlReader`:

```python
from generated.microsoft.ui.xaml.controls import StackPanel
from generated.microsoft.ui.xaml.markup import XamlReader

class PythonPanel(StackPanel):
    def measure_override(self, available_size):
        return available_size

registration = StackPanel.register_xaml_runtime_class(
    "MyApp.Controls.PythonPanel",
    PythonPanel,
)
raw_panel = XamlReader.load(
    '<local:PythonPanel '
    'xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" '
    'xmlns:local="using:MyApp.Controls" />'
)
if raw_panel is None:
    raise RuntimeError("XamlReader returned no value")
panel = StackPanel(raw_panel)

# First remove every instance from the XAML tree and release application owners.
panel = None
raw_panel = None
registration.unregister()
registration.release_instances()
```

Registrations are process-local, and duplicate names fail. Unregistering,
closing, or dropping the registration prevents new XAML metadata lookups.
XAML-created Python owners remain rooted until `release_instances()`; call it
only after every corresponding native control has left the XAML tree.
Registration does not make the class globally activatable through
`RoActivateInstance`.

Generated `Application.start()` and `DispatcherQueue.run_event_loop()` calls
stay on the caller's native thread but release the Python GIL while WinUI pumps
messages. WinRT callbacks reacquire the GIL, and worker threads can use
`DispatcherQueue.try_enqueue()` to return to the UI thread.

Use a projection lifetime scope inside the COM apartment so wrappers release
their native values before `RoUninitialize`:

```python
from dynwinrt import RoApartment, projected_lifetime_scope

with RoApartment(0), projected_lifetime_scope():
    app = Application.create()
    # Create and use WinUI objects here.
```

Scopes nest in LIFO order. Wrappers that survive a closed scope remain Python
objects, but their native values are released and further WinRT calls fail.
Each scope is thread-affine: enter, use, and close it inside that thread's
`RoApartment`. Same-thread asyncio tasks inherit the active scope, while worker
threads must open their own ordered
`with RoApartment(...), projected_lifetime_scope():`. Native callbacks invoked
on a foreign thread preserve other captured context but do not inherit the
creator thread's lifetime scope. This includes generated delegates, raw progress
handlers, and element-factory callbacks. Retained callback values remain
user-owned; open an explicit callback-local scope for deterministic temporary
cleanup.

Normal construction remains unavailable for protected-only composable classes
and system-returned classes without public activation metadata. Named Python
XAML registration does not support generic names, collection or dictionary
bases, markup-extension bases, or Python-defined XAML members.

## Develop

From `bindings\py`:

```powershell
python -m pip install "maturin>=1.11,<2" "pytest>=8.3.5" "mypy>=1.13,<2"
python -m maturin develop
python -m pytest
```

The focused WinUI lifetime regressions are in `tests\test_winui_lifetime.py`.
Without optional inputs they check ordinary WinRT imports and cleanup. Set
`DYNWINRT_WINUI_SAMPLE_ROOT` to a prepared Hello World directory (unchanged
`app.py`, `generated`, and `.runtime`) to exercise real STA, managed/external
MTA, exception, and default-async process teardown. Run each case in its own
process; process exit status is part of the assertion.

For native ownership inspection and the test-only shutdownable Tokio fixture,
build a separate test environment with `maturin develop --features test-hooks`.
This adds private test helpers only, not a production executor or public API.
`DYNWINRT_WINUI_REQUIRE_OWNER_HOOK=1` additionally checks the retained owner.
From a matching-architecture Visual Studio developer shell, build
`tests\e2e\fixtures\winui_activation.cpp` with
`cl /LD /EHsc /std:c++17 winui_activation.cpp /link runtimeobject.lib /OUT:Microsoft.UI.Xaml.dll`
in a private output directory and set `DYNWINRT_WINUI_FIXTURE_DIR` to it.
Copy that fixture DLL as `Microsoft.UI.Xaml.Controls.dll` in the same directory
for the dependency path-spelling regressions.
That SDK-typed factory tests actual loader-reference transfer, concurrent
fallback activation, failed/null factories, identity mismatch, and retry
without requiring WinUI installation. Do not use this fixture DLL in the real
sample directory.

## Release process

The release tag supplies one unified npm/Cargo version. For example,
`v0.1.0-preview.21` produces npm version `0.1.0-preview.21` and Python version
`0.1.0rc21` after PEP 440 normalization.

1. GitHub Actions builds and consumes eight CPython 3.11–3.14 runtime wheels
   and two standalone codegen wheels on Windows x64 and native ARM64.
2. The official 1ES ADO pipeline builds both npm packages and waits for the
   complete Python wheel matrix.
3. ADO creates one shared GitHub Release with the npm tarballs. GitHub Actions
   attaches the ten tested Python wheels, and ADO downloads and revalidates the
   complete set.
4. With `DoEsrp` enabled, ADO publishes both npm packages. `PublishPyPI`
   defaults to enabled and publishes the eight `dynwinrt` wheels before the two
   `dynwinrt-codegen` wheels. Disable it only for a non-PyPI rehearsal.

PyPI publication uses the Microsoft ESRP release identity and is not available
from GitHub Actions.
