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
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
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):
71
73 tablename = tablename + '_' + app
74 table = db.get(tablename, None)
75 if table is None:
76 db.rollback()
77
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
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
188
189
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
217 raise
218 except Exception, error:
219
220 etype, evalue, tb = sys.exc_info()
221
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
239 etype, evalue, etb = info or sys.exc_info()
240
241 if isinstance(etype, types.ClassType):
242 etype = etype.__name__
243
244
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
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
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
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
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
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
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
315 s['locals'] = {}
316 for name, value in locals.items():
317 s['locals'][name] = pydoc.text.repr(value)
318
319
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