2
# Copyright 2004 Apache Software Foundation
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
8
# http://www.apache.org/licenses/LICENSE-2.0
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.
16
# This file originally written by Sterling Hughes
18
# $Id: psp.py,v 1.26 2004/02/16 19:47:27 grisha Exp $
20
import apache, Session, util, _psp
27
from cgi import escape
28
import anydbm, whichdb
34
tempdir = tempfile.gettempdir()
36
def path_split(filename):
38
dir, fname = os.path.split(filename)
39
if sys.platform.startswith("win"):
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)
52
return marshal.dumps(ctuple)
56
return new.code(*marshal.loads(s))
60
def __init__(self, req, filename, form):
62
self.filename = filename
63
self.error_page = None
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)
71
# relative to same dir we're in
72
dir = path_split(self.filename)[0]
73
self.error_page = PSP(self.req, dir + page)
75
def apply_data(self, object):
78
self.form = util.FieldStorage(self.req, keep_blank_values=1)
80
return util.apply_fs_data(object, self.form, req=self.req)
82
def redirect(self, location, permanent=0):
84
util.redirect(self.req, location, permanent)
91
def __init__(self, req, filename=None, string=None, vars={}):
93
if (string and filename):
94
raise ValueError, "Must specify either filename or string"
96
self.req, self.vars = req, vars
98
if not filename and not string:
99
filename = req.filename
101
self.filename, self.string = filename, string
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)
111
self.load_from_file()
114
cached = strcache.get(string)
118
self.code = _psp.parsestring(string)
119
strcache.store(string)
121
def cache_get(self, filename, mtime):
123
opts = self.req.get_options()
124
if opts.has_key("PSPDbmCache"):
125
self.dbmcache = opts["PSPDbmCache"]
128
cached = dbm_cache_get(self.req.server, self.dbmcache,
133
cached = mem_fcache.get(filename, mtime)
137
def cache_store(self, filename, mtime, code):
140
dbm_cache_store(self.req.server, self.dbmcache,
141
filename, mtime, code)
143
mem_fcache.store(filename, mtime, code)
145
def cfile_get(self, filename, mtime):
147
# check for a file ending with 'c' (precompiled file)
148
name, ext = os.path.splitext(filename)
149
cname = name + ext[:-1] + 'c'
151
if os.path.isfile(cname):
152
cmtime = os.path.getmtime(cname)
155
return str2code(open(cname).read())
157
def load_from_file(self):
159
filename = self.filename
161
if not os.path.isfile(filename):
162
raise ValueError, "%s is not a file" % filename
164
mtime = os.path.getmtime(filename)
167
code = self.cache_get(filename, mtime)
169
# check for precompiled file
171
code = self.cfile_get(filename, mtime)
173
# finally parse and compile
175
dir, fname = path_split(self.filename)
176
source = _psp.parse(fname, dir)
177
code = compile(source, filename, "exec")
180
self.cache_store(filename, mtime, code)
184
def run(self, vars={}):
186
code, req = self.code, self.req
188
# does this code use session?
190
if "session" in code.co_names:
191
session = Session.Session(req)
193
# does this code use form?
195
if "form" in code.co_names:
196
form = util.FieldStorage(req, keep_blank_values=1)
198
# create psp interface object
199
psp = PSPInterface(req, self.filename, form)
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()
208
exec code in global_scope
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:
216
et, ev, etb = sys.exc_info()
219
psp.error_page.run({"exception": (et, ev, etb)})
223
if session is not None:
227
self.req.content_type = 'text/html'
231
def display_code(self):
233
Display a niceliy HTML-formatted side-by-side of
234
what PSP generated next to orinial code.
237
req, filename = self.req, self.filename
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)
243
dir, fname = path_split(filename)
245
source = open(filename).read().splitlines()
246
pycode = _psp.parse(fname, dir).splitlines()
248
source = [s.rstrip() for s in source]
249
pycode = [s.rstrip() for s in pycode]
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)
260
left = escape(line).replace("\t", " "*4).replace(" ", " ")
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)
272
req.write("</table>\n")
275
def parse(filename, dir=None):
277
return _psp.parse(filename, dir)
279
return _psp.parse(filename)
281
def parsestring(str):
283
return _psp.parsestring(str)
287
req.content_type = "text/html"
289
config = req.get_config()
290
debug = debug = int(config.get("PythonDebug", 0))
292
if debug and req.filename[-1] == "_":
293
p = PSP(req, req.filename[:-1])
301
def dbm_cache_type(dbmfile):
305
if dbm_types.has_key(dbmfile):
306
return dbm_types[dbmfile]
308
module = whichdb.whichdb(dbmfile)
310
dbm_type = __import__(module)
311
dbm_types[dbmfile] = dbm_type
317
def dbm_cache_store(srv, dbmfile, filename, mtime, val):
319
dbm_type = dbm_cache_type(dbmfile)
320
_apache._global_lock(srv, "pspcache")
322
dbm = dbm_type.open(dbmfile, 'c')
323
dbm[filename] = "%d %s" % (mtime, code2str(val))
327
_apache._global_unlock(srv, "pspcache")
329
def dbm_cache_get(srv, dbmfile, filename, mtime):
331
dbm_type = dbm_cache_type(dbmfile)
332
_apache._global_lock(srv, "pspcache")
334
dbm = dbm_type.open(dbmfile, 'c')
336
entry = dbm[filename]
337
t, val = entry.split(" ", 1)
345
_apache._global_unlock(srv, "pspcache")
350
def __init__(self, size=512):
354
def store(self, key, val):
355
self.cache[key] = (1, val)
356
if len(self.cache) > self.size:
360
if self.cache.has_key(key):
361
hist, val = self.cache[key]
362
self.cache[key] = (hits+1, code)
369
byhits = [(n[1], n[0]) for n in self.cache.items()]
372
# delete enough least hit entries to make cache 75% full
373
for item in byhits[:len(self.cache)-int(self.size*.75)]:
377
mem_scache = HitsCache()
379
class FileCache(HitsCache):
381
def store(self, filename, mtime, code):
382
self.cache[filename] = (1, mtime, code)
383
if len(self.cache) > self.size:
386
def get(self, filename, mtime):
388
hits, c_mtime, code = self.cache[filename]
390
del self.cache[filename]
393
self.cache[filename] = (hits+1, mtime, code)
398
mem_fcache = FileCache()