Skip to content

CPython: pyrevit.forms WPF/XAML support (PythonNet3) #3033

Description

@OriAshkenazi

TL;DR (decision needed)

CPython scripts (#! python3) currently cannot use pyrevit.forms (the module hard-fails at import time and depends on the IronPython-only wpf/XAML stack).

Please reply:

  1. A or B for the path forward:
    • A) UI broker/proxy (Effort 1): make the existing pyrevit.forms implementation callable from CPython
    • B) Native pythonnet WPF/XAML support (Effort 2): implement/drive upstream support so CPython can run WPF natively
  2. If you agree with (A), can I open a small Stage 0 PR immediately (import works + actionable exceptions), then follow with the broker?
  3. Definition check: for this issue, does "CPython forms support" require:
    • (a) parity for the existing pyrevit.forms API (built-in dialogs/windows), OR
    • (b) also parity for CPython-authored WPFWindow + custom XAML event handlers?

Context

Goal

  • A CPython script can do from pyrevit import forms and call the same pyrevit.forms API that works today under IronPython.
  • If the chosen path is the UI broker (Effort 1), CPython should be able to invoke the existing IronPython/WPF implementation and get structured results back (and interact with returned window objects via proxies).

Proposed plan (2 main efforts)

Effort 1 (recommended near-term): UI broker/proxy so CPython calls the existing IronPython/WPF implementation

This follows the "known good" practice: keep the existing WPF implementation as the source of truth, and have CPython proxy calls into it.

Effort 1 is best done in stages. Stage 0 matches the first "prep" idea discussed in the PR #3029 thread (my comment):

Stage 0 (small PR): make import pyrevit.forms work under CPython

  • Remove the import-time hard guard in pyrevitlib/pyrevit/forms/__init__.py (do not raise on not IRONPY).
  • Isolate IronPython-only imports (wpf, Autodesk UI modules, etc.) so CPython can import without immediately failing.
  • Keep UI functionality IronPython-only for now (raise an actionable exception on call under CPython) until the broker is in place.
  • Add a minimal CPython DevTools smoke command that:
    • imports pyrevit.forms, and
    • verifies at least one representative UI entrypoint fails with a clear "not yet supported under CPython; see this issue" message (instead of ImportError/AttributeError).

Stage 1+ (broker): make the full API callable from CPython by proxying to the IronPython/WPF implementation

  1. Turn pyrevitlib/pyrevit/forms/__init__.py into an engine-agnostic facade

    • Move the current IronPython/WPF implementation into an IronPython-only module (e.g. pyrevitlib/pyrevit/forms/_ipy_forms.py) without changing IronPython behavior.
    • In __init__.py, dispatch:
      • IronPython (IRONPY=True) -> re-export from _ipy_forms.
      • CPython (IRONPY=False) -> re-export from a proxy module _cpy_forms_proxy.
  2. Add a broker entrypoint on the .NET side (so both engines can reach it)

    Proposed boundary:

    string Invoke(string requestJson); // returns responseJson

    Notes/constraints:

    • JSON alone can not carry live Revit/.NET objects (Elements, UIApplication, WPF objects, delegates).
    • Plan: use an object-handle registry in the broker/handler for any non-JSON-serializable value. Requests/responses pass { kind: "handle", id: "..." } for those values.
    • Keep a strict allowlist of callable functions/methods and validate payloads to avoid turning this into "remote eval".
    • Execute UI actions on the Revit UI thread (Dispatcher/ExternalEvent) and return structured errors.
  3. Implement the IronPython handler (execute real pyrevit.forms calls)

    • Import and execute the existing IronPython/WPF implementation.
    • Support:
      • function calls by name + args/kwargs
      • object lifecycle for WPF windows (create -> return handle id; later calls via handle id)
    • Definition check tie-in: if maintainers want CPython-authored WPFWindow + event handlers parity, this also needs a callback story (UI events firing in IronPython invoking CPython callbacks through a safe, explicit bridge). Otherwise, initial scope can be: "CPython can call the built-in forms API, including windows created by that API".
  4. Implement the CPython proxy module

    • pyrevitlib/pyrevit/forms/_cpy_forms_proxy.py provides the same API surface, but each call serializes a request (name/args/kwargs) to the broker and deserializes the response.
    • For returned WPF objects, return lightweight Python proxy objects that forward method/property calls back to the broker using the handle id.
  5. Add a small DevTools end-to-end smoke command

    • Add a CPython command under extensions/pyRevitDevTools.extension that exercises representative calls end-to-end (alert/select list + at least one WPFWindow-based dialog that is part of pyrevit.forms).

