Page MenuHome

EnumProperty with ENUM_FLAG causes a crash
Closed, ResolvedPublic

Description

System Information
Operating system: Windows 8.1 x64
Graphics card: Intel HD Graphics 520

Blender Version
Broken: Blender 2.91.0, Commit date: 2020-11-18 18:54, Hash ad58999b0d46
Worked: 2.90 (and below).

Additional files
blender.crash.txt is attached to this report.

Short description of error
Blender crashes when you click on the cells of an bpy.props.EnumProperty created with the ENUM_FLAG flag.

Exact steps for others to reproduce the error
On a Text Editor view, create a new text datablock, paste the following script and run it.
It will create an EnumProperty and draw it on the 3D View header.
Clicking on any of its cells crashes Blender.

import bpy
from time import time


def myCustomEnumDrawHeader(self, context):
    layout = self.layout.row(align=True)
    layout.alignment = 'CENTER'
    layout.prop(context.scene, 'myCustomEnum', expand=True, icon_only=True)


def myCustomEnumItems(self, context):
    items = [
        (
            str(index), # String identifier.
            str(index), # UI label.
            '', # UI description.
            'LAYER_ACTIVE',
            2**index # Integer index (always a power-of-two with the 'ENUM_FLAG' option).
        )
        for index in range(9)
    ]
    return items


def myCustomEnumGet(self):
    return 2 ** 3


def myCustomEnumSet(self, value):
    print('myCustomEnumSet()', int(time.time() * 1000))
    

def register():
    bpy.types.Scene.myCustomEnum = bpy.props.EnumProperty(
        name = '',
        items = myCustomEnumItems,
        get = myCustomEnumGet,
        set = myCustomEnumSet,
        description = 'My Custom Enum',
        options = {'ENUM_FLAG', 'SKIP_SAVE', 'HIDDEN'}
    )
    bpy.types.VIEW3D_HT_header.append(myCustomEnumDrawHeader)


def unregister():
    bpy.types.VIEW3D_HT_header.remove(myCustomEnumDrawHeader)
    del bpy.types.Scene.myCustomEnum
    

if __name__ == "__main__":
    register()

{F9588970}

Event Timeline

blender.crash.txt:

No crash here

**System Information**
Operating system: Linux-5.9.16-200.fc33.x86_64-x86_64-with-fedora-33-Thirty_Three 64 Bits
Graphics card: GeForce GTX 970M/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 455.45.01
version: 2.93.0 Alpha, branch: master, commit date: 2021-01-19 17:40, hash: `rB02a63115d25f`, tested 2.91 as well
Robert Guetzkow (rjg) closed this task as Archived.Jan 20 2021, 9:53 AM

The EnumProperty is a bit more complicated to use. You are required to keep a reference to the values returned by the callback assigned to items. This is documented in the manual:

There is a known bug with using a callback, Python must keep a reference to the strings returned by the callback or Blender will misbehave or even crash.

This means you have to modify your script, so that each call to myCustomEnumItems stores the resulting value in a variable, thus keeping a reference. For a similar issue see T83360 and the fix rBAcef282cc9acdc1eb4e77d92fa7631f47a47a867c.

Rafael Navega (RN) added a comment.EditedJan 20 2021, 10:48 AM

@Robert Guetzkow (rjg) Hi, can you advise on how to apply the fix to that example script above? Below you can find a modified version that keeps a reference, but it still crashes in the same way. The change is in myCustomEnumItems:

Crash file:

import bpy


def myCustomEnumDrawHeader(self, context):
    layout = self.layout.row(align=True)
    layout.alignment = 'CENTER'
    layout.prop(context.scene, 'myCustomEnum', expand=True, icon_only=True)


myItems = [ ]

def myCustomEnumItems(self, context):
    global myItems
    myItems.clear()
    myItems.extend(
        (
            str(index), # String identifier.
            str(index), # UI label.
            '', # UI description.
            'LAYER_ACTIVE',
            2**index # Integer index (always a power-of-two with the 'ENUM_FLAG' option).
        )
        for index in range(9)
    )
    return myItems


def myCustomEnumGet(self):
    return 2 ** 3


def myCustomEnumSet(self, value):
    print('myCustomEnumSet()')
    

def register():
    bpy.types.Scene.myCustomEnum = bpy.props.EnumProperty(
        name = '',
        items = myCustomEnumItems,
        get = myCustomEnumGet,
        set = myCustomEnumSet,
        description = 'My Custom Enum',
        options = {'ENUM_FLAG', 'SKIP_SAVE', 'HIDDEN'}
    )
    bpy.types.VIEW3D_HT_header.append(myCustomEnumDrawHeader)


def unregister():
    bpy.types.VIEW3D_HT_header.remove(myCustomEnumDrawHeader)
    del bpy.types.Scene.myCustomEnum
    

if __name__ == "__main__":
    register()

Okay, this is interesting, since the crash is happening with a Windows build and as @Philipp Oeser (lichtwerk) said they couldn't reproduce the crash, I'm thinking this might be a Windows-only issue since the stacktrace has the crash happening with function strchr of VCRUNTIME140.dll.

For completeness, I also did another test with a static list defined as a global variable so it's never lost, but the problem persists, check it out:

import bpy


def myCustomEnumDrawHeader(self, context):
    layout = self.layout.row(align=True)
    layout.alignment = 'CENTER'
    layout.prop(context.scene, 'myCustomEnum', expand=True, icon_only=True)


myItems = [
    ('1', '1', '', 'LAYER_ACTIVE', 1),
    ('2', '2', '', 'LAYER_ACTIVE', 2),
    ('3', '3', '', 'LAYER_ACTIVE', 4),
    ('4', '4', '', 'LAYER_ACTIVE', 8),
]

def myCustomEnumItems(self, context):
    print('myCustomEnumItems()')
    global myItems
    return myItems
    

myValue = 0

def myCustomEnumGet(self):
    print('myCustomEnumGet()')
    global myValue
    return myValue


def myCustomEnumSet(self, value):
    print('myCustomEnumSet()', value)
    global myValue    
    myValue = value
    

def register():
    bpy.types.Scene.myCustomEnum = bpy.props.EnumProperty(
        name = '',
        items = myCustomEnumItems,
        get = myCustomEnumGet,
        set = myCustomEnumSet,
        description = 'My Custom Enum',
        options = {'ENUM_FLAG', 'SKIP_SAVE', 'HIDDEN'}
    )
    bpy.types.VIEW3D_HT_header.append(myCustomEnumDrawHeader)


def unregister():
    bpy.types.VIEW3D_HT_header.remove(myCustomEnumDrawHeader)
    del bpy.types.Scene.myCustomEnum
    

if __name__ == "__main__":
    register()

@Rafael Navega (RN) I will take another look at this shortly.

Robert Guetzkow (rjg) changed the task status from Archived to Resolved.EditedJan 20 2021, 11:43 AM

The issue you're having should be fixed by rB256e77c987d2d8a3c1fc672eb75b3efe32441789. You aren't using the official release version of 2.91.0 from November 25 that contains the fix, please install the official release.

You still need to keep a reference to the items though as I've said in my initial comment.

@Robert Guetzkow (rjg) it's working great on 2.91 from November 25 and up. And I will keep a reference to the dynamic EnumProperty items as recommended.

Thank you very much for your time!