Scripting a new object modifier

Hi all,
I am a novice in Blender although I have experience in Python.
To pierce objects (an operation I use a lot), I usually create an auxiliary object (think of a cylinder), collide it with the target and apply “Add modifier /Boolean/Difference”
According to my terminal, that is:

bpy.context.space_data.context = 'MODIFIER'
bpy.ops.object.modifier_add(type='BOOLEAN')
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects["Punzon"]
bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Boolean")

But when trying to run these lines into a script, it do not works.
Seems the issue is the space_data on the first line.

Any clue to fix that?

If you want to write efficient python scripts, you must try to find low-level API methods to do the same things as operators. At first it is a very good idea to check the info editor to actually see what operators are called, but it is very error-prone since all the operators execution depend on a particular context (in short, where your mouse is hovering at any given time).
If you want to do things with a script in Blender, you want to manipulate data, not operators, which are a level of abstraction upon the data.

TLDR :
This script will add and apply a boolean for each selected object, to the active one.

import bpy

def multi_boolean():
    sel_objs = bpy.context.selected_objects
    obj = bpy.context.active_object
    
    # Make sure we have at least one selected object and one active object of mesh type :
    if not sel_objs or not (obj and obj.type == 'MESH'):
        print("Please select at least two objects and set one as active")
        return

    for sel_obj in sel_objs:
        # Make sure the current object is a mesh object and not the active one : 
        if obj == sel_obj or sel_obj.type != 'MESH':
            continue
        # Add a modifier to the object (context-insensitive - no operator needed)
        new_mod = obj.modifiers.new(type='BOOLEAN', name="boolean")
        new_mod.object = sel_obj
        
        # Here we kind of have to use an operator since your object may have other modifiers
        # That you don't wish to apply.
        # We can't use the evaluated dependency graph :
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=new_mod.name)
        
        # We change the display type so that the holes appear
        # (The boolean objects will still be rendered though)
        sel_obj.display_type = 'WIRE'
        # You can also hide the object :
        # sel_obj.hide_viewport = True
        # sel_obj.hide_render = True
    

if __name__ == '__main__':
    multi_boolean()

BSE.94

Thanks Gorgious for your detailed explanation.
It is very useful for me.
I should definitely study the API in depth. :slightly_smiling_face:

[edited] Trying your script, I got the intersection (not the expected difference).
The reason: I’m running Blender 2.79, and here the modifier.operation is Intersection by default.
I’ve added :

new_mod.operation="DIFFERENCE"

and now the result is what I was looking for.

Thanks again,
Rodolfo