Index: sasmodels/custom/__init__.py
===================================================================
--- sasmodels/custom/__init__.py	(revision 0f48f1e9570fb194a19f0d8faf8c23c879a61d8b)
+++ sasmodels/custom/__init__.py	(revision 91bd550ddd44f57ea241f0f2e3f7bb1d1ab4f5ee)
@@ -37,10 +37,58 @@
         return module
 
+_MODULE_CACHE = {}
+_MODULE_DEPENDS = {}
+_MODULE_DEPENDS_STACK = []
 def load_custom_kernel_module(path):
     """load SAS kernel from *path* as *sasmodels.custom.modelname*"""
     # Pull off the last .ext if it exists; there may be others
     name = basename(splitext(path)[0])
-    # Placing the model in the 'sasmodels.custom' name space.
-    kernel_module = load_module_from_path('sasmodels.custom.'+name,
-                                          os.path.expanduser(path))
-    return kernel_module
+    path = os.path.expanduser(path)
+
+    # reload module if necessary
+    if need_reload(path):
+        # Push to the next dependency level
+        _MODULE_DEPENDS_STACK.append(path)
+        _MODULE_DEPENDS[path] = set([path])
+
+        # Load module into the 'sasmodels.custom' name space.
+        # If this triggers any submodule loads then they will be added
+        # as dependencies below when _MODULE_DEPENDS_STACK is not empty.
+        module = load_module_from_path('sasmodels.custom.'+name, path)
+
+        # Pop the dependency level
+        _MODULE_DEPENDS_STACK.pop()
+
+        # TODO: include external C code in the dependencies
+        # If we had the model info structure we could do the following:
+        #    _MODEL_DEPENDS[path].extend(generate.model_sources(info))
+        # but at this point all we have is the module.  Don't want to
+        # repeat the logic in modelinfo.make_model_info.
+
+        # Cache the module with the newest timestamp
+        timestamp = max(os.path.getmtime(f) for f in _MODULE_DEPENDS[path])
+        _MODULE_CACHE[path] = module, timestamp
+
+        #print("loading", os.path.basename(path), _MODULE_CACHE[path][1],
+        #    [os.path.basename(p) for p in _MODULE_DEPENDS[path]])
+
+    if _MODULE_DEPENDS_STACK:
+        # Add child and all its dependence to the parent module
+        working_on = _MODULE_DEPENDS_STACK[-1]
+        _MODULE_DEPENDS[working_on].update(_MODULE_DEPENDS[path])
+
+    return _MODULE_CACHE[path][0]
+
+def need_reload(path):
+    # TODO: fails if a dependency has a modification time in the future
+    # If the newest dependency has a time stamp in the future, then this
+    # will be recorded as the cached time.  When a second dependency
+    # is updated to the current time stamp, it will still be considered
+    # older than the current build and the reload will not be triggered.
+    # Could instead treat all future times as 0 here and in the code above
+    # which records the newest timestamp.  This will force a reload when
+    # the future time is reached, but other than that should perform
+    # correctly.  Probably not worth the extra code...
+    _, cache_time = _MODULE_CACHE.get(path, (None, -1))
+    depends = _MODULE_DEPENDS.get(path, [path])
+    return any(cache_time < os.path.getmtime(p) for p in depends)
Index: sasmodels/kernelpy.py
===================================================================
--- sasmodels/kernelpy.py	(revision 108e70e22154db937a6d46f66be6dd70408cce6d)
+++ sasmodels/kernelpy.py	(revision 91bd550ddd44f57ea241f0f2e3f7bb1d1ab4f5ee)
@@ -37,5 +37,4 @@
         self.info = model_info
         self.dtype = np.dtype('d')
-        logger.info("load python model " + self.info.name)
 
     def make_kernel(self, q_vectors):
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision d533590d9857d7aef962616cfdaa6ecfc2e6751d)
+++ sasmodels/sasview_model.py	(revision 839fd687ba0eee06a311dd05249ef8ff5abf12bc)
@@ -62,4 +62,5 @@
 #: set of defined models (standard and custom)
 MODELS = {}  # type: Dict[str, SasviewModelType]
+# TODO: remove unused MODEL_BY_PATH cache once sasview no longer references it
 #: custom model {path: model} mapping so we can check timestamps
 MODEL_BY_PATH = {}  # type: Dict[str, SasviewModelType]
@@ -106,9 +107,4 @@
     Load a custom model given the model path.
     """
-    model = MODEL_BY_PATH.get(path, None)
-    if model is not None and model.timestamp == getmtime(path):
-        #logger.info("Model already loaded %s", path)
-        return model
-
     #logger.info("Loading model %s", path)
     kernel_module = custom.load_custom_kernel_module(path)
@@ -127,5 +123,4 @@
         model_info = modelinfo.make_model_info(kernel_module)
         model = make_model_from_info(model_info)
-    model.timestamp = getmtime(path)
 
     # If a model name already exists and we are loading a different model,
@@ -144,5 +139,4 @@
 
     MODELS[model.name] = model
-    MODEL_BY_PATH[path] = model
     return model
 
