11
$\begingroup$

Now that we need to use field for property declarations in Blender 2.8. I was wondering how this can be done when creating class dynamically using type() like so:

type('TestPropGrp', (bpy.types.PropertyGroup,), {‘MyProp’:StringProperty(default=‘MyStr’)})

=> this would work in Blender 2.8 but will throw a 'make field' warning.

Any idea how to declare field when using type() ?

Thank you !

edit (this is actually wrong - see answer): Of course one way to do it is to write this in 2 lines:

clss = type('TestPropGrp', (bpy.types.PropertyGroup,), {})

clss.myProp : StringProperty(default=‘MyStr’)

but I was wondering if there could be a more compact solution ?

$\endgroup$
0

2 Answers 2

12
+50
$\begingroup$

The annotations for an object (which can be a class or a function as well) are stored in its __annotations__ property, as defined by PEP 526 and introduced in Python 3.6. This gives rise to the following solution:

The following approach works as I suspect you're looking for:

>>> clss = type('TestPropGrp2', (bpy.types.PropertyGroup,), {
...     '__annotations__': {'myProp': (StringProperty, {'default': 'MyStr'})}
... })
>>> print(clss.__annotations__)
{'myProp': (<built-in function StringProperty>, {'default': 'MyStr'})}

This also allows you to see that your proposed "one way to do it" approach doesn't actually work as intended:

>>> clss = type('TestPropGrp', (bpy.types.PropertyGroup,), {})
>>> clss.myProp : StringProperty(default='MyStr')
>>> print(clss.__annotations__)
Traceback (most recent call last):
  File "<blender_console>", line 1, in <module>
AttributeError: type object 'TestPropGrp' has no attribute '__annotations__'
$\endgroup$
3
  • $\begingroup$ Thx Sybren! I’m still confuse with the use a field property but that will help. I need to test but There is si till some blurred case like what would be the init value if you declare: myProp: bpy.props.StringProperty(default=‘MyString’) = ‘myString2’. $\endgroup$ Commented Sep 18, 2018 at 11:48
  • $\begingroup$ You wouldn't pass default='MyString', I think. Just write clear, unambiguous code ;-) $\endgroup$
    – dr. Sybren
    Commented Sep 20, 2018 at 14:14
  • $\begingroup$ Well that is the way we were passing a default value for StringProperty before so it means that now with field property this doesn’t make any sense. BTW, This notation would end up with myProp having a value of ´myString’. $\endgroup$ Commented Sep 20, 2018 at 17:07
6
$\begingroup$

I’ll extend Sybren's answer above with a warning, after some bug just found in our @orientation_helper() IO class decorator today (see https://developer.blender.org/T58772 ). It does not directly affect your example, but it could in some slightly different dynamic class generation cases.

Do ensure you have an __annotations__ dict defined in the class you want to add fields to! Otherwise:

  • If one of your parent classes defines it, you will actually add the field to that parent's __annotations__ dict, effectively adding that field to all the children classes as well, and not only the one you wanted to.
  • If there is no __annotations__ defined anywhere in parent's hierarchy, you'll just get an error (which is much simpler to debug than the first case).

Bug linked above leaded to all IO add-ons using ImportHelper as parent of their Import operator to display the Up/Front axes selectors, even though they would not need them. We had to switch from that (simplified code):

def orientation_helper(axis_forward='Y', axis_up='Z'):
    def wrapper(cls):
        cls.__annotations__['axis_forward'] = StringProperty()

…to that:

def orientation_helper(axis_forward='Y', axis_up='Z'):
    def wrapper(cls):
        # Without that, we may end up adding those fields to some **parent** class' __annotations__ property
        # (like the ImportHelper or ExportHelper ones)! See T58772.
        if "__annotations__" not in cls.__dict__:
            setattr(cls, "__annotations__", {})

        cls.__annotations__['axis_forward'] = StringProperty()
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .