def equals(attr_a, attr_b, synchronized_properties=test_properties): type_a = type(attr_a) type_b = type(attr_b) if type_a != type_b: return False if attr_a is None or is_builtin(type_a) or is_vector(type_a) or is_matrix(type_a): return attr_a == attr_b elif type_a == T.bpy_prop_array: return attr_a == attr_b elif issubclass(type_a, T.bpy_prop_collection): for key in attr_a.keys(): attr_a_i = attr_a[key] attr_b_i = attr_b[key] if not equals(attr_a_i, attr_b_i): return False elif issubclass(type_a, T.bpy_struct): for name, _ in synchronized_properties.properties(attr_a.bl_rna): attr_a_i = getattr(attr_a, name) attr_b_i = getattr(attr_b, name) if not equals(attr_a_i, attr_b_i): return False else: raise TypeError("equals: Unexpected type {type_a}") return True
def equals(attr_a, attr_b, context=test_context): type_a = type(attr_a) type_b = type(attr_b) if type_a != type_b: return False if is_builtin(type_a) or is_vector(type_a) or is_matrix(type_a): if attr_a != attr_b: return False elif type_a == T.bpy_prop_array: if attr_a != attr_b: return False elif issubclass(type_a, T.bpy_prop_collection): for key in attr_a.keys(): attr_a_i = attr_a[key] attr_b_i = attr_b[key] if not equals(attr_a_i, attr_b_i): return False elif issubclass(type_a, T.bpy_struct): for name, _ in context.properties(attr_a.bl_rna): attr_a_i = getattr(attr_a, name) attr_b_i = getattr(attr_b, name) if not equals(attr_a_i, attr_b_i): return False else: raise NotImplementedError return True
def bl_equals(attr_a, attr_b, msg=None, skip_name=False, context=None): """ skip_name for the top level name only since cloned objects have different names """ failureException = unittest.TestCase.failureException type_a = type(attr_a) type_b = type(attr_b) if type_a != type_b: raise failureException(f"Different types : {type_a} and {type_b}") if is_builtin(type_a) or is_vector(type_a) or is_matrix(type_a): if attr_a != attr_b: raise failureException(f"Different values : {attr_a} and {attr_b}") elif type_a == T.bpy_prop_array: if list(attr_a) != list(attr_b): raise failureException( f"Different values for array : {attr_a} and {attr_b}") elif issubclass(type_a, T.bpy_prop_collection): for key in attr_a.keys(): attr_a_i = attr_a[key] attr_b_i = attr_b[key] try: equal = bl_equals(attr_a_i, attr_b_i, msg, skip_name=False, context=context) except failureException as e: raise failureException( f'{e}\nDifferent values for collection items at key "{key}" : {attr_a_i} and {attr_b_i}' ) from None if not equal: raise failureException( f'Different values for collection items at key "{key}" : {attr_a_i} and {attr_b_i}' ) elif issubclass(type_a, T.bpy_struct): for name, _ in context.properties(attr_a.bl_rna): if skip_name and (name == "name" or name == "name_full"): continue attr_a_i = getattr(attr_a, name) attr_b_i = getattr(attr_b, name) try: equal = bl_equals(attr_a_i, attr_b_i, msg, skip_name=False, context=context) except failureException as e: raise failureException( f'{e}\nDifferent values for struct items at key "{name}" : {attr_a_i} and {attr_b_i}' ) from None if not equal: raise failureException( f'Different values for struct items at key "{name}" : {attr_a_i} and {attr_b_i}' ) else: raise NotImplementedError return True
def array_attr(self, aos: T.bpy_prop_collection, member_name: str, bl_rna: T.bpy_struct): prototype_item = getattr(aos[0], member_name) member_type = type(prototype_item) if is_vector(member_type): array_size = len(aos) * len(prototype_item) elif member_type is T.bpy_prop_array: member_type = type(prototype_item[0]) if isinstance(bl_rna, T.MeshPolygon) and member_name == "vertices": # polygon sizes can differ array_size = sum((len(polygon.vertices) for polygon in aos)) else: array_size = len(aos) * len(prototype_item) else: array_size = len(aos) return array_size, member_type
def read_attribute(attr: Any, key: Union[int, str], attr_property: T.Property, context: Context): """ Load a property into a python object of the appropriate type, be it a Proxy or a native python object """ attr_type = type(attr) if is_builtin(attr_type): return attr if is_vector(attr_type): return list(attr) if is_matrix(attr_type): return [list(col) for col in attr.col] # We have tested the types that are usefully reported by the python binding, now harder work. # These were implemented first and may be better implemented with the bl_rna property of the parent struct # TODO flatten if attr_type == T.bpy_prop_array: return list(attr) try: context.visit_state.recursion_guard.push(attr_property.identifier) if attr_type == T.bpy_prop_collection: if isinstance(attr_property.fixed_type, bpy.types.ID): from mixer.blender_data.datablock_collection_proxy import DatablockRefCollectionProxy return DatablockRefCollectionProxy().load(attr, key, context) elif is_soable_collection(attr_property): from mixer.blender_data.aos_proxy import AosProxy return AosProxy().load(attr, key, attr_property, context) else: from mixer.blender_data.struct_collection_proxy import StructCollectionProxy return StructCollectionProxy.make(attr_property).load( attr, key, attr_property, context) # TODO merge with previous case if isinstance(attr_property, T.CollectionProperty): from mixer.blender_data.struct_collection_proxy import StructCollectionProxy return StructCollectionProxy().load(attr, key, attr_property, context) bl_rna = attr_property.bl_rna if bl_rna is None: logger.warning("Not implemented: attribute %s", attr) return None if issubclass(attr_type, T.PropertyGroup): from mixer.blender_data.struct_proxy import StructProxy return StructProxy().load(attr, key, context) if issubclass(attr_type, T.ID): if attr.is_embedded_data: from mixer.blender_data.datablock_proxy import DatablockProxy return DatablockProxy.make(attr_property).load( attr, key, context) else: from mixer.blender_data.datablock_ref_proxy import DatablockRefProxy return DatablockRefProxy().load(attr, key, context) if issubclass(attr_type, T.bpy_struct): from mixer.blender_data.struct_proxy import StructProxy return StructProxy().load(attr, key, context) if attr is None and isinstance(attr_property, T.PointerProperty): from mixer.blender_data.misc_proxies import NonePtrProxy return NonePtrProxy() logger.error( f"Unsupported attribute {attr_type} {attr_property} {attr_property.fixed_type} at {context.visit_state.datablock_proxy._class_name}.{context.visit_state.path}.{attr_property.identifier}" ) finally: context.visit_state.recursion_guard.pop()