0
|
1 """
|
|
2 webcalc: a view with webob
|
|
3 """
|
|
4
|
|
5 import math
|
|
6 from pkg_resources import iter_entry_points
|
|
7 from pkg_resources import resource_filename
|
|
8 from webob import Request, Response, exc
|
|
9
|
|
10 class WebcalcView(object):
|
|
11
|
|
12 ### class level variables
|
|
13 defaults = { 'formatter': 'text/plain' }
|
|
14
|
|
15 def __init__(self, **kw):
|
|
16 for key in self.defaults:
|
|
17 setattr(self, key, kw.get(key, self.defaults[key]))
|
|
18 self.formatters = {}
|
|
19 for entry_point in iter_entry_points('webcalc.formatters'):
|
|
20 try:
|
|
21 formatter = entry_point.load()
|
|
22 except:
|
|
23 continue
|
|
24 self.formatters[entry_point.name] = formatter
|
|
25
|
|
26 assert self.formatter in self.formatters
|
|
27
|
|
28 self.response_functions = { 'GET': self.get,
|
|
29 'POST': self.get,
|
|
30 }
|
|
31
|
|
32 ### methods dealing with HTTP
|
|
33 def __call__(self, environ, start_response):
|
|
34 request = Request(environ)
|
|
35 res = self.make_response(request)
|
|
36 return res(environ, start_response)
|
|
37
|
|
38 def make_response(self, request):
|
|
39 return self.response_functions.get(request.method, self.error)(request)
|
|
40
|
|
41 def get_response(self, text, content_type='text/html'):
|
|
42 res = Response(content_type=content_type, body=text)
|
|
43 return res
|
|
44
|
|
45 def get(self, request):
|
|
46 """
|
|
47 return response to a GET requst
|
|
48 """
|
|
49
|
|
50
|
|
51 equation = request.path_info.strip('/').strip()
|
|
52 if not equation:
|
|
53 return self.get_response('<html><body><form method="post"><input type="text" name="equation"/></form></body></html>')
|
|
54
|
|
55
|
|
56 # limit scope of execution
|
|
57 forbidden = set([';', '=', 'import', 'from', 'with'])
|
|
58 forbidden.update(dir(__builtins__))
|
|
59 assert not [ i for i in forbidden
|
|
60 if i in equation ]
|
|
61 math_globals = dict([(i, getattr(math, i))
|
|
62 for i in dir(math)
|
|
63 if not i.startswith('_')])
|
|
64
|
|
65 # get the range of variables
|
|
66 variables = []
|
|
67 assert 'result' not in request.GET
|
|
68 for name, value in request.GET.items():
|
|
69 if name == 'format':
|
|
70 continue
|
|
71 values = self.values(value)
|
|
72 if variables:
|
|
73 if len(values) == 1:
|
|
74 for var in variables:
|
|
75 var[name] = values[0]
|
|
76 else:
|
|
77 old_variables = [ i.copy() for i in variables[:] ]
|
|
78 variables = []
|
|
79 for val in values:
|
|
80 row = [ i.copy() for i in old_variables ]
|
|
81 for item in row:
|
|
82 item[name] = val
|
|
83 variables.extend(row)
|
|
84 else:
|
|
85 variables = [ { name: val }
|
|
86 for val in values ]
|
|
87
|
|
88 # evaluate the equation
|
|
89 if variables:
|
|
90 for var in variables:
|
|
91 result = float(eval(equation, math_globals, var))
|
|
92 var['result'] = result
|
|
93 else:
|
|
94 result = float(eval(equation, math_globals, {}))
|
|
95 variables = [{'result': result}]
|
|
96
|
|
97 # format the result
|
|
98 format = request.params.get('format') or request.content_type or self.formatter
|
|
99 formatter = self.formatters.get(format, self.formatters[self.formatter])
|
|
100 return self.get_response(formatter(variables), format)
|
|
101
|
|
102 def post(self, request):
|
|
103 """
|
|
104 return response to a POST request
|
|
105 """
|
|
106 import pdb; pdb.set_trace()
|
|
107
|
|
108 return exc.HTTPOk("Ok")
|
|
109
|
|
110 def error(self, request):
|
|
111 """deal with non-supported methods"""
|
|
112 return exc.HTTPMethodNotAllowed("Only %r operations are allowed" % self.response_functions.keys())
|
|
113
|
|
114
|
|
115 def values(self, string):
|
|
116 """
|
|
117 >>> values('1,2,3,4')
|
|
118 [1,2,3,4]
|
|
119 >>> values('1..0.2..2')
|
|
120 [1,1.2,1.4,1.6,1.8,2]
|
|
121 """
|
|
122 retval = []
|
|
123 tokens = [i.strip() for i in string.split(',')
|
|
124 if i.strip()]
|
|
125 for token in tokens:
|
|
126 try:
|
|
127 value = float(token)
|
|
128 retval.append(value)
|
|
129 continue
|
|
130 except ValueError:
|
|
131 pass
|
|
132 start, step, end = token.split('..')
|
|
133 start = float(start)
|
|
134 step = float(step)
|
|
135 end = float(end)
|
|
136
|
|
137 values = [ start ]
|
|
138 npoints = (end - start)/step
|
|
139 npoints = int(npoints)
|
|
140 values.extend([i*step + start
|
|
141 for i in range(1, npoints)])
|
|
142 values.append(end)
|
|
143 retval.extend(values)
|
|
144 return retval
|