home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / python / psp.py < prev    next >
Encoding:
Python Source  |  2004-02-16  |  10.8 KB  |  400 lines

  1.  #
  2.  # Copyright 2004 Apache Software Foundation 
  3.  # 
  4.  # Licensed under the Apache License, Version 2.0 (the "License"); you
  5.  # may not use this file except in compliance with the License.  You
  6.  # may obtain a copy of the License at
  7.  #
  8.  #      http://www.apache.org/licenses/LICENSE-2.0
  9.  #
  10.  # Unless required by applicable law or agreed to in writing, software
  11.  # distributed under the License is distributed on an "AS IS" BASIS,
  12.  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  13.  # implied.  See the License for the specific language governing
  14.  # permissions and limitations under the License.
  15.  #
  16.  # This file originally written by Sterling Hughes
  17.  #
  18.  # $Id: psp.py,v 1.26 2004/02/16 19:47:27 grisha Exp $
  19.  
  20. import apache, Session, util, _psp
  21. import _apache
  22.  
  23. import sys
  24. import os
  25. import marshal
  26. import new
  27. from cgi import escape
  28. import anydbm, whichdb
  29. import tempfile
  30.  
  31. # dbm types for cache
  32. dbm_types = {}
  33.  
  34. tempdir = tempfile.gettempdir()
  35.  
  36. def path_split(filename):
  37.  
  38.     dir, fname = os.path.split(filename)
  39.     if sys.platform.startswith("win"):
  40.         dir += "\\"
  41.     else:
  42.         dir += "/"
  43.  
  44.     return dir, fname
  45.  
  46. def code2str(c):
  47.  
  48.     ctuple = (c.co_argcount, c.co_nlocals, c.co_stacksize, c.co_flags,
  49.               c.co_code, c.co_consts, c.co_names, c.co_varnames, c.co_filename,
  50.               c.co_name, c.co_firstlineno, c.co_lnotab)
  51.     
  52.     return marshal.dumps(ctuple)
  53.  
  54. def str2code(s):
  55.  
  56.     return new.code(*marshal.loads(s))
  57.  
  58. class PSPInterface:
  59.  
  60.     def __init__(self, req, filename, form):
  61.         self.req = req
  62.         self.filename = filename
  63.         self.error_page = None
  64.         self.form = form
  65.  
  66.     def set_error_page(self, page):
  67.         if page and page[0] == '/':
  68.             # relative to document root
  69.             self.error_page = PSP(self.req, self.req.document_root() + page)
  70.         else:
  71.             # relative to same dir we're in
  72.             dir = path_split(self.filename)[0]
  73.             self.error_page = PSP(self.req, dir + page)
  74.  
  75.     def apply_data(self, object):
  76.  
  77.         if not self.form:
  78.             self.form = util.FieldStorage(self.req, keep_blank_values=1)
  79.  
  80.         return util.apply_fs_data(object, self.form, req=self.req)
  81.  
  82.     def redirect(self, location, permanent=0):
  83.  
  84.         util.redirect(self.req, location, permanent)
  85.  
  86. class PSP:
  87.  
  88.     code = None
  89.     dbmcache = None
  90.  
  91.     def __init__(self, req, filename=None, string=None, vars={}):
  92.  
  93.         if (string and filename):
  94.             raise ValueError, "Must specify either filename or string"
  95.  
  96.         self.req, self.vars = req, vars
  97.  
  98.         if not filename and not string:
  99.             filename = req.filename
  100.  
  101.         self.filename, self.string = filename, string
  102.  
  103.         if filename:
  104.  
  105.             # if filename is not absolute, default to our guess
  106.             # of current directory
  107.             if not os.path.isabs(filename):
  108.                 base = os.path.split(req.filename)[0]
  109.                 self.filename = os.path.join(base, filename)
  110.  
  111.             self.load_from_file()
  112.         else:
  113.  
  114.             cached = strcache.get(string)
  115.             if cached:
  116.                 self.code = cached
  117.             else:
  118.                 self.code = _psp.parsestring(string)
  119.                 strcache.store(string)
  120.  
  121.     def cache_get(self, filename, mtime):
  122.  
  123.         opts = self.req.get_options()
  124.         if opts.has_key("PSPDbmCache"):
  125.             self.dbmcache = opts["PSPDbmCache"]
  126.  
  127.         if self.dbmcache:
  128.             cached = dbm_cache_get(self.req.server, self.dbmcache,
  129.                                    filename, mtime)
  130.             if cached:
  131.                 return cached
  132.  
  133.         cached = mem_fcache.get(filename, mtime)
  134.         if cached:
  135.             return cached
  136.  
  137.     def cache_store(self, filename, mtime, code):
  138.  
  139.         if self.dbmcache:
  140.             dbm_cache_store(self.req.server, self.dbmcache,
  141.                             filename, mtime, code)
  142.         else:
  143.             mem_fcache.store(filename, mtime, code)
  144.  
  145.     def cfile_get(self, filename, mtime):
  146.  
  147.         # check for a file ending with 'c' (precompiled file)
  148.         name, ext = os.path.splitext(filename)
  149.         cname = name + ext[:-1] + 'c'
  150.  
  151.         if os.path.isfile(cname):
  152.             cmtime = os.path.getmtime(cname)
  153.  
  154.             if cmtime >= mtime:
  155.                 return str2code(open(cname).read())
  156.  
  157.     def load_from_file(self):
  158.  
  159.         filename = self.filename
  160.  
  161.         if not os.path.isfile(filename):
  162.             raise ValueError, "%s is not a file" % filename
  163.  
  164.         mtime = os.path.getmtime(filename)
  165.  
  166.         # check cache
  167.         code = self.cache_get(filename, mtime)
  168.  
  169.         # check for precompiled file
  170.         if not code:
  171.             code = self.cfile_get(filename, mtime)
  172.  
  173.         # finally parse and compile
  174.         if not code:
  175.             dir, fname = path_split(self.filename)
  176.             source = _psp.parse(fname, dir)
  177.             code = compile(source, filename, "exec")
  178.  
  179.         # store in cache
  180.         self.cache_store(filename, mtime, code)
  181.  
  182.         self.code = code
  183.  
  184.     def run(self, vars={}):
  185.  
  186.         code, req = self.code, self.req
  187.  
  188.         # does this code use session?
  189.         session = None
  190.         if "session" in code.co_names:
  191.             session = Session.Session(req)
  192.  
  193.         # does this code use form?
  194.         form = None
  195.         if "form" in code.co_names:
  196.             form = util.FieldStorage(req, keep_blank_values=1)
  197.  
  198.         # create psp interface object
  199.         psp = PSPInterface(req, self.filename, form)
  200.  
  201.         try:
  202.             global_scope = globals().copy()
  203.             global_scope.update({"req":req, "session":session,
  204.                                  "form":form, "psp":psp})
  205.             global_scope.update(self.vars) # passed in __init__()
  206.             global_scope.update(vars)      # passed in run()
  207.             try:
  208.                 exec code in global_scope
  209.                 req.flush()
  210.                 
  211.                 # the mere instantiation of a session changes it
  212.                 # (access time), so it *always* has to be saved
  213.                 if session is not None:
  214.                     session.save()
  215.             except:
  216.                 et, ev, etb = sys.exc_info()
  217.                 if psp.error_page:
  218.                     # run error page
  219.                     psp.error_page.run({"exception": (et, ev, etb)})
  220.                 else:
  221.                     raise et, ev, etb
  222.         finally:
  223.             if session is not None:
  224.                     session.unlock()
  225.  
  226.     def __str__(self):
  227.         self.req.content_type = 'text/html'
  228.         self.run()
  229.         return ""
  230.  
  231.     def display_code(self):
  232.         """
  233.         Display a niceliy HTML-formatted side-by-side of
  234.         what PSP generated next to orinial code.
  235.         """
  236.  
  237.         req, filename = self.req, self.filename
  238.  
  239.         # Because of caching, source code is most often not
  240.         # available in this object, so we read it here
  241.         # (instead of trying to get it in __init__ somewhere)
  242.  
  243.         dir, fname = path_split(filename)
  244.  
  245.         source = open(filename).read().splitlines()
  246.         pycode = _psp.parse(fname, dir).splitlines()
  247.  
  248.         source = [s.rstrip() for s in source]
  249.         pycode = [s.rstrip() for s in pycode]
  250.  
  251.         req.write("<table>\n<tr>")
  252.         for s in ("", " PSP-produced Python Code:",
  253.                   " %s:" % filename):
  254.             req.write("<td><tt>%s</tt></td>" % s)
  255.         req.write("</tr>\n")
  256.  
  257.         n = 1
  258.         for line in pycode:
  259.             req.write("<tr>")
  260.             left = escape(line).replace("\t", " "*4).replace(" ", " ")
  261.             if len(source) < n:
  262.                 right = ""
  263.             else:
  264.                 right = escape(source[n-1]).replace("\t", " "*4).replace(" ", " ")
  265.             for s in ("%d. " % n,
  266.                       "<font color=blue>%s</font>" % left,
  267.                       " <font color=green>%s</font>" % right):
  268.                 req.write("<td><tt>%s</tt></td>" % s)
  269.             req.write("</tr>\n")
  270.  
  271.             n += 1
  272.         req.write("</table>\n")
  273.  
  274.  
  275. def parse(filename, dir=None):
  276.     if dir:
  277.         return _psp.parse(filename, dir)
  278.     else:
  279.         return _psp.parse(filename)
  280.  
  281. def parsestring(str):
  282.  
  283.     return _psp.parsestring(str)
  284.  
  285. def handler(req):
  286.  
  287.     req.content_type = "text/html"
  288.  
  289.     config = req.get_config()
  290.     debug = debug = int(config.get("PythonDebug", 0))
  291.  
  292.     if debug and req.filename[-1] == "_":
  293.         p = PSP(req, req.filename[:-1])
  294.         p.display_code()
  295.     else:
  296.         p = PSP(req)
  297.         p.run()
  298.  
  299.     return apache.OK
  300.  
  301. def dbm_cache_type(dbmfile):
  302.  
  303.     global dbm_types
  304.  
  305.     if dbm_types.has_key(dbmfile):
  306.         return dbm_types[dbmfile]
  307.  
  308.     module = whichdb.whichdb(dbmfile)
  309.     if module:
  310.         dbm_type = __import__(module)
  311.         dbm_types[dbmfile] = dbm_type
  312.         return dbm_type
  313.     else:
  314.         # this is a new file
  315.         return anydbm
  316.  
  317. def dbm_cache_store(srv, dbmfile, filename, mtime, val):
  318.     
  319.     dbm_type = dbm_cache_type(dbmfile)
  320.     _apache._global_lock(srv, "pspcache")
  321.     try:
  322.         dbm = dbm_type.open(dbmfile, 'c')
  323.         dbm[filename] = "%d %s" % (mtime, code2str(val))
  324.     finally:
  325.         try: dbm.close()
  326.         except: pass
  327.         _apache._global_unlock(srv, "pspcache")
  328.  
  329. def dbm_cache_get(srv, dbmfile, filename, mtime):
  330.  
  331.     dbm_type = dbm_cache_type(dbmfile)
  332.     _apache._global_lock(srv, "pspcache")
  333.     try:
  334.         dbm = dbm_type.open(dbmfile, 'c')
  335.         try:
  336.             entry = dbm[filename]
  337.             t, val = entry.split(" ", 1)
  338.             if long(t) == mtime:
  339.                 return str2code(val)
  340.         except KeyError:
  341.             return None
  342.     finally:
  343.         try: dbm.close()
  344.         except: pass
  345.         _apache._global_unlock(srv, "pspcache")
  346.  
  347.  
  348. class HitsCache:
  349.  
  350.     def __init__(self, size=512):
  351.         self.cache = {}
  352.         self.size = size
  353.  
  354.     def store(self, key, val):
  355.         self.cache[key] = (1, val)
  356.         if len(self.cache) > self.size:
  357.             self.clean()
  358.  
  359.     def get(self, key):
  360.         if self.cache.has_key(key):
  361.             hist, val = self.cache[key]
  362.             self.cache[key] = (hits+1, code)
  363.             return val
  364.         else:
  365.             return None
  366.  
  367.     def clean(self):
  368.         
  369.         byhits = [(n[1], n[0]) for n in self.cache.items()]
  370.         byhits.sort()
  371.  
  372.         # delete enough least hit entries to make cache 75% full
  373.         for item in byhits[:len(self.cache)-int(self.size*.75)]:
  374.             val, key = item
  375.             del self.cache[key]
  376.  
  377. mem_scache = HitsCache()
  378.  
  379. class FileCache(HitsCache):
  380.  
  381.     def store(self, filename, mtime, code):
  382.         self.cache[filename] = (1, mtime, code)
  383.         if len(self.cache) > self.size:
  384.             self.clean()
  385.  
  386.     def get(self, filename, mtime):
  387.         try:
  388.             hits, c_mtime, code = self.cache[filename]
  389.             if mtime != c_mtime:
  390.                 del self.cache[filename]
  391.                 return None
  392.             else:
  393.                 self.cache[filename] = (hits+1, mtime, code)
  394.                 return code
  395.         except KeyError:
  396.             return None
  397.  
  398. mem_fcache = FileCache()
  399.  
  400.