Potential Rocket Mode Bug: IExternalEventHandler loses locals/scope/imports

The purpose of this post is so I can decide if I should create an issue for this on Github. The problem is that I’m not sure what is the expected behavior between Rocket Mode and IExternalEventHandler, so I don’t know if it is indeed an issue with pyRevit, or with my code.

When I use a modeless form that triggers external events, the event handler loses references to all functions and imports, thus a NameError is thrown for any function or import called. This only happens while Rocket Mode is enabled in pyRevit 6.5.3. The same exact code was working with previous versions of pyRevit, so I’m thinking that this is a bug.

Running the script with a clean engine fixes the NameErrors, but obviously, this defeats the purpose of Rocket Mode.

To repeat the question: Is it expected that Rocket Mode will cause an IExternalEventHandler to lose all defined names/scope/functions/variables?

Does anyone know off the top of their head?

P.S. I did not share any of my code because it would not illustrate anything.

Enable Rocket Mode > Use IExternalEventHandler > Calling any function or import from the event handler’s Execute() method triggers NameError.

That’s all there is to it. The code works without Rocket Mode, and it used to work with previous versions of pyRevit.

@romangolev could you take a look?

Here is a minimum reproducible example. Throw these files in a pushbutton and you should get inexplicable name errors:

script.py:

# Dependencies
import clr
clr.AddReference('System.Windows.Forms')
clr.AddReference('PresentationCore')

# Import from pyrevit
from pyrevit       import script
from pyrevit.forms import WPFWindow
from pyrevit.revit import Transaction as pyrevit_transaction, TransactionGroup
xamlfile = script.get_bundle_file('ui.xaml') # find the path of ui.xaml

# Import WPF creator and base Window
from System import EventHandler as WinEventHandler

# Import python system module
import sys
from collections import namedtuple

# Import Revit API objects
from Autodesk.Revit.DB import (ElementId, FilteredElementCollector, BuiltInCategory,
                               BuiltInParameter, Element, RevitLinkInstance, ViewPlan, UV, LinkElementId,
                               ParameterFilterRuleFactory, ElementParameterFilter, ElementCategoryFilter,
                               RevitLinkType, XYZ, ElementTransformUtils,
                               GeometryCreationUtilities, SolidUtils, BooleanOperationsUtils,
                               BooleanOperationsType, Options, PlanViewPlane,
                               ViewType, SubTransaction)

from Autodesk.Revit.DB.Architecture import RoomTag, Room
from Autodesk.Revit.UI              import IExternalEventHandler, ExternalEvent
from Autodesk.Revit.UI.Events       import DialogBoxShowingEventArgs, TaskDialogShowingEventArgs, ViewActivatedEventArgs

# Import .NET List
from System.Collections.Generic     import List

# Setup doc
uiapp = __revit__
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document

# Classes
class EventHandler(IExternalEventHandler):
	def __init__(self, func_to_run):
		self.func = func_to_run
	
	def Execute(self, uiapp):
		try:
			self.func()
		except Exception as ex:
			import traceback
			print traceback.format_exc() # Prints the stack traceback

	def GetName(self):
		return "simple function executed by an IExternalEventHandler in a Form"

# WPF form used to call the ExternalEvents
class ModelessForm(WPFWindow):
	def __init__(self, xaml_file_name):
		WPFWindow.__init__(self, xaml_file_name)
		
		self.recenter_all_tags_in_view_handler = EventHandler(self.recenter_all_tags_in_view)
		self.recenter_all_tags_in_view_event_instance = ExternalEvent.Create(self.recenter_all_tags_in_view_handler)
		
		# Show modeless window
		self.show_self()
	
	
	# Properties
	@property
	def active_view(self):
		return uidoc.ActiveView
	
	@property
	def room_tags_in_model(self):
		result = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_RoomTags).WhereElementIsNotElementType()
		if result: return result
		else: pass
	
	@property
	def room_tags_in_view(self):
		result = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_RoomTags).WhereElementIsNotElementType().OwnedByView(self.active_view.Id)
		return result
	
	@property
	def misplaced_room_tags_in_model(self):
		result = [room_tag for room_tag in self.room_tags_in_model if not room_tag.IsInRoom]
		return result
	
	@property
	def misplaced_in_view(self):
		result = [room_tag for room_tag in self.room_tags_in_view if not room_tag.IsInRoom]
		return result
	
	# Methods
	def show_self(self):
		self.Show()
	
	# Methods to call via external event
	def recenter_all_tags_in_view(self):
		misplaced_in_view = self.misplaced_in_view
		print(misplaced_in_view)

	
	# Events to call from xaml UI
	def unsubscribe_event(self, sender, args):
		self.unsubscribe_event_instance.Raise()

	def recenter_all_tags_in_view_event(self, sender, args):
		self.recenter_all_tags_in_view_event_instance.Raise()

# Instantiate the form (entry point)
if __name__ == '__main__':
	modeless_form = ModelessForm("ui.xaml")

ui.xaml:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:av="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib" mc:Ignorable="av"
		Title="Fix Room Tags" ResizeMode="NoResize" SizeToContent="WidthAndHeight" Background="WhiteSmoke" BorderBrush="#FF89AF98" Topmost="True"
        Closing="unsubscribe_event">
	<StackPanel x:Name="main_stackpanel" Margin="10">
		<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
			<StackPanel Orientation="Vertical" Margin="0,0,10,0">
                <Button Content="Re-center all misplaced tags in view" Click="recenter_all_tags_in_view_event" HorizontalContentAlignment="Right" Padding="1,1,5,1" />
            </StackPanel>
		</StackPanel>
	</StackPanel>
</Window>

And errors log please
Reading quickly through the code,
Not sure why the CLR imports