4
$\begingroup$

Let's say I have an otherwise empty file with a single Text object, (imaginatively) named "Text". I want to convert this to a mesh using a Python script (obviously this is a ridiculously simplified example). I have the following code:

import bpy

object = bpy.data.objects['Text']
override = bpy.context.copy()
bpy.ops.object.convert(override, target='MESH')

If I select the Text object myself, then run this script, it works. If I select nothing and run it, it fails (RuntimeError: Operator bpy.ops.object.convert.poll() failed, context is incorrect).

Fine, that's because the context is incomplete. That's fine*, I get it, that's why I was ready with the override.

I inspected the difference between the context with the Text object selected (and script successful) and nothing selected (and script failing) using inspect.getmembers(bpy.context) and found that there were only two differences: selected_objects and selected_editable_objects.

So, I added those to the override, giving me:

import bpy

object = bpy.data.objects['Text']
override = bpy.context.copy()
override['selected_objects'] = [object]
override['selected_editable_objects'] = [object]
bpy.ops.object.convert(override, target='MESH')

As far as I can work out, the context should now be functionally identical, and yet, the behaviour is unchanged. It works only if I select the Text object myself using the UI.

From poking around in the source code, it looks like it only cares about context.active_object, which remained set in both cases - I can add that in too, of course, but to no avail. I have tried adding every combination of object, active_object, selected_objects and selected_editable_objects, and seen no difference in behaviour.

What am I missing?

* well, not fine, exactly - it is painful to attempt to puzzle out what parts of a context are required, given how generic that error message is.

$\endgroup$

2 Answers 2

3
$\begingroup$

Appears to have a glitch, see: #93188.

It appears to work correctly if there is a selected context object, and not otherwise.

Test in python console.

Set font object to convert to ob

>>> ob = C.object
>>> ob
bpy.data.objects['Text']

Change up the context, "Cube" only object selected

>>> C.object
bpy.data.objects['Cube']

>>> C.selected_objects
[bpy.data.objects['Cube']]

All objects in scene

>>> C.scene.objects[:]
[bpy.data.objects['Text'], bpy.data.objects['Cube'], bpy.data.objects['Lamp'], bpy.data.objects['Camera']]

make a real simple override dictionary

>>> c = {}
>>> c['selected_editable_objects'] = ob
>>> c
{'selected_editable_objects': bpy.data.objects['Text']}

Run the operator with the override,

>>> ob.type
'FONT'

>>> with C.temp_override(**c):
...     bpy.ops.object.convert()
...
{'FINISHED'}

>>> ob
bpy.data.objects['Text']

>>> ob.type
'MESH'

Now hit AA in the UI to deselect all. As above the cube is still the context object, however it is not selected,

Trying to run as above with the context object not selected.. bingo "context incorrect error, as in question" Feel it's prob bug report worthy

>>> C.object
bpy.data.objects['Cube']

>>> C.object.select_get()
False

>>> bpy.ops.object.convert(c)
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
  File "/home/batfinger/blender/vers/blender-2.91.2-linux64/2.91/scripts/modules/bpy/ops.py", line 130, in __call__
    ret = _op_call(self.idname_py(), C_dict, kw, C_exec, C_undo)
RuntimeError: Operator bpy.ops.object.convert.poll() failed, context is incorrect

As mentioned in Ron's answer can set the active object with,

>>> C.view_layer.objects.active = ob

and make sure it is selected with

>>> ob.select_set(True)

which kind of defeats the purpose of overriding context if we need to set context... but for now as a work-around as long as the context object is convertable (not an empty for example) not none and selected, can pass a list of font objects to convert via override dictionary. The active object is not converted if not in override list.

$\endgroup$
1
  • $\begingroup$ Whew, so I wasn't going mad; context overrides should allow an op to be effectively stateless. I've only just started to dive into Blender scripting, so seemed safer to assume it was my misunderstanding. Talk about a bad introduction! Still salty about the generic error message! Thanks for the help! $\endgroup$ Commented Aug 22, 2021 at 22:41
2
$\begingroup$

So your question is really, "How do I select an arbitrary object?" Note in the code fragment below I used obj to represent our object to avoid confusion with the object keyword:

import bpy

obj = bpy.data.objects['Text']

bpy.ops.object.mode_set(mode='OBJECT') #make sure you're in object mode
bpy.ops.object.select_all(action='DESELECT') #deselect everything
 
obj.select_set(True) #select the text obj
bpy.context.view_layer.objects.active = obj #make the the active object


override = bpy.context.copy()

# Blender 3.2+
with bpy.context.temp_override(**override):
    bpy.ops.object.convert(target='MESH')

# Blender 2.8+
#bpy.ops.object.convert(override, target='MESH')
$\endgroup$
1
  • $\begingroup$ There is probably no need to both set context and then "override" to same. However setting an object as active and selected appears to be the key. $\endgroup$
    – batFINGER
    Commented Aug 22, 2021 at 10:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .