Script Randomly crashes Blender I need to automate the process of joining similar objects from an imported .IFC file from Revit, this can take quite long time to sort out manually because this files can contain from 600 to 800 objects. The names have "." and ":" that I can use for separate the strings and join the same name objects.

I´m not a programmer, and I don't have experience with Python, but after some studying I manage to write this little script. 



But I'm kinda lost here because it works fine with small list of objects but it crashes when dealing with longer lists.

In this one it runs ok, at the end all cubes are one object, spheres are other object, etc.



But when I run it against more longer lists of objects, it crashes Blender:



I'm assuming it has something to do when it tries to read objects that are no longer in the list.

Do you know if this assumption is correct? If so could you suggest a way to avoid the crash? Is there a better way to do what I'm trying to do based on the names of the objects?

This is script:

import bpy
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = None

for obs in bpy.context.scene.objects:
    if obs.name in bpy.context.view_layer.objects:     
        if obs.type == 'MESH':
            bpy.ops.object.select_all(action='DESELECT')
            nocolon = obs.name.rsplit(maxsplit=1, sep=':')
            print ('nocolon pass done for ' + nocolon[0])
            tname = nocolon[0].rsplit(maxsplit=1, sep='.')
            print ('nodot pass done for ' + tname[0])
            bpy.ops.object.select_pattern(pattern= tname[0]+'*', case_sensitive=False, extend=True)
            print ('selection pass done for ' + tname[0])
            obs.select_set(True)
            print ('selection set done for ' + tname[0])
            bpy.context.view_layer.objects.active = obs
            print ('set active object done for ' + tname[0])
            bpy.ops.object.join()
            print ('join pass done for ' + tname[0])
            print ('iteration done for ' + tname[0])
            bpy.context.view_layer.objects.active = None

        else:
            print(obs.name + ' is not a MESH.')

    else:
        print('There is no ' + obs.name + ' in view_layer')
        bpy.data.objects.remove(obs)
        print('removed from data')

print('script done')


I'm using Blender 2.81.16a
Using Operator override

Here are a couple of examples.  This one finds all the mesh objects in the scene, sorts them alphabetically by name, then groups them by if they start with the first.

meshobs ['Cone', 'Cone.001', 'Cone.002', 'Cube', 'Cube.001', 'Cube.002', 'Cylinder', 'Cylinder.001', 'Cylinder.002']
Join ['Cone', 'Cone.001', 'Cone.002']
Join ['Cube', 'Cube.001', 'Cube.002']
Join ['Cylinder', 'Cylinder.001', 'Cylinder.002']


This is only a proof of concept and relies on "Cube" being in scene to match "Cube.nnn", add string partitioning etc as required.

The names of the matching objects are sent to the join method, which calls the operator with a context override dictionary, for active object and selected objects.

Note this Uses only the join operator which will greatly reduce the amount of operator calls from question code

Python performance with Blender operators

import bpy

context = bpy.context
scene = context.scene

def join(names):
    obs = list(filter(None, (scene.objects.get(n) for n in names)))
    print(obs)

    obs[0].select_set(True)
    bpy.ops.object.join(
            {"active_object" : obs[0],
             "object" : obs[0],
             "selected_objects" : obs,
             "selected_editable_objects" : obs
             })


meshobs = [o.name for o in scene.objects if o.type == 'MESH']
meshobs.sort()

while meshobs:
    n0 = meshobs[0]
    x = [n for n in meshobs if n.startswith(n0)]
    print("Joining", x)
    join(x)
    meshobs = meshobs[len(x):]


Alternatively using bmesh and  no operators

Can use bmesh to read in first mesh, then convert each other mesh into object space of first, and write back to first, then remove others.

This will keep only the materials of the first, etc etc, and may have to be altered accordingly.

import bpy
import bmesh


context = bpy.context
scene = context.scene

def bmesh_join(names):
    obs = list(filter(None, (scene.objects.get(n) for n in names)))
    print(obs)
    bm = bmesh.new()
    ob = obs.pop(0)
    mwi = ob.matrix_world.inverted()
    me = ob.data
    bm.from_mesh(me)
    for o in obs:
        o.data.transform(mwi @ o.matrix_world)
        bm.from_mesh(o.data)
        bpy.data.objects.remove(o)
    bm.to_mesh(me) 
    bm.free()
    me.update()   

meshobs = [o.name for o in scene.objects if o.type == 'MESH']
meshobs.sort()

while meshobs:
    n0 = meshobs[0] # fix for lpart on . : etc
    x = [n for n in meshobs if n.startswith(n0)]
    print("Join", x)
    bmesh_join(x)
    meshobs = meshobs[len(x):]