Is there a better way to make addon working on both blender 2.80 and 2.79?












6












$begingroup$


I know this works:



import bpy

class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
if bpy.app.version >= (2, 80, 0):
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
else:
type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name= bpy.props.StringProperty(update=unedit)

bpy.utils.register_class(Test)


but it is coping and paste solution (bed for many properties). Is there a better way?










share|improve this question









$endgroup$

















    6












    $begingroup$


    I know this works:



    import bpy

    class Test(bpy.types.PropertyGroup):
    def unedit(self, context):
    print(self.type)
    if bpy.app.version >= (2, 80, 0):
    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
    name: bpy.props.StringProperty(update=unedit)
    else:
    type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
    name= bpy.props.StringProperty(update=unedit)

    bpy.utils.register_class(Test)


    but it is coping and paste solution (bed for many properties). Is there a better way?










    share|improve this question









    $endgroup$















      6












      6








      6





      $begingroup$


      I know this works:



      import bpy

      class Test(bpy.types.PropertyGroup):
      def unedit(self, context):
      print(self.type)
      if bpy.app.version >= (2, 80, 0):
      type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
      name: bpy.props.StringProperty(update=unedit)
      else:
      type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
      name= bpy.props.StringProperty(update=unedit)

      bpy.utils.register_class(Test)


      but it is coping and paste solution (bed for many properties). Is there a better way?










      share|improve this question









      $endgroup$




      I know this works:



      import bpy

      class Test(bpy.types.PropertyGroup):
      def unedit(self, context):
      print(self.type)
      if bpy.app.version >= (2, 80, 0):
      type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
      name: bpy.props.StringProperty(update=unedit)
      else:
      type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
      name= bpy.props.StringProperty(update=unedit)

      bpy.utils.register_class(Test)


      but it is coping and paste solution (bed for many properties). Is there a better way?







      python add-on compatibility






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 16 hours ago









      Piotr KowalczykPiotr Kowalczyk

      765




      765






















          4 Answers
          4






          active

          oldest

          votes


















          4












          $begingroup$

          Two separate addons



          The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.



          I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)



          Distribute as an addon for that version. If using github a branch is often used.



          If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py check once for version and import modules from that version folder.



          See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130






          share|improve this answer









          $endgroup$













          • $begingroup$
            I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
            $endgroup$
            – Craig D Jones
            15 hours ago



















          1












          $begingroup$

          I am answering my own question, maybe someone finds it useful.



          import bpy

          class Test_2_80(bpy.types.PropertyGroup):
          def unedit(self, context):
          print(self.type)
          type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
          name: bpy.props.StringProperty(update=unedit)


          class Test(Test_2_80):
          if bpy.app.version < (2, 80, 0):
          for val, fun in Test_2_80.__annotations__.items():
          exec('{} = fun'.format(val))
          val, fun = None, None
          else:
          pass

          bpy.utils.register_class(Test)

          bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
          bpy.context.scene.test.name = 'g'


          Any ideas for better solutions?






          share|improve this answer









          $endgroup$





















            1












            $begingroup$

            The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.



            Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.



            In python this is not so easy. You can use __debug__ which is pre-processed by the compiler and evaluated based on -O command line argument and any if False: part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.



            The solution I found is to use pypreprocessor. It lets you do:



            from pypreprocessor import pypreprocessor
            pypreprocessor.parse()

            #define 2_80

            #ifdef 2_80
            print('2_80 code')
            #else
            print('2_79 code')
            #endif


            Normally when you call .parse() it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.



            The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:



            pypreprocessor.defines.append('define')
            pypreprocessor.run = False
            pypreprocessor.save = True
            pypreprocessor.output = 'addon_2_80.py'
            pypreprocessor.removeMeta = True


            This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.






            share|improve this answer









            $endgroup$





















              0












              $begingroup$

              Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).



              Warning: I also have not tested either.



              Another option is to create a decorator:



              import bpy

              def copy_annotations_to_dict_if_old_blender(x):
              if bpy.app.version < (2, 80):
              x.__dict__.update(x.__annotations__)

              return x

              @copy_annotations_to_dict_if_old_blender
              class Test(bpy.types.PropertyGroup):
              def unedit(self, context):
              print(self.type)
              type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
              name: bpy.props.StringProperty(update=unedit)



              Previous code that used a metaclass instead:



              import bpy

              if bpy.app.version >= (2, 80):
              MyPropertyGroupMeta = bpy.RNAMetaPropGroup
              else:
              class MyPropertyGroup(bpy.types.PropertyGroup):
              def __new__(cls, name, bases, namespace, **kwargs):
              result = super().__new__(cls, name, bases, namespace, **kwargs)
              result.__dict__.update(result.__annotations__)
              return result

              class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
              __slots__ = ()

              class Test(MyPropertyGroup):
              def unedit(self, context):
              print(self.type)

              type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
              name: bpy.props.StringProperty(update=unedit)


              (the decorator code is more readable and more likely to work)






              share|improve this answer








              New contributor




              Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.






              $endgroup$













                Your Answer





                StackExchange.ifUsing("editor", function () {
                return StackExchange.using("mathjaxEditing", function () {
                StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
                StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
                });
                });
                }, "mathjax-editing");

                StackExchange.ready(function() {
                var channelOptions = {
                tags: "".split(" "),
                id: "502"
                };
                initTagRenderer("".split(" "), "".split(" "), channelOptions);

                StackExchange.using("externalEditor", function() {
                // Have to fire editor after snippets, if snippets enabled
                if (StackExchange.settings.snippets.snippetsEnabled) {
                StackExchange.using("snippets", function() {
                createEditor();
                });
                }
                else {
                createEditor();
                }
                });

                function createEditor() {
                StackExchange.prepareEditor({
                heartbeatType: 'answer',
                autoActivateHeartbeat: false,
                convertImagesToLinks: false,
                noModals: true,
                showLowRepImageUploadWarning: true,
                reputationToPostImages: null,
                bindNavPrevention: true,
                postfix: "",
                imageUploader: {
                brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                allowUrls: true
                },
                onDemand: true,
                discardSelector: ".discard-answer"
                ,immediatelyShowMarkdownHelp:true
                });


                }
                });














                draft saved

                draft discarded


















                StackExchange.ready(
                function () {
                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fblender.stackexchange.com%2fquestions%2f133457%2fis-there-a-better-way-to-make-addon-working-on-both-blender-2-80-and-2-79%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                4 Answers
                4






                active

                oldest

                votes








                4 Answers
                4






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                4












                $begingroup$

                Two separate addons



                The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.



                I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)



                Distribute as an addon for that version. If using github a branch is often used.



                If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py check once for version and import modules from that version folder.



                See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130






                share|improve this answer









                $endgroup$













                • $begingroup$
                  I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                  $endgroup$
                  – Craig D Jones
                  15 hours ago
















                4












                $begingroup$

                Two separate addons



                The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.



                I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)



                Distribute as an addon for that version. If using github a branch is often used.



                If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py check once for version and import modules from that version folder.



                See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130






                share|improve this answer









                $endgroup$













                • $begingroup$
                  I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                  $endgroup$
                  – Craig D Jones
                  15 hours ago














                4












                4








                4





                $begingroup$

                Two separate addons



                The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.



                I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)



                Distribute as an addon for that version. If using github a branch is often used.



                If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py check once for version and import modules from that version folder.



                See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130






                share|improve this answer









                $endgroup$



                Two separate addons



                The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.



                I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)



                Distribute as an addon for that version. If using github a branch is often used.



                If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py check once for version and import modules from that version folder.



                See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 15 hours ago









                batFINGERbatFINGER

                25.1k42674




                25.1k42674












                • $begingroup$
                  I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                  $endgroup$
                  – Craig D Jones
                  15 hours ago


















                • $begingroup$
                  I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                  $endgroup$
                  – Craig D Jones
                  15 hours ago
















                $begingroup$
                I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                $endgroup$
                – Craig D Jones
                15 hours ago




                $begingroup$
                I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
                $endgroup$
                – Craig D Jones
                15 hours ago













                1












                $begingroup$

                I am answering my own question, maybe someone finds it useful.



                import bpy

                class Test_2_80(bpy.types.PropertyGroup):
                def unedit(self, context):
                print(self.type)
                type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                name: bpy.props.StringProperty(update=unedit)


                class Test(Test_2_80):
                if bpy.app.version < (2, 80, 0):
                for val, fun in Test_2_80.__annotations__.items():
                exec('{} = fun'.format(val))
                val, fun = None, None
                else:
                pass

                bpy.utils.register_class(Test)

                bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
                bpy.context.scene.test.name = 'g'


                Any ideas for better solutions?






                share|improve this answer









                $endgroup$


















                  1












                  $begingroup$

                  I am answering my own question, maybe someone finds it useful.



                  import bpy

                  class Test_2_80(bpy.types.PropertyGroup):
                  def unedit(self, context):
                  print(self.type)
                  type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                  name: bpy.props.StringProperty(update=unedit)


                  class Test(Test_2_80):
                  if bpy.app.version < (2, 80, 0):
                  for val, fun in Test_2_80.__annotations__.items():
                  exec('{} = fun'.format(val))
                  val, fun = None, None
                  else:
                  pass

                  bpy.utils.register_class(Test)

                  bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
                  bpy.context.scene.test.name = 'g'


                  Any ideas for better solutions?






                  share|improve this answer









                  $endgroup$
















                    1












                    1








                    1





                    $begingroup$

                    I am answering my own question, maybe someone finds it useful.



                    import bpy

                    class Test_2_80(bpy.types.PropertyGroup):
                    def unedit(self, context):
                    print(self.type)
                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                    name: bpy.props.StringProperty(update=unedit)


                    class Test(Test_2_80):
                    if bpy.app.version < (2, 80, 0):
                    for val, fun in Test_2_80.__annotations__.items():
                    exec('{} = fun'.format(val))
                    val, fun = None, None
                    else:
                    pass

                    bpy.utils.register_class(Test)

                    bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
                    bpy.context.scene.test.name = 'g'


                    Any ideas for better solutions?






                    share|improve this answer









                    $endgroup$



                    I am answering my own question, maybe someone finds it useful.



                    import bpy

                    class Test_2_80(bpy.types.PropertyGroup):
                    def unedit(self, context):
                    print(self.type)
                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                    name: bpy.props.StringProperty(update=unedit)


                    class Test(Test_2_80):
                    if bpy.app.version < (2, 80, 0):
                    for val, fun in Test_2_80.__annotations__.items():
                    exec('{} = fun'.format(val))
                    val, fun = None, None
                    else:
                    pass

                    bpy.utils.register_class(Test)

                    bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
                    bpy.context.scene.test.name = 'g'


                    Any ideas for better solutions?







                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered 16 hours ago









                    Piotr KowalczykPiotr Kowalczyk

                    765




                    765























                        1












                        $begingroup$

                        The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.



                        Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.



                        In python this is not so easy. You can use __debug__ which is pre-processed by the compiler and evaluated based on -O command line argument and any if False: part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.



                        The solution I found is to use pypreprocessor. It lets you do:



                        from pypreprocessor import pypreprocessor
                        pypreprocessor.parse()

                        #define 2_80

                        #ifdef 2_80
                        print('2_80 code')
                        #else
                        print('2_79 code')
                        #endif


                        Normally when you call .parse() it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.



                        The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:



                        pypreprocessor.defines.append('define')
                        pypreprocessor.run = False
                        pypreprocessor.save = True
                        pypreprocessor.output = 'addon_2_80.py'
                        pypreprocessor.removeMeta = True


                        This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.






                        share|improve this answer









                        $endgroup$


















                          1












                          $begingroup$

                          The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.



                          Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.



                          In python this is not so easy. You can use __debug__ which is pre-processed by the compiler and evaluated based on -O command line argument and any if False: part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.



                          The solution I found is to use pypreprocessor. It lets you do:



                          from pypreprocessor import pypreprocessor
                          pypreprocessor.parse()

                          #define 2_80

                          #ifdef 2_80
                          print('2_80 code')
                          #else
                          print('2_79 code')
                          #endif


                          Normally when you call .parse() it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.



                          The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:



                          pypreprocessor.defines.append('define')
                          pypreprocessor.run = False
                          pypreprocessor.save = True
                          pypreprocessor.output = 'addon_2_80.py'
                          pypreprocessor.removeMeta = True


                          This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.






                          share|improve this answer









                          $endgroup$
















                            1












                            1








                            1





                            $begingroup$

                            The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.



                            Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.



                            In python this is not so easy. You can use __debug__ which is pre-processed by the compiler and evaluated based on -O command line argument and any if False: part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.



                            The solution I found is to use pypreprocessor. It lets you do:



                            from pypreprocessor import pypreprocessor
                            pypreprocessor.parse()

                            #define 2_80

                            #ifdef 2_80
                            print('2_80 code')
                            #else
                            print('2_79 code')
                            #endif


                            Normally when you call .parse() it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.



                            The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:



                            pypreprocessor.defines.append('define')
                            pypreprocessor.run = False
                            pypreprocessor.save = True
                            pypreprocessor.output = 'addon_2_80.py'
                            pypreprocessor.removeMeta = True


                            This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.






                            share|improve this answer









                            $endgroup$



                            The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.



                            Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.



                            In python this is not so easy. You can use __debug__ which is pre-processed by the compiler and evaluated based on -O command line argument and any if False: part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.



                            The solution I found is to use pypreprocessor. It lets you do:



                            from pypreprocessor import pypreprocessor
                            pypreprocessor.parse()

                            #define 2_80

                            #ifdef 2_80
                            print('2_80 code')
                            #else
                            print('2_79 code')
                            #endif


                            Normally when you call .parse() it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.



                            The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:



                            pypreprocessor.defines.append('define')
                            pypreprocessor.run = False
                            pypreprocessor.save = True
                            pypreprocessor.output = 'addon_2_80.py'
                            pypreprocessor.removeMeta = True


                            This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.







                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered 14 hours ago









                            Jaroslav Jerryno NovotnyJaroslav Jerryno Novotny

                            39.3k176155




                            39.3k176155























                                0












                                $begingroup$

                                Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).



                                Warning: I also have not tested either.



                                Another option is to create a decorator:



                                import bpy

                                def copy_annotations_to_dict_if_old_blender(x):
                                if bpy.app.version < (2, 80):
                                x.__dict__.update(x.__annotations__)

                                return x

                                @copy_annotations_to_dict_if_old_blender
                                class Test(bpy.types.PropertyGroup):
                                def unedit(self, context):
                                print(self.type)
                                type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                name: bpy.props.StringProperty(update=unedit)



                                Previous code that used a metaclass instead:



                                import bpy

                                if bpy.app.version >= (2, 80):
                                MyPropertyGroupMeta = bpy.RNAMetaPropGroup
                                else:
                                class MyPropertyGroup(bpy.types.PropertyGroup):
                                def __new__(cls, name, bases, namespace, **kwargs):
                                result = super().__new__(cls, name, bases, namespace, **kwargs)
                                result.__dict__.update(result.__annotations__)
                                return result

                                class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
                                __slots__ = ()

                                class Test(MyPropertyGroup):
                                def unedit(self, context):
                                print(self.type)

                                type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                name: bpy.props.StringProperty(update=unedit)


                                (the decorator code is more readable and more likely to work)






                                share|improve this answer








                                New contributor




                                Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                Check out our Code of Conduct.






                                $endgroup$


















                                  0












                                  $begingroup$

                                  Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).



                                  Warning: I also have not tested either.



                                  Another option is to create a decorator:



                                  import bpy

                                  def copy_annotations_to_dict_if_old_blender(x):
                                  if bpy.app.version < (2, 80):
                                  x.__dict__.update(x.__annotations__)

                                  return x

                                  @copy_annotations_to_dict_if_old_blender
                                  class Test(bpy.types.PropertyGroup):
                                  def unedit(self, context):
                                  print(self.type)
                                  type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                  name: bpy.props.StringProperty(update=unedit)



                                  Previous code that used a metaclass instead:



                                  import bpy

                                  if bpy.app.version >= (2, 80):
                                  MyPropertyGroupMeta = bpy.RNAMetaPropGroup
                                  else:
                                  class MyPropertyGroup(bpy.types.PropertyGroup):
                                  def __new__(cls, name, bases, namespace, **kwargs):
                                  result = super().__new__(cls, name, bases, namespace, **kwargs)
                                  result.__dict__.update(result.__annotations__)
                                  return result

                                  class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
                                  __slots__ = ()

                                  class Test(MyPropertyGroup):
                                  def unedit(self, context):
                                  print(self.type)

                                  type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                  name: bpy.props.StringProperty(update=unedit)


                                  (the decorator code is more readable and more likely to work)






                                  share|improve this answer








                                  New contributor




                                  Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                  Check out our Code of Conduct.






                                  $endgroup$
















                                    0












                                    0








                                    0





                                    $begingroup$

                                    Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).



                                    Warning: I also have not tested either.



                                    Another option is to create a decorator:



                                    import bpy

                                    def copy_annotations_to_dict_if_old_blender(x):
                                    if bpy.app.version < (2, 80):
                                    x.__dict__.update(x.__annotations__)

                                    return x

                                    @copy_annotations_to_dict_if_old_blender
                                    class Test(bpy.types.PropertyGroup):
                                    def unedit(self, context):
                                    print(self.type)
                                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                    name: bpy.props.StringProperty(update=unedit)



                                    Previous code that used a metaclass instead:



                                    import bpy

                                    if bpy.app.version >= (2, 80):
                                    MyPropertyGroupMeta = bpy.RNAMetaPropGroup
                                    else:
                                    class MyPropertyGroup(bpy.types.PropertyGroup):
                                    def __new__(cls, name, bases, namespace, **kwargs):
                                    result = super().__new__(cls, name, bases, namespace, **kwargs)
                                    result.__dict__.update(result.__annotations__)
                                    return result

                                    class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
                                    __slots__ = ()

                                    class Test(MyPropertyGroup):
                                    def unedit(self, context):
                                    print(self.type)

                                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                    name: bpy.props.StringProperty(update=unedit)


                                    (the decorator code is more readable and more likely to work)






                                    share|improve this answer








                                    New contributor




                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.






                                    $endgroup$



                                    Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).



                                    Warning: I also have not tested either.



                                    Another option is to create a decorator:



                                    import bpy

                                    def copy_annotations_to_dict_if_old_blender(x):
                                    if bpy.app.version < (2, 80):
                                    x.__dict__.update(x.__annotations__)

                                    return x

                                    @copy_annotations_to_dict_if_old_blender
                                    class Test(bpy.types.PropertyGroup):
                                    def unedit(self, context):
                                    print(self.type)
                                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                    name: bpy.props.StringProperty(update=unedit)



                                    Previous code that used a metaclass instead:



                                    import bpy

                                    if bpy.app.version >= (2, 80):
                                    MyPropertyGroupMeta = bpy.RNAMetaPropGroup
                                    else:
                                    class MyPropertyGroup(bpy.types.PropertyGroup):
                                    def __new__(cls, name, bases, namespace, **kwargs):
                                    result = super().__new__(cls, name, bases, namespace, **kwargs)
                                    result.__dict__.update(result.__annotations__)
                                    return result

                                    class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
                                    __slots__ = ()

                                    class Test(MyPropertyGroup):
                                    def unedit(self, context):
                                    print(self.type)

                                    type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
                                    name: bpy.props.StringProperty(update=unedit)


                                    (the decorator code is more readable and more likely to work)







                                    share|improve this answer








                                    New contributor




                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.









                                    share|improve this answer



                                    share|improve this answer






                                    New contributor




                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.









                                    answered 5 hours ago









                                    Solomon UckoSolomon Ucko

                                    1012




                                    1012




                                    New contributor




                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.





                                    New contributor





                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.






                                    Solomon Ucko is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                                    Check out our Code of Conduct.






























                                        draft saved

                                        draft discarded




















































                                        Thanks for contributing an answer to Blender Stack Exchange!


                                        • Please be sure to answer the question. Provide details and share your research!

                                        But avoid



                                        • Asking for help, clarification, or responding to other answers.

                                        • Making statements based on opinion; back them up with references or personal experience.


                                        Use MathJax to format equations. MathJax reference.


                                        To learn more, see our tips on writing great answers.




                                        draft saved


                                        draft discarded














                                        StackExchange.ready(
                                        function () {
                                        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fblender.stackexchange.com%2fquestions%2f133457%2fis-there-a-better-way-to-make-addon-working-on-both-blender-2-80-and-2-79%23new-answer', 'question_page');
                                        }
                                        );

                                        Post as a guest















                                        Required, but never shown





















































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown

































                                        Required, but never shown














                                        Required, but never shown












                                        Required, but never shown







                                        Required, but never shown







                                        Popular posts from this blog

                                        How to label and detect the document text images

                                        Vallis Paradisi

                                        Tabula Rosettana