import bpy
'''Bridge for the geometry nodes modifier input storage change in Blender 5.2.\n
Up to 5.1 inputs live in the modifier's IDProperties, keyed by socket identifier:\n
modifier['Socket_1']\n
modifier['Socket_1_use_attribute']\n
modifier['Socket_1_attribute_name']\n
5.2 dropped IDProperty support on NodesModifier entirely and moved inputs to typed RNA:\n
modifier.properties.inputs.Socket_1.value\n
modifier.properties.inputs.Socket_1.type # VALUE / ATTRIBUTE / LAYER\n
modifier.properties.inputs.Socket_1.attribute_name\n
Subscripting a modifier on 5.2 raises TypeError: this type doesn't support IDProperties.
'''
RNA_INPUTS = 'properties' in bpy.types.NodesModifier.bl_rna.properties
def socket(modifier, identifier):
'''RNA input struct for identifier, or None. 5.2+ only'''
return getattr(modifier.properties.inputs, identifier, None)
class ModifierInputs:
'''Dict-like view of a geometry nodes modifier's inputs, keyed by socket identifier'''
__slots__ = ('modifier',)
def __init__(self, modifier):
self.modifier = modifier
def __contains__(self, identifier):
if not RNA_INPUTS:
return identifier in self.modifier
return hasattr(self.modifier.properties.inputs, identifier)
def __getitem__(self, identifier):
if not RNA_INPUTS:
return self.modifier[identifier]
input = socket(self.modifier, identifier)
if input is None:
raise KeyError(identifier)
try:
return input.value
except AttributeError: # geometry sockets carry no value
raise KeyError(identifier)
def __setitem__(self, identifier, value):
if not RNA_INPUTS:
self.modifier[identifier] = value
return
input = socket(self.modifier, identifier)
if input is None:
raise KeyError(identifier)
input.value = value
def get(self, identifier, default=None):
try:
return self[identifier]
except KeyError:
return default
def supports_attribute(modifier, identifier) -> bool:
'''True if the input can be driven by an attribute'''
if not RNA_INPUTS:
return f'{identifier}_use_attribute' in modifier
input = socket(modifier, identifier)
return bool(input) and hasattr(input, 'attribute_name')
def get_use_attribute(modifier, identifier) -> bool:
if not RNA_INPUTS:
return bool(modifier.get(f'{identifier}_use_attribute', False))
input = socket(modifier, identifier)
return bool(input) and input.type != 'VALUE'
def get_attribute_name(modifier, identifier) -> str:
if not RNA_INPUTS:
return modifier.get(f'{identifier}_attribute_name', '')
input = socket(modifier, identifier)
if input is None:
return ''
return getattr(input, 'attribute_name', '')
def set_attribute_name(modifier, identifier, name):
if not RNA_INPUTS:
modifier[f'{identifier}_attribute_name'] = name
return
input = socket(modifier, identifier)
if input is None:
return
input.attribute_name = name
def value_prop(modifier, identifier):
'''(data, property) pair for drawing the input's value with layout.prop'''
if not RNA_INPUTS:
return modifier, f'["{identifier}"]'
return socket(modifier, identifier), 'value'
def attribute_name_prop(modifier, identifier):
'''(data, property) pair for drawing the input's attribute name with layout.prop'''
if not RNA_INPUTS:
return modifier, f'["{identifier}_attribute_name"]'
return socket(modifier, identifier), 'attribute_name'
def output_attribute_name_prop(modifier, identifier):
'''(data, property) pair for drawing an output's attribute name, or (None, '') if absent'''
if not RNA_INPUTS:
id_name = f'{identifier}_attribute_name'
if id_name not in modifier:
return None, ''
return modifier, f'["{id_name}"]'
output = getattr(modifier.properties.outputs, identifier, None)
if output is None or not hasattr(output, 'attribute_name'):
return None, ''
return output, 'attribute_name'
def set_output_attribute_name(modifier, identifier, name):
if not RNA_INPUTS:
modifier[f'{identifier}_attribute_name'] = name
return
output = getattr(modifier.properties.outputs, identifier, None)
if output is None:
return
output.attribute_name = name