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=None, 116 ):
117 """ 118 layer here is some description of where in the system the exception 119 occurred. 120 """ 121 if environment is None: environment = {} 122 self.layer = layer 123 self.code = code 124 self.output = output 125 self.environment = environment 126 if layer: 127 try: 128 self.traceback = traceback.format_exc() 129 except: 130 self.traceback = 'no traceback because template parting error' 131 try: 132 self.snapshot = snapshot(context=10,code=code, 133 environment=self.environment) 134 except: 135 self.snapshot = {} 136 else: 137 self.traceback = '(no error)' 138 self.snapshot = {}
139
140 - def log(self, request):
141 """ 142 logs the exception. 143 """ 144 145 try: 146 d = { 147 'layer': str(self.layer), 148 'code': str(self.code), 149 'output': str(self.output), 150 'traceback': str(self.traceback), 151 'snapshot': self.snapshot, 152 } 153 ticket_storage = TicketStorage(db=request.tickets_db) 154 ticket_storage.store(request, request.uuid.split('/',1)[1], d) 155 return request.uuid 156 except: 157 logger.error(self.traceback) 158 return None
159 160
161 - def load(self, request, app, ticket_id):
162 """ 163 loads a logged exception. 164 """ 165 ticket_storage = TicketStorage(db=request.tickets_db) 166 d = ticket_storage.load(request, app, ticket_id) 167 168 self.layer = d['layer'] 169 self.code = d['code'] 170 self.output = d['output'] 171 self.traceback = d['traceback'] 172 self.snapshot = d.get('snapshot')
173 174
175 -def compile2(code,layer):
176 """ 177 The +'\n' is necessary else compile fails when code ends in a comment. 178 """ 179 return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
180
181 -def restricted(code, environment=None, layer='Unknown'):
182 """ 183 runs code in environment and returns the output. if an exception occurs 184 in code it raises a RestrictedError containing the traceback. layer is 185 passed to RestrictedError to identify where the error occurred. 186 """ 187 if environment is None: environment = {} 188 environment['__file__'] = layer 189 try: 190 if type(code) == types.CodeType: 191 ccode = code 192 else: 193 ccode = compile2(code,layer) 194 exec ccode in environment 195 except HTTP: 196 raise 197 except Exception, error: 198 # XXX Show exception in Wing IDE if running in debugger 199 if __debug__ and 'WINGDB_ACTIVE' in os.environ: 200 etype, evalue, tb = sys.exc_info() 201 sys.excepthook(etype, evalue, tb) 202 raise RestrictedError(layer, code, '', environment)
203
204 -def snapshot(info=None, context=5, code=None, environment=None):
205 """Return a dict describing a given traceback (based on cgitb.text).""" 206 import os, types, time, traceback, linecache, inspect, pydoc, cgitb 207 208 # if no exception info given, get current: 209 etype, evalue, etb = info or sys.exc_info() 210 211 if type(etype) is types.ClassType: 212 etype = etype.__name__ 213 214 # create a snapshot dict with some basic information 215 s = {} 216 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable 217 s['date'] = time.ctime(time.time()) 218 219 # start to process frames 220 records = inspect.getinnerframes(etb, context) 221 s['frames'] = [] 222 for frame, file, lnum, func, lines, index in records: 223 file = file and os.path.abspath(file) or '?' 224 args, varargs, varkw, locals = inspect.getargvalues(frame) 225 call = '' 226 if func != '?': 227 call = inspect.formatargvalues(args, varargs, varkw, locals, 228 formatvalue=lambda value: '=' + pydoc.text.repr(value)) 229 230 # basic frame information 231 f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum} 232 233 highlight = {} 234 def reader(lnum=[lnum]): 235 highlight[lnum[0]] = 1 236 try: return linecache.getline(file, lnum[0]) 237 finally: lnum[0] += 1
238 vars = cgitb.scanvars(reader, frame, locals) 239 240 # if it is a view, replace with generated code 241 if file.endswith('html'): 242 lmin = lnum>context and (lnum-context) or 0 243 lmax = lnum+context 244 lines = code.split("\n")[lmin:lmax] 245 index = min(context, lnum) - 1 246 247 if index is not None: 248 i = lnum - index 249 for line in lines: 250 f['lines'][i] = line.rstrip() 251 i += 1 252 253 # dump local variables (referenced in current line only) 254 f['dump'] = {} 255 for name, where, value in vars: 256 if name in f['dump']: continue 257 if value is not cgitb.__UNDEF__: 258 if where == 'global': name = 'global ' + name 259 elif where != 'local': name = where + name.split('.')[-1] 260 f['dump'][name] = pydoc.text.repr(value) 261 else: 262 f['dump'][name] = 'undefined' 263 264 s['frames'].append(f) 265 266 # add exception type, value and attributes 267 s['etype'] = str(etype) 268 s['evalue'] = str(evalue) 269 s['exception'] = {} 270 if isinstance(evalue, BaseException): 271 for name in dir(evalue): 272 # prevent py26 DeprecatedWarning: 273 if name!='message' or sys.version_info<(2.6): 274 value = pydoc.text.repr(getattr(evalue, name)) 275 s['exception'][name] = value 276 277 # add all local values (of last frame) to the snapshot 278 s['locals'] = {} 279 for name, value in locals.items(): 280 s['locals'][name] = pydoc.text.repr(value) 281 282 # add web2py environment variables 283 for k,v in environment.items(): 284 if k in ('request', 'response', 'session'): 285 s[k] = BEAUTIFY(v) 286 287 return s 288