diff --git a/blender_asset_tracer/blendfile/__init__.py b/blender_asset_tracer/blendfile/__init__.py index 8579d14..4b6cd10 100644 --- a/blender_asset_tracer/blendfile/__init__.py +++ b/blender_asset_tracer/blendfile/__init__.py @@ -206,7 +206,8 @@ class BlendFile: self.log.debug("Copying %s to %s", self.filepath, path) # TODO(Sybren): remove str() calls when targeting Python 3.6+ - shutil.copy(str(self.filepath), str(path)) + # dst needs to be a file and not a directory + shutil.copyfile(str(self.filepath), str(path)) self.fileobj = self._open_file(path, mode=mode) _cache(path, self) diff --git a/blender_asset_tracer/pack/filesystem.py b/blender_asset_tracer/pack/filesystem.py index d64fddf..b2deac7 100644 --- a/blender_asset_tracer/pack/filesystem.py +++ b/blender_asset_tracer/pack/filesystem.py @@ -152,12 +152,12 @@ class FileCopier(transfer.FileTransferer): return True def _move(self, srcpath: pathlib.Path, dstpath: pathlib.Path): - """Low-level file move""" + """Low-level file move.""" shutil.move(str(srcpath), str(dstpath)) def _copy(self, srcpath: pathlib.Path, dstpath: pathlib.Path): - """Low-level file copy""" - shutil.copy2(str(srcpath), str(dstpath)) + """Low-level file copy. dstpath needs to be a file and not a directory.""" + shutil.copyfile(str(srcpath), str(dstpath)) def move(self, srcpath: pathlib.Path, dstpath: pathlib.Path): s_stat = srcpath.stat() diff --git a/tests/test_pack.py b/tests/test_pack.py index f89fa98..11b0660 100644 --- a/tests/test_pack.py +++ b/tests/test_pack.py @@ -1,5 +1,5 @@ import logging -import os +import stat import platform import shutil import tempfile @@ -218,6 +218,39 @@ class PackTest(AbstractPackTest): packer.close() self.assertFalse(packer._rewrite_in.exists()) + def test_execute_rewrite_readonly_files(self): + ppath = self.blendfiles / "subdir" + infile = ppath / "doubly_linked_up.blend" + + # Make the input file read-only for everybody (owner, group, world). + orig_mode = infile.stat().st_mode + infile.chmod(stat.S_IREAD | stat.S_IRGRP | stat.S_IROTH) + try: + packer = pack.Packer(infile, ppath, self.tpath) + packer.strategise() + packer.execute() + finally: + # Restore the original file permissions. + infile.chmod(orig_mode) + + if platform.system() == "Windows": + extpath = PurePosixPath( + "//_outside_project", + self.blendfiles.drive[0], + *self.blendfiles.parts[1:], + ) + else: + extpath = PurePosixPath("//_outside_project", *self.blendfiles.parts[1:]) + extbpath = bpathlib.BlendPath(extpath) + + # Those libraries should be properly rewritten. + bfile = blendfile.open_cached(self.tpath / infile.name, assert_cached=False) + libs = sorted(bfile.code_index[b"LI"]) + self.assertEqual(b"LILib", libs[0].id_name) + self.assertEqual(extbpath / b"linked_cube.blend", libs[0][b"name"]) + self.assertEqual(b"LILib.002", libs[1].id_name) + self.assertEqual(extbpath / b"material_textures.blend", libs[1][b"name"]) + @unittest.skipIf( platform.system() == "Windows", "Symlinks on Windows require Administrator rights",