Skip to content

Conversation

@romangolev
Copy link
Contributor

@romangolev romangolev commented Apr 14, 2025

Rewrite Loader into C#

Description

Implementing an AssemblyBuilder, UIBuilder and SessionManager refactored in C# as a substitute for the original Loader which is written in Python.
This feature aims to increase the load speeds on startup and reload events.


Checklist

Before submitting your pull request, ensure the following requirements are met:

  • Code follows the PEP 8 style guide. -- Not applicable
  • Code has been formatted with Black using the command: -- Not applicable
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected. - No, just a draft for now

Related Issues

If applicable, link the issues resolved by this pull request:

  • N/A

Additional Notes

THIS IS A DRAFT
Entry point is at PyRevitLoaderApplication which now have 2 modules for loading pyRevit sessions:

  • legacy: ExecuteStartUpPython
  • refactored: ExecuteStartUpCsharp

Adds new project pyRevitAssemblyBuilder which is set as a dependency project for pyRevitLoader and pyRevitRunner.
Whenever one of them built, dependency is built and loaded automatically.

pyRevitAssemblyBuilder is using DI (Dependency Injection) and have a set of services being instantiated on Revit startup as singletons.

On the moment of opening this draft PR, the module is able to build dummy dlls and simplified UI for them (without images and tooltips). Yet does not support yaml bundle files, does not build lib files.

Reviewers

@jmcouffin @sanzoghenzo @dosymep what do you think? Is it a way to go?


Thank you for contributing to pyRevit! 🎉

devloai[bot]

This comment was marked as outdated.

@sanzoghenzo
Copy link
Contributor

Thanks @romangolev for the huge job!

If it was me, I would already got rid of the legacy/ExecuteStartUpPython method and the IronPython dependency, so that we can put the dlls into the bin/netcore and bin/netfx regardless of the selected python engines.

for the bundle files, I had an idea a while ago to simplify things and switch from folder-based extensions to manifest-based extensions, I'll open a feature request to see if I'm the only one or if this could be a valuable feature that could already be implemented in this PR

@dosymep
Copy link
Member

dosymep commented Apr 15, 2025

manifest-based extensions

I think if we create good architecture, we can support two way to create tabs :)

@dosymep
Copy link
Member

dosymep commented Apr 15, 2025

@romangolev Good Job!

I have some doubts about the DI container from Microsoft, we can get into a situation with dll hell and I would like to see a more OOP architecture of this. It would be cool to separate the structure parser from the main libs into a separate one, where there would be no dependence on revit, python and other things, only pure code (service) for parsing the structure.

To implement these things that I listed, you need a lot of effort, and make many important decisions, so I think, for a start, to get rid of the python code, your implementation is perfect.

@jmcouffin
Copy link
Contributor

manifest-based extensions

I think if we create good architecture, we can support two way to create tabs :)

Agreed; but if we start supporting extra worklflows, I will need pyRevit to provide me a paid job ;o
Joke aside, I like the idea of manifest-based extensions:

  • Simplifies greatly the extensions part of the code base
  • Allow for a specific UX to create those
Side projects I work on that could leverage a manifest based extensions and streamline extension creation

WIP: https://builder.pyrevitlabs.io/

@romangolev
Copy link
Contributor Author

@sanzoghenzo it's just the start! The legacy loader has quite a lot of code on and because of that, I believe that gradual approach is preferable. I would say that after implementing this feature we may end up with some unpredictable bugs, so for users to be able to make a step back and activate the old way of loading things might be a walk-around in order not to lose main functionality.

@romangolev
Copy link
Contributor Author

@romangolev Good Job!

I have some doubts about the DI container from Microsoft, we can get into a situation with dll hell and I would like to see a more OOP architecture of this. It would be cool to separate the structure parser from the main libs into a separate one, where there would be no dependence on revit, python and other things, only pure code (service) for parsing the structure.

To implement these things that I listed, you need a lot of effort, and make many important decisions, so I think, for a start, to get rid of the python code, your implementation is perfect.

@dosymep thanks man🙏
I had a doubt about the packages as well. Should we degrade this and implement execution without DI? I'll dig it to see if it's a big deal, but yeah, in case somebody else loads that same dlls, the result might be unpredictable.

Also, the Parser itself doesn't have a lot now, so it wouldn't be a problem to make a separate csproject from it.

@jmcouffin
Copy link
Contributor

@romangolev how is your progress, I just saw the commits but cannot really test, I need to keep my dev environment clean for a presentation next week.
Let me know if you need me/us to test ride the thing.

@romangolev
Copy link
Contributor Author

@jmcouffin these are the commits regarding to the comments of @dosymep
I managed to got rid of DI and moved the extension parser to a separate file.
That's it for the moment.
Next move is going to be implementing functionality gradually to make it work similar like a python loader 😎
So yeah, probably a little early for a full fledged testing

@jmcouffin
Copy link
Contributor

