0

I'm trying to figure out how to generate a dynamic property group class, but I keep getting the annotations error: Warning: class PropBools contains a property which should be an annotation!. And following this error, I get the following printed line for each property: assign as a type annotation: PropBools.******.

Here is the code I have so far. I can show details about the list of data if it will help at all, but I don't think it will influence the error I'm getting.

PropBools = type(

        # typename
        'PropBools',

        # base class
        ( bpy.types.PropertyGroup , ),

        # dictionary of bool properties
        {
            ft.ID : bpy.props.BoolProperty(
                default         = False,
                name            = ft.Name,
                description     = ft.Desc,

                ) for ft in prop_list_data
        }
    )

Is it possible to do this without throwing the warning/error? Has anyone successfully generated a dynamic property group since the annotation requirement update?

Edit: I found Blender 2.8 - Field property declaration and dynamic class creation , which may be the only solution. Here is the same code, updated to define an annotations dictionary:

PropBools = type(

        # typename
        'PropBools',

        # base class
        ( bpy.types.PropertyGroup , ),

        # dictionary of bool properties
        {
            '__annotations__' :
            {
                ft.ID : ( BoolProperty,
                {
                    'default'       : False,
                    'name'          : ft.Name,
                    'description'   : ft.Desc

                } ) for ft in fuse.Type.Index
            }
        }
    )

You can even unpack dictionaries of extra parameters into it (this is a different dynamic class):

PropValues = type(

        # typename
        'PropValues',

        # base class
        ( bpy.types.PropertyGroup , ),

        # generate dictionary of override properties
        {
            '__annotations__' :
            {
                ft.ID : ( ft.Prop.Func,
                {
                    'default'       : ft.Default,
                    'name'          : ft.Name,
                    'description'   : ft.Desc,

                    # specify our property event function for updates
                    'update'        : ft.ValUpdate,

                    # unpack dictionary of keyword arguments to this property function
                    **ft.xop

                } ) for ft in fuse.Type.Index
            }
        }
    )

No warnings or errors using these yet, so seems promising.

Edit: This works great. The problems listed below were caused by an issue in the property list data, which was causing a silent error, which prevented ALL of the properties from registering, making the dynamic class appear empty. After finding and fixing the silent error, everything appears to be working.

However, the properties don't exist as they typically would in an instanced property group (of either dynamic class). Attempting to access a property using the syntax my_prop_group["id_name"] throws an error as if it doesn't exist. (A silent property error was causing this).

I'm not sure if I need to manually assign each one to default values (my_prop_group["id_name"] = DEFAULT).

Not sure if I need to include the entire dictionary of properties along with the __annotations__ dictionary of the same properties. Printing dir(*) of non-dynamic property groups seems to indicate that they should have both defined (silent error was causing properties to not register).

Robert
  • 1,265
  • 1
  • 13
  • 29
  • Using syntax my_prop_group.id_name gives me the error 'PropValues' object has no attribute 'id_name', even when I attempt to manually set its default value first (using my_prop_group.id_name = x). By comparing my dynamic class to non-dynamic classes (using dir()), it looks like there is still information missing. Non-dynamic classes have the properties listed in their primary members, where my dynamic class only has them listed inside of the annotations member. So it seems like something is still missing. I really appreciate the help, though. Please let me know if you have any ideas. – Robert Oct 28 '19 at 11:39
  • I may have made a mistake when I said non-dynamic classes look like they have more information. After setting up a simpler test, they actually look identical. In addition, I can access the prop of the simpler dynamic class. So there may be some other issue with my more complex version. I will try to figure this out when I get back from work. Thanks again. – Robert Oct 28 '19 at 12:16
  • It was actually being caused by a silent property error. I edited question to reflect. It appears to be working now. Is there any way to access or see these silent errors? There is nothing at all listed in the console. – Robert Oct 28 '19 at 12:40
  • 2
    What I am using for dynamic annotations is, define the class, eg class Foo: give it an empty dictionary as the __annotations__ member. Then populate with Foo.__annotations__[propname] = bpy.props.SomeProperty(**kwargs) – batFINGER Oct 28 '19 at 13:39
  • I didn't know that was possible. That would certainly make things simpler in many situations. Just out of curiosity, I wonder if this would work for classes that have at least one normal property defined? Assuming you would not have to manually define the annotations member, but it seems like everything else would work. – Robert Oct 28 '19 at 21:04
  • @batFINGER I'm wary about doing that as I'm not totally sure how the annotations are implemented and used as a Python-level language feature. Do you know for sure or is it explicitly guaranteed that populating __annotations__ will be equivalent for Blender's purposes to defining them as literals? If they're added after class creation, for example, then I wouldn't expect any __init_subclass__ or metaclass magic to work from bpy.types.operator. – Will Chen Mar 27 '21 at 17:49

0 Answers0