[Bf-blender-cvs] [eecb90d8d2e] master: PyAPI: bpy.utils.execfile temporarily overrides the __main__ module

Campbell Barton noreply at git.blender.org
Fri Feb 19 01:23:50 CET 2021


Commit: eecb90d8d2ec68f4f3cb8b47ee9828dad5af60d6
Author: Campbell Barton
Date:   Fri Feb 19 11:13:35 2021 +1100
Branches: master
https://developer.blender.org/rBeecb90d8d2ec68f4f3cb8b47ee9828dad5af60d6

PyAPI: bpy.utils.execfile temporarily overrides the __main__ module

This is needed to support Python 3.10's `typing.get_type_hints`,
to access the name-space used when creating the class.

Also added a docstring for execfile.

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

M	release/scripts/modules/bpy/utils/__init__.py

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

diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index d984a6f54a4..2b19d23a367 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -82,14 +82,39 @@ _is_factory_startup = _bpy.app.factory_startup
 
 
 def execfile(filepath, mod=None):
-    # module name isn't used or added to 'sys.modules'.
-    # passing in 'mod' allows re-execution without having to reload.
+    """
+    Execute a file path as a Python script.
+
+    :arg filepath: Path of the script to execute.
+    :type filepath: string
+    :arg mod: Optional cached module, the result of a previous execution.
+    :type mod: Module or None
+    :return: The module which can be passed back in as ``mod``.
+    :rtype: ModuleType
+    """
 
     import importlib.util
-    mod_spec = importlib.util.spec_from_file_location("__main__", filepath)
+    mod_name = "__main__"
+    mod_spec = importlib.util.spec_from_file_location(mod_name, filepath)
     if mod is None:
         mod = importlib.util.module_from_spec(mod_spec)
-    mod_spec.loader.exec_module(mod)
+
+    # While the module name is not added to `sys.modules`, it's important to temporarily
+    # include this so statements such as `sys.modules[cls.__module__].__dict__` behave as expected.
+    # See: https://bugs.python.org/issue9499 for details.
+    modules = _sys.modules
+    mod_orig = modules.get(mod_name, None)
+    modules[mod_name] = mod
+
+    # No error supression, just ensure `sys.modules[mod_name]` is properly restored in the case of an error.
+    try:
+        mod_spec.loader.exec_module(mod)
+    finally:
+        if mod_orig is None:
+            modules.pop(mod_name, None)
+        else:
+            modules[mod_name] = mod_orig
+
     return mod



More information about the Bf-blender-cvs mailing list