diff --git a/blender_asset_tracer/blendfile/__init__.py b/blender_asset_tracer/blendfile/__init__.py index a564ee6..7fd00c5 100644 --- a/blender_asset_tracer/blendfile/__init__.py +++ b/blender_asset_tracer/blendfile/__init__.py @@ -116,9 +116,7 @@ class BlendFile: self.filepath = path self.raw_filepath = path self._is_modified = False - self.fileobj = None # type: typing.IO[bytes] - - self._open_file(path, mode) + self.fileobj = self._open_file(path, mode) self.blocks = [] # type: BFBList """BlendFileBlocks of this file, in disk order.""" @@ -132,7 +130,7 @@ class BlendFile: self.block_header_struct = self.header.create_block_header_struct() self._load_blocks() - def _open_file(self, path: pathlib.Path, mode: str): + def _open_file(self, path: pathlib.Path, mode: str) -> typing.IO[bytes]: """Open a blend file, decompressing if necessary. This does not parse the blend file yet, just makes sure that @@ -155,9 +153,9 @@ class BlendFile: if magic == BLENDFILE_MAGIC: self.is_compressed = False self.raw_filepath = path - self.fileobj = fileobj + return fileobj - elif magic[:2] == GZIP_MAGIC: + if magic[:2] == GZIP_MAGIC: self.is_compressed = True log.debug("compressed blendfile detected: %s", path) @@ -177,11 +175,10 @@ class BlendFile: # Further interaction should be done with the uncompressed file. self.raw_filepath = pathlib.Path(tmpfile.name) fileobj.close() - self.fileobj = tmpfile + return tmpfile - elif magic != BLENDFILE_MAGIC: - fileobj.close() - raise exceptions.BlendFileError("File is not a blend file", path) + fileobj.close() + raise exceptions.BlendFileError("File is not a blend file", path) def _load_blocks(self) -> None: """Read the blend file to load its DNA structure to memory.""" @@ -233,7 +230,7 @@ class BlendFile: # TODO(Sybren): remove str() calls when targeting Python 3.6+ shutil.copy(str(self.filepath), str(path)) - self._open_file(path, mode=mode) + self.fileobj = self._open_file(path, mode=mode) _cache(path, self) @property @@ -658,7 +655,7 @@ class BlendFileBlock: def get_pointer( self, path: dna.FieldPath, default=..., - ) -> typing.Union[None, 'BlendFileBlock', typing.Any]: + ) -> typing.Union[None, 'BlendFileBlock']: """Same as get() but dereferences a pointer. :raises exceptions.SegmentationFault: when there is no datablock with @@ -696,6 +693,7 @@ class BlendFileBlock: return array = self.get_pointer(path) + assert array is not None assert array.code == b'DATA', \ 'Array data block should have code DATA, is %r' % array.code.decode() file_offset = array.file_offset diff --git a/blender_asset_tracer/blendfile/iterators.py b/blender_asset_tracer/blendfile/iterators.py index 0bc5070..35b5694 100644 --- a/blender_asset_tracer/blendfile/iterators.py +++ b/blender_asset_tracer/blendfile/iterators.py @@ -26,7 +26,7 @@ from . import BlendFileBlock from .dna import FieldPath -def listbase(block: BlendFileBlock, next_path: FieldPath = b'next') \ +def listbase(block: typing.Optional[BlendFileBlock], next_path: FieldPath = b'next') \ -> typing.Iterator[BlendFileBlock]: """Generator, yields all blocks in the ListBase linked list.""" while block: diff --git a/blender_asset_tracer/bpathlib.py b/blender_asset_tracer/bpathlib.py index 9d0ccb2..7de4e31 100644 --- a/blender_asset_tracer/bpathlib.py +++ b/blender_asset_tracer/bpathlib.py @@ -118,7 +118,7 @@ class BlendPath(bytes): return False - def absolute(self, root: bytes = None) -> 'BlendPath': + def absolute(self, root: bytes = b'') -> 'BlendPath': """Determine absolute path. :param root: root directory to compute paths relative to. diff --git a/blender_asset_tracer/pack/__init__.py b/blender_asset_tracer/pack/__init__.py index c26a566..1738ef3 100644 --- a/blender_asset_tracer/pack/__init__.py +++ b/blender_asset_tracer/pack/__init__.py @@ -50,7 +50,7 @@ class AssetAction: (if the asset is a blend file) or in another blend file. """ - self.new_path = None # type: pathlib.Path + self.new_path = None # type: typing.Optional[pathlib.Path] """Absolute path to the asset in the BAT Pack. This path may not exist on the local file system at all, for example @@ -123,10 +123,10 @@ class Packer: # type: typing.DefaultDict[pathlib.Path, AssetAction] self.missing_files = set() # type: typing.Set[pathlib.Path] self._new_location_paths = set() # type: typing.Set[pathlib.Path] - self._output_path = None # type: pathlib.Path + self._output_path = None # type: typing.Optional[pathlib.Path] # Filled by execute() - self._file_transferer = None # type: transfer.FileTransferer + self._file_transferer = None # type: typing.Optional[transfer.FileTransferer] # Number of files we would copy, if not for --noop self._file_count = 0 @@ -148,6 +148,7 @@ class Packer: @property def output_path(self) -> pathlib.Path: """The path of the packed blend file in the target directory.""" + assert self._output_path is not None return self._output_path @property @@ -367,6 +368,8 @@ class Packer: """ log.debug('Executing %d copy actions', len(self._actions)) + assert self._file_transferer is not None + try: for asset_path, action in self._actions.items(): self._check_aborted() @@ -406,6 +409,7 @@ class Packer: # It is *not* used for any disk I/O, since the file may not even # exist on the local filesystem. bfile_pp = self._actions[bfile_path].new_path + assert bfile_pp is not None # Use tempfile to create a unique name in our temporary directoy. # The file should be deleted when self.close() is called, and not @@ -439,10 +443,13 @@ class Packer: # Find the same block in the newly copied file. block = bfile.dereference_pointer(usage.block.addr_old) if usage.path_full_field is None: + dir_field = usage.path_dir_field + assert dir_field is not None log.debug(' - updating field %s of block %s', - usage.path_dir_field.name.name_only, block) + dir_field.name.name_only, + block) reldir = bpathlib.BlendPath.mkrelative(asset_pp.parent, bfile_pp) - written = block.set(usage.path_dir_field.name.name_only, reldir) + written = block.set(dir_field.name.name_only, reldir) log.debug(' - written %d bytes', written) # BIG FAT ASSUMPTION that the filename (e.g. basename @@ -467,6 +474,7 @@ class Packer: # handled below in the for-loop). if '*' not in str(asset_path): packed_path = action.new_path + assert packed_path is not None read_path = action.read_from or asset_path self._send_to_target(read_path, packed_path, may_move=action.read_from is not None) @@ -477,6 +485,7 @@ class Packer: continue first_pp = self._actions[usage.abspath].new_path + assert first_pp is not None # In case of globbing, we only support globbing by filename, # and not by directory. @@ -504,6 +513,8 @@ class Packer: log.debug('Queueing %s of %s', verb, asset_path) self._tscb.flush() + + assert self._file_transferer is not None if may_move: self._file_transferer.queue_move(asset_path, target) else: diff --git a/blender_asset_tracer/trace/expanders.py b/blender_asset_tracer/trace/expanders.py index f90d46a..67aec8a 100644 --- a/blender_asset_tracer/trace/expanders.py +++ b/blender_asset_tracer/trace/expanders.py @@ -138,6 +138,8 @@ def _expand_group(block: blendfile.BlendFileBlock): else: for child in iterators.listbase(children): subcoll = child.get_pointer(b'collection') + if subcoll is None: + continue log.debug('recursing into child collection %s', subcoll.id_name) yield from _expand_group(subcoll) diff --git a/blender_asset_tracer/trace/modifier_walkers.py b/blender_asset_tracer/trace/modifier_walkers.py index 0d8ce29..8a7d3e3 100644 --- a/blender_asset_tracer/trace/modifier_walkers.py +++ b/blender_asset_tracer/trace/modifier_walkers.py @@ -87,7 +87,9 @@ def _get_texture(prop_name: bytes, dblock: blendfile.BlendFileBlock, block_name: yield from _get_image(b'ima', tx, block_name) -def _get_image(prop_name: bytes, dblock: blendfile.BlendFileBlock, block_name: bytes) \ +def _get_image(prop_name: bytes, + dblock: typing.Optional[blendfile.BlendFileBlock], + block_name: bytes) \ -> typing.Iterator[result.BlockUsage]: """Yield block usages from an image propery. diff --git a/blender_asset_tracer/trace/result.py b/blender_asset_tracer/trace/result.py index b22a527..5f4431e 100644 --- a/blender_asset_tracer/trace/result.py +++ b/blender_asset_tracer/trace/result.py @@ -90,7 +90,7 @@ class BlockUsage: self.path_base_field = path_base_field # cached by __fspath__() - self._abspath = None # type: pathlib.Path + self._abspath = None # type: typing.Optional[pathlib.Path] @staticmethod def guess_block_name(block: blendfile.BlendFileBlock) -> bytes: