flowchart LR
subgraph Load["Load Phase"]
UI["UI/ViewModel"] --> Orch["ExecutionOrchestrator"]
Orch --> Provider["IExecutionProvider"]
Provider --> Tree["ExecutionNodeRoot"]
end
subgraph Execute["Execute Phase"]
Tree --> Strategy["IExecutionStrategy\n(Python/IronPy/FSharp/CSharp/Assembly)"]
Strategy --> Host["Host Adapter\n(IHostContextExecutor)"]
Host --> Result["ExecutionResult"]
end
subgraph Watch["File Watch"]
Watcher["FileWatcherService"] -.->|file changed| Orch
end
Load: UI requests a path → orchestrator asks providers to discover nodes → builds execution tree.
Execute: User selects a node → strategy compiles/interprets → dispatches to host thread → returns result.
Watch: File watcher monitors roots → triggers reload on changes → TreeStateManager preserves UI state.
| Mode | Provider | Notes |
|---|---|---|
Script |
ScriptExecutionProvider |
Folder scan for *script.py, *script.fsx, *script.csx. |
Assembly |
AssemblyExecutionProvider |
.dll reflection. Host-specific ICommandDiscovery. |
ScriptExecutionProvider skips folders: docs, resources, bin, obj, packages, node_modules, output, caches, virtualenvs, and agent/tool folders.
Init, backends, host-attach, and native constraints: python-runtime.md.
- Host owns CPython → attach pythonnet, then uv. Pixi is not tried.
- No host interpreter → Pixi. uv is not tried.
- Pip only if that chosen manager’s setup/
VerifyRunnableAsyncfails. - Overlay,
python3.dllforwarder, and init order: python-runtime.md.
IronPythonExecutionStrategyexecutes*_ipy_script.py.- Two stacks: pyRevit loaded → ScriptExecutor +
IronPythonDebuggeron the loader engine (clean: false,full_frame: false, reuse). No pyRevit → embedded 3.4.2 viaGetOrCreateEngine. Same debugger type; do notCreateEngineorRuntime.Shutdownon the loader engine (0033). - Host bridges configure builtins and CLR assemblies only. ScriptExecutor search paths are pyRevit's command-generator list plus the directories of TFM-selected extension DLLs and the pydevd extract root.
pyrevitlib/site-packagescome from walkingPyRevitLoaderup topyRevitfile.PyRevitAssemblyLoaderpicks one managed DLL per simple name fromlib/**andbin/*/byTargetFrameworkAttributevs the host runtime. One load per*.extension. Embedded 3.4.2 usesIronPythonSearchPaths.ForNativeHostonly.DlrScriptHostis the DLR façade (typed 3.4 orReflectionBoundon a foreign loader engine). pyRevitScriptExecutor/PyRevitLoaderbindings stay in the Revit host.IronPythonDebugger.RefreshUserModulestakes a host drop root and skip list (Revit:*.extension, skip the pyRevit install root that containspyRevitfile). - Embedded 3.4.2 uses Frames without
Tracing. The pyRevit loader engine must not setfull_frame(that enablesTracingand IronPython 3.4 cannotimport pydevd). User-extensionsys.modulesentries are dropped before each pyRevit Run.from pyrevit import HOST_APPruns on the loader engine before pydevdenable_tracing(warmup and each Run) so_perf.mark()cannot importcoreutilswhileHOST_APPis still unset. Listen imports pydevd underclithen forcesIS_WINDOWS=True. - In-process debugger is PyDev.Debugger 2.8.0 under
%APPDATA%\RevitDevTool\pydevd\PyDev.Debugger-pydev_debugger_2_8_0. The host listens withHTTP_JSON_PROTOCOL+pydevd._enable_attachon 4567 (no_wait_for_attach). VS Code/Cursor attaches with"type": "debugpy"connectlocalhost:4567(same client as CPython on 5678; two sockets). Handshake and expand-getattr quieting live inIpyDebugger.py. Execution UI shows a dedicated port strip under Search (bug status + runtime icon + port);IDebuggerBridgestill exposes one aggregateIsConnectedplus per-runtime flags. Policy: 0033.
FSharpExecutionStrategycompiles.fsxthroughFSharpCompilationCache.FSharpDependencyResolverhandles#r "nuget: ..."directives.NugetManagerrestores packages under%APPDATA%\RevitDevTool\nuget.- When nuget or file
#rmust be rewritten, the graph is copied under%TEMP%\DevTools\fsx_cache(commented#r, remapped#load,--reference:). Each temp file starts with#line 1 "<original>"so FSI--debug+sequence points map back to the source the user edits. Eval of an unchanged graph still uses the original path. - Compilation has a hard timeout.
- Host year
#ifsymbols (REVIT/AUTOCAD,{HOST}{year},{HOST}{year}_OR_GREATER) come fromCompileScriptSymbolsviaIHostAppInfo.VersionNumber, matchingprops/Revit.targetsandprops/AutoCad.targets. FSI gets them as--define:.
CSharpExecutionStrategycompiles.csxthroughCSharpCompilationCache.CSharpDirectiveParserhandles references and package directives.#r/#loadare commented in place (line numbers stay aligned with the file on disk).- AppDomain assemblies are still imported as Roslyn metadata refs (
#r/ NuGet first). Duplicate simple names (e.g. Revit 2027Autodesk.Http.*underAddIns\IssuesManagement) are skipped so CS1704 does not fail the compile. - Emit is Debug + portable PDB, loaded with the collectible/net48 isolation session so an attached host debugger can bind the original
.csx.#r nugetstill resolves throughNugetManager; it is not left as compiler syntax. - Compiled script outputs use the feature-owned
ScriptIsolationPlanwith the shared assembly-isolation session. Identity and lifecycle behavior follows the assembly-isolation product contract. - Compilation has a hard timeout.
- Roslyn parse options take the same
CompileScriptSymbolslist, so.csxcan use#if REVIT2025_OR_GREATER(and AutoCAD-family equivalents) like host add-in code.
AssemblyExecutionStrategyloads IL from.dll, invokes method.- Also used as MCP source kind for .NET assembly tools.
PackageService is the UI facade. It branches on marketplace only:
| Marketplace | Store |
|---|---|
| NuGet | NugetPackageStore |
| CondaForge / PyPI | IPythonPackageStore for the current PythonBackend |
Python backends are equal implementations of IPythonPackageStore (PixiPackageStore, UvPackageStore, PipPackageStore). PackageService picks the store whose Backend matches PythonInitializer.Provider. No switch on backend inside PackageService.
- uv: host-owned-interpreter sidecar (PyPI, version-matched).
- Pixi: owns in-process CPython when the host has no interpreter (conda-forge + PyPI).
- pip: last chain step — pyRevit
cengineswhen the chosen Pixi or uv manager cannot run.
Operations: list, remove, remove all, update latest, and repair.