Package gluon :: Module restricted
[hide private]
[frames] | no frames]

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