So the other day on Twitter someone who is both really cool and smart said they didn’t understand what happens with all the import stuff in the Dynamo Python environment.
And so in an attempt to provide some insight to the masses I decided to share out my annotated Python code for the imports I use in the context of for Dynamo for Revit. Each line is annotated to explain it’s purpose, and at least give enough bread crumbs on what to each for if you want to learn more.
The imports
########################################
### Configure the Python environment ###
########################################
### standard imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries
### Dynamo Revit imports ###
clr.AddReference("RevitNodes") #add Dynamo's Revit nodes library to the clr
import Revit #import Dynamo's Revit node class
clr.ImportExtensions(Revit.Elements) #add the element conversion methods to the CLR
clr.ImportExtensions(Revit.GeometryConversion) #add the geometry conversion methods to the CLR
clr.AddReference("RevitServices") #add the Revit services library to the CLR
import RevitServices #import the Revit services class to the Python environment
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment
from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment
### Revit API imports ###
clr.AddReference("RevitAPI") #add the Revit API to the CLR
import Autodesk #add the Autodesk class to the Python environment
from Autodesk.Revit.DB import * #import every class in the DB section of the Revit API to the Python environment.
#The full list can be seen here: https://www.revitapidocs.com/2024/87546ba7-461b-c646-cbb1-2cb8f5bff8b2.htm
### trade specific Revit imports ###
from Autodesk.Revit.DB.Architecture import * #import every class of the Revit architecture API to the Python environment
from Autodesk.Revit.DB.Structure import * #import every class of the Revit Structure API to the Python environment
from Autodesk.Revit.DB.Plumbing import * #import every class of the Revit plumbing API to the Python environment
from Autodesk.Revit.DB.Electrical import * #import every class of the Revit electrical API to the Python environment
from Autodesk.Revit.DB.Mechanical import * #import every class of the Revit mechanical API to the Python environment
### .net imports ###
clr.AddReference("System") #add the .net System library to the CLR
from System.Collections.Generic import List as NetList #imports the .net list module to the Python environment as the alias NetList
#elementIds = NetList[ClassHere](ListOfObjectsHere) #how to build a .net list from a python list of objects
I appreciate the post! (Being the person who mentioned it on twitter, and still not knowing anything about it).
I think i saw in the link you posted last night (to the python primer) that it explains WHY each line might get added, yeah? Thats where it gets confusing. I took an API coding class (back in 2012), and the teacher was like “Here is what i put at the top of my code, you can copy it in and use it in the future.” Trouble is, that meant i didnt learn WHY any of that had to be there.
BTW, TOTALLY not asking you to explain every line. Thats not your job. Just expanding on the confusion i mentioned from last nights Tweets.
For instance:
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment
from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment
What i need to work on learning is: How do i know if/when i need either of these things?
I like a “subtractive” template for code rather than an additive. My process is more or less “load in my full template” which contains all of that, the basic transaction calls, document and application variables, unwrapping and ‘to list’ methods. Can’t post that at the moment (wrong PC), but it is basically what is in the Dynamo Python primer and the Dynamo Developer primer.
So starting from that full template, my process is as follows:
Stage 1: I write v0.1.1 of my code, starting in the oldest build I am going to support (currently 2022 for me as there isn’t enough runway for me to get ROI on 2021 stuff, and 2020 is unsupported). The goal is to 'get the result with a minimal dataset - two inputs. At this stage I don’t use any definitions yet as while they are better long term they are harder to debug up front. I also avoid try statements - they are a last resort once i see the error happen. I omit any UI, additional version issues, or edge cases at this stage.
Step 2: I write v0.1.1 of my code. This adds the UI and addresses any versioning issues between my oldest and newest supported version, and addresses any simple edge cases i can find.
Step 3: I prep to ship v0.1.2. This cleans up the imports to reduce loading. I typically comment out lines I don’t need, confirm it still runs in oldest and newest build, and then eventually delete them after confirmation. I also add the code properties and such.
Step 4: I hand off the code and wait for additional edge cases and testing feedback.
So in the case of the lines you mentioned:
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment
These would be in the python node until step 3 was complete. At which point they’d go away if I didn’t use TransactionManger or DocumentManager at any point after the imports.
If it helps anyone too this is my typical Python boilerplate for noding. Subtractive is definitely the way to go, I’d never remeber these off the top of my head.
Like @Aaron_Maller, even though I have written several Python scripts in Dynamo using both DesignScript geometry and the Revit API, and I manage in Python, I struggle to understand the import of libraries and their implementation in scripts (I mean their hierarchy or assemblies where they are derived from).
01- As a comparison why no class is imported beofore using DesignScript.Geometry and we wrote: from Autodesk.DesignScript.Geometry import *,
however when we’re using Revit.GeometryConversion we should import Revit class before?
02- When using DocumentManager and TransactionManager why we should import RevitServices while we add it’s reference before by writing: clr.AddReference('RevitServices')
03- in order to use Revit API why we wrote import Autodesk instead off import Revit ?
Can you please explain more about imports or provide resources where I can better understand this topic?
If you look at the Python I posted and read the comments you can see what we add to the CLR prior to importing to the Python environment. If you skip a CLR add your import is apt to fail in one of if not all environments. So to import geometry conversion to have to:
Add RevitNodes to the CLR
Import Revit to the Python environment
Import the geometry conversion extension to the CLR
This is lines 9, 10, and 12 of the code I posted.
The CLR and your Python environment are two different things. In order to import into your Python environment it has to be in the Common Language Runtime, and so you have to add a reference to the library into the CLR first.
The Revit API classes are prefixed with Autodesk to prevent namespace collision in the NET environment and ensure things that shouldn’t use it attempt to utilize the Revit API. The first is relevant as some other Autodesk tools also have a Revit class (i.e. importers and exporters for some of our other tools, stuff that happens in Autodesk Platform Services, the Dynamo Revit nodes, etc.). A good example of this is Dynamo where in that context Python would grab the Dynamo for Revit nodes, but you want the Revit API. The second is relevant as Revit files are VERY particular, and it is very easy to corrupt them by messing with them, often in ways which aren’t apparent for a few weeks or months later when there is already no way to repair the file that doesn’t result in significant data loss and rework for design teams.