keep hierarchy option
This commit is contained in:
parent
6af9567a44
commit
cbbe8be0d8
21
blender_asset_tracer/cli/pack.py
Normal file → Executable file
21
blender_asset_tracer/cli/pack.py
Normal file → Executable file
@ -88,6 +88,15 @@ def add_parser(subparsers):
|
||||
help="Only pack assets that are referred to with a relative path (e.g. "
|
||||
"starting with `//`.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--keep-hierarchy",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Preserve the full filesystem directory hierarchy in the pack. "
|
||||
"All files (including the blend file) are placed at their absolute "
|
||||
"path structure under the target directory. Paths in blend files are "
|
||||
"rewritten to relative paths within this structure.",
|
||||
)
|
||||
|
||||
|
||||
def cli_pack(args):
|
||||
@ -119,6 +128,9 @@ def create_packer(
|
||||
if args.relative_only:
|
||||
raise ValueError("S3 uploader does not support the --relative-only option")
|
||||
|
||||
if args.keep_hierarchy:
|
||||
raise ValueError("S3 uploader does not support the --keep-hierarchy option")
|
||||
|
||||
packer = create_s3packer(bpath, ppath, pathlib.PurePosixPath(target))
|
||||
|
||||
elif (
|
||||
@ -137,6 +149,11 @@ def create_packer(
|
||||
"Shaman uploader does not support the --relative-only option"
|
||||
)
|
||||
|
||||
if args.keep_hierarchy:
|
||||
raise ValueError(
|
||||
"Shaman uploader does not support the --keep-hierarchy option"
|
||||
)
|
||||
|
||||
packer = create_shamanpacker(bpath, ppath, target)
|
||||
|
||||
elif target.lower().endswith(".zip"):
|
||||
@ -146,7 +163,8 @@ def create_packer(
|
||||
raise ValueError("ZIP packer does not support on-the-fly compression")
|
||||
|
||||
packer = zipped.ZipPacker(
|
||||
bpath, ppath, target, noop=args.noop, relative_only=args.relative_only
|
||||
bpath, ppath, target, noop=args.noop, relative_only=args.relative_only,
|
||||
keep_hierarchy=args.keep_hierarchy,
|
||||
)
|
||||
else:
|
||||
packer = pack.Packer(
|
||||
@ -156,6 +174,7 @@ def create_packer(
|
||||
noop=args.noop,
|
||||
compress=args.compress,
|
||||
relative_only=args.relative_only,
|
||||
keep_hierarchy=args.keep_hierarchy,
|
||||
)
|
||||
|
||||
if args.exclude:
|
||||
|
||||
11
blender_asset_tracer/pack/__init__.py
Normal file → Executable file
11
blender_asset_tracer/pack/__init__.py
Normal file → Executable file
@ -103,6 +103,7 @@ class Packer:
|
||||
noop=False,
|
||||
compress=False,
|
||||
relative_only=False,
|
||||
keep_hierarchy=False,
|
||||
) -> None:
|
||||
self.blendfile = bfile
|
||||
self.project = project
|
||||
@ -111,6 +112,7 @@ class Packer:
|
||||
self.noop = noop
|
||||
self.compress = compress
|
||||
self.relative_only = relative_only
|
||||
self.keep_hierarchy = keep_hierarchy
|
||||
self._aborted = threading.Event()
|
||||
self._abort_lock = threading.RLock()
|
||||
self._abort_reason = ""
|
||||
@ -241,6 +243,9 @@ class Packer:
|
||||
# network shares mapped to Windows drive letters back to their UNC
|
||||
# notation. Only resolving one but not the other (which can happen
|
||||
# with the abosolute() call above) can cause errors.
|
||||
if self.keep_hierarchy:
|
||||
bfile_pp = self._target_path / bpathlib.strip_root(bfile_path)
|
||||
else:
|
||||
bfile_pp = self._target_path / bfile_path.relative_to(
|
||||
bpathlib.make_absolute(self.project)
|
||||
)
|
||||
@ -335,6 +340,9 @@ class Packer:
|
||||
self._new_location_paths.add(asset_path)
|
||||
else:
|
||||
log.debug("%s can keep using %s", bfile_path, usage.asset_path)
|
||||
if self.keep_hierarchy:
|
||||
asset_pp = self._target_path / bpathlib.strip_root(asset_path)
|
||||
else:
|
||||
asset_pp = self._target_path / asset_path.relative_to(self.project)
|
||||
act.new_path = asset_pp
|
||||
|
||||
@ -346,6 +354,9 @@ class Packer:
|
||||
assert isinstance(act, AssetAction)
|
||||
|
||||
relpath = bpathlib.strip_root(path)
|
||||
if self.keep_hierarchy:
|
||||
act.new_path = pathlib.Path(self._target_path, relpath)
|
||||
else:
|
||||
act.new_path = pathlib.Path(self._target_path, "_outside_project", relpath)
|
||||
|
||||
def _group_rewrites(self) -> None:
|
||||
|
||||
146
tests/test_pack.py
Normal file → Executable file
146
tests/test_pack.py
Normal file → Executable file
@ -694,6 +694,152 @@ class ProgressTest(AbstractPackTest):
|
||||
)
|
||||
|
||||
|
||||
class KeepHierarchyPackTest(AbstractPackTest):
|
||||
def hierarchy_path(self, filepath) -> Path:
|
||||
"""Return the keep-hierarchy path for a file: target / strip_root(abs_path)."""
|
||||
return Path(self.tpath, bpathlib.strip_root(filepath))
|
||||
|
||||
def test_strategise_keep_hierarchy_no_rewrite(self):
|
||||
"""When all deps are in-project with relative paths, no rewriting is needed."""
|
||||
infile = self.blendfiles / "doubly_linked.blend"
|
||||
|
||||
packer = pack.Packer(
|
||||
infile, self.blendfiles, self.tpath, keep_hierarchy=True
|
||||
)
|
||||
packer.strategise()
|
||||
|
||||
packed_files = (
|
||||
"doubly_linked.blend",
|
||||
"linked_cube.blend",
|
||||
"basic_file.blend",
|
||||
"material_textures.blend",
|
||||
"textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
"textures/Bricks/brick_dotted_04-color.jpg",
|
||||
)
|
||||
for pf in packed_files:
|
||||
path = self.blendfiles / pf
|
||||
act = packer._actions[path]
|
||||
self.assertEqual(
|
||||
pack.PathAction.KEEP_PATH, act.path_action, "for %s" % pf
|
||||
)
|
||||
# In keep_hierarchy mode, paths use strip_root(abs_path) instead of
|
||||
# relative_to(project).
|
||||
self.assertEqual(
|
||||
self.hierarchy_path(path), act.new_path, "for %s" % pf
|
||||
)
|
||||
|
||||
self.assertEqual({}, self.rewrites(packer))
|
||||
self.assertEqual(len(packed_files), len(packer._actions))
|
||||
|
||||
def test_strategise_keep_hierarchy_rewrite(self):
|
||||
"""Deps outside the project go to target/strip_root(path), not _outside_project/."""
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up.blend"
|
||||
|
||||
packer = pack.Packer(infile, ppath, self.tpath, keep_hierarchy=True)
|
||||
packer.strategise()
|
||||
|
||||
# The blendfile itself should be at target / strip_root(abs_path)
|
||||
act = packer._actions[infile]
|
||||
self.assertEqual(pack.PathAction.KEEP_PATH, act.path_action)
|
||||
self.assertEqual(self.hierarchy_path(infile), act.new_path)
|
||||
|
||||
# External files should NOT be under _outside_project/
|
||||
external_files = (
|
||||
"linked_cube.blend",
|
||||
"basic_file.blend",
|
||||
"material_textures.blend",
|
||||
"textures/Bricks/brick_dotted_04-bump.jpg",
|
||||
"textures/Bricks/brick_dotted_04-color.jpg",
|
||||
)
|
||||
for fn in external_files:
|
||||
path = self.blendfiles / fn
|
||||
act = packer._actions[path]
|
||||
self.assertEqual(
|
||||
pack.PathAction.FIND_NEW_LOCATION, act.path_action, "for %s" % fn
|
||||
)
|
||||
# Should be at target / strip_root(abs_path), NOT target/_outside_project/...
|
||||
expected = self.hierarchy_path(path)
|
||||
self.assertEqual(
|
||||
expected,
|
||||
act.new_path,
|
||||
f"\nEXPECT: {expected}\nACTUAL: {act.new_path}\nfor {fn}",
|
||||
)
|
||||
|
||||
# There should be no _outside_project in any new_path
|
||||
for path, action in packer._actions.items():
|
||||
self.assertNotIn(
|
||||
"_outside_project",
|
||||
str(action.new_path),
|
||||
f"_outside_project should not appear in keep_hierarchy mode for {path}",
|
||||
)
|
||||
|
||||
def test_execute_keep_hierarchy(self):
|
||||
"""Verify files are copied to correct hierarchy and paths are rewritten."""
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up.blend"
|
||||
|
||||
with pack.Packer(infile, ppath, self.tpath, keep_hierarchy=True) as packer:
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
# The blendfile should be at its hierarchy position
|
||||
packed_blend = self.hierarchy_path(infile)
|
||||
self.assertTrue(packed_blend.exists(), "Blendfile should be in hierarchy")
|
||||
|
||||
# There should be NO _outside_project directory
|
||||
self.assertFalse(
|
||||
(self.tpath / "_outside_project").exists(),
|
||||
"_outside_project should not exist in keep_hierarchy mode",
|
||||
)
|
||||
|
||||
# Dependencies should be at their hierarchy positions
|
||||
for fn in ("linked_cube.blend", "basic_file.blend", "material_textures.blend"):
|
||||
dep_path = self.hierarchy_path(self.blendfiles / fn)
|
||||
self.assertTrue(dep_path.exists(), "%s should be in hierarchy" % fn)
|
||||
|
||||
# Verify paths were rewritten correctly in the packed blend file.
|
||||
# The rewritten paths should be relative from the packed blend to
|
||||
# the packed dependencies.
|
||||
bfile = blendfile.open_cached(packed_blend, assert_cached=False)
|
||||
libs = sorted(bfile.code_index[b"LI"])
|
||||
|
||||
# Since keep_hierarchy preserves relative positions, the relative paths
|
||||
# from subdir/doubly_linked_up.blend to the parent blendfiles should
|
||||
# be the same as the originals (//../linked_cube.blend etc.)
|
||||
self.assertEqual(b"LILib", libs[0].id_name)
|
||||
self.assertEqual(b"//../linked_cube.blend", libs[0][b"name"])
|
||||
self.assertEqual(b"LILib.002", libs[1].id_name)
|
||||
self.assertEqual(b"//../material_textures.blend", libs[1][b"name"])
|
||||
|
||||
def test_execute_keep_hierarchy_no_touch_origs(self):
|
||||
"""Original files should not be modified."""
|
||||
ppath = self.blendfiles / "subdir"
|
||||
infile = ppath / "doubly_linked_up.blend"
|
||||
|
||||
with pack.Packer(infile, ppath, self.tpath, keep_hierarchy=True) as packer:
|
||||
packer.strategise()
|
||||
packer.execute()
|
||||
|
||||
# The original file shouldn't be touched.
|
||||
bfile = blendfile.open_cached(infile, assert_cached=False)
|
||||
libs = sorted(bfile.code_index[b"LI"])
|
||||
self.assertEqual(b"LILib", libs[0].id_name)
|
||||
self.assertEqual(b"//../linked_cube.blend", libs[0][b"name"])
|
||||
self.assertEqual(b"LILib.002", libs[1].id_name)
|
||||
self.assertEqual(b"//../material_textures.blend", libs[1][b"name"])
|
||||
|
||||
def test_keep_hierarchy_output_path(self):
|
||||
"""output_path should use the full hierarchy path."""
|
||||
infile = self.blendfiles / "basic_file.blend"
|
||||
packer = pack.Packer(
|
||||
infile, self.blendfiles, self.tpath, keep_hierarchy=True
|
||||
)
|
||||
packer.strategise()
|
||||
|
||||
self.assertEqual(self.hierarchy_path(infile), packer.output_path)
|
||||
|
||||
|
||||
class AbortTest(AbstractPackTest):
|
||||
def test_abort_strategise(self):
|
||||
infile = self.blendfiles / "subdir/doubly_linked_up.blend"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user