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