source: sasmodels/sasmodels/custom/__init__.py @ 91bd550

core_shell_microgelsmagnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 91bd550 was 91bd550, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

handle nested dependencies

  • Property mode set to 100644
File size: 3.8 KB
Line 
1"""
2Custom Models
3-------------
4
5This is a place holder for the custom models namespace.  When models are
6loaded from a file by :func:`generate.load_kernel_module` they are loaded
7as if they exist in *sasmodels.custom*.  This package needs to exist for this
8to occur without error.
9"""
10from __future__ import division, print_function
11
12import sys
13import os
14from os.path import basename, splitext
15
16try:
17    # Python 3.5 and up
18    from importlib.util import spec_from_file_location, module_from_spec  # type: ignore
19    def load_module_from_path(fullname, path):
20        """load module from *path* as *fullname*"""
21        spec = spec_from_file_location(fullname, os.path.expanduser(path))
22        module = module_from_spec(spec)
23        spec.loader.exec_module(module)
24        return module
25except ImportError:
26    # CRUFT: python 2
27    import imp
28    def load_module_from_path(fullname, path):
29        """load module from *path* as *fullname*"""
30        # Clear out old definitions, if any
31        if fullname in sys.modules:
32            del sys.modules[fullname]
33        if path.endswith(".py") and os.path.exists(path) and os.path.exists(path+"c"):
34            # remove automatic pyc file before loading a py file
35            os.unlink(path+"c")
36        module = imp.load_source(fullname, os.path.expanduser(path))
37        return module
38
39_MODULE_CACHE = {}
40_MODULE_DEPENDS = {}
41_MODULE_DEPENDS_STACK = []
42def load_custom_kernel_module(path):
43    """load SAS kernel from *path* as *sasmodels.custom.modelname*"""
44    # Pull off the last .ext if it exists; there may be others
45    name = basename(splitext(path)[0])
46    path = os.path.expanduser(path)
47
48    # reload module if necessary
49    if need_reload(path):
50        # Push to the next dependency level
51        _MODULE_DEPENDS_STACK.append(path)
52        _MODULE_DEPENDS[path] = set([path])
53
54        # Load module into the 'sasmodels.custom' name space.
55        # If this triggers any submodule loads then they will be added
56        # as dependencies below when _MODULE_DEPENDS_STACK is not empty.
57        module = load_module_from_path('sasmodels.custom.'+name, path)
58
59        # Pop the dependency level
60        _MODULE_DEPENDS_STACK.pop()
61
62        # TODO: include external C code in the dependencies
63        # If we had the model info structure we could do the following:
64        #    _MODEL_DEPENDS[path].extend(generate.model_sources(info))
65        # but at this point all we have is the module.  Don't want to
66        # repeat the logic in modelinfo.make_model_info.
67
68        # Cache the module with the newest timestamp
69        timestamp = max(os.path.getmtime(f) for f in _MODULE_DEPENDS[path])
70        _MODULE_CACHE[path] = module, timestamp
71
72        #print("loading", os.path.basename(path), _MODULE_CACHE[path][1],
73        #    [os.path.basename(p) for p in _MODULE_DEPENDS[path]])
74
75    if _MODULE_DEPENDS_STACK:
76        # Add child and all its dependence to the parent module
77        working_on = _MODULE_DEPENDS_STACK[-1]
78        _MODULE_DEPENDS[working_on].update(_MODULE_DEPENDS[path])
79
80    return _MODULE_CACHE[path][0]
81
82def need_reload(path):
83    # TODO: fails if a dependency has a modification time in the future
84    # If the newest dependency has a time stamp in the future, then this
85    # will be recorded as the cached time.  When a second dependency
86    # is updated to the current time stamp, it will still be considered
87    # older than the current build and the reload will not be triggered.
88    # Could instead treat all future times as 0 here and in the code above
89    # which records the newest timestamp.  This will force a reload when
90    # the future time is reached, but other than that should perform
91    # correctly.  Probably not worth the extra code...
92    _, cache_time = _MODULE_CACHE.get(path, (None, -1))
93    depends = _MODULE_DEPENDS.get(path, [path])
94    return any(cache_time < os.path.getmtime(p) for p in depends)
Note: See TracBrowser for help on using the repository browser.