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={},
116 ):
117 """
118 layer here is some description of where in the system the exception
119 occurred.
120 """
121
122 self.layer = layer
123 self.code = code
124 self.output = output
125 if layer:
126 try:
127 self.traceback = traceback.format_exc()
128 except:
129 self.traceback = 'no traceback because template parting error'
130 try:
131 self.snapshot = snapshot(context=10,code=code,environment=environment)
132 except:
133 self.snapshot = {}
134 else:
135 self.traceback = '(no error)'
136 self.snapshot = {}
137 self.environment = environment
138
139 - def log(self, request):
140 """
141 logs the exception.
142 """
143
144 try:
145 d = {
146 'layer': str(self.layer),
147 'code': str(self.code),
148 'output': str(self.output),
149 'traceback': str(self.traceback),
150 'snapshot': self.snapshot,
151 }
152 ticket_storage = TicketStorage(db=request.tickets_db)
153 ticket_storage.store(request, request.uuid.split('/',1)[1], d)
154 return request.uuid
155 except:
156 logger.error(self.traceback)
157 return None
158
159
160 - def load(self, request, app, ticket_id):
161 """
162 loads a logged exception.
163 """
164 ticket_storage = TicketStorage(db=request.tickets_db)
165 d = ticket_storage.load(request, app, ticket_id)
166
167 self.layer = d['layer']
168 self.code = d['code']
169 self.output = d['output']
170 self.traceback = d['traceback']
171 self.snapshot = d.get('snapshot')
172
173
175 """
176 The +'\n' is necessary else compile fails when code ends in a comment.
177 """
178 return compile(code.rstrip().replace('\r\n','\n')+'\n', layer, 'exec')
179
180 -def restricted(code, environment={}, layer='Unknown'):
181 """
182 runs code in environment and returns the output. if an exception occurs
183 in code it raises a RestrictedError containing the traceback. layer is
184 passed to RestrictedError to identify where the error occurred.
185 """
186 environment['__file__'] = layer
187 try:
188 if type(code) == types.CodeType:
189 ccode = code
190 else:
191 ccode = compile2(code,layer)
192 exec ccode in environment
193 except HTTP:
194 raise
195 except Exception, error:
196
197 if __debug__ and 'WINGDB_ACTIVE' in os.environ:
198 etype, evalue, tb = sys.exc_info()
199 sys.excepthook(etype, evalue, tb)
200 raise RestrictedError(layer, code, '', environment)
201
202 -def snapshot(info=None, context=5, code=None, environment=None):
203 """Return a dict describing a given traceback (based on cgitb.text)."""
204 import os, types, time, traceback, linecache, inspect, pydoc, cgitb
205
206
207 etype, evalue, etb = info or sys.exc_info()
208
209 if type(etype) is types.ClassType:
210 etype = etype.__name__
211
212
213 s = {}
214 s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable
215 s['date'] = time.ctime(time.time())
216
217
218 records = inspect.getinnerframes(etb, context)
219 s['frames'] = []
220 for frame, file, lnum, func, lines, index in records:
221 file = file and os.path.abspath(file) or '?'
222 args, varargs, varkw, locals = inspect.getargvalues(frame)
223 call = ''
224 if func != '?':
225 call = inspect.formatargvalues(args, varargs, varkw, locals,
226 formatvalue=lambda value: '=' + pydoc.text.repr(value))
227
228
229 f = {'file': file, 'func': func, 'call': call, 'lines': {}, 'lnum': lnum}
230
231 highlight = {}
232 def reader(lnum=[lnum]):
233 highlight[lnum[0]] = 1
234 try: return linecache.getline(file, lnum[0])
235 finally: lnum[0] += 1
236 vars = cgitb.scanvars(reader, frame, locals)
237
238
239 if file.endswith('html'):
240 lmin = lnum>context and (lnum-context) or 0
241 lmax = lnum+context
242 lines = code.split("\n")[lmin:lmax]
243 index = min(context, lnum) - 1
244
245 if index is not None:
246 i = lnum - index
247 for line in lines:
248 f['lines'][i] = line.rstrip()
249 i += 1
250
251
252 f['dump'] = {}
253 for name, where, value in vars:
254 if name in f['dump']: continue
255 if value is not cgitb.__UNDEF__:
256 if where == 'global': name = 'global ' + name
257 elif where != 'local': name = where + name.split('.')[-1]
258 f['dump'][name] = pydoc.text.repr(value)
259 else:
260 f['dump'][name] = 'undefined'
261
262 s['frames'].append(f)
263
264
265 s['etype'] = str(etype)
266 s['evalue'] = str(evalue)
267 s['exception'] = {}
268 if isinstance(evalue, BaseException):
269 for name in dir(evalue):
270
271 if name!='message' or sys.version_info<(2.6):
272 value = pydoc.text.repr(getattr(evalue, name))
273 s['exception'][name] = value
274
275
276 s['locals'] = {}
277 for name, value in locals.items():
278 s['locals'][name] = pydoc.text.repr(value)
279
280
281 for k,v in environment.items():
282 if k in ('request', 'response', 'session'):
283 s[k] = BEAUTIFY(v)
284
285 return s
286