BLENDER_USER_SCRIPTS and multiple paths?

From what I can tell the BLENDER_USER_SCRIPTS does not support multiple paths. Has there been any discussion regarding this? In some situations it’s very useful to be able to have multiple paths here (separated by colon/semi-colon).

  • I know there is a BLENDER_SYSTEM_SCRIPTS, and it would be nice if that supported multiple paths as well.

Found this: https://blender.stackexchange.com/questions/66497/multiple-paths-to-blender-user-scripts

The most common use case for this in a production is that you have the BLENDER_SYSTEM_SCRIPTS set to a global path, i.e D:/tools/blender. This is were you keep the global tools that are used in all productions. Then when you launch Blender (via some launcher tool) it appends a project specific path that appends project specific tools to that session. That path is different for each project. Also tools for a sequence or a shot can be appended to that path.

From blender --help
Environment Variables:
$BLENDER_USER_CONFIG Directory for user configuration files.
$BLENDER_USER_SCRIPTS Directory for user scripts.
$BLENDER_SYSTEM_SCRIPTS Directory for system wide scripts.

The env vars point to directories. They are not search paths.
If you are simply looking to import Python modules, you can set the env var PYTHONPATH or append to sys.path inside Blender.

Thanks for your reply.

I’m not looking for to just python modules, I want to use different directories for tools/add-ons.

1 Like

I’ve just run into this trying to integrate Blender as a package in the pipeline of of the studio I’m working at. Having the BLENDER_USER_SCRIPTS and BLENDER_SYSTEM_SCRIPTS as single locations rather than search paths is really not flexible enough for most studio pipelines.

In most studio pipelines, applications and plugins are usually installed individually, and centrally on the network, and an environment is dynamically built at runtime to make sure the application has all it needs to start up. This allows systems like Rez to handle the complex dependencies between different applications, with different versions of software running in parallel, collections of software to be versioned per project, etc.

Usually with other applications (Maya, Houdini, etc) this takes the form of appending all the relevant locations to $HOUDINI_PATH etc, i.e. going through all the various plugins and adding them to the search path.

As an example you can see the level of flexibility you get with Houdini and its ability to customise many of its search paths using environment variables: https://www.sidefx.com/docs/houdini/ref/env.html

Right now there’s only a single addon that we want installed, so I have to abuse BLENDER_USER_SCRIPTS in this way, pointing to that single addon. This won’t work if we get another addon since we can’t add multiple directories to the path.

It would be extremely useful if Blender could interpret these environment variables as search paths with multiple ordered items rather than static directories.

10 Likes

I’ve just run into this trying to integrate Blender as a package in the pipeline of of the studio I’m working at. Having the BLENDER_USER_SCRIPTS and BLENDER_SYSTEM_SCRIPTS as single locations rather than search paths is really not flexible enough for most studio pipelines.

In most studio pipelines, applications and plugins are usually installed individually, and centrally on the network, and an environment is dynamically built at runtime to make sure the application has all it needs to start up.

+1 to that. We also have to face this limitation at our studio in our current production, where we develop our own addons for pipeline management. To avoid having to install/update every addon on each single machine of the studio (50-60 artists plus render farm) we set up the environment pointing to a shared location, and the content of this location is handled by CI/CD tools, which are connected to the environment-building tools.

Since the location is unique, we have to include all addons (ours and external) at the same place. The flexibility of the departments is limited since they need to come to DEVs to ask for an addon to be installed or updated, even if just for testing puposes.

There are other side effects. For instance, the user keymaps are also stored at this shared location, so each user must give a unique name across the studio for his preferred keymap.

It would be extremely useful if Blender could interpret these environment variables as search paths with multiple ordered items rather than static directories.

Indeed, this would certainly help pipeline ops.

5 Likes

Making BLENDER_USER_SCRIPTS and BLENDER_SYSTEM_SCRIPTS multipath is somewhat tricky, since there’s all kinds of checks in there to validate it is a valid path, however if you add a new var blender checks it’s not that hard to do by the looks of it (although the python guys probably have a thing or two to say about the implementation side of it) something like this will do it (BLENDER_ADDITIONAL_SCRIPTS environment variable, with ; separated paths)

diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index de8fd87db58..3b23b3223a8 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -24,6 +24,8 @@
 
 #include <Python.h>
 
+#include "BLI_alloca.h"
+#include "BLI_path_util.h"
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 
@@ -64,13 +66,24 @@ PyObject *bpy_package_py = NULL;
 PyDoc_STRVAR(bpy_script_paths_doc,
              ".. function:: script_paths()\n"
              "\n"
-             "   Return 2 paths to blender scripts directories.\n"
+             "   Return paths to blender scripts directories.\n"
              "\n"
              "   :return: (system, user) strings will be empty when not found.\n"
              "   :rtype: tuple of strings\n");
 static PyObject *bpy_script_paths(PyObject *UNUSED(self))
 {
-  PyObject *ret = PyTuple_New(2);
+  int(*words)[2];
+  int words_len = 0;
+
+  const char *add_path = BLI_getenv("BLENDER_ADDITIONAL_SCRIPTS");
+  if (add_path) {
+    const size_t str_len = strlen(add_path);
+    const int words_max = (str_len / 2) + 1;
+    words = BLI_array_alloca(words, words_max);
+    words_len = BLI_string_find_split_words(add_path, str_len, ';', words, words_max);
+  }
+
+  PyObject *ret = PyTuple_New(2 + words_len);
   PyObject *item;
   const char *path;
 
@@ -82,7 +95,11 @@ static PyObject *bpy_script_paths(PyObject *UNUSED(self))
   item = PyC_UnicodeFromByte(path ? path : "");
   BLI_assert(item != NULL);
   PyTuple_SET_ITEM(ret, 1, item);
-
+  for (int i = 0; i < words_len; i++) {
+    item = PyC_UnicodeFromByteAndSize(add_path + words[i][0], words[i][1]);
+    BLI_assert(item != NULL);
+    PyTuple_SET_ITEM(ret, 2 + i, item);
+  }
   return ret;
 }
 
4 Likes

Bumping this thread again. Using a new environment variable sounds like a good idea. Any change this could be added?

I posted a quick proof of concept, but don’t have the time to drag it across the finish line, anyone is free to take it and run with it, could be a fun first patch for an aspiring new developer.

2 Likes

Hi, I’m facing the same issue and created an issue a long ago regarding the app templates: https://developer.blender.org/T85553

I wanted to do something like this. I went into this file scripts\modules\addon_utils.py and done this change.

def paths():
    
    # RELEASE SCRIPTS: official scripts distributed in Blender releases
    addon_paths = _bpy.utils.script_paths("addons")
    
    # CONTRIB SCRIPTS: good for testing but not official scripts yet
    # if folder addons_contrib/ exists, scripts in there will be loaded too
    addon_paths += _bpy.utils.script_paths("addons_contrib")
 
    # append here as many paths as you like
    addon_paths.append('D:\\programming\\sources\\blender\\develop\\imageoutline')

    return addon_paths

Also the addon was shown in the GUI as normal and can be activated.
2021-04-16 20_21_19-Blender Preferences

Now I might think of some better way to make it more data driven. Otherwise one would have to change the script each time and recreate the list in every new Blender installation.

Such as getting an environmental variable? Or a specific text file? Any ideas to make it more proper are welcomed.