@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://plugincafe.maxon.net/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://plugincafe.maxon.net/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)