1
$\begingroup$

I'm trying to get the class of the operator I made. The operator's bl_idname is "object.primitive_manipulator" and it's class is Object_OT_Edit_PrimitiveManipulator.

What works (Give the correct operator as type()=bpy.ops.BPyOpsSubModOp)

  • bpy.ops.object.primitive_manipulator works fine.
  • getattr(bpy.ops.object, "primitive_manipulator")

What doesn't work (Gives the wrong class as type()=bpy.ops.BPyOpsSubMod):

  • bpy.types.Object_OT_Edit_PrimitiveManipulator
  • getattr(bpy.ops, "object.primitive_manipulator") (hasattr() returns true but it doesnt show up with dir() on bpy.ops, only object does which is odd)

Both of these last two are the actual methods I need to use to programmatically iterate over an array of operator strings to then get their classes and do stuff (I can use string manipulation but I'm still curious to my below question).

What type is this being returned by the two methods described that won't work, why don't they have any parameters, and what is their purpose?

$\endgroup$
7
$\begingroup$

The actual operator class is only accessible if it's defined in the same script, or imported from another file. It's not a standard Python class however, because it is derived from the Operator type:

import bpy

class PrimitiveManipulatorOperator(bpy.types.Operator):
    bl_idname = "object.primitive_manipulator"
    bl_label = "Primitive Manipulator"

    def execute(self, context):
        return {'FINISHED'}

type(PrimitiveManipulatorOperator) # returns <class 'bpy_types.OrderedMeta'>

Upon class registration of an operator class, it is made available as type as bpy.types.OBJECT_OT_primitive_manipulator (which is the bl_idname transformed from Python to C notation):

def register():
    bpy.utils.register_class(PrimitiveManipulatorOperator)

    # returns <class 'bpy_types.OrderedMeta'>
    print(type(bpy.types.OBJECT_OT_primitive_manipulator))

    # fails, the class name isn't used in case of operators
    print(type(bpy.types.PrimitiveManipulatorOperator))

The interface to call operators is via bpy.ops:

bpy.ops.object.primitive_manipulator()

# returns <class 'bpy.ops.BPyOpsSubModOp'>
type(bpy.ops.object.primitive_manipulator())

What can be confusing about bpy.ops (which is <class 'bpy.ops.BPyOps'>), is that hasattr() will always return True:

hasattr(bpy.ops, "object.primitive_manipulator") # True
hasattr(bpy.ops, "foobar") # also True

This is caused by the design of this module-like class and presumably necessary to make proper calls work.

The correct way to programmatically get a reference to the operator for a call is to getattr() twice, as you want to retrieve a property and its sub-property:

op = getattr(getattr(bpy.ops, "object"), "primitive_manipulator")
op() # equivalent to bpy.ops.object.primitive_manipulator()

getattr(bpy.ops, "object.primitive_manipulator") can't work because there is no property "object.primitive_manipulator", there's only "object", which in turn has the property "primitive_manipulator".

>>> type(getattr(bpy.ops, "object"))
<class 'bpy.ops.BPyOpsSubMod'>

>>> type(getattr(bpy.ops, "foo.bar"))
<class 'bpy.ops.BPyOpsSubMod'>

>>> type(getattr(getattr(bpy.ops, "object"), "primitive_manipulator"))
<class 'bpy.ops.BPyOpsSubModOp'>

You may want to resolve the operator name in a loop like this:

def get_operator(idname):
    op = bpy.ops
    for attr in idname.split("."):
        op = getattr(op, attr)
    return op

get_operator("object.primitive_operator")()

# if there's a poll() classmethod, you can also:
get_operator("object.primitive_operator.poll")()


# return a blank function to suppress AttributeErrors
# in case the operator idname does not exist
def get_operator_failsafe(idname):
    op = bpy.ops
    for attr in idname.split("."):
        if attr not in dir(op):
            return lambda: None
        op = getattr(op, attr)
    return op

get_operator_failsafe("foo.bar")()
| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.