In Metal/Vulkan handles are raw memory addresses and should be treated with care as their are no bounds checks on those platforms as they don’t do any runtime validation at all. To get this working on those platforms would also need the source textures to be created with the correct usage flags.
Lets say that those flags are setup correctly by the libraries and tools you are using. A direct copy of pixels isn’t sufficient as the texture format can be different, than requested just based on its usage (void GPU_texture_copy(GPUTexture *dst, GPUTexture *src);
isn’t sufficient). This can be solved by exposing blit functionality. Currently we don’t have an API for this and each backend (OpenGL/Vulkan/Metal) does its own thing.
Wrapping a handle sounds easy, but might need more information how it was created, otherwise internal state of the GPUTexture might not work. For Metal specific blitting process can be done via a compute shader, which would select the correct shader based on some internal state. If this internal state is incorrect it may fail.
Next to that mipmaps needs to be updated, otherwise the rendering will be incorrect when looked at from different distances in EEVEE/Workbench. This exists in C/CPP, but isn’t exposed to the python module.