This new bpy.types.ID.make_local(clear_proxies=True) allows Python code to press the "Make Local" button on any ID block. I chose clear_proxies=True as the default, since it's the default behaviour of id_make_local() (defined in library.c).
The caller does need to take care of ensuring that linked-in objects don't refer to local data, and that proxies aren't broken. Currently I use Python code like this to iterate over all ID blocks that need to be made local:
def bottom_up_from_idblock(idblock): """Generator, yields datablocks from the bottom (i.e. uses nothing) upward. Stupid in that it doesn't detect cycles yet. :param idblock: the idblock whose users to yield. """ visited = set() def visit(idblock): # Prevent visiting the same idblock multiple times if idblock in visited: return visited.add(idblock) user_map = bpy.data.user_map([idblock]) # There is only one entry here, for the idblock we requested. for user in user_map[idblock]: yield from visit(user) yield idblock yield from visit(idblock) for idblock in bottom_up_from_idblock(idblock_to_make_local): if idblock.library is None: continue idblock.make_local(clear_proxy=False)
@Dalai Felinto (dfelinto) suggested it might be useful to port this to C and have bpy.types.ID.make_local() optionally use it. If anyone knows anything in C (maybe the depsgraph) that can already provide me with this info, without having to call user_map([idblock]) on every iteration, that would be great.