For the complete documentation index, see llms.txt. This page is also available as Markdown.
Obstacles encountered with PythonNet3 and workarounds
Properties of type “Indexer”
Properties of type “Indexer” (like access to Space in the Revit API) are no longer handled directly with the bracket syntax. The indexer must be replaced by the corresponding get method.
Example:
Old code:
space = elem.Space[phase]
New code:
space = elem.get_Space(phase)
Error in Python set collection
The Python Collection set() does not accept objects of type ElementId.InvalidId.
Workarounds:
Filter invalid IDs when building the set()
Use the .NET class _HashSet<T>
Example(Filtering):
setViewsIds =set([w.OwnerViewId for w in allWires if w.OwnerViewId != ElementId.InvalidElementId])
"with" statement on .NET objects
The use of the keyword with on .NET objects (which implement the interface IDisposable) currently throws an error with PythonNet3, unlike IronPython.
Workaround: Build a custom context manager to explicitly handle the method Dispose().
Example (Custom Context Manager CManager):
.NET Objects that Inherit from an Interface
In the case of objects that inherit from a .NET Interface, it may be necessary to cast (convert) the object to that interface to use the inherited methods.
Example (Call BeginInit() on a PictureBox):
In the example below, you will find the PythonNet syntax to perform an explicit cast to a .NET interface.
The line _System.ComponentModel.ISupportInitialize(self.pictureBox1).BeginInit() is therefore a method call on an explicit interface:
It “casts” (or rather, it accesses) the object _self.pictureBox1 via its implementation of the interface ISupportInitialize.
It then calls the method BeginInit() of this interface
Attributes lost on .NET Class Instances when assigning to OUT
Python class instances deriving from a .NET class may have their attributes removed by the Dynamo Wrapper when assigning to the variable OUT.
Workaround:
Wrap the .NET object in a simple Python class before assigning it to OUT.
Example:
inner object
no inner object
Missing error line
In rare cases, the error line number in the Python node error message may be missing.
Here are some solutions while waiting for a fix:
Implement a try-except block with the traceback library and print the error. Implement a try-except block with a logger and write the error. Implement a debugger, for example, using sys.settrace().
Conclusion PythonNet3 establishes itself not only as the current standard but as the only sustainable path forward for development in Dynamo.
import sys
import clr
import System
# Add Assemblies for AutoCAD and Civil3D
clr.AddReference('AcMgd')
clr.AddReference('AcCoreMgd')
clr.AddReference('AcDbMgd')
clr.AddReference('AecBaseMgd')
clr.AddReference('AecPropDataMgd')
clr.AddReference('AeccDbMgd')
# Import references from AutoCAD
from Autodesk.AutoCAD.Runtime import *
from Autodesk.AutoCAD.ApplicationServices import *
from Autodesk.AutoCAD.EditorInput import *
from Autodesk.AutoCAD.DatabaseServices import *
from Autodesk.AutoCAD.Geometry import *
# Import references from Civil3D
from Autodesk.Civil.ApplicationServices import *
from Autodesk.Civil.DatabaseServices import *
# The inputs to this node will be stored as a list in the IN variables.
dataEnteringNode = IN
adoc = Application.DocumentManager.MdiActiveDocument
editor = adoc.Editor
class CManager:
"""
a custom context manager for Disposable Object
"""
def __init__(self, obj):
self.obj = obj
def __enter__(self):
return self.obj
def __exit__(self, exc_type, exc_value, exc_tb):
self.obj.Dispose()
if exc_type:
error = f"{exc_value} at line {exc_tb.tb_lineno}"
raise ValueError( error)
return self
dynCADObjects = IN[0]
with adoc.LockDocument():
with CManager(adoc.Database) as db:
print(db)
with CManager(db.TransactionManager.StartTransaction()) as t:
print(t)
for dynObj in dynCADObjects:
ent = t.GetObject(dynObj.AcadObjectId, OpenMode.ForWrite)
ent.Layer = "Layer1"
#a = 10 /0
t.Commit()
print(f"{db.IsDisposed=}")
print(f"{t.IsDisposed=}")
class Form8(Form):
def __init__(self):
super().__init__() # necessary if you override the __init__ method
self.InitializeComponent()
def InitializeComponent(self):
self._pictureBox1 = System.Windows.Forms.PictureBox()
System.ComponentModel.ISupportInitialize(self._pictureBox1).BeginInit()
self.SuspendLayout()
# Load the Python Standard and DesignScript Libraries
import sys
import clr
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
class WrappNetObj:
def __init__(self, obj):
self.InnerObject = obj
def __repr__(self):
return "WrappNetObj_" + self.InnerObject.GetType().ToString()
class Form8(Form):
def __init__(self):
super().__init__()
self.out_value = 7
self.InitializeComponent()
def InitializeComponent(self):
self.SuspendLayout()
#
#Form8
#
self.ClientSize = System.Drawing.Size(284, 261)
self.Name = "Form8"
self.Text = "Form8"
self.ResumeLayout(False)
win_obj = Form8()
OUT = WrappNetObj(win_obj)