# Python Debugging with VSCode **Live debugging of Python scripts in Revit using VSCode's debugger.** RevitDevTool integrates `debugpy` to enable full VSCode debugging capabilities: set breakpoints, step through code, inspect variables, and debug Revit API calls in real-time. ![Python Debugger Demo](images/RevitDevTool_PythonDebugger.gif) --- ## Overview ```mermaid flowchart TD subgraph VSCode["VSCode (Debugger)"] VS1[Set breakpoints in Python scripts] VS2[Press F5 to attach to Revit] VS3[Step through code execution] VS4[Inspect variables and Revit API objects] end subgraph Revit["Revit + RevitDevTool"] R1[Start debugpy listener on port 5678] R2[Wait for VSCode to attach] R3[Show connection status in UI] R4[Execute scripts with debugpy active] R5[Pause at breakpoints] R6[Send variable data to VSCode] R1 --> R2 --> R3 --> R4 --> R5 --> R6 end VSCode <-->|debugpy protocol
port 5678| Revit ``` --- ## Quick Start ### 1. Configure Debug Port (Optional) Open RevitDevTool Settings and set the debug port (default: 5678): ![General Settings](images/RevitDevTool_GeneralSettings.png) **Debug status indicator** in Trace panel toolbar: - 🔴 **Red dot** - Debugger not connected - 🟢 **Green dot** - VSCode debugger attached and ready ### 2. Create VSCode Launch Configuration Add to `.vscode/launch.json` in your script folder: ```json { "version": "0.2.0", "configurations": [ { "name": "Attach Revit Python", "type": "debugpy", "request": "attach", "connect": { "host": "localhost", "port": 5678 } } ] } ``` **Configuration options:** - `port: 5678` - Must match RevitDevTool debug port setting - `justMyCode: false` - Allows debugging into Revit API and libraries - `pathMappings` - Maps local files to execution paths ### 3. Start Debugging **Step-by-step workflow:** 1. **Launch Revit** with RevitDevTool installed 2. **Open script folder** in VSCode 3. **Set breakpoints** (click left margin, red dot appears) 4. **Press F5** in VSCode (or Run → Start Debugging) 5. **Check status** - Green dot 🟢 in Trace panel = connected 6. **Execute script** in RevitDevTool 7. **Debugger pauses** at your breakpoints --- ## Debugging Features ### Full VSCode Debugging Capabilities ✅ **Breakpoints** - Click line number margin to set/remove - Red dot indicates active breakpoint - Execution pauses when breakpoint is hit ✅ **Step Controls** - **Step Over (F10)** - Execute current line, move to next - **Step Into (F11)** - Enter function calls - **Step Out (Shift+F11)** - Exit current function - **Continue (F5)** - Resume until next breakpoint ✅ **Variable Inspection** - **Hover** over variables to see values - **Variables panel** shows all local/global variables - **Watch expressions** - Add custom expressions to monitor - **Call stack** - Navigate execution frames ✅ **Debug Console** - Evaluate Python expressions during pause - Access all variables in current scope - Test Revit API calls interactively ✅ **Conditional Breakpoints** - Right-click breakpoint → Edit Breakpoint - Add condition: `wall.Id.IntegerValue > 1000` - Breaks only when condition is true ✅ **Revit API Inspection** - Inspect `__revit__` (UIApplication) - Examine `doc` (Document) - Explore elements, parameters, geometry - View collection contents --- ## Example Debugging Session ### Script with Breakpoints ```python # /// script # dependencies = ["numpy"] # /// import numpy as np from Autodesk.Revit import DB doc = __revit__.ActiveUIDocument.Document # Set breakpoint here (line 10) walls = DB.FilteredElementCollector(doc).OfClass(DB.Wall).ToElements() areas = [] for wall in walls: # Set breakpoint here (line 15) to inspect each wall curve = wall.Location.Curve length = curve.Length # Conditional breakpoint: length > 10 areas.append(length) print(f"Wall {wall.Id}: {length:.2f} ft") # Set breakpoint here (line 23) avg_length = np.mean(areas) print(f"Average wall length: {avg_length:.2f} ft") ``` ### Debugging Workflow **1. Set breakpoints:** - Line 10: Before collecting walls - Line 15: Inside loop to inspect each wall - Line 23: After calculation **2. Execute script:** - Script runs until line 10 - Debugger pauses **3. Inspect variables:** - Hover over `doc` → See Document properties - Variables panel shows `__revit__`, `doc`, `np` **4. Step through:** - Press F10 (Step Over) → Line 13 - Variables panel now shows `walls` collection - Expand `walls` to see elements **5. Continue to loop:** - Press F5 (Continue) → Pauses at line 15 - Inspect `wall` variable - Check `curve`, `length` values - Debug Console: Type `wall.Name` to see wall name **6. Conditional breakpoint:** - Right-click line 15 → Edit Breakpoint - Condition: `length > 10` - Press F5 → Only pauses for long walls **7. Complete execution:** - Press F5 to finish - Check final output in Trace panel --- ## Advanced Debugging Patterns ### Pattern 1: Debug Element Collection ```python from Autodesk.Revit import DB doc = __revit__.ActiveUIDocument.Document # Set breakpoint here collector = DB.FilteredElementCollector(doc) walls = collector.OfClass(DB.Wall).ToElements() # Debug Console during pause: # >>> len(list(walls)) # >>> walls[0].Name # >>> walls[0].GetType().Name ``` ### Pattern 2: Debug Parameter Access ```python from Autodesk.Revit import DB doc = __revit__.ActiveUIDocument.Document wall = doc.GetElement(DB.ElementId(123456)) # Set breakpoint here params = wall.Parameters # Inspect in Variables panel: # - Expand 'params' to see all parameters # - Hover over individual parameters # - Check 'param.Definition.Name' ``` ### Pattern 3: Debug Geometry Operations ```python from Autodesk.Revit import DB doc = __revit__.ActiveUIDocument.Document wall = doc.GetElement(DB.ElementId(123456)) # Set breakpoint here curve = wall.Location.Curve start = curve.GetEndPoint(0) end = curve.GetEndPoint(1) # Inspect geometry: # - Check 'start.X', 'start.Y', 'start.Z' # - Verify 'curve.Length' # - Test transformations in Debug Console ``` ### Pattern 4: Debug Exception Handling ```python from Autodesk.Revit import DB doc = __revit__.ActiveUIDocument.Document try: # Set breakpoint here element = doc.GetElement(DB.ElementId(999999)) name = element.Name # May throw if element is None except Exception as e: # Set breakpoint here to inspect exception print(f"Error: {e}") # Debug Console: Check 'type(e)', 'str(e)' ``` --- ## Troubleshooting ### Debugger Won't Connect **Symptoms:** - Status indicator stays red 🔴 - VSCode shows "Connection refused" - Timeout when attaching **Solutions:** 1. **Check port availability:** ```powershell netstat -ano | findstr :5678 ``` If port is in use, change port in RevitDevTool Settings 2. **Verify debugpy installation:** - Check Trace panel for debugpy setup logs - Look for "Debugpy listening on port 5678" message 3. **Restart Revit:** - Close Revit completely - Relaunch and try again 4. **Check firewall:** - Allow Python.exe through Windows Firewall - Allow localhost connections on configured port ### Breakpoints Not Hit **Symptoms:** - Breakpoints show as gray (not red) - Script executes without pausing - "Breakpoint in file that does not exist" warning **Solutions:** 1. **Verify VSCode is attached:** - Check green dot 🟢 in Trace panel - VSCode Debug toolbar should be visible 2. **Check path mappings:** ```json "pathMappings": [ { "localRoot": "${workspaceFolder}", "remoteRoot": "${workspaceFolder}" } ] ``` Ensure paths match your script location 3. **Use absolute paths:** - Open script folder as VSCode workspace root - Avoid nested folders in pathMappings 4. **Set `justMyCode: false`:** ```json "justMyCode": false ``` Allows debugging into libraries ### Variables Not Showing **Symptoms:** - Variables panel empty - Hover shows no information - Objects show as `` **Solutions:** 1. **Use Debug Console:** - Type variable names directly - Access properties: `wall.Name`, `doc.Title` - Call methods: `element.GetType().Name` 2. **Check call stack:** - Click different frames in Call Stack panel - Variables change per frame 3. **Expand collections:** - Click arrow next to collection variables - May take time for large collections 4. **Revit API objects:** - Some show as `` (expected) - Access properties directly in Debug Console ### Performance Issues **Symptoms:** - Slow stepping through code - UI freezes during debugging - Long pauses between steps **Solutions:** 1. **Use conditional breakpoints:** - Instead of breaking every iteration - Only break when condition is true 2. **Disable breakpoints:** - Right-click breakpoint → Disable - Or uncheck in Breakpoints panel 3. **Detach when done:** - Press Shift+F5 to detach - Or click Stop in Debug toolbar 4. **Limit collection inspection:** - Don't expand large collections in Variables panel - Use Debug Console for specific items --- ## Best Practices ### 1. Strategic Breakpoint Placement **Good:** ```python # Set breakpoint before critical operations result = complex_calculation(data) # ← Breakpoint here print(result) ``` **Avoid:** ```python # Don't set breakpoints in tight loops for i in range(10000): process(i) # ← Avoid breakpoint here (too slow) ``` ### 2. Use Conditional Breakpoints **Instead of:** ```python for wall in walls: if wall.Id.IntegerValue == 123456: print(wall.Name) # ← Breakpoint here ``` **Use conditional breakpoint:** ```python for wall in walls: print(wall.Name) # ← Conditional: wall.Id.IntegerValue == 123456 ``` ### 3. Debug Console for Quick Checks **During pause, test expressions:** ```python # In Debug Console: >>> wall.Name 'Basic Wall' >>> wall.GetType().Name 'Wall' >>> dir(wall) # See all available methods [...] ``` ### 4. Inspect Revit API Objects **Pattern:** ```python element = doc.GetElement(element_id) # Set breakpoint here # In Debug Console: >>> element.Category.Name >>> element.Parameters.Size >>> [p.Definition.Name for p in element.Parameters] ``` ### 5. Clean Up After Debugging **Before production:** - Remove all breakpoints (Ctrl+Shift+F9) - Detach debugger (Shift+F5) - Test script runs normally without debugger --- ## Performance Notes ### Debugging Overhead **Minimal when not attached:** - debugpy listener runs in background - No performance impact on script execution - Status check is lightweight **Moderate when attached:** - Slight slowdown during execution (expected) - Stepping through code is slower (by design) - Variable inspection may take time for large objects **Best practices:** - Attach only when debugging needed - Detach when done (Shift+F5) - Use conditional breakpoints for loops ### Memory Considerations **debugpy memory usage:** - ~10-20 MB when listener is active - Additional memory during debugging session - Released when debugger detaches **Large collections:** - Avoid expanding huge collections in Variables panel - Use Debug Console for specific items - Consider filtering data before inspection --- ## Comparison with Other Tools ### vs pyRevit Debugging | Feature | RevitDevTool | pyRevit | | ----------------------------- | -------------- | ------------------------- | | **Debugger** | VSCode debugpy | Limited (print debugging) | | **Breakpoints** | Full support | Not available | | **Variable inspection** | Full VSCode UI | Print statements only | | **Step through code** | Yes (F10, F11) | No | | **Python version** | Python 3.13 | IronPython 2.7 | | **IDE integration** | Native VSCode | External tools only | ### vs Traditional Add-in Debugging | Feature | Python Debugging | C# Add-in Debugging | | -------------------------- | ------------------ | ------------------------ | | **Setup** | Launch config only | Visual Studio project | | **Attach time** | Instant (F5) | Attach to process | | **Restart required** | No | Yes (after code changes) | | **Breakpoints** | Full support | Full support | | **Performance** | Slight overhead | Native speed | | **Ease of use** | Very easy | Moderate | --- ## See Also - **[Python Runtime](CodeExecute-PythonExecution.md)** - Complete Python execution mechanism - **[Stub Generation](CodeExecute-StubGeneration.md)** - IDE autocomplete setup - **[vs pyRevit](CodeExecute-VsPyRevit.md)** - Comparison with pyRevit - **[CodeExecute Overview](CodeExecute-Overview.md)** - Module overview --- ## Demo Script Try the included demo script to test debugging: **File:** `source/RevitDevTool.PythonDemo/commands/debugpy_script.py` ```python # /// script # dependencies = [ # "numpy", # ] # /// from Autodesk.Revit import UI uidoc : UI.UIDocument = __revit__.ActiveUIDocument sel_ids = uidoc.Selection.GetElementIds() for eid in sel_ids: el = uidoc.Document.GetElement(eid) print(el.Id, el.Name) ``` **Steps:** 1. Select some elements in Revit 2. Set breakpoint on line 12 (`for eid in sel_ids:`) 3. Attach debugger (F5 in VSCode) 4. Execute script in RevitDevTool 5. Inspect `sel_ids`, `eid`, `el` variables 6. Step through loop (F10)