diff options
author | Campbell Barton <ideasman42@gmail.com> | 2021-02-19 03:13:35 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2021-02-19 03:23:30 +0300 |
commit | eecb90d8d2ec68f4f3cb8b47ee9828dad5af60d6 (patch) | |
tree | 203ec739076df64f95c1a2f951f5e3846d87fe6e /release/scripts/modules/bpy | |
parent | 4604350eefabbfeeb97347bc3e6a996750de15aa (diff) |
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.
Diffstat (limited to 'release/scripts/modules/bpy')
-rw-r--r-- | release/scripts/modules/bpy/utils/__init__.py | 33 |
1 files changed, 29 insertions, 4 deletions
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 |