def assert_deps(self, blend_fname, expects: dict): for dep in trace.deps(self.blendfiles / blend_fname): actual_type = dep.block.dna_type.dna_type_id.decode() actual_full_field = self.field_name(dep.path_full_field) actual_dirname = self.field_name(dep.path_dir_field) actual_basename = self.field_name(dep.path_base_field) actual = Expect(actual_type, actual_full_field, actual_dirname, actual_basename, dep.asset_path, dep.is_sequence) exp = expects.get(dep.block_name, None) if isinstance(exp, (set, list)): self.assertIn(actual, exp, msg='for block %s' % dep.block_name) exp.remove(actual) if not exp: # Don't leave empty sets in expects. del expects[dep.block_name] elif exp is None: self.assertIsNone(actual, msg='unexpected dependency of block %s' % dep.block_name) del expects[dep.block_name] else: self.assertEqual(exp, actual, msg='for block %s' % dep.block_name) del expects[dep.block_name] # All expected uses should have been seen. self.assertEqual(expects, {}, 'Expected results were not seen.')
def test_seq_image_sequence(self): expects = { b'-unnamed-': [ Expect('Strip', None, 'dir[768]', 'name[256]', b'//imgseq/000210.png', True), # Video strip reference. Expect('Strip', None, 'dir[768]', 'name[256]', b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False), # The sound will be referenced twice, from the sequence strip and an SO data block. Expect('Strip', None, 'dir[768]', 'name[256]', b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False), ], b'SOvideo-tiny.mkv': Expect('bSound', 'name[1024]', None, None, b'//../../../../cloud/pillar/testfiles/video-tiny.mkv', False), } self.assert_deps('image_sequencer.blend', expects) # Test the filename expansion. expected = [ self.blendfiles / ('imgseq/%06d.png' % num) for num in range(210, 215) ] for dep in trace.deps(self.blendfiles / 'image_sequencer.blend'): if dep.block_name != b'SQ000210.png': continue actual = list(dep.files()) self.assertEqual(actual, expected)
def test_usage_abspath(self): deps = [ dep for dep in trace.deps(self.blendfiles / 'doubly_linked.blend') if dep.asset_path == b'//material_textures.blend' ] usage = deps[0] expect = self.blendfiles / 'material_textures.blend' self.assertEqual(expect, usage.abspath)
def test_recursion_loop(self): infinite_bfile = self.blendfiles / 'recursive_dependency_1.blend' reclim = sys.getrecursionlimit() try: sys.setrecursionlimit(100) # This should finish without hitting the recursion limit. for _ in trace.deps(infinite_bfile): pass finally: sys.setrecursionlimit(reclim)
def report_json(bpath): import collections # Mapping from blend file to its dependencies. report = collections.defaultdict(set) for usage in trace.deps(bpath): filepath = usage.block.bfile.filepath.absolute() for assetpath in usage.files(): assetpath = assetpath.resolve() report[str(filepath)].add(assetpath) json.dump(report, sys.stdout, cls=JSONSerialiser, indent=4)
def strategise(self) -> None: """Determine what to do with the assets. Places an asset into one of these categories: - Can be copied as-is, nothing smart required. - Blend files referring to this asset need to be rewritten. This function does *not* expand globs. Globs are seen as single assets, and are only evaluated when performing the actual transfer in the execute() function. """ # The blendfile that we pack is generally not its own dependency, so # we have to explicitly add it to the _packed_paths. bfile_path = bpathlib.make_absolute(self.blendfile) # Both paths have to be resolved first, because this also translates # 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. bfile_pp = self._target_path / bfile_path.relative_to( bpathlib.make_absolute(self.project)) self._output_path = bfile_pp self._progress_cb.pack_start() act = self._actions[bfile_path] act.path_action = PathAction.KEEP_PATH act.new_path = bfile_pp self._check_aborted() self._new_location_paths = set() for usage in trace.deps(self.blendfile, self._progress_cb): self._check_aborted() asset_path = usage.abspath if any(asset_path.match(glob) for glob in self._exclude_globs): log.info('Excluding file: %s', asset_path) continue if self.relative_only and not usage.asset_path.startswith(b'//'): log.info('Skipping absolute path: %s', usage.asset_path) continue if usage.is_sequence: self._visit_sequence(asset_path, usage) else: self._visit_asset(asset_path, usage) self._find_new_paths() self._group_rewrites()
def report_text(bpath, *, include_sha256: bool, show_timing: bool): reported_assets = set() # type: typing.Set[pathlib.Path] last_reported_bfile = None shorten = functools.partial(common.shorten, pathlib.Path.cwd()) time_spent_on_shasums = 0.0 start_time = time.time() for usage in trace.deps(bpath): filepath = usage.block.bfile.filepath.absolute() if filepath != last_reported_bfile: if include_sha256: shasum, time_spent = calc_sha_sum(filepath) time_spent_on_shasums += time_spent print(shorten(filepath), shasum) else: print(shorten(filepath)) last_reported_bfile = filepath for assetpath in usage.files(): assetpath = bpathlib.make_absolute(assetpath) if assetpath in reported_assets: log.debug('Already reported %s', assetpath) continue if include_sha256: shasum, time_spent = calc_sha_sum(assetpath) time_spent_on_shasums += time_spent print(' ', shorten(assetpath), shasum) else: print(' ', shorten(assetpath)) reported_assets.add(assetpath) if show_timing: duration = time.time() - start_time print('Spent %.2f seconds on producing this listing' % duration) if include_sha256: print('Spent %.2f seconds on calculating SHA sums' % time_spent_on_shasums) percentage = time_spent_on_shasums / duration * 100 print(' (that is %d%% of the total time' % percentage)
def trace_blend_file(blend_file_path, progress_cb=None): """ Finds all the dependencies for a given .blend file :param blend_file_path: the .blend file to inspect :type blend_file_path: str :param progress_cb: The progress reporting callback instance :type progress_cb: blender_asset_tracer.pack.progress.Callback :return: A dictionary of .blend files and their sets of dependencies :rtype: collections.defaultdict(set) """ # the dependencies as a mapping from the blend file to its set of dependencies. dependencies = collections.defaultdict(set) # Find the dependencies for usage in trace.deps(pathlib.Path(blend_file_path), progress_cb): file_path = usage.block.bfile.filepath.absolute() for assetPath in usage.files(): asset_path = assetPath.resolve() dependencies[str(file_path)].add(assetPath) return dependencies
def test_alembic_sequence(self): self.assert_deps( 'alembic-sequence-user.blend', { b'CFclothsim_alembic': Expect('CacheFile', 'filepath[1024]', None, None, b'//clothsim.030.abc', True), }) # Test the filename expansion. expected = [ self.blendfiles / ('clothsim.%03d.abc' % num) for num in range(30, 36) ] performed_test = False for dep in trace.deps(self.blendfiles / 'alembic-sequence-user.blend'): if dep.block_name != b'CFclothsim_alembic': continue actual = list(dep.files()) self.assertEqual(actual, expected) performed_test = True self.assertTrue(performed_test)
## list dependancies with BAT import bpy from blender_asset_tracer.trace import deps from pathlib import Path for lib in deps(Path(bpy.data.filepath)): # print(lib) if lib.asset_path.is_absolute(): ## print abs lib only print(lib)
print('-' * 10, '\n' * 3) # Set filepath here fp = bpy.data.filepath fp = Path(fp) assert fp.exists start = time() absol = [] not_exists = [] errors = [] for lib in deps(fp): try: if not lib.abspath.exists(): print('!! not found:', lib) not_exists.append(lib) continue if lib.asset_path.is_absolute(): ## print abs lib only absol.append(lib) print('absolute', lib) except error as e: print('Error accessing:', lib) print(e) errors.append(lib)