Effort 2 (larger + upstream dependent): native CPython WPF/XAML support

Summary of what we know so far (Discourse saga + repo references):

  • In WIP pyRevit, pythonnet was updated to v3, which improves CPython integration but still has no WPF support out-of-the-box. In the saga thread, @sanzoghenzo documented an attempted path to make pyrevit.forms work under CPython by tackling the wpf.LoadComponent gap.
  • Removing the IRONPY guard in pyrevitlib/pyrevit/forms/__init__.py quickly hits:
    • Autodesk-only imports (Autodesk.Windows.ComponentManager) used by forms.show_balloon.
    • The core blocker: wpf is None under CPython because pyrevitlib/pyrevit/framework.py only loads the IronPython wpf module when IRONPY is true. WPFWindow.load_xaml relies on wpf.LoadComponent.
  • The thread highlights that LoadComponent is effectively the key missing primitive: under IronPython it comes from IronPython.Wpf and uses the DLR DynamicXamlReader + DynamicOperations to:
    • populate x:Name fields on the Python window instance
    • hook up XAML event handlers
    • reference implementation lives in this repo under dev/modules/pyRevitLabs.ironpython3/Src/DLR/Src/Microsoft.Dynamic/Xaml/DynamicXamlReader.cs (depends on DLR dynamic ops that pythonnet does not provide)
  • An attempted Python port of DynamicXamlReader using System.Xaml (XamlXmlReader, XamlObjectWriter, custom XamlMemberInvoker) got further but failed early when loading pyrevitlib/pyrevit/forms/GetValueWindow.xaml with:
    • Set property "System.Windows.Window.ShowInTaskbar" threw an exception
      This suggests additional WPF/XAML loading semantics need to be replicated correctly (property setting, root instance handling, name scopes, event binding, etc.).
  • @sanzoghenzo shared a WIP branch with the refactor/experiments: https://github.com/sanzoghenzo/pyRevit/tree/forms-refactor
  • The repo also includes pythonnet WPF demos (not pyRevit-integrated) such as dev/modules/pyRevitLabs.Python.Net/demo/DynamicGrid.py that load XAML via System.Windows.Markup.XamlReader.Load on an STA thread.
  • I did not find any repo-local pointers to the mentioned "McNeel/Dynamo pythonnet WPF patches" beyond @sanzoghenzo's note in the PR PyRevitRunner: honor cpython hashbang #3029 thread; no first-party code/docs/issues in this repo reference those patches explicitly.
  • Quote for context (from @sanzoghenzo in the PR PyRevitRunner: honor cpython hashbang #3029 thread): "Ehsan (@eirannejad) told us about pythonnet patches made internally by mcneel, same thing by dynamo guys, I don't know if any of it reach upstream..."

Given the above, Effort 2 likely depends on upstream/downstream pythonnet work. Before investing heavily, it would help to identify:

  • Is there an upstream pythonnet issue/branch for WPF/XAML parity?
  • Are McNeel/Dynamo patches published, and can they be upstreamed/adopted?

Next step after decision

  • If maintainers prefer (A), I can start with Stage 0 immediately, then follow with the facade + broker skeleton and a small CPython DevTools end-to-end smoke test.
  • If maintainers prefer (B), I can help track down the relevant upstream pythonnet work/pointers first (issues/branches/patches), before starting a large in-repo WPF implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions