python: Handle errors when loading chained modules more properly.
[ashd.git] / python3 / ashd / wsgidir.py
index e7c168c..c2efcca 100644 (file)
@@ -17,7 +17,7 @@ omitted (such that the name is a string with no dots), in which case
 the handler object is looked up from this module.
 
 By default, this module will handle files with the extensions `.wsgi'
 the handler object is looked up from this module.
 
 By default, this module will handle files with the extensions `.wsgi'
-or `.wsgi2' using the `chain' handler, which chainloads such files and
+or `.wsgi3' using the `chain' handler, which chainloads such files and
 runs them as independent WSGI applications. See its documentation for
 details.
 
 runs them as independent WSGI applications. See its documentation for
 details.
 
@@ -33,10 +33,12 @@ argument `.fpy=my.module.foohandler' can be given to pass requests for
 functions, you may want to use the getmod() function in this module.
 """
 
 functions, you may want to use the getmod() function in this module.
 """
 
-import os, threading, types, importlib
+import sys, os, threading, types, logging, importlib, getopt
 from . import wsgiutil
 
 from . import wsgiutil
 
-__all__ = ["application", "wmain", "getmod", "cachedmod"]
+__all__ = ["application", "wmain", "getmod", "cachedmod", "chain"]
+
+log = logging.getLogger("wsgidir")
 
 class cachedmod(object):
     """Cache entry for modules loaded by getmod()
 
 class cachedmod(object):
     """Cache entry for modules loaded by getmod()
@@ -86,19 +88,18 @@ def getmod(path):
         if path in modcache:
             entry = modcache[path]
         else:
         if path in modcache:
             entry = modcache[path]
         else:
-            entry = cachedmod()
+            entry = [threading.Lock(), None]
             modcache[path] = entry
             modcache[path] = entry
-    with entry.lock:
-        if entry.mod is None or sb.st_mtime > entry.mtime:
+    with entry[0]:
+        if entry[1] is None or sb.st_mtime > entry[1].mtime:
             with open(path, "rb") as f:
                 text = f.read()
             code = compile(text, path, "exec")
             mod = types.ModuleType(mangle(path))
             mod.__file__ = path
             exec(code, mod.__dict__)
             with open(path, "rb") as f:
                 text = f.read()
             code = compile(text, path, "exec")
             mod = types.ModuleType(mangle(path))
             mod.__file__ = path
             exec(code, mod.__dict__)
-            entry.mod = mod
-            entry.mtime = sb.st_mtime
-        return entry
+            entry[1] = cachedmod(mod, sb.st_mtime)
+        return entry[1]
 
 class handler(object):
     def __init__(self):
 
 class handler(object):
     def __init__(self):
@@ -150,12 +151,20 @@ def wmain(*argv):
     Returns the `application' function. If any arguments are given,
     they are parsed according to the module documentation.
     """
     Returns the `application' function. If any arguments are given,
     they are parsed according to the module documentation.
     """
-    ret = handler()
-    for arg in argv:
+    hnd = handler()
+    ret = hnd.handle
+
+    opts, args = getopt.getopt(argv, "-V")
+    for o, a in opts:
+        if o == "-V":
+            import wsgiref.validate
+            ret = wsgiref.validate.validator(ret)
+
+    for arg in args:
         if arg[0] == '.':
             p = arg.index('=')
         if arg[0] == '.':
             p = arg.index('=')
-            ret.addext(arg[1:p], arg[p + 1:])
-    return ret.handle
+            hnd.addext(arg[1:p], arg[p + 1:])
+    return ret
 
 def chain(env, startreq):
     """Chain-loading WSGI handler
 
 def chain(env, startreq):
     """Chain-loading WSGI handler
@@ -173,7 +182,11 @@ def chain(env, startreq):
     object.
     """
     path = env["SCRIPT_FILENAME"]
     object.
     """
     path = env["SCRIPT_FILENAME"]
-    mod = getmod(path)
+    try:
+        mod = getmod(path)
+    except Exception:
+        log.error("Exception occurred when loading %s" % path, exc_info=sys.exc_info())
+        return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "Could not load WSGI handler.")
     entry = None
     if mod is not None:
         with mod.lock:
     entry = None
     if mod is not None:
         with mod.lock: