Script crashes Blender - Is there a way to somehow bypass the hidden states set by UI widgets?

Hello there.

I was having a problem with Blender’s hidden states and I figured that in the UI widgets there’s a ‘scene’ context that makes operations work, however, that is causing more problems now, I’ve pinpointed the issue again to hidden states, here’s a piece of code that seems fine but simply crashes blender with no debug info:

import bpy

def selectAndActivate(obj, select_children = False):
    bpy.ops.object.select_all(action='DESELECT')
    obj.select = True
    bpy.context.scene.objects.active = obj
    if select_children:
        bpy.ops.object.select_grouped(extend=True, type='CHILDREN_RECURSIVE')
        
def TestObj():
    selectAndActivate(bpy.context.selected_objects[0])
    bpy.ops.mesh.customdata_custom_splitnormals_clear()
    
class TestItOp(bpy.types.Operator):
    bl_idname = "object.test_selection"
    bl_label = "Test Selection"

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):
        TestObj()
        return {'FINISHED'}
        
class TestPanel(bpy.types.Panel):
    bl_label = "Test It!"
    bl_idname = "TEST_it_panel"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout
        layout.row().operator("object.test_selection")
        
if __name__ == "__main__":
    bpy.utils.register_class(TestItOp)
    bpy.utils.register_class(TestPanel)

Just add the script to the default scene, run it, select the default cube, and in the Scene panel hit the button there. Blender simply crashes and there’s no debug info whatsoever.

When I run that same logic in a normal python script everything works fine, so I’m wondering if there’s a simple way to add a button to the UI that doesn’t mess with these hidden states at all. I just want to run some logic in a convenient way. Perhaps there’s a way to add buttons to some panel that simply execute some scripts, but without breaking them altogether, just the exact same way scripts run when you select them in the text editor and hit “Run Script”, is there a way to do that?

Hey there.
That’s super-bad and fucked up!

Anyways, there’s a hack around it, that I use if I do not feel like writing an operator:
(Ab-)Using the update-function of a bool property

It works like this:
write a function that’ll do what your operator should do
write a second function, that’ll check for a bool value.
If the bool is true, set it to false and return,
If it is false, call your first function
Then define the bool variable you tested for and with the keyword “update=” add the second function.

You just expose the bool-value with layout.prop (I think you have to use “toggle=True” or “emboss=False” to make the checkbox dissapear.
If you click on it, the update-function will reset it.
That will retrigger the update-function, forcing it to call your main function.

Be aware this has some limitations:
It will most likely not appear in your undo-stack (since its no operator)
Your context might differ, forcing you to provide the correct context or data yourself.
Probably some programmers may start to hate you.

If you use it ADD AN EXPLANATORY COMMENT!
WHY did you use it, WHAT does it do, WHY no operator.
And in a week or so, try again with an operator.

Cheers

Hey, so here your crash happens within the customdata_custom_splitnormals_clear() and you are somehow requesting freed data.
First: I think you’ll have to switch to edit mode for that? Don’t know though.
Second: probably you have to update the scene, before doing that.
Third: you may try using Modal instead of execute.

EDIT: FOUND IT!
You are providing the wrong context.
It works perfectly well, if you change the bl_context to data
There’s something like context override. That should do the trick too.

cheers