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

    Distributing Python Plugins that have Dependencies

    Cinema 4D SDK
    3
    14
    3.4k
    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.
    • M
      m_adam
      last edited by

      Hi @dskeithbuck, unfortunately, we have nothing to offer there. I know some user have an installer that runs some pre-build script. But it's still a bit tricky as c4dpy need to be launched at least once before to be used in order to login from MyMaxon.
      Another solution may be to directly install into the python located in the resource/modules/python/libs/... folder.
      So this way it's also possible to create a CommandData that will download and install pip and all others needed module not sure if it's a good solution for you but at least it should work. Note that once pip is installed you can use pip at runtime on your script How to install and import python modules at runtime.

      In any case please feel free to give us some feedback or any idea on this topic, or if you have an idea of how it could work, please let us know.

      Cheers,
      Maxime.

      MAXON SDK Specialist

      Development Blog, MAXON Registered Developer

      ferdinandF dskeithbuckD 2 Replies Last reply Reply Quote 0
      • ferdinandF
        ferdinand @m_adam
        last edited by

        @m_adam said in Distributing Python Plugins that have Dependencies:

        Note that once pip is installed you can use pip at runtime on your script How to install and import python modules at runtime.

        Hi,

        I might be misunderstanding something here, but I still want to point out that espically pip2 is not thread safe and it is explicitly disencouraged to use pip as a module.

        Cheers
        zipit

        MAXON SDK Specialist
        developers.maxon.net

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

          Thanks for pointing to theses limitations I was not aware of.

          However, if you execute pip into a running Cinema 4D as CommandData in the Execute function it shouldn't be an issue:

          • The pip code assumes that is in sole control of the global state of the program. It's the case in theCommandData::Execute you are in charge of the global state of the program.
          • Pip’s code is not thread safe. You are in the Main Thread.
          • Pip assumes that once it has finished its work, the process will terminate. A Python script in Cinema 4D can't close Cinema 4D or the python interpreter.

          So while I never tested, looking at the limitation it should work without any issue. As you can have you plugin checking if import can be done, if not simply leave. Then have another CommandData plugin to install your stuffs. Restart Cinema 4D and it should work.

          Cheers,
          Maxime.

          MAXON SDK Specialist

          Development Blog, MAXON Registered Developer

          1 Reply Last reply Reply Quote 0
          • dskeithbuckD
            dskeithbuck @ferdinand
            last edited by

            @zipit Request's dependency chain isn't too bad, but a couple of those libraries also have dependencies which is where it starts to get a little ugly. I'll give it another go.

            1 Reply Last reply Reply Quote 0
            • dskeithbuckD
              dskeithbuck @m_adam
              last edited by

              @m_adam Thanks, I'll have a look at pip and/or an installer script. I'm a bit concerned about adding anything directly to the resource/modules/python/libs folder as there's a chance some other plugin or script will have installed/need a different version of the same library. But, I suppose that I can report this as an installation error to the user and let them sort out which is more important for them to have installed.

              I'll be sure to let you know if I come to a resolution.

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

                While I know is not the perfect solution, and is a really quick and dirty I would say find bellow a script to setup a build with some python module.

                Just call c4dpy file.py.

                import subprocess
                import urllib
                import sys
                import c4d
                import os
                import ssl
                import shutil
                import importlib
                import maxon
                
                currentDir = os.path.dirname(__file__)
                c4dpyPath = sys.executable
                c4dTempFolder = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_STARTUPWRITE)
                
                c4dDir = maxon.Application.GetUrl(maxon.APPLICATION_URLTYPE.STARTUP_DIR).GetPath()
                if os.name != "nt" and not c4dDir.startswith(os.sep):
                    c4dDir = os.sep + c4dDir
                
                try:
                    import pip
                    print "pip already installed"
                except ImportError:
                    print('start downloading get-pip.py')
                
                    url = 'https://bootstrap.pypa.io/get-pip.py'
                    pipPath = os.path.join(c4dTempFolder, "get-pip.py")
                
                    # Quick hack for MAC until https://developers.maxon.net/forum/topic/11370/urllib2-urlopen-fails-on-c4d-for-mac/8 is fixed
                    f = os.path.join(c4dDir, "resource", "ssl", "cacert.pem")
                    context = ssl.create_default_context(cafile=f)
                
                    # Downloads pip
                    urllib.urlretrieve(url, pipPath, context=context)
                
                    print('start installing pip')
                
                    if os.name == "nt":
                        subprocess.call([c4dpyPath, pipPath, "--no-warn-script-location"], shell=True)
                    else:
                        os.system("{0} {1} {2}".format(c4dpyPath, pipPath, "--no-warn-script-location"))
                
                    shutil.rmtree(pipPath, ignore_errors=True)
                
                def installModule(moduleName):
                    try:
                        importlib.import_module(moduleName)
                        print "{0} already installed".format(moduleName)
                    except ImportError:
                
                        print('start installing {0}'.format(moduleName))
                
                        if os.name == "nt":
                            subprocess.call([c4dpyPath, "-m", "pip", "install", moduleName, "--no-warn-script-location"], shell=True)
                        else:
                            os.system("{0} {1} {2} {3}".format(c4dpyPath, "-m pip install", moduleName,"--no-warn-script-location"))
                
                        print('{0} installation is done'.format(moduleName))
                
                installModule("numpy")
                

                Cheers,
                Maxime

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                dskeithbuckD 2 Replies Last reply Reply Quote 1
                • dskeithbuckD
                  dskeithbuck @m_adam
                  last edited by

                  @m_adam said in Distributing Python Plugins that have Dependencies:

                  Just call c4dpy file.py.

                  I'm not having much luck running or calling c4dpy in R21.

                  Microsoft Windows [Version 10.0.17763.737]
                  (c) 2018 Microsoft Corporation. All rights reserved.
                  
                  C:\Users\donovan>cd "C:\Program Files\Maxon Cinema 4D R21"
                  
                  C:\Program Files\Maxon Cinema 4D R21>c4dpy
                  Error running authentication: invalid http response 400.  (https://id.maxon.net/oauth2/access?scope=openid+profile+email&grant_type=refresh_token&refresh_token=[[REDACTED]]&client_secret=[[REDACTED]]) [http_file.cpp(313)]
                  ----
                  Enter Maxon Account Settings (ENTER to keep input):
                  License Check error: invalid http response 400.  (https://id.maxon.net/oauth2/access?scope=openid+profile+email&grant_type=refresh_token&refresh_token=[[REDACTED]]&&client_id=[[REDACTED]]&client_secret=[[REDACTED]]) [http_file.cpp(313)]
                  Error: Invalid License
                  

                  I'm able to open Cinema 4D and don't have any licensing issues there. I also did a complete uninstall/reinstall of C4D and had the same issues.

                  Thank you for writing that script, it seems like it will do the trick if I can get c4dpy to run.

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

                    Try to open Cinema 4D.
                    Go to the preference opens the preference folder.
                    Go one level before. There is normally another cinéma 4d pref folder called like the previous one with the prefix _p (or _c Im on my phone writting by memory i will confirme tomorrow). Delete thid folder. Try again.

                    Cheers,
                    Maxime.

                    MAXON SDK Specialist

                    Development Blog, MAXON Registered Developer

                    dskeithbuckD 1 Reply Last reply Reply Quote 0
                    • dskeithbuckD
                      dskeithbuck @m_adam
                      last edited by

                      @m_adam It's a _p suffix. Deleting it eliminated the error messages and allowed me to log in. Thank you!

                      Is there any chance that c4dpy.exe can get its license from the C4D installation without having to manually enter it in the command prompt? It's not too much of an issue for me, but I imagine seeing the terminal will be a bit intimidating for end users.

                      1 Reply Last reply Reply Quote 0
                      • dskeithbuckD
                        dskeithbuck @m_adam
                        last edited by dskeithbuck

                        @m_adam said in Distributing Python Plugins that have Dependencies:

                        While I know is not the perfect solution, and is a really quick and dirty I would say find bellow a script to setup a build with some python module.

                        Hi Maxime,

                        Your script got me most of the way there, but I ran into some issues.

                        It installs libraries to the default location for system python libraries rather than C4D's lib folder. So, I started modifying it to install to userprefs/python27/lib using the --target flag. But I ran into issues with spaces in the pathname. Which led me to switch to calling with a list of commands/parameters rather than a string.

                        After some tweaking, I arrived at this:

                        """Install C4D Python Modules
                        Installs python modules in C4D's embedded python installation
                        
                        ## Authors
                        
                        Maxime of Maxon Computer
                        Donovan Keith of Buck Design
                        
                        Reference:
                        https://developers.maxon.net/forum/topic/11775/distributing-python-plugins-that-have-dependencies/8
                        
                        ## Requirements
                        
                        Cinema 4D R21+
                        
                        ## Usage Instructions
                        
                        1. Copy this file onto your desktop.
                        2. Open `terminal` (Mac OS) or `cmd` (Windows) and navigate to your Cinema 4D installation.
                        3. `$ c4dpy`
                            1. This will run a Cinema 4D specific version of python.
                            2. If prompted to login, do so.
                            3. If you get an HTTP/authentication error:
                                1. Open Cinema 4D.
                                2. Edit > Preferences and click on "Open Preferences Folder"
                                3. Go up one level in finder/explorer.
                                4. Look for a folder with the same name and a `_p` suffix.
                                5. Delete this folder.
                                6. Try running `c4dpy` again from the console.
                        4. Exit the interactive python session by typing `exit()`
                        5. Run this script.  
                        `>>> c4dpy "/path/to/this/script/install_c4dpy_modules.py"`
                        
                        ## Known Limitations
                        
                        1. Hasn't been tested on Mac OS
                        2. Is checking the `c4dpy` installation for whether a python module has been installed, but is installing in the `c4d` installation.
                        
                        """
                        
                        print "C4D Py Requirements Installer"
                        
                        import subprocess
                        import urllib
                        import sys
                        import c4d
                        import os
                        import ssl
                        import shutil
                        import importlib
                        import maxon
                        
                        currentDir = os.path.dirname(__file__)
                        c4dpyPath = sys.executable
                        c4dpyTempFolder = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_STARTUPWRITE)
                        
                        # Because we're using `c4dpy`, it will pull a different prefs folder from the user's c4d installation.
                        # `Maxon Cinema 4D R21_64C2B3BD_p` becomes `Maxon Cinema 4D R21_64C2B3BD`
                        c4dTempFolder = c4dpyTempFolder[:-2] if c4dpyTempFolder.endswith("_p") else c4dpyTempFolder
                        
                        c4dDir = maxon.Application.GetUrl(maxon.APPLICATION_URLTYPE.STARTUP_DIR).GetPath()
                        if os.name != "nt" and not c4dDir.startswith(os.sep):
                            c4dDir = os.sep + c4dDir
                        
                        # Just using the `--user` flag with `pip` will install to the System python dir, rather than the
                        # C4D embedded python's dir. So we specify exactly where we want to install.
                        c4dUserPythonLibPath = os.path.join(c4dTempFolder, "python27", "libs")
                        
                        try:
                            import pip
                            print "pip already installed"
                        except ImportError:
                            print('start downloading get-pip.py')
                        
                            url = 'https://bootstrap.pypa.io/get-pip.py'
                            pipPath = os.path.join(c4dTempFolder, "get-pip.py")
                        
                            # Quick hack for MAC until https://developers.maxon.net/forum/topic/11370/urllib2-urlopen-fails-on-c4d-for-mac/8 is fixed
                            f = os.path.join(c4dDir, "resource", "ssl", "cacert.pem")
                            context = ssl.create_default_context(cafile=f)
                        
                            # Downloads pip
                            urllib.urlretrieve(url, pipPath, context=context)
                        
                            print('start installing pip')
                        
                            if os.name == "nt":
                                subprocess.call([c4dpyPath, pipPath, "--no-warn-script-location", "--user"], shell=True)
                            else:
                                os.system("{0} {1} {2} {3}".format(c4dpyPath, pipPath, "--no-warn-script-location", "--user"))
                        
                            shutil.rmtree(pipPath, ignore_errors=True)
                        
                        def installModule(moduleName):
                            try:
                                importlib.import_module(moduleName)
                                print "{0} already installed".format(moduleName)
                            except ImportError:
                        
                                try:
                                    print('start installing {0}'.format(moduleName))
                        
                                    # We build up the command as list rather than a string so that Windows will properly handle paths that include spaces
                                    cmd = [c4dpyPath, '-m', 'pip', 'install', moduleName, '--target', c4dUserPythonLibPath]
                                    subprocess.call(cmd)
                                except subprocess.CalledProcessError as e:
                                    print e.output
                                else:
                                    print('{0} installation is done'.format(moduleName))
                        
                        # Add any modules you want installed to this list.
                        required_modules = ["requests"]
                        
                        for module in required_modules:
                            installModule(module)
                        
                        1 Reply Last reply Reply Quote 0
                        • M
                          m_adam
                          last edited by

                          Hi, @dskeithbuck thanks for sharing yours though. And I'm glad if it works for you. But I still understand that an interpreter can be quiet intimidating for the user. And if you found a nicer solution, don't hesitate to share.

                          As an Idea but didn't try and will not have the time to do it today (so if you try, do it at your own risks), but you could try to install things using directly the python executable from the resource folder (as previously when c4dpy didn't exist). Keep in mind this python is a standard python so that means it does not come with any c4d related stuff (so no Maxon/c4d module). This way you don't need the user to log in and can execute the whole thing without the need to pop up the console, and maybe a check if Cinema 4D is running is safer to avoid any issue. You can retrieve a tempfile using the tempfile module in python which comes by default with python.

                          With that's said regarding the previous error 400, did you already used this particular c4dpy previously, or it was the first time? Did you already "setupped" this particular c4dpy in any IDE? Any more information to reproduce is welcome.

                          Cheers,
                          Maxime.

                          MAXON SDK Specialist

                          Development Blog, MAXON Registered Developer

                          dskeithbuckD 1 Reply Last reply Reply Quote 0
                          • dskeithbuckD
                            dskeithbuck @m_adam
                            last edited by

                            @m_adam said in Distributing Python Plugins that have Dependencies:

                            As an Idea but didn't try and will not have the time to do it today (so if you try, do it at your own risks), but you could try to install things using directly the python executable from the resource folder

                            I'm moving onto different parts of development, but I'll likely want to investigate this later and will post an update if I do. Thanks!

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