[Bf-blender-cvs] [7bb8eeb3a87] master: PyAPI: Add Context.path_resolve wrapper that supports context members

Campbell Barton noreply at git.blender.org
Wed Apr 6 10:03:39 CEST 2022


Commit: 7bb8eeb3a871eb1e72ee129f8ff441f2752b37be
Author: Campbell Barton
Date:   Wed Apr 6 11:42:46 2022 +1000
Branches: master
https://developer.blender.org/rB7bb8eeb3a871eb1e72ee129f8ff441f2752b37be

PyAPI: Add Context.path_resolve wrapper that supports context members

This avoids script authors using `eval("context.%s" % data_path)`
to access paths starting from the context,
which isn't good practice especially if the data_path isn't trusted.

Now it's possible to resplve paths such as:

   context.path_resolve('active_object.modifiers[0].name')

===================================================================

M	release/scripts/modules/bpy_types.py

===================================================================

diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index e0e20d0f8c9..45fd6d29dfa 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -8,12 +8,65 @@ StructRNA = bpy_types.bpy_struct
 StructMetaPropGroup = bpy_types.bpy_struct_meta_idprop
 # StructRNA = bpy_types.Struct
 
+# Private dummy object use for comparison only.
+_sentinal = object()
+
 # Note that methods extended in C are defined in: 'bpy_rna_types_capi.c'
 
 
 class Context(StructRNA):
     __slots__ = ()
 
+    def path_resolve(self, path, coerce=True):
+        """
+        Returns the property from the path, raise an exception when not found.
+
+        :arg path: patch which this property resolves.
+        :type path: string
+        :arg coerce: optional argument, when True, the property will be converted into its Python representation.
+        :type coerce: boolean
+        """
+        # This is a convenience wrapper around `StructRNA.path_resolve` which doesn't support accessing context members.
+        # Without this wrapper many users were writing `exec("context.%s" % data_path)` which is a security
+        # concern if the `data_path` comes from an unknown source.
+        # This function performs the initial lookup, after that the regular `path_resolve` function is used.
+
+        # Extract the initial attribute into `(attr, path_rest)`.
+        sep = len(path)
+        div = ""
+        for div_test in (".", "["):
+            sep_test = path.find(div_test, 0, sep)
+            if sep_test != -1 and sep_test < sep:
+                sep = sep_test
+                div = div_test
+        if div:
+            attr = path[:sep]
+            if div == ".":
+                sep += 1
+            path_rest = path[sep:]
+        else:
+            attr = path
+            path_rest = ""
+
+        # Retrieve the value for `attr`.
+        # Match the value error exception with that of "path_resolve"
+        # to simplify exception handling for the caller.
+        value = getattr(self, attr, _sentinal)
+        if value is _sentinal:
+            raise ValueError("Path could not be resolved: %r" % attr)
+
+        if value is None:
+            return value
+
+        # Resolve the rest of the path if necessary.
+        if path_rest:
+            path_resolve_fn = getattr(value, "path_resolve", None)
+            if path_resolve_fn is None:
+                raise ValueError("Path %s resolves to a non RNA value" % attr)
+            return path_resolve_fn(path_rest, coerce)
+
+        return value
+
     def copy(self):
         from types import BuiltinMethodType
         new_context = {}



More information about the Bf-blender-cvs mailing list