This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
pyRevit is a Rapid Application Development (RAD) environment for Autodesk Revit. It is a hybrid C#/.NET and Python (IronPython) project that bootstraps a scripting layer into Revit via an add-in loader.
Active development happens on the develop branch. master is the stable release branch.
Python 3.10 + pipenv orchestrates the entire build. All build commands use:
pipenv run pyrevit <command>pip install pipenv
pipenv install --dev # install all Python deps
pipenv run pyrevit check # verify build environment (MSBuild, .NET, etc.)Requirements: Visual Studio 2022 with .NET desktop workload, .NET 8.0, .NET Framework 4.7.2/4.8, MSBuild.
pipenv run pyrevit build products # build all binaries (Release)
pipenv run pyrevit build products Debug # build in Debug config
pipenv run pyrevit build labs # build C# labs only
pipenv run pyrevit build engines # build Python engines
pipenv run pyrevit build installers # create Inno Setup + MSI installers
pipenv run pyrevit build autocmp # build CLI autocompleteBuild outputs:
bin/netfx/- .NET Framework binariesbin/netcore/- .NET 8.0 binariesdist/- installer packages
pipenv run check-docstrings # ruff check --fix pyrevitlib/pyrevit
pipenv run ruff # lint Python code
pipenv run black # format Python code
pipenv run mypy # type checkingDocstrings follow Google style convention (enforced by ruff with select = ["D"]).
pyRevitLoader (C#) -- IExternalApplication; bootstraps IronPython into Revit
--> pyrevitloader.py -- Python entry point; loads the session
--> pyrevitlib/pyrevit/ -- Core Python library (IronPython 2.7.12 compatible)
loader/ -- Extension discovery, session management
extensions/ -- Extension installation/uninstallation
revit/ -- Revit API wrappers
forms/ -- WPF/XAML UI components
routes/ -- HTTP routing for telemetry API
coreutils/ -- Shared utilities
pyRevitLabs.PyRevit.Runtime (C#) -- Executes commands when ribbon buttons are clicked
ScriptCommand.cs -- IExternalCommand implementation; dispatches to engines
*Engine.cs -- Per-language engines: IronPython, CPython, Dynamo, C#, VB
pyRevitCLI (C#) -- `pyrevit` command-line tool for install/config/clone
pyRevitTelemetryServer (Go) -- Usage statistics server (MongoDB/PostgreSQL backend)
Extensions live in /extensions/ and are registered in extensions/extensions.json. Each extension is a directory with the .extension suffix containing panels, buttons, and scripts.
Scripts in extensions run inside the Runtime engine: Python files execute via IronPython (or CPython if configured), and can import from pyrevitlib/pyrevit/. Multiple scripting languages are supported per-button: Python, C#, VB.Net, Dynamo.
Most C# projects dual-target net48 (Revit 2017-2024) and net8.0 (Revit 2025+). Build artifacts are placed in bin/netfx/ and bin/netcore/ respectively. dev/Directory.Build.props and dev/Directory.Build.targets centralize shared MSBuild config.
Heavy dependencies (IronPython 2.7.12, IronPython 3.4.0, Python.Net, MahApps.Metro, etc.) are git submodules under dev/modules/. Run git submodule update --init --recursive after cloning.
| Solution | Purpose |
|---|---|
dev/pyRevitLabs/pyRevitLabs.sln |
CLI, Doctor, common libraries |
dev/pyRevitLabs.PyRevit.Runtime/pyRevitLabs.PyRevit.Runtime.sln |
Runtime command execution |
dev/pyRevitLoader/pyRevitLoader.sln |
Add-in loader DLL |
There is no automated Python test runner at the repo level. C# unit tests are in dev/pyRevitLabs/pyRevitLabs.UnitTests/. Manual/integration testing is done through the pyRevitDevTools.extension UI inside Revit.
pipenv run pyrevit test telem # start telemetry server for manual testingBuilt with MkDocs + Material theme. Source in docs/. Deploy via:
pipenv run mkdocs gh-deploy --forceAny edit to pyrevitlib/ must remain compatible with IronPython 2.7.12 (the default engine).
- No f-strings (use
.format()) - No
typingmodule,dataclasses, walrus operator (:=), ormatch/case - No
pathlib(not available in IronPython 2) unicodeandstrare distinct types in IronPython 2 — usecompat.safe_strtype()instead ofstr()- Use
pyrevit.compatfor version-gating instead of inlinesys.versionchecks:compat.PY2/compat.PY3— Python major versioncompat.IRONPY2/compat.IRONPY3— IronPython 2 or 3compat.NETCORE/compat.NETFRAMEWORK— .NET runtime (.NET 8 vs .NET Framework)compat.get_elementid_value_func()— returns correctElementIdaccessor per Revit version
Any C# change must work under both build targets defined in dev/pyRevitLoader/Directory.Build.props:
net48-->bin/netfx/--> Revit 2017-2024 (.NET Framework 4.8)net8.0-windows-->bin/netcore/--> Revit 2025+ (.NET 8.0)
Check for #if NETCOREAPP / #if NETFRAMEWORK guards in the runtime project before adding new .NET API calls.
The runtime solution is at dev/pyRevitLabs.PyRevit.Runtime/.
Where to look when changing behavior:
| Goal | Location |
|---|---|
| Change how ribbon/extensions load at startup | pyrevitlib/pyrevit/loader/ |
| Change runtime command execution | dev/pyRevitLabs.PyRevit.Runtime/ |
| Change how the add-in bootstraps into Revit | dev/pyRevitLoader/Source/PyRevitLoaderApplication.cs |
| Change Revit API wrappers (DB/UI helpers) | pyrevitlib/pyrevit/revit/ |
| Change WPF UI components (forms, dialogs) | pyrevitlib/pyrevit/forms/ |
| Change CLI behavior | dev/pyRevitLabs/pyRevitCLI/ |
| Change extension/attachment management | dev/pyRevitLabs/pyRevitLabs.PyRevit/ |
| Add a version compatibility shim | pyrevitlib/pyrevit/compat.py |
| Find how a config key works | pyrevitlib/pyrevit/userconfig.py |
| API | Revit <= 2023 | Revit 2024+ |
|---|---|---|
| ElementId integer value | .IntegerValue |
.Value |
| .NET runtime | .NET Framework 4.8 | .NET 8.0 |
Always use HOST_APP.is_newer_than(year) or compat.get_elementid_value_func() rather than hardcoding version checks inline.
MyExtension.extension/
Tab.tab/
Panel.panel/
Button.pushbutton/
script.py # executed when button clicked
bundle.yaml # title, tooltip, author (supports i18n)
icon.png # 32x32 px
Supported button types: .pushbutton, .smartbutton, .pulldown, .splitbutton, .stack, .panelbutton, .linkbutton, .urlbutton
Scripts receive these globals injected by the loader:
__revit__-- UIApplication instance__commandpath__-- directory of the running script__shiftclick__-- True if Shift was held when clicking