Python Libraries¶
Learn how to expose third party libraries to the Python environments of Cinema 4D.
Note
MAXON cannot provide support for third party libraries written for CPython and their compatibility with the Python interpreters shipped with Cinema 4D. While our Python interpreters are derived from the respective CPython builds, they are not identical to them. Popular Python libraries as for example numpy
might not work at all or only with a reduced feature set. The Cinema 4D Python interpreters are provided as-is and not meant to be fully CPython compliant.
The Python interprets shipped with Cinema 4D are pre-configured to allow users to import custom modules from multiple search paths. The user can also add custom search paths with environment variables or on a plugin level by modifying the search paths of a Python instance at runtime.
Table of Contents
Installation & App Specific Search Paths¶
There are in principle two predefined search paths into which libraries can be placed for each app inside an installation of Cinema 4D. One for libraries which are intended for a specific installation of an app, and one for all apps that use a specific version of Python. The app specific search paths are all tied to a specific installation of Cinema 4D and also differentiate between the apps that have been shipped with that Cinema 4D installation, as for example the Cinema 4D, c4dpy, or commandline executable. The search paths can be located with the following patterns:
Cinema 4D Version |
Operating System |
Path |
---|---|---|
R20 <= C4D_VERSION < R23 |
Windows |
|
R20 <= C4D_VERSION < R23 |
MacOS |
|
R23 < C4D_VERSION <= S24 |
Windows |
|
R23 < C4D_VERSION <= S24 |
MacOS |
|
S24 < C4D_VERSION <= 2023.1 |
Windows |
|
S24 < C4D_VERSION <= 2023.1 |
MacOS |
|
2023.1 < C4D_VERSION |
Windows |
|
2023.1 < C4D_VERSION |
MacOS |
|
Within these patterns, the symbol {prefs}
stands for the distinct preferences directories of the different apps that are being shipped with a Cinema 4D installation. For a singular Cinema 4D installation up to six preferences directories can exist, one directory for each app shipped with Cinema 4D. These six directories will all share a common hash prefix for the installation but are differentiated by a postfix. Note that these directories only do exist when their respective app has been run at least once.
Preferences Directory Patterns¶
Pattern |
Example |
Application |
---|---|---|
name |
|
Cinema 4D |
name_p |
|
c4dpy |
name_x |
|
Commandline |
name_c |
|
Teamrender Client |
name_s |
|
Teamrender Server |
name_w |
|
Cineware |
Python Interpreter Bound Search Paths¶
For cases where the complexity of app specific search paths is not required, the search paths for specific Python versions can be used instead. All apps that use the targeted Python interpreter will be able import modules from these paths.
Python Core |
Operating System |
Path |
---|---|---|
Python 2.7 ( |
Windows |
|
Python 2.7 ( |
MacOS |
|
Python 3.7 ( |
Windows |
|
Python 3.7 ( |
MacOS |
|
Python 3.9 ( |
Windows |
|
Python 3.9 ( |
MacOS |
|
Python 3.10 ( |
Windows |
|
Python 3.10 ( |
MacOS |
|
Custom Interpreter Bound Search Paths¶
Cinema 4D also offers the ability to register custom interpreter bound search paths, similar to the PYTHONPATH
environment variable of the standard CPython interpreter. The paths must be setup as an environment variable in the OS, as shown at the example of Windows below.
Setting up a module search path in E:\C4DModules39
for all Cinema 4D apps the make use of a CPython 3.9 core.
The environment variable of the Python interpreters are both on Windows and MacOS as follows:
Python Core |
Variable |
---|---|
Python 2.X ( |
|
Python 2.7 ( |
|
Python 3.7 ( |
|
Python 3.9 ( |
|
Python 3.10 ( |
|
Note
To register multiple search paths, the elements must be separated by a semicolon on Windows and by a colon on MacOS in the value of the variable. This feature is only available from C4DPYTHONPATH37
onwards.
Executing Code with python_init.py
¶
For each app in a Cinema 4D installation also two files named python_init.py
are being executed before the plugin registrations of a booting app are being carried out.
Note
See Message System for a per plugin approach of executing code at the different stages of the Cinema 4D boot sequence.
The python_init.py
files do not exist by default and must be created manually. One file can be placed at the root of the interpreter bound search path, and one at the root of the search path in the preferences directory of the booting app. The code contained in these python_init modules can already make use of the c4d and maxon packages. The module located in the interpreter bound search path is being executed first, the one in the search path in the preferences directory of the booting app after that. For booting the Cinema 4D executable of an S26 installation, the files and order could be as follows:
- Windows:
%APPDATA%/MAXON/python39/python_init.py
.
%APPDATA%/MAXON/S26_F0AA9E5D/python39/python_init.py
.
- MacOS:
~/Library/Preferences/MAXON/python39/python_init.py
.
~/Library/Preferences/MAXON/S26_F0AA9E5D/python39/python_init.py
.
The exact location of the python_init module in the app specific search paths depends on the information lined out under Installation & App Specific Search Paths.
Note
In R20 SP1 the python_init modules are not being loaded.
Module Attributes of python_init.py
¶
Before execution, the following module attributes will be injected into each module. This means these symbols can be used in code although they are not defined in the module.
doc: (
BaseDocument
) The active document.op: (
c4d.BaseObject
) The selected object. Can beNone
.tp: (
c4d.modules.thinkingparticles.TP_MasterSystem
) The Thinking Particles Master system of the document. Can beNone
.
Note
In R20 SP2 these module attributes are not available and must be defined by the user.
Local Plugin Search Paths¶
It is important to understand that the directory of a plugin does not lie in the default search paths of a Cinema 4D Python environment. In order to import a module or package which is located in relation to a pyp
file, the path must be added to the sys.path
list of the Python instance which is loading the plugin. This approach can cause multiple problems of which some are being lined out in the code example below. It is recommended to install modules globally when possible.
"""Code example for loading local modules for a plugin with the following structure: MyPlugin +-- res +-- ... +-- modules <-- Contains the local modules/packages | +-- __init__.py | +-- myModule.py <-- A module to import +-- myPlugin.pyp <-- The plugin that is meant to import from MyPlugin/modules """ import os import sys # Get the directory the plugin is located in and add the directory called "modules" in this root # directory to the search paths of the Python instance executing this file. root = os.path.dirname(__file__) sys.path.insert(0, os.path.join(root, "modules")) # One must check if there is already a module present before importing a local module and if # this is the case, refrain from importing the local module. There are two scenarios how a local # module called "myModule" could already be present: # # 1. One's plugin has run before and already imported the module before. Invoking # "import myModule" again will do no harm in this case (and also have no effect). # 2. There is either another plugin installed on the machine which happens to also have a # local module called "myModule" or there is a globally installed module "myModule. # Invoking "import myModule" might replace the module of that other plugin with the local # module "myModule", rendering the other plugin non-functional or even causing Cinema 4D # to crash. # Attempt to import a local module called "myModule" if "myModule" not in sys.modules.keys(): import myModule # This else statement is optional, but useful for debugging. The condition should be removed in # release versions of the plugin as it is not necessary there. This condition will ensure that # the command "Reload Python Plugins" of Cinema 4D not only reloads/executes the pyp files again, # but that also their local modules are being reloaded. When not done, Cinema 4D must be restarted # to reflect changes made in "myModule.py" while the app was already running. Note that this is # Python 3.x specific code, in Python 2.x one must use the builtin function ``reload`` instead, # e.g.: ``reload(myModule)``. else: import importlib importlib.reload(myModule) # After the local module has been imported, the module search paths must be cleaned up, so that # other code does not accidentally import a module from it. sys.path.pop(0) # A few notes: # # 1. The module search paths in ``sys.path`` are inspected by ``import`` in a lazy sequential # order. I.e., if there is a ``math`` module to be found in ``sys.path[0]`` and one in # ``sys.path[1]``, the ``math`` module in ``sys.path[1]`` will be shadowed. # 2. There are however exceptions to that rule, and a Python interpreter might reorder # ``sys.path`` to move a builtin path to the start of the list, or outright ignore # ``sys.path`` and search a builtin search path before searching any path in ``sys.path`` # at all. # 3. It is therefore recommended to avoid name collisions as best as possible: # a. Do not name a module ``math``, name it ``myplugin_math`` # b. When shipping popular public packages with your plugin, rename the packages when # possible. When you for example want to ship ``urllib3`` with your plugin, it is best # to rename it to ``myplugin_urllib3`` to avoid version collisions. # The normal plugin code starts here ... import c4d class MyPluginData(c4d.plugins.ObjectData): """My Plugin. """ ...