PyAPI: Skip user scripts w/ factory-startup
[blender.git] / release / scripts / modules / bpy / utils / __init__.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 """
22 This module contains utility functions specific to blender but
23 not associated with blenders internal data.
24 """
25
26 __all__ = (
27     "blend_paths",
28     "escape_identifier",
29     "keyconfig_set",
30     "load_scripts",
31     "modules_from_path",
32     "preset_find",
33     "preset_paths",
34     "refresh_script_paths",
35     "app_template_paths",
36     "register_class",
37     "register_module",
38     "register_manual_map",
39     "unregister_manual_map",
40     "register_submodule_factory",
41     "make_rna_paths",
42     "manual_map",
43     "previews",
44     "resource_path",
45     "script_path_user",
46     "script_path_pref",
47     "script_paths",
48     "smpte_from_frame",
49     "smpte_from_seconds",
50     "units",
51     "unregister_class",
52     "unregister_module",
53     "user_resource",
54 )
55
56 from _bpy import (
57     _utils_units as units,
58     blend_paths,
59     escape_identifier,
60     register_class,
61     resource_path,
62     script_paths as _bpy_script_paths,
63     unregister_class,
64     user_resource as _user_resource,
65 )
66
67 import bpy as _bpy
68 import os as _os
69 import sys as _sys
70
71 import addon_utils as _addon_utils
72
73 _user_preferences = _bpy.context.user_preferences
74 _script_module_dirs = "startup", "modules"
75 _is_factory_startup = _bpy.app.factory_startup
76
77
78 def _test_import(module_name, loaded_modules):
79     use_time = _bpy.app.debug_python
80
81     if module_name in loaded_modules:
82         return None
83     if "." in module_name:
84         print("Ignoring '%s', can't import files containing "
85               "multiple periods" % module_name)
86         return None
87
88     if use_time:
89         import time
90         t = time.time()
91
92     try:
93         mod = __import__(module_name)
94     except:
95         import traceback
96         traceback.print_exc()
97         return None
98
99     if use_time:
100         print("time %s %.4f" % (module_name, time.time() - t))
101
102     loaded_modules.add(mod.__name__)  # should match mod.__name__ too
103     return mod
104
105
106 def _sys_path_ensure(path):
107     if path not in _sys.path:  # reloading would add twice
108         _sys.path.insert(0, path)
109
110
111 def modules_from_path(path, loaded_modules):
112     """
113     Load all modules in a path and return them as a list.
114
115     :arg path: this path is scanned for scripts and packages.
116     :type path: string
117     :arg loaded_modules: already loaded module names, files matching these
118        names will be ignored.
119     :type loaded_modules: set
120     :return: all loaded modules.
121     :rtype: list
122     """
123     modules = []
124
125     for mod_name, mod_path in _bpy.path.module_names(path):
126         mod = _test_import(mod_name, loaded_modules)
127         if mod:
128             modules.append(mod)
129
130     return modules
131
132
133 _global_loaded_modules = []  # store loaded module names for reloading.
134 import bpy_types as _bpy_types  # keep for comparisons, never ever reload this.
135
136
137 def load_scripts(reload_scripts=False, refresh_scripts=False):
138     """
139     Load scripts and run each modules register function.
140
141     :arg reload_scripts: Causes all scripts to have their unregister method
142        called before loading.
143     :type reload_scripts: bool
144     :arg refresh_scripts: only load scripts which are not already loaded
145        as modules.
146     :type refresh_scripts: bool
147     """
148     use_time = use_class_register_check = _bpy.app.debug_python
149     use_user = not _is_factory_startup
150
151     if use_time:
152         import time
153         t_main = time.time()
154
155     loaded_modules = set()
156
157     if refresh_scripts:
158         original_modules = _sys.modules.values()
159
160     if reload_scripts:
161         # just unload, don't change user defaults, this means we can sync
162         # to reload. note that they will only actually reload of the
163         # modification time changes. This `won't` work for packages so...
164         # its not perfect.
165         for module_name in [ext.module for ext in _user_preferences.addons]:
166             _addon_utils.disable(module_name)
167
168         # *AFTER* unregistering all add-ons, otherwise all calls to
169         # unregister_module() will silently fail (do nothing).
170         _bpy_types.TypeMap.clear()
171
172     def register_module_call(mod):
173         register = getattr(mod, "register", None)
174         if register:
175             try:
176                 register()
177             except:
178                 import traceback
179                 traceback.print_exc()
180         else:
181             print("\nWarning! '%s' has no register function, "
182                   "this is now a requirement for registerable scripts" %
183                   mod.__file__)
184
185     def unregister_module_call(mod):
186         unregister = getattr(mod, "unregister", None)
187         if unregister:
188             try:
189                 unregister()
190             except:
191                 import traceback
192                 traceback.print_exc()
193
194     def test_reload(mod):
195         import importlib
196         # reloading this causes internal errors
197         # because the classes from this module are stored internally
198         # possibly to refresh internal references too but for now, best not to.
199         if mod == _bpy_types:
200             return mod
201
202         try:
203             return importlib.reload(mod)
204         except:
205             import traceback
206             traceback.print_exc()
207
208     def test_register(mod):
209
210         if refresh_scripts and mod in original_modules:
211             return
212
213         if reload_scripts and mod:
214             print("Reloading:", mod)
215             mod = test_reload(mod)
216
217         if mod:
218             register_module_call(mod)
219             _global_loaded_modules.append(mod.__name__)
220
221     if reload_scripts:
222
223         # module names -> modules
224         _global_loaded_modules[:] = [_sys.modules[mod_name]
225                                      for mod_name in _global_loaded_modules]
226
227         # loop over and unload all scripts
228         _global_loaded_modules.reverse()
229         for mod in _global_loaded_modules:
230             unregister_module_call(mod)
231
232         for mod in _global_loaded_modules:
233             test_reload(mod)
234
235         del _global_loaded_modules[:]
236
237     from bpy_restrict_state import RestrictBlend
238
239     with RestrictBlend():
240         for base_path in script_paths(use_user=use_user):
241             for path_subdir in _script_module_dirs:
242                 path = _os.path.join(base_path, path_subdir)
243                 if _os.path.isdir(path):
244                     _sys_path_ensure(path)
245
246                     # only add this to sys.modules, don't run
247                     if path_subdir == "modules":
248                         continue
249
250                     for mod in modules_from_path(path, loaded_modules):
251                         test_register(mod)
252
253     # load template (if set)
254     if any(_bpy.utils.app_template_paths()):
255         import bl_app_template_utils
256         bl_app_template_utils.reset(reload_scripts=reload_scripts)
257         del bl_app_template_utils
258
259     # deal with addons separately
260     _initialize = getattr(_addon_utils, "_initialize", None)
261     if _initialize is not None:
262         # first time, use fast-path
263         _initialize()
264         del _addon_utils._initialize
265     else:
266         _addon_utils.reset_all(reload_scripts=reload_scripts)
267     del _initialize
268
269     # run the active integration preset
270     filepath = preset_find(_user_preferences.inputs.active_keyconfig,
271                            "keyconfig")
272
273     if filepath:
274         keyconfig_set(filepath)
275
276     if reload_scripts:
277         import gc
278         print("gc.collect() -> %d" % gc.collect())
279
280     if use_time:
281         print("Python Script Load Time %.4f" % (time.time() - t_main))
282
283     if use_class_register_check:
284         for cls in _bpy.types.bpy_struct.__subclasses__():
285             if getattr(cls, "is_registered", False):
286                 for subcls in cls.__subclasses__():
287                     if not subcls.is_registered:
288                         print(
289                             "Warning, unregistered class: %s(%s)" %
290                             (subcls.__name__, cls.__name__)
291                         )
292
293
294 # base scripts
295 _scripts = (
296     _os.path.dirname(_os.path.dirname(_os.path.dirname(__file__))),
297 )
298
299
300 def script_path_user():
301     """returns the env var and falls back to home dir or None"""
302     path = _user_resource('SCRIPTS')
303     return _os.path.normpath(path) if path else None
304
305
306 def script_path_pref():
307     """returns the user preference or None"""
308     path = _user_preferences.filepaths.script_directory
309     return _os.path.normpath(path) if path else None
310
311
312 def script_paths(subdir=None, user_pref=True, check_all=False, use_user=True):
313     """
314     Returns a list of valid script paths.
315
316     :arg subdir: Optional subdir.
317     :type subdir: string
318     :arg user_pref: Include the user preference script path.
319     :type user_pref: bool
320     :arg check_all: Include local, user and system paths rather just the paths
321        blender uses.
322     :type check_all: bool
323     :return: script paths.
324     :rtype: list
325     """
326     scripts = list(_scripts)
327
328     # Only paths Blender uses.
329     #
330     # Needed this is needed even when 'check_all' is enabled,
331     # so the 'BLENDER_SYSTEM_SCRIPTS' environment variable will be used.
332     base_paths = _bpy_script_paths()
333
334     if check_all:
335         # All possible paths, no duplicates, keep order.
336         if use_user:
337             test_paths = ('LOCAL', 'USER', 'SYSTEM')
338         else:
339             test_paths = ('LOCAL', 'SYSTEM')
340
341         base_paths = (
342             *(path for path in (
343                 _os.path.join(resource_path(res), "scripts")
344                 for res in test_paths) if path not in base_paths),
345             *base_paths,
346         )
347
348     if use_user:
349         test_paths = (*base_paths, script_path_user(), script_path_pref())
350     else:
351         test_paths = (*base_paths, script_path_pref())
352
353     for path in test_paths:
354         if path:
355             path = _os.path.normpath(path)
356             if path not in scripts and _os.path.isdir(path):
357                 scripts.append(path)
358
359     if subdir is None:
360         return scripts
361
362     scripts_subdir = []
363     for path in scripts:
364         path_subdir = _os.path.join(path, subdir)
365         if _os.path.isdir(path_subdir):
366             scripts_subdir.append(path_subdir)
367
368     return scripts_subdir
369
370
371 def refresh_script_paths():
372     """
373     Run this after creating new script paths to update sys.path
374     """
375
376     for base_path in script_paths():
377         for path_subdir in _script_module_dirs:
378             path = _os.path.join(base_path, path_subdir)
379             if _os.path.isdir(path):
380                 _sys_path_ensure(path)
381
382     for path in _addon_utils.paths():
383         _sys_path_ensure(path)
384         path = _os.path.join(path, "modules")
385         if _os.path.isdir(path):
386             _sys_path_ensure(path)
387
388
389 def app_template_paths(subdir=None):
390     """
391     Returns valid application template paths.
392
393     :arg subdir: Optional subdir.
394     :type subdir: string
395     :return: app template paths.
396     :rtype: generator
397     """
398
399     # note: LOCAL, USER, SYSTEM order matches script resolution order.
400     subdir_tuple = (subdir,) if subdir is not None else ()
401
402     path = _os.path.join(*(
403         resource_path('LOCAL'), "scripts", "startup",
404         "bl_app_templates_user", *subdir_tuple))
405     if _os.path.isdir(path):
406         yield path
407     else:
408         path = _os.path.join(*(
409             resource_path('USER'), "scripts", "startup",
410             "bl_app_templates_user", *subdir_tuple))
411         if _os.path.isdir(path):
412             yield path
413
414     path = _os.path.join(*(
415         resource_path('SYSTEM'), "scripts", "startup",
416         "bl_app_templates_system", *subdir_tuple))
417     if _os.path.isdir(path):
418         yield path
419
420
421 def preset_paths(subdir):
422     """
423     Returns a list of paths for a specific preset.
424
425     :arg subdir: preset subdirectory (must not be an absolute path).
426     :type subdir: string
427     :return: script paths.
428     :rtype: list
429     """
430     dirs = []
431     for path in script_paths("presets", check_all=True):
432         directory = _os.path.join(path, subdir)
433         if not directory.startswith(path):
434             raise Exception("invalid subdir given %r" % subdir)
435         elif _os.path.isdir(directory):
436             dirs.append(directory)
437
438     # Find addons preset paths
439     for path in _addon_utils.paths():
440         directory = _os.path.join(path, "presets", subdir)
441         if _os.path.isdir(directory):
442             dirs.append(directory)
443
444     return dirs
445
446
447 def smpte_from_seconds(time, fps=None):
448     """
449     Returns an SMPTE formatted string from the *time*:
450     ``HH:MM:SS:FF``.
451
452     If the *fps* is not given the current scene is used.
453
454     :arg time: time in seconds.
455     :type time: int, float or ``datetime.timedelta``.
456     :return: the frame string.
457     :rtype: string
458     """
459
460     return smpte_from_frame(time_to_frame(time, fps=fps), fps)
461
462
463 def smpte_from_frame(frame, fps=None, fps_base=None):
464     """
465     Returns an SMPTE formatted string from the *frame*:
466     ``HH:MM:SS:FF``.
467
468     If *fps* and *fps_base* are not given the current scene is used.
469
470     :arg frame: frame number.
471     :type frame: int or float.
472     :return: the frame string.
473     :rtype: string
474     """
475
476     if fps is None:
477         fps = _bpy.context.scene.render.fps
478
479     if fps_base is None:
480         fps_base = _bpy.context.scene.render.fps_base
481
482     sign = "-" if frame < 0 else ""
483     frame = abs(frame * fps_base)
484
485     return (
486         "%s%02d:%02d:%02d:%02d" % (
487         sign,
488         int(frame / (3600 * fps)),          # HH
489         int((frame / (60 * fps)) % 60),     # MM
490         int((frame / fps) % 60),            # SS
491         int(frame % fps),                   # FF
492         ))
493
494
495 def time_from_frame(frame, fps=None, fps_base=None):
496     """
497     Returns the time from a frame number .
498
499     If *fps* and *fps_base* are not given the current scene is used.
500
501     :arg frame: number.
502     :type frame: int or float.
503     :return: the time in seconds.
504     :rtype: datetime.timedelta
505     """
506
507     if fps is None:
508         fps = _bpy.context.scene.render.fps
509
510     if fps_base is None:
511         fps_base = _bpy.context.scene.render.fps_base
512
513     from datetime import timedelta
514
515     return timedelta(0, (frame * fps_base) / fps)
516
517
518 def time_to_frame(time, fps=None, fps_base=None):
519     """
520     Returns a float frame number from a time given in seconds or
521     as a datetime.timedelta object.
522
523     If *fps* and *fps_base* are not given the current scene is used.
524
525     :arg time: time in seconds.
526     :type time: number or a ``datetime.timedelta`` object
527     :return: the frame.
528     :rtype: float
529     """
530
531     if fps is None:
532         fps = _bpy.context.scene.render.fps
533
534     if fps_base is None:
535         fps_base = _bpy.context.scene.render.fps_base
536
537     from datetime import timedelta
538
539     if isinstance(time, timedelta):
540         time = time.total_seconds()
541
542     return (time / fps_base) * fps
543
544
545 def preset_find(name, preset_path, display_name=False, ext=".py"):
546     if not name:
547         return None
548
549     for directory in preset_paths(preset_path):
550
551         if display_name:
552             filename = ""
553             for fn in _os.listdir(directory):
554                 if fn.endswith(ext) and name == _bpy.path.display_name(fn):
555                     filename = fn
556                     break
557         else:
558             filename = name + ext
559
560         if filename:
561             filepath = _os.path.join(directory, filename)
562             if _os.path.exists(filepath):
563                 return filepath
564
565
566 def keyconfig_set(filepath, report=None):
567     from os.path import basename, splitext
568     from itertools import chain
569
570     if _bpy.app.debug_python:
571         print("loading preset:", filepath)
572
573     keyconfigs = _bpy.context.window_manager.keyconfigs
574
575     keyconfigs_old = keyconfigs[:]
576
577     try:
578         error_msg = ""
579         with open(filepath, 'r', encoding='utf-8') as keyfile:
580             exec(compile(keyfile.read(), filepath, "exec"),
581                  {"__file__": filepath})
582     except:
583         import traceback
584         error_msg = traceback.format_exc()
585
586     if error_msg:
587         if report is not None:
588             report({'ERROR'}, error_msg)
589         print(error_msg)
590
591     kc_new = next(chain(iter(kc for kc in keyconfigs
592                              if kc not in keyconfigs_old), (None,)))
593     if kc_new is None:
594         if report is not None:
595             report({'ERROR'}, "Failed to load keymap %r" % filepath)
596         return False
597     else:
598         kc_new.name = ""
599
600         # remove duplicates
601         name = splitext(basename(filepath))[0]
602         while True:
603             kc_dupe = keyconfigs.get(name)
604             if kc_dupe:
605                 keyconfigs.remove(kc_dupe)
606             else:
607                 break
608
609         kc_new.name = name
610         keyconfigs.active = kc_new
611         return True
612
613
614 def user_resource(resource_type, path="", create=False):
615     """
616     Return a user resource path (normally from the users home directory).
617
618     :arg type: Resource type in ['DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'].
619     :type type: string
620     :arg subdir: Optional subdirectory.
621     :type subdir: string
622     :arg create: Treat the path as a directory and create
623        it if its not existing.
624     :type create: boolean
625     :return: a path.
626     :rtype: string
627     """
628
629     target_path = _user_resource(resource_type, path)
630
631     if create:
632         # should always be true.
633         if target_path:
634             # create path if not existing.
635             if not _os.path.exists(target_path):
636                 try:
637                     _os.makedirs(target_path)
638                 except:
639                     import traceback
640                     traceback.print_exc()
641                     target_path = ""
642             elif not _os.path.isdir(target_path):
643                 print("Path %r found but isn't a directory!" % target_path)
644                 target_path = ""
645
646     return target_path
647
648
649 def _bpy_module_classes(module, is_registered=False):
650     typemap_list = _bpy_types.TypeMap.get(module, ())
651     i = 0
652     while i < len(typemap_list):
653         cls_weakref = typemap_list[i]
654         cls = cls_weakref()
655
656         if cls is None:
657             del typemap_list[i]
658         else:
659             if is_registered == cls.is_registered:
660                 yield cls
661             i += 1
662
663
664 def register_module(module, verbose=False):
665     if verbose:
666         print("bpy.utils.register_module(%r): ..." % module)
667     cls = None
668     for cls in _bpy_module_classes(module, is_registered=False):
669         if verbose:
670             print("    %r" % cls)
671         try:
672             register_class(cls)
673         except:
674             print("bpy.utils.register_module(): "
675                   "failed to registering class %r" % cls)
676             import traceback
677             traceback.print_exc()
678     if verbose:
679         print("done.\n")
680     if cls is None:
681         raise Exception("register_module(%r): defines no classes" % module)
682
683
684 def unregister_module(module, verbose=False):
685     if verbose:
686         print("bpy.utils.unregister_module(%r): ..." % module)
687     for cls in _bpy_module_classes(module, is_registered=True):
688         if verbose:
689             print("    %r" % cls)
690         try:
691             unregister_class(cls)
692         except:
693             print("bpy.utils.unregister_module(): "
694                   "failed to unregistering class %r" % cls)
695             import traceback
696             traceback.print_exc()
697     if verbose:
698         print("done.\n")
699
700
701 def register_submodule_factory(module_name, submodule_names):
702     """
703     Utility function to create register and unregister functions
704     which simply load submodules,
705     calling their register & unregister functions.
706
707     .. note::
708
709        Modules are registered in the order given,
710        unregistered in reverse order.
711
712     :arg module_name: The module name, typically ``__name__``.
713     :type module_name: string
714     :arg submodule_names: List of submodule names to load and unload.
715     :type submodule_names: list of strings
716     :return: register and unregister functions.
717     :rtype: tuple pair of functions
718     """
719
720     module = None
721     submodules = []
722
723     def register():
724         nonlocal module
725         module = __import__(name=module_name, fromlist=submodule_names)
726         submodules[:] = [getattr(module, name) for name in submodule_names]
727         for mod in submodules:
728             mod.register()
729
730     def unregister():
731         from sys import modules
732         for mod in reversed(submodules):
733             mod.unregister()
734             name = mod.__name__
735             delattr(module, name.partition(".")[2])
736             del modules[name]
737         submodules.clear()
738
739     return register, unregister
740
741
742 # -----------------------------------------------------------------------------
743 # Manual lookups, each function has to return a basepath and a sequence
744 # of...
745
746 # we start with the built-in default mapping
747 def _blender_default_map():
748     import rna_manual_reference as ref_mod
749     ret = (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping)
750     # avoid storing in memory
751     del _sys.modules["rna_manual_reference"]
752     return ret
753
754 # hooks for doc lookups
755 _manual_map = [_blender_default_map]
756
757
758 def register_manual_map(manual_hook):
759     _manual_map.append(manual_hook)
760
761
762 def unregister_manual_map(manual_hook):
763     _manual_map.remove(manual_hook)
764
765
766 def manual_map():
767     # reverse so default is called last
768     for cb in reversed(_manual_map):
769         try:
770             prefix, url_manual_mapping = cb()
771         except:
772             print("Error calling %r" % cb)
773             import traceback
774             traceback.print_exc()
775             continue
776
777         yield prefix, url_manual_mapping
778
779
780 # Build an RNA path from struct/property/enum names.
781 def make_rna_paths(struct_name, prop_name, enum_name):
782     """
783     Create RNA "paths" from given names.
784
785     :arg struct_name: Name of a RNA struct (like e.g. "Scene").
786     :type struct_name: string
787     :arg prop_name: Name of a RNA struct's property.
788     :type prop_name: string
789     :arg enum_name: Name of a RNA enum identifier.
790     :type enum_name: string
791     :return: A triple of three "RNA paths"
792        (most_complete_path, "struct.prop", "struct.prop:'enum'").
793        If no enum_name is given, the third element will always be void.
794     :rtype: tuple of strings
795     """
796     src = src_rna = src_enum = ""
797     if struct_name:
798         if prop_name:
799             src = src_rna = ".".join((struct_name, prop_name))
800             if enum_name:
801                 src = src_enum = "%s:'%s'" % (src_rna, enum_name)
802         else:
803             src = src_rna = struct_name
804     return src, src_rna, src_enum