Using context.temp_override() correctly

Hey guys!
Unfortunately I get the error
Operator bpy.ops.object.convert.poll() failed, context is incorrect
from
bpy.ops.object.convert(target="MESH")
When using context overrides.
So do I have a misconception of what those overrides actually do?
Checking the API docs I cannot spot any mistakes in my code:
https://docs.blender.org/api/current/bpy.types.Context.html

The script should duplicate some objects in a collection and then apply all of their modifiers.
I could use apply_modifiers() but that has other troubles for me, I really want to understand whats wrong with the context overrides.

Thanks in advance for any hints!

import bpy

for collection in [c for c in bpy.data.collections if 'M_' in c.name]:
    for obj in [o.copy() for o in collection.objects if o.type == 'MESH']:
        obj.data = obj.data.copy()
        if obj.animation_data:
            obj.animation_data.action = obj.animation_data.action.copy()
        bpy.context.collection.objects.link(obj)
    
    with bpy.context.temp_override(active_object=obj):
        bpy.ops.object.convert(target="MESH")
2 Likes

each operator uses its own unique poll function whose criteria can vary wildly. you’ve overridden the active object for your temp context, but there’s something else the object.convert op is looking for that is not overridden. unfortunately the only way to know for sure what needs to be overridden is to look at the source for the poll function in question. I have a detailed post on how I go about doing this over on devtalk.

for your situation, looking at the poll function for object.convert, there are several requirements for a successful poll:

  • active object is valid
  • object has valid data
  • object is not in edit mode
  • object data is not linked (must be unique)
  • it’s not in an override library
  • object is in a scene
  • object’s scene is not linked

regarding selected objects- keep in mind that the context structure has many areas for selected objects you have to override in some cases. for example:
object, active_object, selected_objects, selected_editable_objects

the later two being lists of objects. anecdotally I can tell you that overriding the four I just mentioned will work, since I also use object.convert in one of my scripts and those are the ones I had overridden.

3 Likes

Also, why are you trying to convert a duplicated mesh object to type mesh when it already is of type mesh?

object.convert will apply all modifiers when used on a mesh, this is actually how I use it as well

1 Like

To Apply modifiers as stated in the post.

2 Likes
# Evaluated
dg = bpy.context.evaluated_depsgraph_get()
evaled = modified_object.evaluated_get(dg)
result = evaled.data.copy()

Couldn’t you do this instead? I rarely do anything that requires it so perhaps there’s a limitation I’m unaware of. In this case it was to bool something.

1 Like

yeah, technically copying the evaluated mesh would do the same thing- i’ve found that copying large meshes in this way can be a bit crashy though so I use the operator for safety reasons.

edit: another limitation (I had to go back and look at my comments, because I remember running into a few different things with depsgraph)- if you’re trying to write a ‘one size fits all’ apply modifiers function that will work on any type of object (such as Curves for example), you’ll need to use object.convert on anything that is non-mesh since the base object.type will remain a curve and you’ll get an error when you try to assign the copied depsgraph data to the original object. that’s more of an esoteric edge case specific to my usage, but probably worth pointing out in case somebody else runs into the same thing.

2 Likes

You are both my favorite humans today, Im currently looking into both suggestions and will post later again.

I tried the evaluated mesh prior, but didnt got the mesh like you do by doing:

evaled = modified_object.evaluated_get(dg)

I’ll have to make a note of that.
I vaguely recall using singledispatch to handle different types cleanly but anything involving context is rarely necessary for what I do and always a last resort (usually out of stubbornness).

I believe it’s necessary to explicitly copy the evaluated mesh as a reference won’t survive long. Perhaps that’s the issue you ran into previously.

And this is still an understatement as I just found out.

import bpy

ao = bpy.context.active_object.copy()
ao.data = ao.data.copy()
bpy.context.collection.objects.link(ao)
    
depsgraph = bpy.context.evaluated_depsgraph_get()
mesh_eval = ao.evaluated_get(depsgraph)
ao.data = mesh_eval.data.copy()

This is what I got for now, as an example.

Thx @testure and @init_pixel for helping me out!

As this is more of a smart workaround to my initial question, I think the closest answer to the thread title is given by testure, so im gonna mark this response as a solution. Both seem to work!

Sure enough blender crashes if I get the evaluated meshes while they have materials applied.
3.3 and also 3.4

If someone wants to try this out:
evaluated_mesh.blend (122.9 KB)
Just run the script

Smells like a bug. I don’t have a developer environment on this computer, but according to x64dbg Blender is dereferencing a nullpointer in the function library_foreach_ID_link in lib_query.c when an object is linked to the scene.

For now it’s probably best to create a new empty mesh, then use bmesh to join the converted meshes into it, and just not deal with the copied evaluated objects directly.

2 Likes

Agreed, I still created a bugreport, a crash warrants that imho.
Thanks @iceythe !

1 Like

Just to put it out there. Regarding the original question about object.convert - blender devs confirmed that currently it’s not possible to run it by specifying object with temp_override.
More about it here - https://projects.blender.org/blender/blender/issues/93188

Works fine here.

The poll needs an active object, and it must be selected.
The exec method needs a list of selected_editable_bases.

import bpy

bpy.ops.object.metaball_add(location=(-2, 0, 0))
bpy.ops.object.metaball_add(location=(0, 0, 0))
bpy.ops.object.metaball_add(location=(2, 0, 0))

objs = list(bpy.data.objects)

with bpy.context.temp_override(active_object=objs[0], selected_editable_objects=objs):
    objs[0].select_set(True)
    bpy.ops.object.convert(target="MESH")

I don’t think so, objs[0].select_set(True) defeats the whole purpose of the override, which is to not have to worry about object selection or active object. If you remove this line, it will not work anymore.

Changing selection state in a script can have unintended consequences. It will also make Blender unresponsive if you have tens of thousands of objects.

I’m not contesting the bug report. Albeit a missing addendum, just proving there is way to weasel around the incorrect context.