Python: Support multiple custom script directories in Preferences #104876

Merged
Julian Eisel merged 9 commits from JulianEisel/blender:temp-multiple-script-dirs into main 2023-04-11 15:21:06 +02:00
4 changed files with 24 additions and 8 deletions
Showing only changes of commit 135c8aa032 - Show all commits

View File

@ -345,17 +345,23 @@ def script_path_pref():
DEPRECATED. Use `script_paths_pref` which supports multiple script paths now. Returns the
first valid of these script paths for now, for compatibility.
"""
# TODO how to handle deprecation of this?
from warnings import warn
warn(
"bpy.utils.script_path_pref() is deprecated use script_paths_pref() instead!",
DeprecationWarning,
stacklevel=2,
)
script_paths = script_paths_pref()
return script_paths[0] if len(script_paths) > 0 else ""
def script_paths_pref():
"""returns the user preference script directory paths or None"""
"""Returns a list of user preference script directories."""
paths = []
for script_directory in _preferences.filepaths.script_directories:
if script_directory.directory:
paths.append(_os.path.normpath(script_directory.directory))
directory = script_directory.directory
if directory:
paths.append(_os.path.normpath(directory))
return paths

View File

@ -1170,7 +1170,7 @@ class PREFERENCES_OT_script_directory_new(Operator):
new_dir.directory = self.directory
new_dir.name = os.path.basename(self.directory.rstrip(os.sep))
assert(context.preferences.is_dirty == True)
assert context.preferences.is_dirty == True
return {'FINISHED'}
@ -1197,7 +1197,7 @@ class PREFERENCES_OT_script_directory_remove(Operator):
script_directories.remove(script_directory)
break
assert(context.preferences.is_dirty == True)
assert context.preferences.is_dirty == True
return {'FINISHED'}

View File

@ -685,6 +685,7 @@ typedef struct UserDef_Experimental {
typedef struct NamedDirectoryPathEntry {
struct NamedDirectoryPathEntry *next, *prev;
/** Depending on the use case of this struct, this should probably be a unique name. */
char name[64]; /* MAX_NAME */
char dir_path[768]; /* FILE_MAXDIR */
} NamedDirectoryPathEntry;

View File

@ -348,10 +348,19 @@ static void rna_userdef_script_autoexec_update(Main *UNUSED(bmain),
static void rna_userdef_script_directory_name_set(PointerRNA *ptr, const char *value)
JulianEisel marked this conversation as resolved
Review

If DEFAULT or an empty string is passed in, use a fallback name such as "Untitled" or "Path", this avoids having to account for unlikely corner cases - maybe the user has a points to a script dir called DEFAULT and gets an error in the operator.

Disallowing empty strings is just a convention from data-block naming, which I think would be good to enforce here too since it's not expected and means the enum identifier for e.g. would be an empty string - probably it works for the most-part but could cause issues (empty strings have a special meaning for enum separators .. for e.g.). Python scripts may do truth checks on a value without realizing an empty string is a valid value... so we can avoid all this with a default name.

If `DEFAULT` or an empty string is passed in, use a fallback name such as "Untitled" or "Path", this avoids having to account for unlikely corner cases - maybe the user has a points to a script dir called `DEFAULT` and gets an error in the operator. Disallowing empty strings is just a convention from data-block naming, which I think would be good to enforce here too since it's not expected and means the enum identifier for e.g. would be an empty string - probably it works for the most-part but could cause issues (empty strings have a special meaning for enum separators .. for e.g.). Python scripts may do truth checks on a value without realizing an empty string is a valid value... so we can avoid all this with a default name.
{
NamedDirectoryPathEntry *script_dir = ptr->data;
bool value_invalid = false;
if (!value[0]) {
value_invalid = true;
}
if (STREQ(value, "DEFAULT")) {
BKE_report(NULL, RPT_ERROR, "Name 'DEFAULT' is reserved for internal use and cannot be used");
return;
BKE_report(
NULL, RPT_WARNING, "Name 'DEFAULT' is reserved for internal use and cannot be used");
JulianEisel marked this conversation as resolved
Review

Rather not warn as in the rare case a user runs into this - it's not as if there is anything to "fix", besides the script author adding explicit checks for "DEFAULT" which isn't useful.

Over long names will also be clipped for e.g. which doesn't warn. In general it's possible the name requested in Blender is manipulated. It can't be assumed a string literal will be used verbatim.

Rather not warn as in the rare case a user runs into this - it's not as if there is anything to "fix", besides the script author adding explicit checks for "DEFAULT" which isn't useful. Over long names will also be clipped for e.g. which doesn't warn. In general it's possible the name requested in Blender is manipulated. It can't be assumed a string literal will be used verbatim.
value_invalid = true;
}
if (value_invalid) {
value = DATA_("Untitled");
}
BLI_strncpy_utf8(script_dir->name, value, sizeof(script_dir->name));