How do you copy an object including all its properties, and make it single user? I specify that I know how to copy an object but I haven't found an answer for some time.
I want to copy an object and its date, then its vertex groups, including all the custom properties of that object, without it being linked to the original object anymore.
First example, copy entire object:
copy_ob = bpy.data.objects['ob'].copy()
bpy.context.scene.collection.objects.link(copy_ob)

Good solution , but have a problem, he object is still linked to the first, and any changes will also occur to the second object and vice versa.
Second example:
#This is a small function that I am using at the moment:
def copy_object(ob,new_name):
    copy_data = ob.data.copy()
    copy_ob = bpy.data.objects.new(new_name,copy_data)
    copy_ob.location = ob.location
    copy_ob.rotation_euler = ob.rotation_euler
    copy_ob.scale = ob.scale
    return copy_ob

new_ob = copy_object(context.object,'New Object')
bpy.context.collection.objects.link(new_ob)

The second solution is proposed many times in many sites, but it presents a very big problem, which hardly anyone takes into account.
Loss of any property related to the first object, loss of positions, locations, rotation, vertex group, and any other property that is not contained in the ob.data.
How can you do to avoid this?
The first solution, if you could make it &quot;Single User&quot; would be the best. But I really can't find an answer about it.
I am trying at all costs to avoid bpy.ops
Definitely:
Thanks to Andrey Sokolov's answer, I have finally come to a conclusion on how to copy an object without it being linked to the First. Here is my little code, with a function that does this copy. I leave it here, so those who need it can easily use it:
import bpy

def copy_object(ob,new_name):
    
    copy_data = ob.data.copy()
    copy_ob = ob.copy()
    copy_ob.data = copy_data
    copy_ob.name = new_name
    copy_ob.data.name = new_name
    return copy_ob


ob = copy_object(bpy.context.object,'New Object')
bpy.context.collection.objects.link(ob) #context collection is the collectionin which you are working
In the first case it is not unlinked because it continues to use the same mesh data block. So all you need to do is copy object, copy its data and assign the new data to the new object like this (with default cube):
import bpy

obj = bpy.data.objects['Cube']

new_mesh = obj.data.copy()
new_obj = obj.copy()
new_obj.data = new_mesh
for col in bpy.data.collections:
    if obj.name in col.objects:
        col.objects.link(new_obj)