1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 # simple script to enable all addons, and disable
24 ./blender.bin --background -noaudio --factory-startup --python tests/python/bl_load_py_modules.py
37 "io_export_dxf", # TODO, check on why this fails
38 'io_import_dxf', # Because of cydxfentity.so dependency
40 # The unpacked wheel is only loaded when actually used, not directly on import:
41 os.path.join("io_blend_utils", "blender_bam-unpacked.whl"),
44 # Some modules need to add to the `sys.path`.
46 # Runs in a Python subprocess, so its expected its basedir can be imported.
47 "io_blend_utils.blendfile_pack": ".",
50 if not bpy.app.build_options.freestyle:
51 BLACKLIST.add("render_freestyle_svg")
54 os.path.join(bpy.utils.resource_path('USER'), "scripts"),
55 ) + tuple(addon_utils.paths()[1:])
58 def module_names_recursive(mod_dir, *, parent=None):
60 a version of bpy.path.module_names that includes non-packages
63 is_package = os.path.exists(os.path.join(mod_dir, "__init__.py"))
65 for n in os.listdir(mod_dir):
66 if not n.startswith((".", "_")):
67 submod_full = os.path.join(mod_dir, n)
68 if os.path.isdir(submod_full):
72 subparent = parent + "." + n
73 yield from module_names_recursive(submod_full, parent=subparent)
74 elif n.endswith(".py") and is_package is False:
77 submod = parent + "." + submod
78 yield submod, submod_full
81 def module_names_all(mod_dir):
82 yield from bpy.path.module_names(mod_dir)
83 yield from module_names_recursive(mod_dir)
86 def addon_modules_sorted():
87 modules = addon_utils.modules({})
88 modules[:] = [mod for mod in modules if not mod.__file__.startswith(BLACKLIST_DIRS)]
89 modules.sort(key=lambda mod: mod.__name__)
93 def source_list(path, filename_check=None):
94 from os.path import join
95 for dirpath, dirnames, filenames in os.walk(path):
97 dirnames[:] = [d for d in dirnames if not d.startswith(".")]
99 for filename in filenames:
100 filepath = join(dirpath, filename)
101 if filename_check is None or filename_check(filepath):
106 modules = addon_modules_sorted()
107 addons = bpy.context.user_preferences.addons
110 for mod_name in list(addons.keys()):
111 addon_utils.disable(mod_name, default_set=True)
113 assert(bool(addons) is False)
116 mod_name = mod.__name__
117 if mod_name in BLACKLIST:
119 addon_utils.enable(mod_name, default_set=True)
120 if not (mod_name in addons):
121 raise Exception("'addon_utils.enable(%r)' call failed" % mod_name)
125 VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None
130 # paths blender stores scripts in.
131 paths = bpy.utils.script_paths()
134 for script_path in paths:
135 print("\t'%s'" % script_path)
138 # find all sys.path we added
139 for script_path in paths:
140 for mod_dir in sys.path:
141 if mod_dir.startswith(script_path):
142 if not mod_dir.startswith(BLACKLIST_DIRS):
143 if mod_dir not in module_paths:
144 if os.path.exists(mod_dir):
145 module_paths.append(mod_dir)
148 # collect modules from our paths.
150 for mod_dir in module_paths:
151 # print("mod_dir", mod_dir)
152 for mod, mod_full in bpy.path.module_names(mod_dir):
155 if mod in module_names:
156 mod_dir_prev, mod_full_prev = module_names[mod]
157 raise Exception("Module found twice %r.\n (%r -> %r, %r -> %r)" %
158 (mod, mod_dir, mod_full, mod_dir_prev, mod_full_prev))
160 modules.append(__import__(mod))
162 module_names[mod] = mod_dir, mod_full
166 # test we tested all files except for presets and templates
168 os.sep + "presets" + os.sep,
169 os.sep + "templates" + os.sep,
170 ] + ([(os.sep + f + os.sep) for f in BLACKLIST] +
171 [(os.sep + f + ".py") for f in BLACKLIST])
176 filepath = m.__file__
177 if os.path.basename(filepath).startswith("__init__."):
178 mod_dir = os.path.dirname(filepath)
179 for submod, submod_full in module_names_all(mod_dir):
180 # fromlist is ignored, ugh.
181 mod_name_full = m.__name__ + "." + submod
183 sys_path_back = sys.path[:]
186 os.path.normpath(os.path.join(mod_dir, f))
187 for f in MODULE_SYS_PATHS.get(mod_name_full, ())
191 __import__(mod_name_full)
192 mod_imp = sys.modules[mod_name_full]
194 sys.path[:] = sys_path_back
196 # check we load what we ask for.
197 assert(os.path.samefile(mod_imp.__file__, submod_full))
199 modules.append(mod_imp)
200 except Exception as e:
202 # Module might fail to import, but we don't want whole test to fail here.
204 # - This module might be in ignored list (for example, preset or template),
205 # so failing here will cause false-positive test failure.
206 # - If this is module which should not be ignored, it is not added to list
207 # of successfully loaded modules, meaning the test will catch this
209 # - We want to catch all failures of this script instead of stopping on
210 # a first big failure.
213 for ignore in ignore_paths:
214 if ignore in submod_full:
218 traceback.print_exc()
221 # check which filepaths we didn't load
223 for mod_dir in module_paths:
224 source_files.extend(source_list(mod_dir, filename_check=lambda f: f.endswith(".py")))
226 source_files = list(set(source_files))
230 # remove loaded files
231 loaded_files = list({m.__file__ for m in modules})
234 for f in loaded_files:
235 source_files.remove(f)
237 for f in source_files:
238 for ignore in ignore_paths:
242 raise Exception("Source file %r not loaded in test" % f)
244 print("loaded %d modules" % len(loaded_files))
251 if __name__ == "__main__":
252 # So a python error exits(1)
257 traceback.print_exc()