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

      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