I gave it a test ride @romangolev
figured out most of the testing and how to setup thanks to cursor.
If @dosymep or @sanzoghenzo or even otehr want to give it a ride:

How to Test PR #2647

  1. Enable the C# Loader
    Enable the new loader in pyRevit settings:
    Option A: Via pyRevit Settings UI
    Open Revit with pyRevit loaded
    Go to pyRevit tab → pyRevit panel → Settings
    Enable the "New Loader" toggle
    Optionally enable "Use Roslyn Loader" (uses Roslyn instead of ILPack for assembly building)
    Option B: Via Python Script
    from pyrevit.userconfig import user_configuser_config.new_loader = Trueuser_config.use_roslyn_loader = False # or True for Roslyn
    Option C: Via Configuration File
    Edit the pyRevit config file and set:
    [core]new_loader = trueuse_roslyn_loader = false
  2. Build the Solution
    Open the solution in Visual Studio
    Build pyRevitAssemblyBuilder (dependency)
    Build pyRevitLoader (for your Revit version)
    Ensure all DLLs are copied to the correct output directories
  3. Test in Revit
    Basic Functionality Test:
    Close Revit if open
    Open Revit (the loader will start automatically)
    Check the pyRevit output window for logs:
    Look for: "Creating new Session with pyRevitAssemblyMaker.dll..."
    Look for: "Using ILPack build strategy for C# session manager" (or Roslyn)
    Look for: "C# session loading completed successfully"
    Verify the UI loads correctly:
    Check that all extension tabs/panels appear
    Verify buttons have icons and tooltips
    Test clicking a few buttons to ensure commands execute
    Performance Test:
    Time the startup with the new loader enabled
    Compare with the Python loader (disable new_loader)
    The C# loader should be faster, especially on reload
    Reload Test:
    Use pyRevit → Reload pyRevit command
    Verify it reloads correctly with the C# loader
    Check logs for any errors
  4. Run Unit Tests
    The PR includes a test project pyRevitExtensionParserTester:
    Location: dev/pyRevitLoader/pyRevitExtensionParserTester/
    Test Files Include:
    ParserBasicTests.cs - Basic parsing functionality
    ParsedComponentTests.cs - Component parsing
    ParsedBundleTests.cs - Bundle YAML parsing
    PanelButtonBundleTests.cs - Panel button creation
    IconParsingTests.cs - Icon parsing (including dark mode)
    LocalizationTests.cs - Localized tooltips/text
    BundleTooltipFixTest.cs - Tooltip handling
    And more...
    To Run Tests:

Using dotnet CLIdotnet test dev/pyRevitLoader/pyRevitExtensionParserTester/pyRevitExtensionParserTest.csproj# Or in Visual Studio# Right-click the test project → Run Tests

Test Resources:
The tests use a test bundle extension located at:
dev/pyRevitLoader/pyRevitExtensionParserTester/Resources/TestBundleExtension.extension/
This includes:
Multiple panels and buttons
Icons (light and dark)
Tooltips (including SWF and MP4)
Bundle YAML files
Localized content
5. Test Specific Features
Icon Support:
Test extensions with both light and dark icons
Verify correct icon is shown based on Revit theme
Check pulldown button icons
Tooltip Support:
Test buttons with text tooltips
Test buttons with SWF tooltips
Test buttons with MP4 tooltips
Verify tooltips appear on hover
Bundle YAML Parsing:
Test extensions with bundle.yaml files
Verify layout, icons, tooltips are parsed correctly
Test quoted values in YAML
UI Components:
Test pushbuttons
Test pulldowns
Test stacked buttons
Test panels and tabs
6. Error Handling Test
Intentionally break something (e.g., invalid YAML)
Verify it falls back to Python loader gracefully
Check error messages in logs
7. Compare with Legacy Loader
Side-by-Side Comparison:
Test the same extensions with both loaders
Compare:
Load time
UI appearance (should be identical)
Functionality (should be identical)
Memory usage
What to Look For
Success Indicators:
✅ Faster startup/reload times
✅ All extensions load correctly
✅ UI matches the Python loader
✅ All buttons/icons/tooltips work
✅ No errors in logs
✅ Commands execute correctly
Potential Issues:
❌ Missing icons or tooltips
❌ UI elements not appearing
❌ Errors in output window
❌ Fallback to Python loader (check logs for why)
❌ Performance not improved
Debugging
If issues occur:
Check the pyRevit output window for detailed logs
Look for messages like:
"New loader disabled. Using Python session creation."
"PyRevitLoaderApplication not found in loaded assemblies"
"Falling back to Python session creation..."
Verify DLLs are in the correct locations
Check that pyRevitAssemblyBuilder.dll is loaded
The PR is designed to fail gracefully, so if the C# loader has issues, it will automatically use the Python loader.

@jmcouffin
Copy link
Contributor

That's a lot of commits! Impressed. @romangolev
What about we make it a specific branch and worklof to build wip-new-loader installers for general users to try it out onced it is ready?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants