I’m developing a Dynamo package in C# and am running into a limitation with Python integration. Specifically, I’ve noticed that the built-in Python Script node in Dynamo can execute Python code from a string, but as a package developer, I can’t access the same functionality from my own custom C# nodes.
After some research and experimentation, it seems that classes and methods like
CPythonEvaluator.Evaluate
and other internals used by the built-in PythonNode are not exposed in the public API. This makes it impossible to execute Python code from a string within a custom node, even though this is core to the built-in Python node’s functionality.
My main goals:
I want to avoid maintaining two separate libraries (one in C#, one in Python).
I’d like to keep all logic in a single C# library, but still leverage Python for certain operations where it makes sense.
Ideally, I’d like to be able to run an internalized Python script (stored as a string in the node) from within my custom node, just like the built-in Python node does.
Questions:
Why are these PythonNode internals not exposed to the public API for package developers?
Is there a recommended or supported way to invoke Python code from a C# custom node in Dynamo, using the same context and bindings as the built-in Python node?
What is the best practice for integrating Python code into a C# Dynamo library if I want to avoid code duplication and keep everything in one place?
Are there plans to make this kind of integration easier or more officially supported in future Dynamo releases?
Any advice or insight from the Dynamo team or experienced package developers would be greatly appreciated!
Thanks in advance.
I would argue that the best practice is to avoid doing this. It’ll be significantly slower than what you would get with pure C#, and that speed is usually a primary driver for leaving Python. You also double your security threat vectors and thereby double the likelihood of having to put a major patch together overnight, and introduce another dependency to a method which isn’t consistent heat to year in the form of the Python engine. Fortunately most Python automation can be quickly converted to C# without much effort, though there is an exception involving Python packages which mean you need a matching C# library. If I had to mix languages for Python package use I would keep the Python running external to Dynamo as a standalone app, and then call it from the ZT node. This would enable the Python package to be utilized on it’s own, without the need for an intermediate application (or two depending on if you’re using sandbox or an integration).
It’s pretty easy now, but you need to build it appropriately. Personally I would t want to see the team spend any time on it due to the stuff outlined in my previous comment.
Usually private methods are built that way as there is a security or stability concern. With Python it could be both.
My question was, because the PythonNodeModel is accessible and usable, why does it not have functionalities that can be used to execute the Python script? It is like you gave me a piece of cake, but I couldn’t eat it. In that sense, my question was. Of course, there are development decisions behind it.
I didn’t see that post, but I have tried a similar solution in the past and am currently investigating a similar approach using CSnake.
The Python scripts aren’t overly complex, and they can certainly be rewritten in C#. I understand your concerns about not mixing languages. We make an effort to convert the Python node into C# whenever possible. However, as you pointed out, there are times when specific libraries are required, or when we want to make the library accessible to individuals who may not be familiar with C#. In these cases, having a hybrid node can simplify things. Of course, it’s essential to evaluate each script carefully, as failing to do so could risk overwhelming the library.
Great point about the conflicts. Testing at scale will be a must. I recommend starting by pulling a blank Revit build (no customization) and getting the list of assembly conflicts from your journal and Dynamo. Then do the same with every customization in place except the one being built. And finally doing one with ALL tools in place. At each step if there are any new assembly conflicts in noted in the Revit journal or the Dynamo log you’ll want to consider the impact on your tool. At bigger companies this is much harder as the number of customizations is absurd (the AEC super firms struggle the most with this). If the use can be limited to particular subset of users or an environment that will help alot. Keep in mind you’ll want to do this for each Revit release you support (i.e. 2023, 2024, 2025, 2026).
@Mirco.BianchiniVJUJG the community would likely benefit quite a bit from a write up / lessons learned / wiki / blog post when you get something complete; both from the perspective of ‘building a custom node with a Python interpreter in it’ and ‘CSnake’. Even notes on integrating the primer page linked above would be a huge help (the content is a bit stale at this point).