Correct way to override Operator report method?

Hi, I would like to override the report method of my custom operator, mainly to manipulate the message string before calling super().report, but this doesn’t seem to work (my overridden method is never called). Is this supported? If so, how’s to the way to proceed?

I think @sybren may be able to help you with this :slight_smile:

This may seem strange, but it’s hard to diagnose a patient without being allowed to see the patient.

Without having seen any of the code, I would recommend to not override the report() method at all, but to create a new method that calls self.report() with the desired arguments. That way it’s clearer which parameters are required, which manipulation is done, etc.

Agree with @sybren. Supplying an example here would really help.

I recently was looking at this problem, and acheived a solution. You just have to make sure you are subclassing the Operator the right way, and then calling the the new class that is created using the Operator base class. Then default operation needs to be in the base class so that when you super it in the new class you can effectively wrap it.

Sounds complicated, and it is. I actually found my answer was better performed using a decorator. It allows the Operator class to remain untouched except for the addition of the decorator, which if you are in a debug phase and be easily switched off later.

This code allows me to assign the variables used in an operator so a variable that can be read after the operator has completed:

def execute_record(func):
    if not hasattr(bpy.types.Scene, "debug"):
        bpy.types.Scene.debug = {}
    
    def function_wrapper(self, x):
        if not self.bl_idname in bpy.types.Scene.debug:
            bpy.types.Scene.debug[self.bl_idname] = {}
        for key in self.__annotations__.keys():
            bpy.types.Scene.debug[self.bl_idname][key] = getattr(self, key)
        y = func(self, x)
        return y
    return function_wrapper

class IMPORT_OT_xxx(bpy.types.Operator):
    """FIX ME"""

    bl_idname = "import_scene.xxx"
    bl_label = "Import STUFF"
    bl_description = "FIX ME"
    bl_options = {"REGISTER", "UNDO"}

    VAR0 : bpy.props.BoolProperty(
        name="Variable 0",
        description="Set a Boolean value",
        default=False,
    )

    def invoke(self, context, event): 
        raise Exception(self.VAR0)
        wm = context.window_manager
        wm.invoke_props_dialog(self)
        return {"RUNNING_MODAL"}

    @execute_record
    def execute(self, context):
        return {"FINISHED"}

I will make sure to always include a piece of script from now on.

Since I posted this over a month ago I don’t have the actual example anymore, unfortunately. Back then I just went @sybren way, used another method and dismissed the idea of overriding.

It seems possible though, according to @mavek, so I wonder what he means by “subclassing the right way”. I wasn’t doing anything fancy AFAIK, just following the standard, something like this:

class ReportOperator(bpy.types.Operator):
    def execute(self, context):
        self.report({'WARNING'}, "A message")
        return {'FINISHED'}
 
    def report(self, *args, **kwargs):
        # This method never gets called
        my_message = "Overridden message"
        super(ReportOperator, self).report({'INFO'}, my_message)
 
 def register():
     bpy.utils.register_class(ReportOperator)
 
 if __name__ == "__main__":
     register()

But as mentioned in the code, the report method of the ReportOperator class does not get called.

It can very well be that Blender injects some symbols into the instance of the operator class, instead of using only an object-oriented approach. This would simply overwrite your report() function.

Just for code clarity, I wouldn’t override the built-in report() function so that it all of a sudden gets different responsibilities. Instead of just “report this string”, it becomes “pre-process this string AND report it”. These concerns should be separated into different functions, possibly even three of them:

  1. Process a string
  2. Report a string (so this is just the built-in report function)
  3. Use the above two functions to preprocess & then report.

It can very well be that Blender injects some symbols into the instance of the operator class, instead of using only an object-oriented approach. This would simply overwrite your report() function.

If I understand correctly, we really don’t know what’s happening, so we are just assuming this kind of overwrite is not supported? :sweat_smile:

As I haved already mentioned, I’ve ended up circumventing this issue the way @sybren has just explained.

I take it as whenever you try some Python (like in this case, overwriting a method of the class you inherit from) that doesn’t work as expected (the method is not overwritten), you have to assume it’s unsupported unless explicitly mentioned somewhere in the documentation as supported.

In any case, thank you! :+1: