python: Cleaned up dispatching in wsgidir.
[ashd.git] / python3 / ashd / wsgidir.py
index d4289a7..5761761 100644 (file)
@@ -37,7 +37,7 @@ must, of course, be importable). When writing such handler functions,
 you will probably want to use the getmod() function in this module.
 """
 
 you will probably want to use the getmod() function in this module.
 """
 
-import os, threading, types
+import os, threading, types, importlib
 from . import wsgiutil
 
 __all__ = ["application", "wmain", "getmod", "cachedmod"]
 from . import wsgiutil
 
 __all__ = ["application", "wmain", "getmod", "cachedmod"]
@@ -55,12 +55,11 @@ class cachedmod(object):
     Additional data attributes can be arbitrarily added for recording
     any meta-data about the module.
     """
     Additional data attributes can be arbitrarily added for recording
     any meta-data about the module.
     """
-    def __init__(self, mod, mtime):
+    def __init__(self, mod = None, mtime = -1):
         self.lock = threading.Lock()
         self.mod = mod
         self.mtime = mtime
 
         self.lock = threading.Lock()
         self.mod = mod
         self.mtime = mtime
 
-exts = {}
 modcache = {}
 cachelock = threading.Lock()
 
 modcache = {}
 cachelock = threading.Lock()
 
@@ -90,22 +89,72 @@ def getmod(path):
     with cachelock:
         if path in modcache:
             entry = modcache[path]
     with cachelock:
         if path in modcache:
             entry = modcache[path]
-            if sb.st_mtime <= entry.mtime:
-                return entry
-        
-        f = open(path)
-        try:
-            text = f.read()
-        finally:
-            f.close()
-        code = compile(text, path, "exec")
-        mod = types.ModuleType(mangle(path))
-        mod.__file__ = path
-        exec(code, mod.__dict__)
-        entry = cachedmod(mod, sb.st_mtime)
-        modcache[path] = entry
+        else:
+            entry = cachedmod()
+            modcache[path] = entry
+    with entry.lock:
+        if entry.mod is None or sb.st_mtime > entry.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__)
+            entry.mod = mod
+            entry.mtime = sb.st_mtime
         return entry
 
         return entry
 
+class handler(object):
+    def __init__(self):
+        self.lock = threading.Lock()
+        self.handlers = {}
+        self.exts = {}
+        self.addext("wsgi", "chain")
+        self.addext("wsgi3", "chain")
+
+    def resolve(self, name):
+        with self.lock:
+            if name in self.handlers:
+                return self.handlers[name]
+            p = name.rfind('.')
+            if p < 0:
+                return globals()[name]
+            mname = name[:p]
+            hname = name[p + 1:]
+            mod = importlib.import_module(mname)
+            ret = getattr(mod, hname)
+            self.handlers[name] = ret
+            return ret
+        
+    def addext(self, ext, handler):
+        self.exts[ext] = self.resolve(handler)
+
+    def handle(self, env, startreq):
+        if not "SCRIPT_FILENAME" in env:
+            return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
+        path = env["SCRIPT_FILENAME"]
+        base = os.path.basename(path)
+        p = base.rfind('.')
+        if p < 0 or not os.access(path, os.R_OK):
+            return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
+        ext = base[p + 1:]
+        if not ext in self.exts:
+            return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
+        return(self.exts[ext](env, startreq))
+
+def wmain(*argv):
+    """Main function for ashd(7)-compatible WSGI handlers
+
+    Returns the `application' function. If any arguments are given,
+    they are parsed according to the module documentation.
+    """
+    ret = handler()
+    for arg in argv:
+        if arg[0] == '.':
+            p = arg.index('=')
+            ret.addext(arg[1:p], arg[p + 1:])
+    return ret.handle
+
 def chain(env, startreq):
     path = env["SCRIPT_FILENAME"]
     mod = getmod(path)
 def chain(env, startreq):
     path = env["SCRIPT_FILENAME"]
     mod = getmod(path)
@@ -123,41 +172,5 @@ def chain(env, startreq):
     if entry is not None:
         return entry(env, startreq)
     return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "Invalid WSGI handler.")
     if entry is not None:
         return entry(env, startreq)
     return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "Invalid WSGI handler.")
-exts["wsgi"] = chain
-exts["wsgi3"] = chain
 
 
-def addext(ext, handler):
-    p = handler.rindex('.')
-    mname = handler[:p]
-    hname = handler[p + 1:]
-    mod = __import__(mname, fromlist = ["dummy"])
-    exts[ext] = getattr(mod, hname)
-
-def application(env, startreq):
-    """WSGI handler function
-
-    Handles WSGI requests as per the module documentation.
-    """
-    if not "SCRIPT_FILENAME" in env:
-        return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
-    path = env["SCRIPT_FILENAME"]
-    base = os.path.basename(path)
-    p = base.rfind('.')
-    if p < 0 or not os.access(path, os.R_OK):
-        return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
-    ext = base[p + 1:]
-    if not ext in exts:
-        return wsgiutil.simpleerror(env, startreq, 500, "Internal Error", "The server is erroneously configured.")
-    return(exts[ext](env, startreq))
-
-def wmain(*argv):
-    """Main function for ashd(7)-compatible WSGI handlers
-
-    Returns the `application' function. If any arguments are given,
-    they are parsed according to the module documentation.
-    """
-    for arg in argv:
-        if arg[0] == '.':
-            p = arg.index('=')
-            addext(arg[1:p], arg[p + 1:])
-    return application
+application = handler().handle