You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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
If you agree with (A), can I open a small Stage 0 PR immediately (import works + actionable exceptions), then follow with the broker?
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
CPython scripts currently cannot import pyrevit.forms because pyrevitlib/pyrevit/forms/__init__.py raises PyRevitCPythonNotSupported("pyrevit.forms").
In PR PyRevitRunner: honor cpython hashbang #3029 discussion, @sanzoghenzo pointed to the long-running CPython+WPF effort and mentioned possible internal pythonnet patches (McNeel/Dynamo), suggesting we coordinate/nudge upstream rather than reinvent the wheel:
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
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.
Add a broker entrypoint on the .NET side (so both engines can reach it)
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.
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".
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.
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.).
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.
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.
TL;DR (decision needed)
CPython scripts (
#! python3) currently cannot usepyrevit.forms(the module hard-fails at import time and depends on the IronPython-onlywpf/XAML stack).Please reply:
pyrevit.formsimplementation callable from CPythonpyrevit.formsAPI (built-in dialogs/windows), ORWPFWindow+ custom XAML event handlers?Context
import pyrevit.formsbecausepyrevitlib/pyrevit/forms/__init__.pyraisesPyRevitCPythonNotSupported("pyrevit.forms").Goal
from pyrevit import formsand call the samepyrevit.formsAPI that works today under IronPython.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.formswork under CPythonpyrevitlib/pyrevit/forms/__init__.py(do not raise onnot IRONPY).wpf, Autodesk UI modules, etc.) so CPython can import without immediately failing.pyrevit.forms, andStage 1+ (broker): make the full API callable from CPython by proxying to the IronPython/WPF implementation
Turn
pyrevitlib/pyrevit/forms/__init__.pyinto an engine-agnostic facadepyrevitlib/pyrevit/forms/_ipy_forms.py) without changing IronPython behavior.__init__.py, dispatch:IRONPY=True) -> re-export from_ipy_forms.IRONPY=False) -> re-export from a proxy module_cpy_forms_proxy.Add a broker entrypoint on the .NET side (so both engines can reach it)
Proposed boundary:
Notes/constraints:
{ kind: "handle", id: "..." }for those values.Implement the IronPython handler (execute real
pyrevit.formscalls)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".Implement the CPython proxy module
pyrevitlib/pyrevit/forms/_cpy_forms_proxy.pyprovides the same API surface, but each call serializes a request (name/args/kwargs) to the broker and deserializes the response.Add a small DevTools end-to-end smoke command
extensions/pyRevitDevTools.extensionthat exercises representative calls end-to-end (alert/select list + at least oneWPFWindow-based dialog that is part ofpyrevit.forms).Effort 2 (larger + upstream dependent): native CPython WPF/XAML support
Summary of what we know so far (Discourse saga + repo references):
pyrevit.formswork under CPython by tackling thewpf.LoadComponentgap.IRONPYguard inpyrevitlib/pyrevit/forms/__init__.pyquickly hits:Autodesk.Windows.ComponentManager) used byforms.show_balloon.wpfisNoneunder CPython becausepyrevitlib/pyrevit/framework.pyonly loads the IronPythonwpfmodule whenIRONPYis true.WPFWindow.load_xamlrelies onwpf.LoadComponent.LoadComponentis effectively the key missing primitive: under IronPython it comes fromIronPython.Wpfand uses the DLRDynamicXamlReader+DynamicOperationsto:x:Namefields on the Python window instancedev/modules/pyRevitLabs.ironpython3/Src/DLR/Src/Microsoft.Dynamic/Xaml/DynamicXamlReader.cs(depends on DLR dynamic ops that pythonnet does not provide)DynamicXamlReaderusingSystem.Xaml(XamlXmlReader,XamlObjectWriter, customXamlMemberInvoker) got further but failed early when loadingpyrevitlib/pyrevit/forms/GetValueWindow.xamlwith:Set property "System.Windows.Window.ShowInTaskbar" threw an exceptionThis suggests additional WPF/XAML loading semantics need to be replicated correctly (property setting, root instance handling, name scopes, event binding, etc.).
dev/modules/pyRevitLabs.Python.Net/demo/DynamicGrid.pythat load XAML viaSystem.Windows.Markup.XamlReader.Loadon an STA thread.Given the above, Effort 2 likely depends on upstream/downstream pythonnet work. Before investing heavily, it would help to identify:
Next step after decision