getting the WSGI app dressed up for the ball

This commit is contained in:
Dan Buch 2010-02-18 12:16:48 -05:00
parent 6249efbac8
commit f40357c79d

View File

@ -1,37 +1,66 @@
"""
standalone WSGI application for serving plaintext and xml suitable
for the Adobe Flex "Getting Started" tutorial, "Part II. Exchanging Data"
For the plaintext data flex app, set the HTTPService url to:
http://<hostname>:<port>/text
For the xml data flex app, set the HTTPService url to:
http://<hostname>:<port>/xml
"""
from __future__ import print_function from __future__ import print_function
import sys import sys
import cgi import cgi
import optparse
from wsgiref.simple_server import make_server from wsgiref.simple_server import make_server
__author__ = 'Dan Buch dbuch@ag.com'
__license__ = 'AGI'
USAGE = """\
%prog [options]
Run a standalone WSGI app for the Adobe Flex Tutorial
"Getting Started", "Part II. Exchanging Data"
"""
def main(sysargs=sys.argv[:]): def main(sysargs=sys.argv[:]):
port = 18080 parser = optparse.OptionParser(usage=USAGE)
server = make_server('0.0.0.0', port, shipping_app) parser.add_option('-p', '--port', dest='port', type='int',
print('serving {0.__name__} on port {1}'.format(shipping_app, port)) help='port number on which to serve app, default=%default',
default=18080)
parser.add_option('-i', '--interface', dest='interface',
help='interface on which to serve app, default=%default',
default='0.0.0.0')
opts = parser.parse_args(sysargs[1:])[0]
server = make_server(opts.interface, opts.port, ShippingCostApp())
print('serving {0.__name__} on '
'port {1}'.format(ShippingCostApp, opts.port))
server.serve_forever() server.serve_forever()
return 0 return 0
def shipping_app(environ, start_response): class NotFoundError(ValueError):
path_info = environ.get('PATH_INFO', '').strip('/') pass
handler = {
'text': plaintext_handler,
'xml': xml_handler,
'crossdomain.xml': crossdomain_xml_handler,
}.get(path_info)
if handler: class ShippingCostApp(object):
def __call__(self, environ, start_response):
try: try:
return handler(environ, start_response) return ShippingCostHandler(environ, start_response).handle()
except Exception: except NotFoundError:
start_response('500 Internal Server Error', [ return self._handle_404(environ, start_response)
('content-type', 'text/plain'),
]) @classmethod
return ['OUCH'] def _handle_404(cls, environ, start_response):
else: ret = 'nothin at {0!r}'.format(environ.get('PATH_INFO', '/'))
ret = 'nothin at {0!r}'.format(path_info)
start_response('404 Not Found', [ start_response('404 Not Found', [
('content-type', 'text/plain'), ('content-type', 'text/plain'),
('content-length', str(len(ret))) ('content-length', str(len(ret)))
@ -39,72 +68,105 @@ def shipping_app(environ, start_response):
return [ret] return [ret]
def get_params(environ): class ShippingCostHandler(object):
params = cgi.parse(fp=environ.get('wsgi.input'), environ=environ) _shipping_options = None
for key, value in params.iteritems(): _zipcode = 0
if len(value) == 1: _pounds = 0
params[key] = value[0] _handler_method = ''
return params
def __init__(self, environ, start_response):
self.environ = environ
self.start_response = start_response
self._path_info = self.environ.get('PATH_INFO', '').strip('/')
self._handler_method = \
'_handle_{0}'.format(self._path_info.replace('.', '_'))
def _get_params(self):
"""some dumb param fetching, ignores multiple values for
same var name, e.g. query of "zipcode=12345&zipcode=90210"
would effectively discard the 90210 value
"""
params = cgi.parse_qs(self.environ.get('QUERY_STRING', ''))
for intvar in ('zipcode', 'pounds'):
if params.get(intvar):
value = int(float(params.get(intvar)[0]))
setattr(self, '_{0}'.format(intvar), value)
def _get_shipping_options(self):
base_cost = (float(self._zipcode) / 10000.0) + (self._pounds * 5.0)
self._shipping_options = {
"Next Day": int(base_cost * 4),
"Two Day Air": int(base_cost * 2),
"Saver Ground": int(base_cost)
}
def handle(self):
if hasattr(self, self._handler_method):
self._get_params()
self._get_shipping_options()
return getattr(self, self._handler_method)()
else:
raise NotFoundError
def _handle_xml(self):
response = XMLShippingOptionsResponse(self._shipping_options)
return response(self.environ, self.start_response)
def _handle_text(self):
response = PlaintextShippingOptionsResponse(self._shipping_options)
return response(self.environ, self.start_response)
def _handle_crossdomain_xml(self):
response = CrossdomainXMLResponse()
return response(self.environ, self.start_response)
def xml_handler(environ, start_response): class PlaintextShippingOptionsResponse(object):
params = get_params(environ) _head = ''
zipcode = int(float(params.get('zipcode', 0))) _record = '{service}: {price} USD'
pounds = int(float(params.get('pounds', 0))) _tail = ''
_content_type = 'text/plain'
ret = ['<options>'] def __init__(self, shipping_options):
for service, price in get_shipping_options(zipcode, pounds).iteritems(): self._shipping_options = shipping_options
ret.append('<option><service>{service}</service>'
'<price>{price}</price></option>'.format(**locals()))
ret.append('</options>')
body = '\n'.join(ret)
start_response('200 OK', [ def __call__(self, environ, start_response):
('content-type', 'text/xml'), body = str(self)
('content-length', str(len(body))) start_response('200 OK', [
]) ('content-type', self._content_type),
return [body] ('content-length', str(len(body))),
])
return [body]
def __str__(self):
ret = [self._head]
for service, price in self._shipping_options.iteritems():
ret.append(self._record.format(service=service, price=price))
ret.append(self._tail)
return '\n'.join(ret)
def plaintext_handler(environ, start_response): class XMLShippingOptionsResponse(PlaintextShippingOptionsResponse):
params = get_params(environ) _head = '<options>'
zipcode = int(float(params.get('zipcode', 0))) _record = ('<option><service>{service}</service>'
pounds = int(float(params.get('pounds', 0))) '<price>{price}</price></option>')
_tail = '</options>'
ret = [] _content_type = 'text/xml'
for service, price in get_shipping_options(zipcode, pounds).iteritems():
ret.append('{service}: {price} USD'.format(**locals()))
body = '\n'.join(ret)
start_response('200 OK', [
('content-type', 'text/plain'),
('content-length', str(len(body)))
])
return [body]
def crossdomain_xml_handler(environ, start_response): class CrossdomainXMLResponse(PlaintextShippingOptionsResponse):
start_response('200 OK', [ _content_type = 'text/xml'
('content-type', 'text/xml'),
('content-length', str(len(XD_XML))),
])
return [XD_XML]
def __init__(self):
pass
XD_XML = """\ def __str__(self):
<cross-domain-policy> return (
<allow-access-from domain="*"/> '<cross-domain-policy>'
</cross-domain-policy> '<allow-access-from domain="*"/>'
""" '</cross-domain-policy>'
)
def get_shipping_options(zipcode, pounds):
base_cost = (float(zipcode) / 10000.0) + (pounds * 5.0)
return {
"Next Day": int(base_cost * 4),
"Two Day Air": int(base_cost * 2),
"Saver Ground": int(base_cost)
}
if __name__ == '__main__': if __name__ == '__main__':