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