Difference between Viewport context and Render Animation context?

Blender 2.91.0, Linux.

This code, called from the bpy.app.handlers.frame_change_pre handler runs fine in the viewport, even while playing back an animation, but the calls to poll() return False at render time. Any insights are appreciated:

def adjust_hair( obj, hair )
    depsgraph = bpy.context.evaluated_depsgraph_get()
    particle_systems = obj.evaluated_get(depsgraph).particle_systems
    for m, particle in enumerate(particle_systems[0].particles):
        setattr(particle, 'location', hair[m][0])
        for n in range(len(particle.hair_keys)):
            particle.hair_keys[n].co[0] =  hair[m][n][0]
            particle.hair_keys[n].co[1] =  -hair[m][n][2]
            particle.hair_keys[n].co[2] =  hair[m][n][1]
            
    if ( bpy.ops.particle.particle_edit_toggle.poll() ):
        bpy.ops.particle.particle_edit_toggle()
        bpy.ops.particle.particle_edit_toggle()

What happens if you comment the 2 lines out, it runs but the particle keys aren’t actually updated?

What is the error message? (start blender from the command line in linux)

Thanks for responding.

Yes, exactly what you said: No crash, but the keys aren’t updated and the hair looks increasingly corrupted as the emitter moves but the hair keys don’t.

The error message was the typical:

RuntimeError: Operator bpy.ops.particle.particle_edit_toggle.poll() failed, context is incorrect

But Blender legitimately crashes instead of just stopping the script.

Man, I’m not sure. I think that because you are in a full render it’s croaking because you can’t just switch to edit mode like you can in viewport. I guess you’re trying to use the toggles to force the cache to update, maybe you should try other update commands, something like:
bpy.context.view_layer.update()

good luck!

Interesting, update() had precisely the same behavior… works in the viewport, crashes during render.

Thanks for the suggestion.

As a last ditch effort to try to figure out what’s going on , I’m going to get the source and see what poll() is checking for. I can’t believe I’m the first person in the Universe to want to change hair keys.

There’s a member here who goes by @Atom, he knows this stuff really well, but I’m not sure if he’s around. By using his handle (which I just did) he’ll get an alert, he would probably know.

Other thoughts, maybe get the context in the main body and pass it into the function?? I’m guessing now.

Thanks again, @Photox, for responding.

Not sure I follow you… can the handler function not always get the context? Please explain and I’ll try to follow.

I have not built Blender from source yet, but I may just do that for the first time just in order to instrument it a little bit. I think this is the relevant code:

static bool particle_edit_toggle_poll(bContext *C)
{
  Object *ob = CTX_data_active_object(C);

  if (ob == NULL || ob->type != OB_MESH) {
    return 0;
  }
  if (!ob->data || ID_IS_LINKED(ob->data)) {
    return 0;
  }

  return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) ||
          BKE_modifiers_findby_type(ob, eModifierType_Softbody));
}

And so I thought that there was no active object, so I made the emitter the active object, but it still crashed. I don’t understand what the other conditions even are…

I’m just diving into how python handles memory, which is much different than what I normally think of regarding the stack and the heap. I guess I’m wondering if by passing in hair you might be assigning local stack references to the hair values, which are garbage collected when the function goes out of scope. I doubt it, but for troubleshooting just hard code in a float(s) I guess, like .5, anywhere you ave hair[m]…

So, I did end up building Blender from source and running it from inside gdb. Whenever it crashed, there was some thread that was accessing the particle info, strongly suggesting the problem was two unsynchronized threads. Googling better, I found this:

https://docs.blender.org/api/current/bpy.app.handlers.html#note-on-altering-data

And that did the trick.

Thanks for pointing me in the right direction.

Nope. This still causes crashes, just less frequently.
I think this approach is broken by design, and the correct way will be to output the hair as a bphys cache file.