Copy the `Part` to target parent, both of them having the same category.

        .. versionadded:: 2.3

        :param target_parent: `Part` object under which the desired `Part` is copied
        :type target_parent: :class:`Part`
        :param name: how the copied top-level `Part` should be called
        :type name: basestring
        :param include_children: True to copy also the descendants of `Part`.
        :type include_children: bool
        :param include_instances: True to copy also the instances of `Part` to ALL the instances of target_parent.
        :type include_instances: bool
        :returns: copied :class:`Part` model.
        :raises IllegalArgumentError: if part and target_parent have different `Category`
        :raises IllegalArgumentError: if part and target_parent are identical

        Example
        -------
        >>> model_to_copy = client.model(name='Model to be copied')
        >>> bike = client.model('Bike')
        >>> model_to_copy.copy(target_parent=bike, name='Copied model',
        >>>                    include_children=True,
        >>>                    include_instances=True)

def copy(self, target_parent, name=None, include_children=True, include_instances=True):
        """
        Copy the `Part` to target parent, both of them having the same category.

        .. versionadded:: 2.3

        :param target_parent: `Part` object under which the desired `Part` is copied
        :type target_parent: :class:`Part`
        :param name: how the copied top-level `Part` should be called
        :type name: basestring
        :param include_children: True to copy also the descendants of `Part`.
        :type include_children: bool
        :param include_instances: True to copy also the instances of `Part` to ALL the instances of target_parent.
        :type include_instances: bool
        :returns: copied :class:`Part` model.
        :raises IllegalArgumentError: if part and target_parent have different `Category`
        :raises IllegalArgumentError: if part and target_parent are identical

        Example
        -------
        >>> model_to_copy = client.model(name='Model to be copied')
        >>> bike = client.model('Bike')
        >>> model_to_copy.copy(target_parent=bike, name='Copied model',
        >>>                    include_children=True,
        >>>                    include_instances=True)

        """
        if self.category == Category.MODEL and target_parent.category == Category.MODEL:
            # Cannot add a model under an instance or vice versa
            copied_model = relocate_model(part=self, target_parent=target_parent, name=name,
                                          include_children=include_children)
            if include_instances:
                instances_to_be_copied = list(self.instances())
                parent_instances = list(target_parent.instances())
                for parent_instance in parent_instances:
                    for instance in instances_to_be_copied:
                        instance.populate_descendants()
                        move_part_instance(part_instance=instance, target_parent=parent_instance,
                                           part_model=self, name=instance.name, include_children=include_children)
            return copied_model

        elif self.category == Category.INSTANCE and target_parent.category == Category.INSTANCE:
            copied_instance = relocate_instance(part=self, target_parent=target_parent, name=name,
                                                include_children=include_children)
            return copied_instance
        else:
            raise IllegalArgumentError('part "{}" and target parent "{}" must have the same category')