diff --git a/doc/python_api/examples/bpy.ops.1.py b/doc/python_api/examples/bpy.ops.1.py index 9b690823a18..1369e5aad5b 100644 --- a/doc/python_api/examples/bpy.ops.1.py +++ b/doc/python_api/examples/bpy.ops.1.py @@ -20,7 +20,7 @@ you would pass ``{'active_object': object}`` to :class:`bpy.types.Context.temp_o # Remove all objects in scene rather than the selected ones. import bpy from bpy import context -override = context.copy() -override["selected_objects"] = list(context.scene.objects) -with context.temp_override(**override): +context_override = context.copy() +context_override["selected_objects"] = list(context.scene.objects) +with context.temp_override(**context_override): bpy.ops.object.delete() diff --git a/doc/python_api/examples/bpy.ops.py b/doc/python_api/examples/bpy.ops.py index aa51c6c5162..c4b04936c87 100644 --- a/doc/python_api/examples/bpy.ops.py +++ b/doc/python_api/examples/bpy.ops.py @@ -27,17 +27,11 @@ Keywords and Positional Arguments For calling operators keywords are used for operator properties and positional arguments are used to define how the operator is called. -There are 3 optional positional arguments (documented in detail below). +There are 2 optional positional arguments (documented in detail below). .. code-block:: python - bpy.ops.test.operator(override_context, execution_context, undo) - -- override_context - ``dict`` type. - - .. deprecated:: 3.2 - - :class:`bpy.types.Context.temp_override` should be used instead of this argument. + bpy.ops.test.operator(execution_context, undo) - execution_context - ``str`` (enum). - undo - ``bool`` type. diff --git a/scripts/modules/bpy/ops.py b/scripts/modules/bpy/ops.py index 4295aa5f237..1e3b1ed2564 100644 --- a/scripts/modules/bpy/ops.py +++ b/scripts/modules/bpy/ops.py @@ -40,19 +40,13 @@ class _BPyOpsSubModOp: @staticmethod def _parse_args(args): - C_dict = None C_exec = 'EXEC_DEFAULT' C_undo = False - is_dict = is_exec = is_undo = False + is_exec = is_undo = False for arg in args: - if is_dict is False and isinstance(arg, dict): - if is_exec is True or is_undo is True: - raise ValueError("dict arg must come first") - C_dict = arg - is_dict = True - elif is_exec is False and isinstance(arg, str): + if is_exec is False and isinstance(arg, str): if is_undo is True: raise ValueError("string arg must come before the boolean") C_exec = arg @@ -61,9 +55,9 @@ class _BPyOpsSubModOp: C_undo = arg is_undo = True else: - raise ValueError("1-3 args execution context is supported") + raise ValueError("1-2 args execution context is supported") - return C_dict, C_exec, C_undo + return C_exec, C_undo @staticmethod def _view_layer_update(context): @@ -83,8 +77,8 @@ class _BPyOpsSubModOp: self._func = func def poll(self, *args): - C_dict, C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args) - return _op_poll(self.idname_py(), C_dict, C_exec) + C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args) + return _op_poll(self.idname_py(), C_exec) def idname(self): # submod.foo -> SUBMOD_OT_foo @@ -107,10 +101,10 @@ class _BPyOpsSubModOp: _BPyOpsSubModOp._view_layer_update(context) if args: - C_dict, C_exec, C_undo = _BPyOpsSubModOp._parse_args(args) - ret = _op_call(self.idname_py(), C_dict, kw, C_exec, C_undo) + C_exec, C_undo = _BPyOpsSubModOp._parse_args(args) + ret = _op_call(self.idname_py(), kw, C_exec, C_undo) else: - ret = _op_call(self.idname_py(), None, kw) + ret = _op_call(self.idname_py(), kw) if 'FINISHED' in ret and context.window_manager == wm: _BPyOpsSubModOp._view_layer_update(context) diff --git a/scripts/startup/bl_operators/object_quick_effects.py b/scripts/startup/bl_operators/object_quick_effects.py index 378e84694b5..6903b550190 100644 --- a/scripts/startup/bl_operators/object_quick_effects.py +++ b/scripts/startup/bl_operators/object_quick_effects.py @@ -256,7 +256,7 @@ class QuickExplode(ObjectModeOperator, Operator): ) def execute(self, context): - fake_context = context.copy() + context_override = context.copy() obj_act = context.active_object if obj_act is None or obj_act.type != 'MESH': @@ -288,8 +288,9 @@ class QuickExplode(ObjectModeOperator, Operator): to_obj = mesh_objects[0] for obj in mesh_objects: - fake_context["object"] = obj - bpy.ops.object.particle_system_add(fake_context) + context_override["object"] = obj + with context.temp_override(**context_override): + bpy.ops.object.particle_system_add() settings = obj.particle_systems[-1].settings settings.count = self.amount @@ -370,9 +371,10 @@ class QuickExplode(ObjectModeOperator, Operator): psys = obj.particle_systems[-1] - fake_context["particle_system"] = obj.particle_systems[-1] - bpy.ops.particle.new_target(fake_context) - bpy.ops.particle.new_target(fake_context) + context_override["particle_system"] = obj.particle_systems[-1] + with context.temp_override(**context_override): + bpy.ops.particle.new_target() + bpy.ops.particle.new_target() if obj == from_obj: psys.targets[1].object = to_obj @@ -436,7 +438,7 @@ class QuickSmoke(ObjectModeOperator, Operator): self.report({'ERROR'}, "Built without Fluid modifier") return {'CANCELLED'} - fake_context = context.copy() + context_override = context.copy() mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH'] min_co = Vector((100000.0, 100000.0, 100000.0)) @@ -447,9 +449,10 @@ class QuickSmoke(ObjectModeOperator, Operator): return {'CANCELLED'} for obj in mesh_objects: - fake_context["object"] = obj + context_override["object"] = obj # make each selected object a smoke flow - bpy.ops.object.modifier_add(fake_context, type='FLUID') + with context.temp_override(**context_override): + bpy.ops.object.modifier_add(type='FLUID') obj.modifiers[-1].fluid_type = 'FLOW' # set type @@ -541,7 +544,7 @@ class QuickLiquid(Operator): self.report({'ERROR'}, "Built without Fluid modifier") return {'CANCELLED'} - fake_context = context.copy() + context_override = context.copy() mesh_objects = [obj for obj in context.selected_objects if obj.type == 'MESH'] min_co = Vector((100000.0, 100000.0, 100000.0)) @@ -559,9 +562,10 @@ class QuickLiquid(Operator): space.shading.type = 'WIREFRAME' for obj in mesh_objects: - fake_context["object"] = obj + context_override["object"] = obj # make each selected object a liquid flow - bpy.ops.object.modifier_add(fake_context, type='FLUID') + with context.temp_override(**context_override): + bpy.ops.object.modifier_add(type='FLUID') obj.modifiers[-1].fluid_type = 'FLOW' # set type diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 585e8902a67..c9b20561627 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -60,29 +60,10 @@ static wmOperatorType *ot_lookup_from_py_string(PyObject *value, const char *py_ return ot; } -static void op_context_override_deprecated_warning(const char *action, const char *opname) -{ - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - /* Use stack level 2 as this call is wrapped by `scripts/modules/bpy/ops.py`, - * An extra stack level is needed to show the warning in the authors script. */ - 2, - "Passing in context overrides is deprecated in favor of " - "Context.temp_override(..), %s \"%s\"", - action, - opname) < 0) - { - /* The function has no return value, the exception cannot - * be reported to the caller, so just log it. */ - PyErr_WriteUnraisable(NULL); - } -} - static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) { wmOperatorType *ot; const char *opname; - PyObject *context_dict = NULL; /* optional args */ const char *context_str = NULL; PyObject *ret; @@ -97,7 +78,7 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) return NULL; } - if (!PyArg_ParseTuple(args, "s|Os:_bpy.ops.poll", &opname, &context_dict, &context_str)) { + if (!PyArg_ParseTuple(args, "s|s:_bpy.ops.poll", &opname, &context_str)) { return NULL; } @@ -128,41 +109,9 @@ static PyObject *pyop_poll(PyObject *UNUSED(self), PyObject *args) context = context_int; } - if (ELEM(context_dict, NULL, Py_None)) { - context_dict = NULL; - } - else if (PyDict_Check(context_dict)) { - op_context_override_deprecated_warning("polling", opname); - } - else { - PyErr_Format(PyExc_TypeError, - "Calling operator \"bpy.ops.%s.poll\" error, " - "custom context expected a dict or None, got a %.200s", - opname, - Py_TYPE(context_dict)->tp_name); - return NULL; - } - - struct bContext_PyState context_py_state; - if (context_dict != NULL) { - CTX_py_state_push(C, &context_py_state, (void *)context_dict); - Py_INCREF(context_dict); /* so we don't lose it */ - } - /* main purpose of this function */ ret = WM_operator_poll_context((bContext *)C, ot, context) ? Py_True : Py_False; - if (context_dict != NULL) { - PyObject *context_dict_test = CTX_py_dict_get(C); - if (context_dict_test != context_dict) { - Py_DECREF(context_dict_test); - } - /* Restore with original context dict, - * probably NULL but need this for nested operator calls. */ - Py_DECREF(context_dict); - CTX_py_state_pop(C, &context_py_state); - } - return Py_INCREF_RET(ret); } @@ -175,8 +124,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) const char *opname; const char *context_str = NULL; - PyObject *kw = NULL; /* optional args */ - PyObject *context_dict = NULL; /* optional args */ + PyObject *kw = NULL; /* optional args */ wmOperatorCallContext context = WM_OP_EXEC_DEFAULT; int is_undo = false; @@ -190,14 +138,8 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - if (!PyArg_ParseTuple(args, - "sO|O!si:_bpy.ops.call", - &opname, - &context_dict, - &PyDict_Type, - &kw, - &context_str, - &is_undo)) + if (!PyArg_ParseTuple( + args, "s|O!si:_bpy.ops.call", &opname, &PyDict_Type, &kw, &context_str, &is_undo)) { return NULL; } @@ -237,32 +179,6 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) context = context_int; } - if (ELEM(context_dict, NULL, Py_None)) { - context_dict = NULL; - } - else if (PyDict_Check(context_dict)) { - op_context_override_deprecated_warning("calling", opname); - } - else { - PyErr_Format(PyExc_TypeError, - "Calling operator \"bpy.ops.%s\" error, " - "custom context expected a dict or None, got a %.200s", - opname, - Py_TYPE(context_dict)->tp_name); - return NULL; - } - - /** - * It might be that there is already a Python context override. We don't want to remove that - * except when this operator call sets a new override explicitly. This is necessary so that - * called operator runs in the same context as the calling code by default. - */ - struct bContext_PyState context_py_state; - if (context_dict != NULL) { - CTX_py_state_push(C, &context_py_state, (void *)context_dict); - Py_INCREF(context_dict); /* so we don't lose it */ - } - if (WM_operator_poll_context((bContext *)C, ot, context) == false) { bool msg_free = false; const char *msg = CTX_wm_operator_poll_msg_get(C, &msg_free); @@ -344,17 +260,6 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) #endif } - if (context_dict != NULL) { - PyObject *context_dict_test = CTX_py_dict_get(C); - if (context_dict_test != context_dict) { - Py_DECREF(context_dict_test); - } - /* Restore with original context dict, - * probably NULL but need this for nested operator calls. */ - Py_DECREF(context_dict); - CTX_py_state_pop(C, &context_py_state); - } - if (error_val == -1) { return NULL; } diff --git a/tests/python/bl_animation_fcurves.py b/tests/python/bl_animation_fcurves.py index 931db3f2d22..b7911a3705d 100644 --- a/tests/python/bl_animation_fcurves.py +++ b/tests/python/bl_animation_fcurves.py @@ -80,7 +80,8 @@ class EulerFilterTest(AbstractAnimationTest, unittest.TestCase): self.assertEqualAngle(111.422, fcu_rot[1], 23) self.assertEqualAngle(76.5996, fcu_rot[2], 23) - bpy.ops.graph.euler_filter(self.get_context()) + with bpy.context.temp_override(**self.get_context()): + bpy.ops.graph.euler_filter() # Keyframes before the "jump". These shouldn't be touched by the filter. self.assertEqualAngle(-87.5742, fcu_rot[0], 22) @@ -105,7 +106,8 @@ class EulerFilterTest(AbstractAnimationTest, unittest.TestCase): self.assertEqualAngle(720, fcu_rot[0], 16) self.assertEqualAngle(72, fcu_rot[1], 22) - bpy.ops.graph.euler_filter(self.get_context()) + with bpy.context.temp_override(**self.get_context()): + bpy.ops.graph.euler_filter() # Keyframes before the "jump". These shouldn't be touched by the filter. self.assertEqualAngle(360, fcu_rot[0], 15) diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py index dc4f06d4799..e02f4b62311 100644 --- a/tests/python/bl_constraints.py +++ b/tests/python/bl_constraints.py @@ -122,8 +122,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.object.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.object.owner') - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') + context_override = self.constraint_context('Child Of', owner_name='Child Of.object.owner') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', Matrix(( (0.9992385506629944, 0.019844001159071922, -0.03359175845980644, 0.10000011324882507), (-0.01744179055094719, 0.997369647026062, 0.07035345584154129, 0.1999998837709427), @@ -131,7 +132,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', initial_matrix) def test_object_rotation_only(self): @@ -149,8 +151,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.object.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.object.owner') - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') + context_override = self.constraint_context('Child Of', owner_name='Child Of.object.owner') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', Matrix(( (0.9992386102676392, 0.019843975082039833, -0.033591702580451965, 0.10000000149011612), (-0.017441781237721443, 0.9973695874214172, 0.0703534483909607, 0.20000000298023224), @@ -158,7 +161,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', initial_matrix) def test_object_no_x_axis(self): @@ -177,8 +181,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.object.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.object.owner',) - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') + context_override = self.constraint_context('Child Of', owner_name='Child Of.object.owner',) + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', Matrix(( (0.9992386102676392, 0.019843991845846176, -0.03359176218509674, 0.10000000149011612), (-0.017441775649785995, 0.997369647026062, 0.0703534483909607, 0.2000001221895218), @@ -186,7 +191,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of') self.matrix_test('Child Of.object.owner', initial_matrix) def test_bone_simple_parent(self): @@ -199,8 +205,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.armature.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.armature.owner') - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') + context_override = self.constraint_context('Child Of', owner_name='Child Of.armature.owner') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of') self.matrix_test('Child Of.armature.owner', Matrix(( (0.9992386102676392, 0.019843988120555878, -0.03359176218509674, 0.8358089923858643), @@ -209,7 +216,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of') self.matrix_test('Child Of.armature.owner', initial_matrix) def test_bone_owner(self): @@ -222,8 +230,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) - context = self.bone_constraint_context('Child Of', owner_name='Child Of.bone.owner') - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of', owner='BONE') + context_override = self.bone_constraint_context('Child Of', owner_name='Child Of.bone.owner') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of', owner='BONE') self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', Matrix(( (0.9659260511398315, 0.2588191032409668, 4.656613428188905e-10, -2.999999761581421), @@ -232,7 +241,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of', owner='BONE') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of', owner='BONE') self.bone_matrix_test('Child Of.bone.owner', 'Child Of.bone', initial_matrix) def test_vertexgroup_simple_parent(self): @@ -245,8 +255,9 @@ class ChildOfTest(AbstractConstraintTests): )) self.matrix_test('Child Of.vertexgroup.owner', initial_matrix) - context = self.constraint_context('Child Of', owner_name='Child Of.vertexgroup.owner') - bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of') + context_override = self.constraint_context('Child Of', owner_name='Child Of.vertexgroup.owner') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_set_inverse(constraint='Child Of') self.matrix_test('Child Of.vertexgroup.owner', Matrix(( (0.9992386102676392, 0.019843988120555878, -0.03359176218509674, 0.10000000149011612), @@ -255,7 +266,8 @@ class ChildOfTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.childof_clear_inverse(constraint='Child Of') self.matrix_test('Child Of.vertexgroup.owner', initial_matrix) @@ -272,8 +284,9 @@ class ObjectSolverTest(AbstractConstraintTests): )) self.matrix_test('Object Solver.owner', initial_matrix) - context = self.constraint_context('Object Solver') - bpy.ops.constraint.objectsolver_set_inverse(context, constraint='Object Solver') + context_override = self.constraint_context('Object Solver') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.objectsolver_set_inverse(constraint='Object Solver') self.matrix_test('Object Solver.owner', Matrix(( (0.9992387294769287, 0.019843989983201027, -0.03359176591038704, 0.10000025480985641), (-0.017441747710108757, 0.9973697662353516, 0.07035345584154129, 0.1999993920326233), @@ -281,7 +294,8 @@ class ObjectSolverTest(AbstractConstraintTests): (0.0, 0.0, 0.0, 1.0), ))) - bpy.ops.constraint.objectsolver_clear_inverse(context, constraint='Object Solver') + with bpy.context.temp_override(**context_override): + bpy.ops.constraint.objectsolver_clear_inverse(constraint='Object Solver') self.matrix_test('Object Solver.owner', initial_matrix) diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index 1fc685023cb..ba84f86ae87 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -592,15 +592,17 @@ class SpecMeshTest(MeshTest): elif modifier.type == 'CLOTH' or modifier.type == 'SOFT_BODY': test_object.modifiers[test_modifier_name].point_cache.frame_end = frame_end override_setting = modifier.point_cache - override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} - bpy.ops.ptcache.bake(override, bake=True) + context_override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} + with bpy.context.temp_override(**context_override): + bpy.ops.ptcache.bake(bake=True) break elif modifier.type == 'DYNAMIC_PAINT': dynamic_paint_setting = modifier.canvas_settings.canvas_surfaces.active override_setting = dynamic_paint_setting.point_cache - override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} - bpy.ops.ptcache.bake(override, bake=True) + context_override = {'scene': scene, 'active_object': test_object, 'point_cache': override_setting} + with bpy.context.temp_override(**context_override): + bpy.ops.ptcache.bake(bake=True) break def _apply_particle_system(self, test_object, particle_sys_spec: ParticleSystemSpec):