Skip to content

Latest commit

 

History

History
138 lines (91 loc) · 6.82 KB

File metadata and controls

138 lines (91 loc) · 6.82 KB

Understanding pyRevit Architecture

This guide provides an overview of pyRevit’s architecture to help new contributors understand how the software works.

Whether you want to create tools, troubleshoot issues, or contribute code, understanding these components will help you navigate the project.

Components of pyRevit

  1. pyRevit Add-In (pyRevitLoader)dev/pyRevitLoader/

    • A small piece of C# code that starts pyRevit inside Revit.
    • Calls the C# session manager directly to build the UI and button commands (no IronPython bootstrap).
  2. pyRevit Python Libraries (pyrevitlib)pyrevitlib/pyrevit/

    • Python packages that simplify working with the .NET Revit API.
    • Provide tools to create ribbon buttons, run scripts, and more.
  3. Extensions — see Extensions for bundle structure, bundle.yaml, script engines, and hooks.

    • These are the tools and features users see inside Revit.
    • They are mostly written in python, but can also be C#/VB.NET scripts, dynamo projects, and so on
    • Bundled extensions appear in the "pyRevit" tab, offering many tools.
    • Users can add extensions by:
      • Enabling listed extensions in extensions/extensions.json via the "Extensions" button in pyRevit.
      • Creating custom extensions and adding their paths to the configuration.
  4. pyRevit Command-Line Interface (CLI)dev/pyRevitLabs/pyRevitCLI/

    • A tool for managing configurations, running scripts in bulk, and troubleshooting.
    • Useful for corporate setups and advanced users.
    • The pyrevit env report includes the configured CPython engine version (activeCpythonEngineVersion in JSON).
  5. Telemetry Server

    • A small server (written in Go) that tracks usage data of pyRevit tools.
    • Stores data in MongoDB or PostgreSQL for business intelligence.

Command execution itself lives in a separate component, PyRevit.Runtime (dev/pyRevitLabs.PyRevit.Runtime/) — see How pyRevit Commands run.

How pyRevit Loads in Revit

!!! tip "TL;DR:"

- Revit reads the `.addin` manifest in the Addins folders
- The `.addin` manifest points to `pyRevitLoader.dll`
- `pyRevitLoader.dll` calls the C# session manager to build the UI and the button commands
- The C# session manager runs `session_preload.py` / `session_postload.py` to drive the Python services that have not yet been ported to C#

.addin Manifest

  • The installer creates a file with .addin extension, called manifest, in the Revit Addins folder, instructing Revit to load pyRevit when it starts.
  • The Addins folder can be located in one of these paths, depending on pyRevit installation:
    • C:\ProgramData\Autodesk\Revit\Addins (for all users)
    • %APPDATA%\Autodesk\Revit\Addins (for the current user only)
  • The manifest points to pyRevitLoader.dll, which acts as the entry point for pyRevit.

pyRevitLoader.dll

The pyRevitLoader.dll file is a small C# program that:

  • Ensures required .NET assemblies are loaded.
  • Calls the C# session manager to load the session.

???+ info

the source code is in `PyRevitLoaderApplication.cs` and it is an implementation of the Revit API `IExternalApplication` _interface_ (the standard way to create a plugin for Revit).

There are multiple versions of pyRevitLoader.dll to support:

  • different Revit versions:
    • One for Revit 2025 and newer, built with .NET 8.
    • Another for Revit 2021-2024, built with the .NET Framework.
  • different IronPython versions; to this date:
    • version 2.7.12, the default one
    • version 3.4.2, more recent but not fully tested.

They share the same source code, but are compiled against the different .net runtimes and IronPython versions.

The legacy pure-Python loader (which supported pre-2021 Revit versions) has been removed; the C# loader requires Revit 2021+.

!!! note

Only one IronPython runtime engine can be attached at a time; switching it (Settings UI or `pyrevit attach`) rewrites the `.addin` manifest to the matching `pyRevitLoader.dll` build. CPython selection is separate and doesn't touch the manifest. Manifests can also go stale from clone switches or mixed attachments — `pyrevit attach`/`switch` fixes them by recreating the manifest.

Session loading

PyRevitLoaderApplication.OnStartup calls SessionManagerService.LoadSession, which:

  • Runs session_preload.py to set up environment variables, the logging/script console, update checks, telemetry, and API routes.
  • Loads extensions and creates UI elements like ribbons and buttons in C# (see below).
  • Registers and activates hooks, which enable features like event-driven scripts.
  • Runs session_postload.py to finalize the session (cleanup, doc colorizer, routes server, output teardown).

A reload re-enters the same LoadSession entry, so initial startup and reload share one code path.

???+ info

`session_preload.py` and `session_postload.py` are small scripts that call
[pyrevit.loader.sessionmgr.perform_preload][] and
[pyrevit.loader.sessionmgr.perform_postload][]. They drive the residual Python
session services that have not yet been ported to C#.

Extensions discovery

  • pyRevit scans known paths and user defined folders to find extensions.
  • For each extension, it builds a .net assembly to create buttons, tabs, and other UI elements.

???+ info

Extensions directories are detected by [pyrevit.userconfig.PyRevitConfig.get_ext_root_dirs][].
Extension components discovery is performed by [pyrevit.extensions.extensionmgr.get_installed_ui_extensions][].
Assemblies are generated by [pyrevit.loader.asmmaker][] with types from [pyrevit.runtime.create_type][].

How pyRevit Commands run

!!! tip "TL;DR:"

Command execution is handled by the c# project `pyRevitLabs.Pyrevit.Runtime`

Each button generated by the Extension discovery is bound to a command derived by the ScriptCommand.cs source code.

This code deals with:

  • detecting the modifier keys hold while clicking the button, and change the behavior accordingly
  • calling ScriptExcecutor.ExcecuteScript passing the python script (or any other supportesd script) for the command

???+ info

The `ScriptCommand` class implements Revit’s `IExternalCommand` interface.
The `Execute` method is the one called when you click the Ribbon button.

In turn, the code in ScriptExecutor.cs calls the appropriate script engine based on the type of (IronPython, CPython, .NET, and so on).

???+ info

You can find the code of the engines in the files that end with `Engine.cs`, in `dev/pyRevitLabs.PyRevit.Runtime/`:

- `IronPythonEngine.cs` — default Python engine.
- `CPythonEngine.cs` — modern Python (3.12).
- `CLREngine.cs` — C#/VB.NET execution.
- `DynamoBIMEngine.cs` — Dynamo graphs.
- `GrasshopperEngine.cs` — Grasshopper definitions.