You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by gr...@apache.org on 2003/09/05 17:04:44 UTC

cvs commit: httpd-python/lib/python/mod_python apache.py psp.py

grisha      2003/09/05 08:04:44

  Modified:    .        CREDITS
               lib/python/mod_python apache.py psp.py
  Log:
  The functions in psp.py placed into a PSP class now, which makes usage
  cleaner and more intuitive, especially when psp is used as a templaing
  mechanism in a custom handler or publisher. Still need to adjust docs
  to reflect this.
  
  Revision  Changes    Path
  1.22      +4 -0      httpd-python/CREDITS
  
  Index: CREDITS
  ===================================================================
  RCS file: /home/cvs/httpd-python/CREDITS,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- CREDITS	22 Aug 2003 02:22:44 -0000	1.21
  +++ CREDITS	5 Sep 2003 15:04:43 -0000	1.22
  @@ -11,6 +11,8 @@
   
   The names are listed alphabetically by last name.
   
  +Ron Alford <ro...@wam.umd.edu>
  +
   Richard Barrett <R....@ftel.co.uk> 
   
   Gary Benson <gb...@redhat.com>
  @@ -44,6 +46,8 @@
   Sean Reifschneider <ja...@tummy.com> 
   
   Conrad Steenberg <co...@hep.caltech.edu>
  +
  +Sean Treadway <se...@superchannel.org>
   
   Chris Trengove <tr...@econdata.com.au>
   
  
  
  
  1.77      +2 -2      httpd-python/lib/python/mod_python/apache.py
  
  Index: apache.py
  ===================================================================
  RCS file: /home/cvs/httpd-python/lib/python/mod_python/apache.py,v
  retrieving revision 1.76
  retrieving revision 1.77
  diff -u -r1.76 -r1.77
  --- apache.py	3 Sep 2003 19:56:29 -0000	1.76
  +++ apache.py	5 Sep 2003 15:04:43 -0000	1.77
  @@ -473,7 +473,7 @@
               # there is a script by this name already imported, but it's in
               # a different directory, therefore it's a different script
               mtime, oldtime = 0, -1
  -        elif:
  +        elif autoreload: 
               oldmtime = module.__dict__.get("__mtime__", 0)
               mtime = module_mtime(module)
           else:
  
  
  
  1.22      +215 -168  httpd-python/lib/python/mod_python/psp.py
  
  Index: psp.py
  ===================================================================
  RCS file: /home/cvs/httpd-python/lib/python/mod_python/psp.py,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- psp.py	2 Sep 2003 20:44:19 -0000	1.21
  +++ psp.py	5 Sep 2003 15:04:44 -0000	1.22
  @@ -72,15 +72,15 @@
   
   tempdir = tempfile.gettempdir()
   
  -def parse(filename, dir=None):
  -    if dir:
  -        return _psp.parse(filename, dir)
  -    else:
  -        return _psp.parse(filename)
  +def path_split(filename):
   
  -def parsestring(str):
  +    dir, fname = os.path.split(filename)
  +    if os.name == "nt":
  +        dir += "\\"
  +    else:
  +        dir += "/"
   
  -    return _psp.parsestring(str)
  +    return dir, fname
   
   def code2str(c):
   
  @@ -94,194 +94,218 @@
   
       return new.code(*marshal.loads(s))
   
  -def load_file(dir, fname, dbmcache=None, srv=None):
  -
  -    """ In addition to dbmcache, this function will check for
  -    existence of a file with same name, but ending with c and load it
  -    instead. The c file contains already compiled code (see code2str
  -    above). My crude tests showed that mileage varies greatly as to
  -    what the actual speedup would be, but on average for files that
  -    are mostly strings and not a lot of Python it was 1.5 - 3
  -    times. The good news is that this means that the PSP lexer and the
  -    Python parser are *very* fast, so compiling PSP pages is only
  -    necessary in extreme cases. """
  -
  -    smtime = 0
  -    cmtime = 0
  +class PSPInterface:
   
  -    filename = os.path.join(dir, fname)
  +    def __init__(self, req, filename, form):
  +        self.req = req
  +        self.filename = filename
  +        self.error_page = None
  +        self.form = form
   
  -    if os.path.isfile(filename):
  -        smtime = os.path.getmtime(filename)
  +    def set_error_page(self, page):
  +        if page and page[0] == '/':
  +            # relative to document root
  +            self.error_page = PSP(self.req, self.req.document_root() + page)
  +        else:
  +            # relative to same dir we're in
  +            dir = path_split(self.filename)[0]
  +            self.error_page = PSP(self.req, dir + page)
   
  -    if dbmcache:
  -        cached = dbm_cache_get(srv, dbmcache, filename, smtime)
  -        if cached:
  -            return cached
  +    def apply_data(self, object):
   
  -    cached = memcache.get(filename, smtime)
  -    if cached:
  -        return cached
  +        if not self.form:
  +            self.form = util.FieldStorage(self.req, keep_blank_values=1)
   
  -    name, ext = os.path.splitext(filename)
  +        return util.apply_fs_data(object, self.form, req=self.req)
   
  -    cext = ext[:-1] + "c"
  -    cname = name + cext
  -    
  -    if os.path.isfile(name + cext):
  -        cmtime = os.path.getmtime(cname)
  +    def redirect(self, location, permanent=0):
   
  -        if cmtime >= smtime:
  -            # we've got a code file!
  -            code = str2code(open(name + cext).read())
  +        util.redirect(self.req, location, permanent)
   
  -            return code
  +class PSP:
   
  -    source = _psp.parse(fname, dir)
  -    code = compile(source, filename, "exec")
  +    code = None
  +    dbmcache = None
   
  -    if dbmcache:
  -        dbm_cache_store(srv, dbmcache, filename, smtime, code)
  -    else:
  -        memcache.store(filename, smtime, code)
  +    def __init__(self, req, filename=None, string=None):
   
  -    return code
  +        if (string and filename):
  +            raise ValueError, "Must specify either filename or string"
   
  -def path_split(filename):
  +        self.req = req
   
  -    dir, fname = os.path.split(filename)
  -    if os.name == "nt":
  -        dir += "\\"
  -    else:
  -        dir += "/"
  +        if not filename and not string:
  +            filename = req.filename
   
  -    return dir, fname
  +        self.filename, self.string = filename, string
   
  -def display_code(req):
  -    """
  -    Display a niceliy HTML-formatted side-by-side of
  -    what PSP generated next to orinial code
  -    """
  -
  -    dir, fname = path_split(req.filename[:-1])
  -
  -    source = open(req.filename[:-1]).read().splitlines()
  -    pycode = _psp.parse(fname, dir).splitlines()
  -
  -    source = [s.rstrip() for s in source]
  -    pycode = [s.rstrip() for s in pycode]
  -
  -    req.write("<table>\n<tr>")
  -    for s in ("", "&nbsp;PSP-produced Python Code:",
  -              "&nbsp;%s:" % req.filename[:-1]):
  -        req.write("<td><tt>%s</tt></td>" % s)
  -    req.write("</tr>\n")
  -
  -    n = 1
  -    for line in pycode:
  -        req.write("<tr>")
  -        left = escape(line).replace("\t", " "*4).replace(" ", "&nbsp;")
  -        if len(source) < n:
  -            right = ""
  +        if filename:
  +            self.load_from_file()
           else:
  -            right = escape(source[n-1]).replace("\t", " "*4).replace(" ", "&nbsp;")
  -        for s in ("%d.&nbsp;" % n,
  -                  "<font color=blue>%s</font>" % left,
  -                  "&nbsp;<font color=green>%s</font>" % right):
  -            req.write("<td><tt>%s</tt></td>" % s)
  -        req.write("</tr>\n")
  -                      
  -        n += 1
  -    req.write("</table>\n")
   
  -    return apache.OK
  +            cached = strcache.get(string)
  +            if cached:
  +                self.code = cached
  +            else:
  +                self.code = _psp.parsestring(string)
  +                strcache.store(string)
   
  -class PSPInterface:
  +    def cache_get(self, filename, mtime):
   
  -    def __init__(self, req, dir, fname, dbmcache, session, form):
  -        self._req = req
  -        self._dir = dir
  -        self._fname = fname
  -        self._dbmcache = dbmcache
  -        self._session = session
  -        self._form = form
  -        self._error_page = None
  +        opts = self.req.get_options()
  +        if opts.has_key("PSPDbmCache"):
  +            self.dbmcache = opts["PSPDbmCache"]
  +
  +        if self.dbmcache:
  +            cached = dbm_cache_get(self.req.server, self.dbmcache,
  +                                   filename, mtime)
  +            if cached:
  +                return cached
   
  -    def set_error_page(self, page):
  -        if page and page[0] == '/':
  -            # absolute relative to document root
  -            self._error_page = load_file(self._req.document_root(), page,
  -                                         self._dbmcache,
  -                                         srv=self._req.server)
  +        cached = mem_fcache.get(filename, mtime)
  +        if cached:
  +            return cached
  +
  +    def cache_store(self, filename, mtime, code):
  +
  +        if self.dbmcache:
  +            dbm_cache_store(self.req.server, self.dbmcache,
  +                            filename, mtime, code)
           else:
  -            # relative to same dir we're in
  -            self._error_page = load_file(self._dir, page, self._dbmcache,
  -                                         srv=self._req.server)
  +            mem_fcache.store(filename, mtime, code)
   
  -    def apply_data(self, object):
  +    def cfile_get(self, filename, mtime):
   
  -        if not self._form:
  -            self._form = util.FieldStorage(self._req,
  -                                           keep_blank_values=1)
  +        # check for a file ending with 'c' (precompiled file)
  +        name, ext = os.path.splitext(filename)
  +        cname = name + ext[:-1] + 'c'
   
  -        return util.apply_fs_data(object, self._form,
  -                                  req=self._req)
  +        if os.path.isfile(cname):
  +            cmtime = os.path.getmtime(cname)
   
  -    def redirect(self, location, permanent=0):
  +            if cmtime >= mtime:
  +                return str2code(open(cname).read())
   
  -        util.redirect(self._req, location, permanent)
  +    def load_from_file(self):
   
  -def run_psp(req):
  +        filename = self.filename
   
  -    dbmcache = None
  -    opts = req.get_options()
  -    if opts.has_key("PSPDbmCache"):
  -        dbmcache = opts["PSPDbmCache"]
  +        if not os.path.isfile(filename):
  +            raise ValueError, "%s is not a file" % filename
   
  -    dir, fname = path_split(req.filename)
  +        mtime = os.path.getmtime(filename)
   
  -    if not fname:
  -        # this is a directory
  -        return apache.DECLINED
  +        # check cache
  +        code = self.cache_get(filename, mtime)
   
  -    code = load_file(dir, fname, dbmcache, srv=req.server)
  +        # check for precompiled file
  +        if not code:
  +            code = self.cfile_get(filename, mtime)
   
  -    session = None
  -    if "session" in code.co_names:
  -        session = Session.Session(req)
  +        # finally parse and compile
  +        if not code:
  +            dir, fname = path_split(self.filename)
  +            source = _psp.parse(fname, dir)
  +            code = compile(source, filename, "exec")
   
  -    form = None
  -    if "form" in code.co_names:
  -        form = util.FieldStorage(req, keep_blank_values=1)
  +        # store in cache
  +        self.cache_store(filename, mtime, code)
   
  -    psp = PSPInterface(req, dir, fname, dbmcache, session, form)
  +        self.code = code
  +
  +    def run(self, vars={}):
  +
  +        code, req = self.code, self.req
  +
  +        # does this code use session?
  +        session = None
  +        if "session" in code.co_names:
  +            session = Session.Session(req)
  +
  +        # does this code use form?
  +        form = None
  +        if "form" in code.co_names:
  +            form = util.FieldStorage(req, keep_blank_values=1)
  +
  +        # create psp interface object
  +        psp = PSPInterface(req, self.filename, form)
   
  -    try:
           try:
               global_scope = globals().copy()
               global_scope.update({"req":req, "session":session,
                                    "form":form, "psp":psp})
  -            exec code in global_scope
  -
  -            # the mere instantiation of a session changes it
  -            # (access time), so it *always* has to be saved
  +            global_scope.update(vars)
  +            try:
  +                exec code in global_scope
  +
  +                # the mere instantiation of a session changes it
  +                # (access time), so it *always* has to be saved
  +                if session:
  +                    session.save()
  +            except:
  +                et, ev, etb = sys.exc_info()
  +                if psp.error_page:
  +                    # run error page
  +                    psp.error_page.run({"exception": (et, ev, etb)})
  +                else:
  +                    raise et, ev, etb
  +        finally:
               if session:
  -                session.save()
  -        except:
  -            et, ev, etb = sys.exc_info()
  -            if psp._error_page:
  -                # run error page
  -                exec psp._error_page in globals(), {"req":req, "session":session,
  -                                                    "form":form, "psp":psp,
  -                                                    "exception": (et, ev, etb)}
  +                    session.unlock()
  +
  +    def display_code(self):
  +        """
  +        Display a niceliy HTML-formatted side-by-side of
  +        what PSP generated next to orinial code.
  +        """
  +
  +        req, filename = self.req, self.filename
  +
  +        # Because of caching, source code is most often not
  +        # available in this object, so we read it here
  +        # (instead of trying to get it in __init__ somewhere)
  +
  +        dir, fname = path_split(filename)
  +
  +        source = open(filename).read().splitlines()
  +        pycode = _psp.parse(fname, dir).splitlines()
  +
  +        source = [s.rstrip() for s in source]
  +        pycode = [s.rstrip() for s in pycode]
  +
  +        req.write("<table>\n<tr>")
  +        for s in ("", "&nbsp;PSP-produced Python Code:",
  +                  "&nbsp;%s:" % filename):
  +            req.write("<td><tt>%s</tt></td>" % s)
  +        req.write("</tr>\n")
  +
  +        n = 1
  +        for line in pycode:
  +            req.write("<tr>")
  +            left = escape(line).replace("\t", " "*4).replace(" ", "&nbsp;")
  +            if len(source) < n:
  +                right = ""
               else:
  -                # pass it on
  -                raise et, ev, etb
  -    finally:
  -        if session:
  -                session.unlock()
  -        
  -    return apache.OK
  +                right = escape(source[n-1]).replace("\t", " "*4).replace(" ", "&nbsp;")
  +            for s in ("%d.&nbsp;" % n,
  +                      "<font color=blue>%s</font>" % left,
  +                      "&nbsp;<font color=green>%s</font>" % right):
  +                req.write("<td><tt>%s</tt></td>" % s)
  +            req.write("</tr>\n")
  +
  +            n += 1
  +        req.write("</table>\n")
  +
  +
  +def parse(filename, dir=None):
  +    if dir:
  +        return _psp.parse(filename, dir)
  +    else:
  +        return _psp.parse(filename)
  +
  +def parsestring(str):
  +
  +    return _psp.parsestring(str)
   
   def handler(req):
   
  @@ -291,9 +315,13 @@
       debug = debug = int(config.get("PythonDebug", 0))
   
       if debug and req.filename[-1] == "_":
  -        return display_code(req)
  +        p = PSP(req, req.filename[:-1])
  +        p.display_code()
       else:
  -        return run_psp(req)
  +        p = PSP(req)
  +        p.run()
  +
  +    return apache.OK
   
   def dbm_cache_type(dbmfile):
   
  @@ -341,12 +369,40 @@
           except: pass
           _apache._global_unlock(srv, "pspcache")
   
  -class MemCache:
  +
  +class HitsCache:
   
       def __init__(self, size=512):
           self.cache = {}
           self.size = size
   
  +    def store(self, key, val):
  +        self.cache[key] = (1, val)
  +        if len(self.cache) > self.size:
  +            self.clean()
  +
  +    def get(self, key):
  +        if self.cache.has_key(key):
  +            hist, val = self.cache[key]
  +            self.cache[key] = (hits+1, code)
  +            return val
  +        else:
  +            return None
  +
  +    def clean(self):
  +        
  +        byhits = [(n[1], n[0]) for n in self.cache.items()]
  +        byhits.sort()
  +
  +        # delete enough least hit entries to make cache 75% full
  +        for item in byhits[:len(self.cache)-int(self.size*.75)]:
  +            val, key = item
  +            del self.cache[key]
  +
  +mem_scache = HitsCache()
  +
  +class FileCache(HitsCache):
  +
       def store(self, filename, mtime, code):
           self.cache[filename] = (1, mtime, code)
           if len(self.cache) > self.size:
  @@ -364,14 +420,5 @@
           except KeyError:
               return None
   
  -    def clean(self):
  -        
  -        byhits = [(n[1], n[0]) for n in self.cache.items()]
  -        byhits.sort()
  -
  -        # delete enough least hit entries to make cache 75% full
  -        for item in byhits[:len(self.cache)-int(self.size*.75)]:
  -            entry, filename = item
  -            del self.cache[filename]
  +mem_fcache = FileCache()
   
  -memcache = MemCache()