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