From 59c0b6df4c5f62991aab8a97ec3e0e8443f6c945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 26 Feb 2018 18:15:14 +0100 Subject: [PATCH] Allow block.get() to return the dna.Field This is needed by the upcoming dependency tracer. --- blender_asset_tracer/blendfile/__init__.py | 8 +++++++- blender_asset_tracer/blendfile/dna.py | 20 +++++++++++--------- tests/test_blendfile_dna.py | 18 +++++++++--------- tests/test_blendfile_loading.py | 4 ++-- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/blender_asset_tracer/blendfile/__init__.py b/blender_asset_tracer/blendfile/__init__.py index b6700f3..433bb58 100644 --- a/blender_asset_tracer/blendfile/__init__.py +++ b/blender_asset_tracer/blendfile/__init__.py @@ -386,6 +386,7 @@ class BlendFileBlock: null_terminated=True, as_str=False, base_index=0, + return_field=False ) -> typing.Any: """Read a property and return the value. @@ -400,6 +401,8 @@ class BlendFileBlock: when reading binary data. :param as_str: When True, automatically decode bytes to string (assumes UTF-8 encoding). + :param return_field: When True, returns tuple (dna.Field, value). + Otherwise just returns the value. """ ofs = self.file_offset if base_index != 0: @@ -410,11 +413,14 @@ class BlendFileBlock: self.bfile.fileobj.seek(ofs, os.SEEK_SET) dna_struct = self.bfile.structs[self.sdna_index] - return dna_struct.field_get( + field, value = dna_struct.field_get( self.bfile.header, self.bfile.fileobj, path, default=default, null_terminated=null_terminated, as_str=as_str, ) + if return_field: + return value, field + return value def get_recursive_iter(self, path: dna.FieldPath, diff --git a/blender_asset_tracer/blendfile/dna.py b/blender_asset_tracer/blendfile/dna.py index aca2f01..4dfa869 100644 --- a/blender_asset_tracer/blendfile/dna.py +++ b/blender_asset_tracer/blendfile/dna.py @@ -184,7 +184,7 @@ class Struct: default=..., null_terminated=True, as_str=True, - ): + ) -> typing.Tuple[typing.Optional[Field], typing.Any]: """Read the value of the field from the blend file. Assumes the file pointer of `fileobj` is seek()ed to the start of the @@ -201,13 +201,15 @@ class Struct: default when reading binary data. :param as_str: When True, automatically decode bytes to string (assumes UTF-8 encoding). + :returns: The field instance and the value. If a default value was passed + and the field was not found, (None, default) is returned. """ try: field, offset = self.field_from_path(file_header.pointer_size, path) except KeyError: if default is ...: raise - return default + return None, default fileobj.seek(offset, os.SEEK_CUR) @@ -217,19 +219,19 @@ class Struct: # Some special cases (pointers, strings/bytes) if dna_name.is_pointer: - return endian.read_pointer(fileobj, file_header.pointer_size) + return field, endian.read_pointer(fileobj, file_header.pointer_size) if dna_type.dna_type_id == b'char': if field.size == 1: # Single char, assume it's bitflag or int value, and not a string/bytes data... - return endian.read_char(fileobj) + return field, endian.read_char(fileobj) if null_terminated or (null_terminated is None and as_str): data = endian.read_bytes0(fileobj, dna_name.array_size) else: data = fileobj.read(dna_name.array_size) if as_str: - return data.decode('utf8') - return data + return field, data.decode('utf8') + return field, data simple_readers = { b'int': endian.read_int, @@ -249,11 +251,11 @@ class Struct: # The caller wants to get a single item from an array. The offset we seeked to already # points to this item. In this case we do not want to look at dna_name.array_size, # because we want a single item from that array. - return simple_reader(fileobj) + return field, simple_reader(fileobj) if dna_name.array_size > 1: - return [simple_reader(fileobj) for _ in range(dna_name.array_size)] - return simple_reader(fileobj) + return field, [simple_reader(fileobj) for _ in range(dna_name.array_size)] + return field, simple_reader(fileobj) def field_set(self, file_header: header.BlendFileHeader, diff --git a/tests/test_blendfile_dna.py b/tests/test_blendfile_dna.py index e57043d..e994d15 100644 --- a/tests/test_blendfile_dna.py +++ b/tests/test_blendfile_dna.py @@ -144,7 +144,7 @@ class StructTest(unittest.TestCase): def test_simple_field_get(self): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa' - val = self.s.field_get(self.FakeHeader(), fileobj, b'numbah') + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'numbah') self.assertEqual(val, 0x1020304fffefdfa) fileobj.seek.assert_called_with(4136, os.SEEK_CUR) @@ -152,7 +152,7 @@ class StructTest(unittest.TestCase): def test_field_get_default(self): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.side_effect = RuntimeError - val = self.s.field_get(self.FakeHeader(), fileobj, b'nonexistant', default=519871531) + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'nonexistant', default=519871531) self.assertEqual(val, 519871531) fileobj.seek.assert_not_called() @@ -176,7 +176,7 @@ class StructTest(unittest.TestCase): def test_pointer_field_get(self): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\xf0\x9f\xa6\x87\x00dum' - val = self.s.field_get(self.FakeHeader(), fileobj, b'ptr') + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'ptr') self.assertEqual(0xf09fa6870064756d, val) fileobj.seek.assert_called_with(4112, os.SEEK_CUR) @@ -184,7 +184,7 @@ class StructTest(unittest.TestCase): def test_string_field_get(self): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\xf0\x9f\xa6\x87\x00dummydata' - val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=True) + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=True) self.assertEqual('🦇', val) fileobj.seek.assert_called_with(16, os.SEEK_CUR) @@ -192,7 +192,7 @@ class StructTest(unittest.TestCase): def test_string_field_get_single_char(self): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\xf0' - val = self.s.field_get(self.FakeHeader(), fileobj, b'bitflag') + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'bitflag') self.assertEqual(0xf0, val) fileobj.seek.assert_called_with(4152, os.SEEK_CUR) @@ -208,7 +208,7 @@ class StructTest(unittest.TestCase): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata' - val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=False) + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'path', as_str=False) self.assertEqual(b'\x01\x02\x03\x04\xff\xfe\xfd\xfa', val) fileobj.seek.assert_called_with(16, os.SEEK_CUR) @@ -216,8 +216,8 @@ class StructTest(unittest.TestCase): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.return_value = b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata' - val = self.s.field_get(self.FakeHeader(), fileobj, b'path', - as_str=False, null_terminated=False) + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'path', + as_str=False, null_terminated=False) self.assertEqual(b'\x01\x02\x03\x04\xff\xfe\xfd\xfa\x00dummydata', val) fileobj.seek.assert_called_with(16, os.SEEK_CUR) @@ -225,7 +225,7 @@ class StructTest(unittest.TestCase): fileobj = mock.MagicMock(io.BufferedReader) fileobj.read.side_effect = (b'@333', b'@2\x8f\\') - val = self.s.field_get(self.FakeHeader(), fileobj, b'floaty') + _, val = self.s.field_get(self.FakeHeader(), fileobj, b'floaty') self.assertAlmostEqual(2.8, val[0]) self.assertAlmostEqual(2.79, val[1]) fileobj.seek.assert_called_with(4144, os.SEEK_CUR) diff --git a/tests/test_blendfile_loading.py b/tests/test_blendfile_loading.py index f614472..041e45a 100644 --- a/tests/test_blendfile_loading.py +++ b/tests/test_blendfile_loading.py @@ -19,12 +19,12 @@ class BlendFileBlockTest(AbstractBlendFileTest): # Try low level operation to read a property. self.bf.fileobj.seek(ob.file_offset, os.SEEK_SET) - loc = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, b'loc') + _, loc = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, b'loc') self.assertEqual([2.0, 3.0, 5.0], loc) # Try low level operation to read an array element. self.bf.fileobj.seek(ob.file_offset, os.SEEK_SET) - loc_z = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, (b'loc', 2)) + _, loc_z = ob.dna_type.field_get(self.bf.header, self.bf.fileobj, (b'loc', 2)) self.assertEqual(5.0, loc_z) # Try high level operation to read the same property.