-
Notifications
You must be signed in to change notification settings - Fork 0
Logging PythonStackTraces
Enhanced error formatting and stack traces for Python scripts in RevitDevTool
RevitDevTool provides a Python helper module (trace.py) that captures and displays Python call stack information directly in the trace log panel, making debugging Python scripts much easier.
The trace() helper captures:
- File names and paths
- Function/method names
- Complete call stack from your code
- Works with IronPython 2.7, IronPython 3.4, and CPython 3.x
Stack traces appear in readable format:
Processing element
Traceback (Last call first):
File "C:\Scripts\wall_analysis.py", in process_walls
File "C:\Scripts\wall_analysis.py", in analyze_project
File "C:\Scripts\wall_analysis.py", in <module>
- Messages appear with appropriate color based on keywords
- Stack frames in readable formatting
- Respects depth settings (configurable in UI)
Copy trace.py from RevitDevTool source to your script folder:
Source: source/RevitDevTool/Logging/Python/trace.py
# If trace.py is in the same folder as your script:
from trace import trace
# If you've installed it as a package:
from revit_dev_tool.trace import tracefrom trace import trace
def process_walls(doc):
trace("Starting wall analysis") # Will show call stack
walls = FilteredElementCollector(doc)\
.OfCategory(BuiltInCategory.OST_Walls)\
.WhereElementIsNotElementType()
trace(f"Found {walls.GetElementCount()} walls")
for wall in walls:
height = get_wall_height(wall)
trace(f"Wall {wall.Id}: {height} ft")
def get_wall_height(wall):
param = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM)
if param:
return param.AsDouble()
return 0.0
# Run
process_walls(__revit__.ActiveUIDocument.Document)Output in RevitDevTool:
Starting wall analysis
Traceback (Last call first):
File "wall_script.py", in process_walls
File "wall_script.py", in <module>
Found 156 walls
Traceback (Last call first):
File "wall_script.py", in process_walls
File "wall_script.py", in <module>
Wall 12345: 10.5 ft
Traceback (Last call first):
File "wall_script.py", in process_walls
File "wall_script.py", in <module>
"""Wall Height Analyzer
Analyzes wall heights with detailed tracing
"""
__title__ = 'Wall Heights'
__author__ = 'Your Name'
from pyrevit import revit, DB, forms
# Import the trace helper (copy trace.py to your scripts folder first)
from trace import trace
def analyze_walls():
trace("[INFO] Starting wall analysis")
doc = revit.doc
walls = DB.FilteredElementCollector(doc, doc.ActiveView.Id)\
.OfCategory(DB.BuiltInCategory.OST_Walls)\
.WhereElementIsNotElementType()
wall_count = walls.GetElementCount()
trace(f"[INFO] Found {wall_count} walls in current view")
results = []
for wall in walls:
try:
height = get_wall_height(wall)
results.append((wall.Id, height))
trace(f"Wall {wall.Id}: {height:.2f} ft")
except Exception as ex:
trace(f"[ERROR] Failed to process wall {wall.Id}: {ex}")
trace(f"[INFO] Analysis complete: {len(results)} walls processed")
return results
def get_wall_height(wall):
"""Get wall height parameter value"""
trace(f"[DEBUG] Getting height for wall {wall.Id}")
param = wall.get_Parameter(DB.BuiltInParameter.WALL_USER_HEIGHT_PARAM)
if not param:
trace(f"[WARN] Wall {wall.Id} has no height parameter")
return 0.0
return param.AsDouble()
# Run analysis
if __name__ == '__main__':
results = analyze_walls()
forms.alert(f"Analyzed {len(results)} walls", title="Complete")Each trace() call will show:
- Your message (with color based on [INFO]/[ERROR]/[WARN] keywords)
- Call stack showing which function called it
- File paths and line numbers (when available)
The helper module:
-
Captures Stack - Uses Python's
traceback.extract_stack()to get call information - Formats Frames - Converts stack frames to readable format
-
Calls PyTrace - Sends message + stack string to
PyTrace.Write()in RevitDevTool - Respects Settings - PyTrace filters based on your Stack Trace Depth setting
The helper works with:
- β IronPython 2.7 (pyRevit default)
- β IronPython 3.4 (pyRevit 3)
- β CPython 3.x (PythonNet3 in RevitDevTool)
If RevitDevTool is not installed, trace() falls back to System.Diagnostics.Trace (without stack traces).
from trace import trace
DEBUG = True # Set to False in production
def process_element(element):
if DEBUG:
trace(f"[DEBUG] Processing element {element.Id}")
# Your processing logic here
result = do_work(element)
if DEBUG:
trace(f"[DEBUG] Result: {result}")
return resultfrom trace import trace
class TracedOperation:
def __init__(self, operation_name):
self.name = operation_name
def __enter__(self):
trace(f"[INFO] Starting: {self.name}")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
trace(f"[ERROR] Failed: {self.name} - {exc_val}")
else:
trace(f"[INFO] Completed: {self.name}")
return False
# Usage
with TracedOperation("Wall Analysis"):
analyze_walls(doc)from trace import trace
class WallAnalyzer:
def __init__(self, doc):
self.doc = doc
trace("[INFO] WallAnalyzer initialized")
def analyze(self):
trace("[INFO] Starting analysis")
walls = self._collect_walls()
results = self._process_walls(walls)
trace(f"[INFO] Analysis complete: {len(results)} results")
return results
def _collect_walls(self):
trace("[DEBUG] Collecting walls")
# Collection logic
return walls
def _process_walls(self, walls):
trace(f"[DEBUG] Processing {len(walls)} walls")
# Processing logic
return resultsStack trace behavior can be configured in RevitDevTool Settings:
-
Enabled (default): Shows call stack with each
trace()call - Disabled: Only shows the message (like regular print)
- Default: 10 frames
- Range: 1-50 frames
- Controls how many stack frames are displayed
Access: RevitDevTool Panel β βοΈ Settings β Logging Tab
from trace import trace
def level_3():
trace("[DEBUG] In level_3")
return "result"
def level_2():
trace("[DEBUG] In level_2")
return level_3()
def level_1():
trace("[DEBUG] In level_1")
return level_2()
# Call
result = level_1()Output shows complete call chain:
[DEBUG] In level_1
Traceback (Last call first):
File "script.py", in level_1
File "script.py", in <module>
[DEBUG] In level_2
Traceback (Last call first):
File "script.py", in level_2
File "script.py", in level_1
File "script.py", in <module>
[DEBUG] In level_3
Traceback (Last call first):
File "script.py", in level_3
File "script.py", in level_2
File "script.py", in level_1
File "script.py", in <module>
from trace import trace
def process_elements(elements):
trace(f"[INFO] Processing {len(elements)} elements")
for i, element in enumerate(elements):
if i % 100 == 0:
trace(f"[INFO] Progress: {i}/{len(elements)}")
try:
do_something(element)
except Exception as ex:
trace(f"[ERROR] Element {element.Id} failed: {ex}")
# Stack trace shows which element caused the errorfrom trace import trace
def get_parameter_value(element, param_name):
trace(f"[DEBUG] Getting '{param_name}' from element {element.Id}")
param = element.LookupParameter(param_name)
if not param:
trace(f"[WARN] Parameter '{param_name}' not found")
return None
try:
value = param.AsString() or param.AsValueString()
trace(f"[DEBUG] Value: {value}")
return value
except Exception as ex:
trace(f"[ERROR] Failed to get value: {ex}")
return None| Method | Stack Trace | Color Coding | Settings Aware | Compatibility |
|---|---|---|---|---|
trace() |
β Yes | β Yes | β Yes | RevitDevTool only |
print() |
β No | β Yes | β No | All environments |
Trace.WriteLine() |
β No | β Yes | β No | .NET environments |
Recommendation: Use trace() in RevitDevTool for best debugging experience, fall back to print() for compatibility.
The helper module is included in RevitDevTool source:
Path: source/RevitDevTool/Logging/Python/trace.py
Installation Options:
-
Copy to Script Folder (Simplest)
YourScripts/ βββ trace.py # Copy here βββ your_script.py # Your script -
Install as Package (For multiple scripts)
C:\RevitScripts\ βββ revit_dev_tool/ β βββ __init__.py β βββ trace.py βββ my_scripts/ βββ script_1.py βββ script_2.pyThen in your scripts:
from revit_dev_tool.trace import trace
-
Copy to your script folder
- Copy
trace.pyto the same folder as your scripts - Import directly:
from trace import trace
- Copy
The trace() function works seamlessly with pyRevit scripts:
"""Element Counter with Tracing
"""
__title__ = 'Count Elements'
from pyrevit import revit, DB
from trace import trace # Assumes trace.py is in same folder
trace("[INFO] Starting element counter")
doc = revit.doc
categories = [
DB.BuiltInCategory.OST_Walls,
DB.BuiltInCategory.OST_Doors,
DB.BuiltInCategory.OST_Windows
]
for cat in categories:
collector = DB.FilteredElementCollector(doc)\
.OfCategory(cat)\
.WhereElementIsNotElementType()
count = collector.GetElementCount()
trace(f"[INFO] {cat}: {count} elements")
trace("[INFO] Counting complete")When using trace():
- Messages appear in both RevitDevTool panel AND pyRevit output window
- Stack traces only appear in RevitDevTool (pyRevit shows just the message)
- Best of both worlds for debugging
| Feature | RevitDevTool trace() | pyRevit output.print() | Dynamo print() |
|---|---|---|---|
| Call stack traces | β Yes | β No | β No |
| Color coding | β Auto | β No | |
| Configurable depth | β Yes | N/A | N/A |
| File export | β Yes | β No | β No |
| Geometry viz | β Yes | β No | |
| CPython support | β Yes | β No | β No |
# β Bad
trace("Processing")
trace("Done")
# β
Good
trace("[INFO] Processing walls in current view")
trace("[INFO] Wall processing completed successfully")# β
Good - Shows which element failed
for element in elements:
try:
process_element(element)
except Exception as ex:
trace(f"[ERROR] Element {element.Id} ({element.Category.Name}): {ex}")trace("[DEBUG] Detailed diagnostic info")
trace("[INFO] Important milestones")
trace("[WARN] Potential issues")
trace("[ERROR] Actual errors")# β
Good - Log every 100 items
for i, element in enumerate(elements):
if i % 100 == 0:
trace(f"[INFO] Progress: {i}/{len(elements)}")
process_element(element)Problem: ImportError: No module named trace
Solutions:
- Verify trace.py is in the same folder as your script
- Check the import statement matches your folder structure
- Add the folder to sys.path before importing
Problem: trace() works but no stack traces appear
Solutions:
- Open RevitDevTool trace panel
- Check Settings β Logging β "Include Stack Trace" is enabled
- Verify you're using the trace() function, not print()
Problem: Too many stack frames clutter the output
Solution: Reduce Stack Trace Depth in Settings β Logging β Stack Trace Depth (default: 10)
Problem: Excessive tracing slows down script
Solutions:
- Reduce trace frequency in loops
- Use conditional tracing (if DEBUG:)
- Disable stack traces for production runs
- Logging Overview - General logging features
- Color Keywords - Automatic log level detection
- Python Runtime - Python integration details
Module: trace.py
Bridge: PyTrace.cs
Demo scripts using trace():
- logging_format_script.py - Logging examples
- data_analysis_script.py - Real-world usage