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
20
21 logger = logging.getLogger("web2py")
22
23 __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
24
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
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
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):
68
70 tablename = tablename + '_' + app
71 table = db.get(tablename, None)
72 if table is None:
73 db.rollback()
74
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
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
183
184
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
210 raise
211 except Exception, error:
212
213 etype, evalue, tb = sys.exc_info()
214
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
225 etype, evalue, etb = info or sys.exc_info()
226
227 if type(etype) is types.ClassType:
228 etype = etype.__name__
229
230
231 s = {}
232 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
233 s['date'] = time.ctime(time.time())
234
235
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
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
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
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
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
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
294 s['locals'] = {}
295 for name, value in locals.items():
296 s['locals'][name] = pydoc.text.repr(value)
297
298
299 for k,v in environment.items():
300 if k in ('request', 'response', 'session'):
301 s[k] = BEAUTIFY(v)
302
303 return s
304