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