Unclear on regisering and unregistering class

I’m not clear on just how to handle registering and unregistering classes. I’m working with a simple script:

import bpy

class ShrinkageCalculator(bpy.types.Operator):
	bl_idname = "object.dialog_operator"
	bl_label = "Shrinkage Scaler"
	shrink_rate: bpy.props.IntProperty(name="Clay Shrink Rate",
		description="Shrinkage rate for clay body",
		default=10, min=0, max=90)
	def execute(self, context):
		expand_rate = 100 / (100 - self.shrink_rate)
		message = "Using shrinkage rate of %s and expansion rate of %s" %
                     (self.shrink_rate, expand_rate)
		self.report({'INFO'}, message)
		return {'FINISHED'}

	def invoke(self, context, event):
		wm = context.window_manager
		return wm.invoke_props_dialog(self)

# Only needed if you want to add into a dynamic menu.
def menu_func(self, context):
    self.layout.operator(ShrinkageCalculator.bl_idname,
        text=ShrinkageCalculator.bl_label)

def register():
    bpy.utils.register_class(ShrinkageCalculator)
    bpy.types.VIEW3D_MT_object.append(menu_func)
    # bpy.types.VIEW3D_MT_object_context_menu.append(menu_func)

def unregister():
    bpy.utils.unregister_class(ShrinkageCalculator)
    bpy.types.VIEW3D_MT_object.remove(menu_func)
    # bpy.types.VIEW3D_MT_object_context_menu.remove(menu_func)

if __name__ == "__main__":
    register()
    # unregister()

I’m using a lot of copy-and-paste code in here. I have registering working, but I found out that if I add it to the object context menu (that line is commented out), then when I pick it from the context menu, the dialog does not show. But that’s not my biggest problem right now.

In main, when I comment out register() and uncomment unregister() to remove it from my menu and to unregister it so I can run a new version, I am getting this error message:

Screenshot 2025-03-11 at 4.31.26 PM

I’ve removed extra comments and blank lines in here, so line #69 is unregister() (uncommented and register() is commented out so I can unregister it). Line 63 is:

 bpy.utils.unregister_class(ShrinkageCalculator)

in the unregister() function. I don’t have an issue registering it, but now, when I try to unregister the same class (and I changed it from DialogOperator in the demo I was working with to a unique class name that describes what I’m doing), I find I can’t unregister it.

Along this line, once I run this and test it, and want to run it again, without being able to use that unregister routine, it looks like the ony way I can clear the previous attempt out of Blender’s memory is to exit and restart Blender. Is there an alternative? An easy way to clear it out?

I believe it’s the same question as

Yup, unregister in a script run inside Blender itself never gets called. Here’s some other explanations. https://blender.stackexchange.com/a/332133/86891

So something like that.

import bpy

class ShrinkageCalculator(bpy.types.Operator):
    bl_idname = "object.dialog_operator"
    bl_label = "Shrinkage Scaler"
    shrink_rate: bpy.props.IntProperty(name="Clay Shrink Rate",
        description="Shrinkage rate for clay body",
        default=10, min=0, max=90)
    def execute(self, context):
        expand_rate = 100 / (100 - self.shrink_rate)
        message = "Using shrinkage rate of %s and expansion rate of %s" %                     (self.shrink_rate, expand_rate)
        self.report({'INFO'}, message)
        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self)

# Only needed if you want to add into a dynamic menu.
def menu_func(self, context):
    self.layout.operator(ShrinkageCalculator.bl_idname,
        text=ShrinkageCalculator.bl_label)

def register():
    bpy.utils.register_class(ShrinkageCalculator)
    bpy.types.VIEW3D_MT_object.append(menu_func)

def unregister():
    for child in bpy.types.Operator.__subclasses__():
        if child.__name__ == ShrinkageCalculator.__name__:
            try:
                bpy.utils.unregister_class(child)
            except RuntimeError:  # This one is not registered
                pass
    
if __name__ == "__main__":
    unregister()
    register()

However I don’t know how to unregister menu_func from bpy.types.VIEW3D_MT_object without a handle on previous function so there is really not much use in doing that. Also you’ll get a bunch of unregister class instances if you run the script multiple times so tiny memory leak warning.