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 cPickle.dump(ticket_data, self._error_file(request, ticket_id, 'wb'))
59
60 - def _error_file(self, request, ticket_id, mode, app=None):
61 root = request.folder 62 if app: 63 root = os.path.join(os.path.join(root, '..'), app) 64 errors_folder = os.path.abspath(os.path.join(root, 'errors'))#.replace('\\', '/') 65 return open(os.path.join(errors_folder, ticket_id), mode)
66
67 - def _get_table(self, db, tablename, app):
68 tablename = tablename + '_' + app 69 table = db.get(tablename, None) 70 if table is None: 71 db.rollback() # not necessary but one day 72 # any app may store tickets on DB 73 table = db.define_table( 74 tablename, 75 db.Field('ticket_id', length=100), 76 db.Field('ticket_data', 'text'), 77 db.Field('created_datetime', 'datetime'), 78 ) 79 return table
80
81 - def load( 82 self, 83 request, 84 app, 85 ticket_id, 86 ):
87 if not self.db: 88 return cPickle.load(self._error_file(request, ticket_id, 'rb', app)) 89 table=self._get_table(self.db, self.tablename, app) 90 rows = self.db(table.ticket_id == ticket_id).select() 91 if rows: 92 return cPickle.loads(rows[0].ticket_data) 93 return None
94 95
96 -class RestrictedError(Exception):
97 """ 98 class used to wrap an exception that occurs in the restricted environment 99 below. the traceback is used to log the exception and generate a ticket. 100 """ 101
102 - def __init__( 103 self, 104 layer='', 105 code='', 106 output='', 107 environment={}, 108 ):
109 """ 110 layer here is some description of where in the system the exception 111 occurred. 112 """ 113 114 self.layer = layer 115 self.code = code 116 self.output = output 117 if layer: 118 try: 119 self.traceback = traceback.format_exc() 120 except: 121 self.traceback = 'no traceback because template parting error' 122 try: 123 self.snapshot = snapshot(context=10,code=code,environment=environment) 124 except: 125 self.snapshot = {} 126 else: 127 self.traceback = '(no error)' 128 self.snapshot = {} 129 self.environment = environment
130
131 - def log(self, request):
132 """ 133 logs the exception. 134 """ 135 136 try: 137 d = { 138 'layer': str(self.layer), 139 'code': str(self.code), 140 'output': str(self.output), 141 'traceback': str(self.traceback), 142 'snapshot': self.snapshot, 143 } 144 ticket_storage = TicketStorage(db=request.tickets_db) 145 ticket_storage.store(request, request.uuid.split('/',1)[1], d) 146 return request.uuid 147 except: 148 logger.error(self.traceback) 149 return None
150 151
152 - def load(self, request, app, ticket_id):
153 """ 154 loads a logged exception. 155 """ 156 ticket_storage = TicketStorage(db=request.tickets_db) 157 d = ticket_storage.load(request, app, ticket_id) 158 159 self.layer = d['layer'] 160 self.code = d['code'] 161 self.output = d['output'] 162 self.traceback = d['traceback'] 163 self.snapshot = d.get('snapshot')
164 165
166 -def compile2(code,layer):
167 """ 168 The +'\n' is necessary else compile fails when code ends in a comment. 169 """ 170 return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
171
172 -def restricted(code, environment={}, layer='Unknown'):
173 """ 174 runs code in environment and returns the output. if an exception occurs 175 in code it raises a RestrictedError containing the traceback. layer is 176 passed to RestrictedError to identify where the error occurred. 177 """ 178 environment['__file__'] = layer 179 try: 180 if type(code) == types.CodeType: 181 ccode = code 182 else: 183 ccode = compile2(code,layer) 184 exec ccode in environment 185 except HTTP: 186 raise 187 except Exception: 188 # XXX Show exception in Wing IDE if running in debugger 189 if __debug__ and 'WINGDB_ACTIVE' in os.environ: 190 etype, evalue, tb = sys.exc_info() 191 sys.excepthook(etype, evalue, tb) 192 raise RestrictedError(layer, code, '', environment)
193
194 -def snapshot(info=None, context=5, code=None, environment=None):
195 """Return a dict describing a given traceback (based on cgitb.text).""" 196 import os, types, time, traceback, linecache, inspect, pydoc, cgitb 197 198 # if no exception info given, get current: 199 etype, evalue, etb = info or sys.exc_info() 200 201 if type(etype) is types.ClassType: 202 etype = etype.__name__ 203 204 # create a snapshot dict with some basic information 205 s = {} 206 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable 207 s['date'] = time.ctime(time.time()) 208 209 # start to process frames 210 records = inspect.getinnerframes(etb, context) 211 s['frames'] = [] 212 for frame, file, lnum, func, lines, index in records: 213 file = file and os.path.abspath(file) or '?' 214 args, varargs, varkw, locals = inspect.getargvalues(frame) 215 call = '' 216 if func != '?': 217 call = inspect.formatargvalues(args, varargs, varkw, locals, 218 formatvalue=lambda value: '=' + pydoc.text.repr(value)) 219 220 # basic frame information 221 f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum} 222 223 highlight = {} 224 def reader(lnum=[lnum]): 225 highlight[lnum[0]] = 1 226 try: return linecache.getline(file, lnum[0]) 227 finally: lnum[0] += 1
228 vars = cgitb.scanvars(reader, frame, locals) 229 230 # if it is a view, replace with generated code 231 if file.endswith('html'): 232 lmin = lnum>context and (lnum-context) or 0 233 lmax = lnum+context 234 lines = code.split("\n")[lmin:lmax] 235 index = min(context, lnum) - 1 236 237 if index is not None: 238 i = lnum - index 239 for line in lines: 240 f['lines'][i] = line.rstrip() 241 i += 1 242 243 # dump local variables (referenced in current line only) 244 f['dump'] = {} 245 for name, where, value in vars: 246 if name in f['dump']: continue 247 if value is not cgitb.__UNDEF__: 248 if where == 'global': name = 'global ' + name 249 elif where != 'local': name = where + name.split('.')[-1] 250 f['dump'][name] = pydoc.text.repr(value) 251 else: 252 f['dump'][name] = 'undefined' 253 254 s['frames'].append(f) 255 256 # add exception type, value and attributes 257 s['etype'] = str(etype) 258 s['evalue'] = str(evalue) 259 s['exception'] = {} 260 if isinstance(evalue, BaseException): 261 for name in dir(evalue): 262 # prevent py26 DeprecatedWarning: 263 if name!='message' or sys.version_info<(2.6): 264 value = pydoc.text.repr(getattr(evalue, name)) 265 s['exception'][name] = value 266 267 # add all local values (of last frame) to the snapshot 268 s['locals'] = {} 269 for name, value in locals.items(): 270 s['locals'][name] = pydoc.text.repr(value) 271 272 # add web2py environment variables 273 for k,v in environment.items(): 274 if k in ('request', 'response', 'session'): 275 s[k] = BEAUTIFY(v) 276 277 return s 278