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