8
$\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
9
+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$ – Stan Paillereau Sep 18 '18 at 11:48
  • $\begingroup$ You wouldn't pass default='MyString', I think. Just write clear, unambiguous code ;-) $\endgroup$ – dr. Sybren Sep 20 '18 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$ – Stan Paillereau Sep 20 '18 at 17:07
4
$\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$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.