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