Package web2py :: Package gluon :: Module restricted
[hide private]
[frames] | no frames]

Source Code for Module web2py.gluon.restricted

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  """ 
  5  This file is part of the web2py Web Framework 
  6  Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8  """ 
  9   
 10  import sys 
 11  import cPickle 
 12  import traceback 
 13  import types 
 14  import os 
 15  import datetime 
 16  import logging 
 17   
 18  from utils import web2py_uuid 
 19  from storage import Storage 
 20  from http import HTTP 
 21  from html import BEAUTIFY 
 22   
 23  logger = logging.getLogger("web2py") 
 24   
 25  __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2'] 
 26   
27 -class TicketStorage(Storage):
28 29 """ 30 defines the ticket object and the default values of its members (None) 31 """ 32
33 - def __init__( 34 self, 35 db=None, 36 tablename='web2py_ticket' 37 ):
38 self.db = db 39 self.tablename = tablename
40
41 - def store(self, request, ticket_id, ticket_data):
42 """ 43 stores the ticket. It will figure out if this must be on disk or in db 44 """ 45 if self.db: 46 self._store_in_db(request, ticket_id, ticket_data) 47 else: 48 self._store_on_disk(request, ticket_id, ticket_data)
49
50 - def _store_in_db(self, request, ticket_id, ticket_data):
51 table = self._get_table(self.db, self.tablename, request.application) 52 table.insert(ticket_id=ticket_id, 53 ticket_data=cPickle.dumps(ticket_data), 54 created_datetime=request.now) 55 logger.error('In FILE: %(layer)s\n\n%(traceback)s\n' % ticket_data)
56
57 - def _store_on_disk(self, request, ticket_id, ticket_data):
58 ef = self._error_file(request, ticket_id, 'wb') 59 try: 60 cPickle.dump(ticket_data, ef) 61 finally: 62 ef.close()
63
64 - def _error_file(self, request, ticket_id, mode, app=None):
65 root = request.folder 66 if app: 67 root = os.path.join(os.path.join(root, '..'), app) 68 errors_folder = os.path.abspath(os.path.join(root, 'errors'))#.replace('\\', '/') 69 return open(os.path.join(errors_folder, ticket_id), mode)
70
71 - def _get_table(self, db, tablename, app):
72 tablename = tablename + '_' + app 73 table = db.get(tablename, None) 74 if table is None: 75 db.rollback() # not necessary but one day 76 # any app may store tickets on DB 77 table = db.define_table( 78 tablename, 79 db.Field('ticket_id', length=100), 80 db.Field('ticket_data', 'text'), 81 db.Field('created_datetime', 'datetime'), 82 ) 83 return table
84
85 - def load( 86 self, 87 request, 88 app, 89 ticket_id, 90 ):
91 if not self.db: 92 ef = self._error_file(request, ticket_id, 'rb', app) 93 try: 94 return cPickle.load(ef) 95 finally: 96 ef.close() 97 table = self._get_table(self.db, self.tablename, app) 98 rows = self.db(table.ticket_id == ticket_id).select() 99 if rows: 100 return cPickle.loads(rows[0].ticket_data) 101 return None
102 103
104 -class RestrictedError(Exception):
105 """ 106 class used to wrap an exception that occurs in the restricted environment 107 below. the traceback is used to log the exception and generate a ticket. 108 """ 109
110 - def __init__( 111 self, 112 layer='', 113 code='', 114 output='', 115 environment={}, 116 ):
117 """ 118 layer here is some description of where in the system the exception 119 occurred. 120 """ 121 122 self.layer = layer 123 self.code = code 124 self.output = output 125 if layer: 126 try: 127 self.traceback = traceback.format_exc() 128 except: 129 self.traceback = 'no traceback because template parting error' 130 try: 131 self.snapshot = snapshot(context=10,code=code,environment=environment) 132 except: 133 self.snapshot = {} 134 else: 135 self.traceback = '(no error)' 136 self.snapshot = {} 137 self.environment = environment
138
139 - def log(self, request):
140 """ 141 logs the exception. 142 """ 143 144 try: 145 d = { 146 'layer': str(self.layer), 147 'code': str(self.code), 148 'output': str(self.output), 149 'traceback': str(self.traceback), 150 'snapshot': self.snapshot, 151 } 152 ticket_storage = TicketStorage(db=request.tickets_db) 153 ticket_storage.store(request, request.uuid.split('/',1)[1], d) 154 return request.uuid 155 except: 156 logger.error(self.traceback) 157 return None
158 159
160 - def load(self, request, app, ticket_id):
161 """ 162 loads a logged exception. 163 """ 164 ticket_storage = TicketStorage(db=request.tickets_db) 165 d = ticket_storage.load(request, app, ticket_id) 166 167 self.layer = d['layer'] 168 self.code = d['code'] 169 self.output = d['output'] 170 self.traceback = d['traceback'] 171 self.snapshot = d.get('snapshot')
172 173
174 -def compile2(code,layer):
175 """ 176 The +'\n' is necessary else compile fails when code ends in a comment. 177 """ 178 return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
179
180 -def restricted(code, environment={}, layer='Unknown'):
181 """ 182 runs code in environment and returns the output. if an exception occurs 183 in code it raises a RestrictedError containing the traceback. layer is 184 passed to RestrictedError to identify where the error occurred. 185 """ 186 environment['__file__'] = layer 187 try: 188 if type(code) == types.CodeType: 189 ccode = code 190 else: 191 ccode = compile2(code,layer) 192 exec ccode in environment 193 except HTTP: 194 raise 195 except Exception, error: 196 # XXX Show exception in Wing IDE if running in debugger 197 if __debug__ and 'WINGDB_ACTIVE' in os.environ: 198 etype, evalue, tb = sys.exc_info() 199 sys.excepthook(etype, evalue, tb) 200 raise RestrictedError(layer, code, '', environment)
201
202 -def snapshot(info=None, context=5, code=None, environment=None):
203 """Return a dict describing a given traceback (based on cgitb.text).""" 204 import os, types, time, traceback, linecache, inspect, pydoc, cgitb 205 206 # if no exception info given, get current: 207 etype, evalue, etb = info or sys.exc_info() 208 209 if type(etype) is types.ClassType: 210 etype = etype.__name__ 211 212 # create a snapshot dict with some basic information 213 s = {} 214 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable 215 s['date'] = time.ctime(time.time()) 216 217 # start to process frames 218 records = inspect.getinnerframes(etb, context) 219 s['frames'] = [] 220 for frame, file, lnum, func, lines, index in records: 221 file = file and os.path.abspath(file) or '?' 222 args, varargs, varkw, locals = inspect.getargvalues(frame) 223 call = '' 224 if func != '?': 225 call = inspect.formatargvalues(args, varargs, varkw, locals, 226 formatvalue=lambda value: '=' + pydoc.text.repr(value)) 227 228 # basic frame information 229 f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum} 230 231 highlight = {} 232 def reader(lnum=[lnum]): 233 highlight[lnum[0]] = 1 234 try: return linecache.getline(file, lnum[0]) 235 finally: lnum[0] += 1
236 vars = cgitb.scanvars(reader, frame, locals) 237 238 # if it is a view, replace with generated code 239 if file.endswith('html'): 240 lmin = lnum>context and (lnum-context) or 0 241 lmax = lnum+context 242 lines = code.split("\n")[lmin:lmax] 243 index = min(context, lnum) - 1 244 245 if index is not None: 246 i = lnum - index 247 for line in lines: 248 f['lines'][i] = line.rstrip() 249 i += 1 250 251 # dump local variables (referenced in current line only) 252 f['dump'] = {} 253 for name, where, value in vars: 254 if name in f['dump']: continue 255 if value is not cgitb.__UNDEF__: 256 if where == 'global': name = 'global ' + name 257 elif where != 'local': name = where + name.split('.')[-1] 258 f['dump'][name] = pydoc.text.repr(value) 259 else: 260 f['dump'][name] = 'undefined' 261 262 s['frames'].append(f) 263 264 # add exception type, value and attributes 265 s['etype'] = str(etype) 266 s['evalue'] = str(evalue) 267 s['exception'] = {} 268 if isinstance(evalue, BaseException): 269 for name in dir(evalue): 270 # prevent py26 DeprecatedWarning: 271 if name!='message' or sys.version_info<(2.6): 272 value = pydoc.text.repr(getattr(evalue, name)) 273 s['exception'][name] = value 274 275 # add all local values (of last frame) to the snapshot 276 s['locals'] = {} 277 for name, value in locals.items(): 278 s['locals'][name] = pydoc.text.repr(value) 279 280 # add web2py environment variables 281 for k,v in environment.items(): 282 if k in ('request', 'response', 'session'): 283 s[k] = BEAUTIFY(v) 284 285 return s 286