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 environment['__name__'] = '__restricted__' 200 try: 201 if type(code) == types.CodeType: 202 ccode = code 203 else: 204 ccode = compile2(code,layer) 205 exec ccode in environment 206 except HTTP: 207 raise 208 except RestrictedError: 209 # do not encapsulate (obfuscate) the original RestrictedError 210 raise 211 except Exception, error: 212 # extract the exception type and value (used as output message) 213 etype, evalue, tb = sys.exc_info() 214 # XXX Show exception in Wing IDE if running in debugger 215 if __debug__ and 'WINGDB_ACTIVE' in os.environ: 216 sys.excepthook(etype, evalue, tb) 217 output = "%s %s" % (etype, evalue) 218 raise RestrictedError(layer, code, output, environment)
219
220 -def snapshot(info=None, context=5, code=None, environment=None):
221 """Return a dict describing a given traceback (based on cgitb.text).""" 222 import os, types, time, linecache, inspect, pydoc, cgitb 223 224 # if no exception info given, get current: 225 etype, evalue, etb = info or sys.exc_info() 226 227 if type(etype) is types.ClassType: 228 etype = etype.__name__ 229 230 # create a snapshot dict with some basic information 231 s = {} 232 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable 233 s['date'] = time.ctime(time.time()) 234 235 # start to process frames 236 records = inspect.getinnerframes(etb, context) 237 s['frames'] = [] 238 for frame, file, lnum, func, lines, index in records: 239 file = file and os.path.abspath(file) or '?' 240 args, varargs, varkw, locals = inspect.getargvalues(frame) 241 call = '' 242 if func != '?': 243 call = inspect.formatargvalues(args, varargs, varkw, locals, 244 formatvalue=lambda value: '=' + pydoc.text.repr(value)) 245 246 # basic frame information 247 f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum} 248 249 highlight = {} 250 def reader(lnum=[lnum]): 251 highlight[lnum[0]] = 1 252 try: return linecache.getline(file, lnum[0]) 253 finally: lnum[0] += 1
254 vars = cgitb.scanvars(reader, frame, locals) 255 256 # if it is a view, replace with generated code 257 if file.endswith('html'): 258 lmin = lnum>context and (lnum-context) or 0 259 lmax = lnum+context 260 lines = code.split("\n")[lmin:lmax] 261 index = min(context, lnum) - 1 262 263 if index is not None: 264 i = lnum - index 265 for line in lines: 266 f['lines'][i] = line.rstrip() 267 i += 1 268 269 # dump local variables (referenced in current line only) 270 f['dump'] = {} 271 for name, where, value in vars: 272 if name in f['dump']: continue 273 if value is not cgitb.__UNDEF__: 274 if where == 'global': name = 'global ' + name 275 elif where != 'local': name = where + name.split('.')[-1] 276 f['dump'][name] = pydoc.text.repr(value) 277 else: 278 f['dump'][name] = 'undefined' 279 280 s['frames'].append(f) 281 282 # add exception type, value and attributes 283 s['etype'] = str(etype) 284 s['evalue'] = str(evalue) 285 s['exception'] = {} 286 if isinstance(evalue, BaseException): 287 for name in dir(evalue): 288 # prevent py26 DeprecatedWarning: 289 if name!='message' or sys.version_info<(2.6): 290 value = pydoc.text.repr(getattr(evalue, name)) 291 s['exception'][name] = value 292 293 # add all local values (of last frame) to the snapshot 294 s['locals'] = {} 295 for name, value in locals.items(): 296 s['locals'][name] = pydoc.text.repr(value) 297 298 # add web2py environment variables 299 for k,v in environment.items(): 300 if k in ('request', 'response', 'session'): 301 s[k] = BEAUTIFY(v) 302 303 return s 304