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