Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Dynamically Import All Python Files

    General Talk
    r21 python
    3
    5
    2.0k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      bentraje
      last edited by bentraje

      Hi,

      I'm trying to import dynamically import python files in a directory.

      For example if the directory is:

      utility
      ------init.py
      ------file1.py
      ------file2.py
      ------file3.py
      

      The code to import them would be

      from utility import file1
      from utility import file2
      from utility import file3
      

      I want this line of code to be executed dynamically. So if I add a file4.py, I don't want to write from utility import import file4. That's why I want them to import dynamically.

      However, my code below gives an error of ImportError: cannot import name 'filename' where it treats the filename as an actual string and not as a variable.

      Is there a way around this?

      Here is the current code:

      import utility
      
      for f in files:
          if f.endswith(".py") and not f.startswith('__init__'):
              filename = f.split('.')  
              from utility import filename[0]
      

      P.S. There are some solutions in the stack overflow like this one:
      https://stackoverflow.com/questions/1057431/how-to-load-all-modules-in-a-folder

      But again, it requires to be executed every time I add a python file in the directory (i.e. no dynamic)

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hi,

        for clarification: I think you do not actually mean importing modules dynamically (which would imply importing them on a "as needed basis" depending on the branching of your code. From what I do understand you want to implicitly import all modules located in a package (folder). I assume you are aware that python has the wildcard syntax for that.

        from package import a, b, c, d
        # is equivalent to (assuming there is only a, b, c, d)
        from package import *
        

        To make this somewhat useful you will have to define in your __init__.py what you want to be imported (as described in your linked post).

        However to import from packages Python has to know where to look for them. If you have your folder utility in your c4d plugin folder, this won't work out of the box. There are multiple ways to achieve this and people will probably boo me for telling you this, but the easiest way is to just add to (or pollute as some people would say) the sys path.

        _path = os.path.dirname(__file__)
        
        if _path not in sys.path:
            sys.path.insert(0, _path)
        
        #assuming foo is a folder/package in the dir of __file__
        from foo import bar
        

        One of the drawbacks is that you have to do this:

        _path = os.path.dirname(__file__)
        
        if _path not in sys.path:
            sys.path.insert(0, _path)
        
        from foo import bar
        
        if SOME_SORT_OF_DEBUG_CONDITION_IS_TRUE:
            reload(bar)
        

        If you want to reload that module on a running Python VM. For example when you hit "reload python plugins" in c4d. Otherwise you have to restart c4d for each code change to apply.

        Cheers
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 1
        • B
          bentraje
          last edited by bentraje

          @zipit
          Thanks for the response. Yes, I ended up "polluting" the sys.path. I don't mind though. It works as expected.
          Here is the working code for me without using the __init__ file and it imports dynamically.

          import c4d
          import os
          import sys
          
          rigDir = "C:/Users/Luke/Dropbox/Scripts/c4d"
          if not rigDir in sys.path:
              sys.path.append(rigDir)
          
          dir_path = "C:/Users/Luke/Dropbox/Scripts/c4d/utility"
          files = os.listdir(dir_path)
          
          import utility
          from utility import hello_world
          
          for f in files:
              if f.endswith(".py") and not f.startswith('__init__'):
                  filename = f.split('.')
                  #from utility import filename[0]
                  exec('from utility import %s' % filename[0])
                  exec('reload(%s)'% filename[0])
          

          The only caveat so far is to convert all scripts to

          if __name__=='__main__':
              main()
          

          Because at this point, all scripts get executed when imported hahaha.
          Should have listed to everyone telling me to use the if __name__=='__main__' from the start.

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by

            Hi @bentraje,

            While @zipit has almost said everything I would like to add few things:

            • if __name__=='__main__' is really needed since its ensure that your code is not called from anywhere, performance-wise its also very important, even if it's not a big issue in your plugin logic if your code is executed each time you import stuff, think to the user and his performance.
            • It's recommended to do local import and not global import (as you do) since if you add a module named xyz to the sys.path from your plugin A and from plugin B you try to import another module named xyz, it will load the one from plugin A (the first one register). And the other plugin will not work. For more information read Best Practise for import and especially the great localimport module from @NiklasR that is mentioned in the Best Practise for Import topic

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 1
            • B
              bentraje
              last edited by

              @m_adam

              Thanks for the additional insight and the related links. Appreciate it a lot.

              1 Reply Last reply Reply Quote 0
              • First post
                Last post