A Functional Approach to Web Services
This application will extract two pieces of information from the request: the PATH_
INFO and the QUERY_STRING methods. The PATH_INFO request will define which set
to extract. The QUERY_STRING request will specify an output format.
The application processing is broken into three functions. A raw_data() function
reads the raw data from a file. The result is a dictionary with lists of Pair objects.
The anscombe_filter() function accepts a selection string and the dictionary of
raw data and returns a single list of Pair objects. The list of pairs is then serialized
into bytes by the serialize() function. The serializer is expected to produce bytes,
which can then be packaged with an appropriate header and returned.
We elected to produce an HTTP Content-Length header. This isn't required, but it's
polite for large downloads Because we decided to emit this header, we are forced to
materialize the results of the serialization so that we can count the bytes.
If we elected to omit the Content-Length header, we could change the structure
of this application dramatically. Each serializer could be changed to a generator
function, which would yield bytes as they are produced. For large datasets, this
can be a helpful optimization. For the user watching a download, however, it might
not be so pleasant because the browser can't display how much of the download
is complete.
All errors are treated as a 404 NOT FOUND error. This could be misleading, since a
number of individual things might go wrong. A more sophisticated error handling
would provide more try:/except: blocks to provide more informative feedback.
For debugging purposes, we've provided a Python stack trace in the resulting web
page. Outside the context of debugging, this is a very bad idea. Feedback from
an API should be just enough to fix the request and nothing more. A stack trace
provides too much information to potentially malicious users.
Getting raw data
The raw_data() function is largely copied from Chapter 3, Functions, Iterators,
and Generators. We included some important changes. Here's what we're using
for this application:
from Chapter_3.ch03_ex5 import series, head_map_filter, row_iter,
Pair
def raw_data():
""""""
raw_data()['I'] #doctest: +ELLIPSIS
(Pair(x=10.0, y=8.04), Pair(x=8.0, y=6.95), ...
""""""