bpy.ops

  1# SPDX-License-Identifier: GPL-2.0-or-later
  2
  3# for slightly faster access
  4from _bpy import ops as _ops_module
  5
  6# op_add = _ops_module.add
  7_op_dir = _ops_module.dir
  8_op_poll = _ops_module.poll
  9_op_call = _ops_module.call
 10_op_as_string = _ops_module.as_string
 11_op_get_rna_type = _ops_module.get_rna_type
 12_op_get_bl_options = _ops_module.get_bl_options
 13
 14_ModuleType = type(_ops_module)
 15
 16
 17# -----------------------------------------------------------------------------
 18# Callable Operator Wrapper
 19
 20class _BPyOpsSubModOp:
 21    """
 22    Utility class to fake submodule operators.
 23
 24    eg. bpy.ops.object.somefunc
 25    """
 26
 27    __slots__ = ("_module", "_func")
 28
 29    def _get_doc(self):
 30        idname = self.idname()
 31        sig = _op_as_string(self.idname())
 32        # XXX You never quite know what you get from bpy.types,
 33        # with operators... Operator and OperatorProperties
 34        # are shadowing each other, and not in the same way for
 35        # native ops and py ones! See T39158.
 36        # op_class = getattr(bpy.types, idname)
 37        op_class = _op_get_rna_type(idname)
 38        descr = op_class.description
 39        return "%s\n%s" % (sig, descr)
 40
 41    @staticmethod
 42    def _parse_args(args):
 43        C_dict = None
 44        C_exec = 'EXEC_DEFAULT'
 45        C_undo = False
 46
 47        is_dict = is_exec = is_undo = False
 48
 49        for arg in args:
 50            if is_dict is False and isinstance(arg, dict):
 51                if is_exec is True or is_undo is True:
 52                    raise ValueError("dict arg must come first")
 53                C_dict = arg
 54                is_dict = True
 55            elif is_exec is False and isinstance(arg, str):
 56                if is_undo is True:
 57                    raise ValueError("string arg must come before the boolean")
 58                C_exec = arg
 59                is_exec = True
 60            elif is_undo is False and isinstance(arg, int):
 61                C_undo = arg
 62                is_undo = True
 63            else:
 64                raise ValueError("1-3 args execution context is supported")
 65
 66        return C_dict, C_exec, C_undo
 67
 68    @staticmethod
 69    def _view_layer_update(context):
 70        view_layer = context.view_layer
 71        if view_layer:  # None in background mode
 72            view_layer.update()
 73        else:
 74            import bpy
 75            for scene in bpy.data.scenes:
 76                for view_layer in scene.view_layers:
 77                    view_layer.update()
 78
 79    __doc__ = property(_get_doc)
 80
 81    def __init__(self, module, func):
 82        self._module = module
 83        self._func = func
 84
 85    def poll(self, *args):
 86        C_dict, C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args)
 87        return _op_poll(self.idname_py(), C_dict, C_exec)
 88
 89    def idname(self):
 90        # submod.foo -> SUBMOD_OT_foo
 91        return self._module.upper() + "_OT_" + self._func
 92
 93    def idname_py(self):
 94        return self._module + "." + self._func
 95
 96    def __call__(self, *args, **kw):
 97        import bpy
 98        context = bpy.context
 99
100        # Get the operator from blender
101        wm = context.window_manager
102
103        # Run to account for any RNA values the user changes.
104        # NOTE: We only update active view-layer, since that's what
105        # operators are supposed to operate on. There might be some
106        # corner cases when operator need a full scene update though.
107        _BPyOpsSubModOp._view_layer_update(context)
108
109        if args:
110            C_dict, C_exec, C_undo = _BPyOpsSubModOp._parse_args(args)
111            ret = _op_call(self.idname_py(), C_dict, kw, C_exec, C_undo)
112        else:
113            ret = _op_call(self.idname_py(), None, kw)
114
115        if 'FINISHED' in ret and context.window_manager == wm:
116            _BPyOpsSubModOp._view_layer_update(context)
117
118        return ret
119
120    def get_rna_type(self):
121        """Internal function for introspection"""
122        return _op_get_rna_type(self.idname())
123
124    @property
125    def bl_options(self):
126        return _op_get_bl_options(self.idname())
127
128    def __repr__(self):  # useful display, repr(op)
129        return _op_as_string(self.idname())
130
131    def __str__(self):  # used for print(...)
132        return ("<function bpy.ops.%s.%s at 0x%x'>" %
133                (self._module, self._func, id(self)))
134
135
136# -----------------------------------------------------------------------------
137# Sub-Module Access
138
139def _bpy_ops_submodule__getattr__(module, func):
140    # Return a value from `bpy.ops.{module}.{func}`
141    if func.startswith("__"):
142        raise AttributeError(func)
143    return _BPyOpsSubModOp(module, func)
144
145
146def _bpy_ops_submodule__dir__(module):
147    functions = set()
148    module_upper = module.upper()
149
150    for id_name in _op_dir():
151        id_split = id_name.split("_OT_", 1)
152        if len(id_split) == 2 and module_upper == id_split[0]:
153            functions.add(id_split[1])
154
155    return list(functions)
156
157
158def _bpy_ops_submodule(module):
159    result = _ModuleType("bpy.ops." + module)
160    result.__getattr__ = lambda func: _bpy_ops_submodule__getattr__(module, func)
161    result.__dir__ = lambda: _bpy_ops_submodule__dir__(module)
162    return result
163
164
165# -----------------------------------------------------------------------------
166# Module Access
167
168def __getattr__(module):
169    # Return a value from `bpy.ops.{module}`.
170    if module.startswith("__"):
171        raise AttributeError(module)
172    return _bpy_ops_submodule(module)
173
174
175def __dir__():
176    submodules = set()
177    for id_name in _op_dir():
178        id_split = id_name.split("_OT_", 1)
179
180        if len(id_split) == 2:
181            submodules.add(id_split[0].lower())
182        else:
183            submodules.add(id_split[0])
184
185    return list(submodules)