Chapter 1 ■ IntroduCtIon to ClIent-Server networkIng
9
Turtles All the Way Down
I hope you have enjoyed these initial examples of what Python network programming can look like. Stepping back,
I can use this series of examples to make several points about network programming in Python.
First, you can perhaps now see more clearly what is meant by the term protocol stack: it means building a
high-level, semantically sophisticated conversation (“I want the geographic location of this mailing address”) on top
of simpler, and more rudimentary, conversations that ultimately are just text strings sent back and forth between two
computers using their network hardware.
The particular protocol stack that you have just explored is four protocols high.
On top is the Google Geocoding API, which tells you how to express your geographic queries •
as URLs that fetch JSON data containing coordinates.
URLs name documents that can be retrieved using HTTP.•
HTTP supports document-oriented commands such as GET using raw TCP/IP sockets.•
TCP/IP sockets know how only to send and receive byte strings.•
Each layer of the stack, you see, uses the tools provided by the layer beneath it and in turn offers capabilities to
the next higher layer.
A second point made clear through these examples is how very complete the Python support is for every one
of the network levels at which you have just operated. Only when using a vendor-specific protocol, and needing to
format requests so that Google would understand them, was it necessary to resort to using a third-party library; I chose
requests for the second listing not because the Standard Library lacks the urllib.request module but because its API
is overly clunky. Every single one of the other protocol levels you encountered already had strong support inside the
Python Standard Library. Whether you wanted to fetch the document at a particular URL or send and receive strings on
a raw network socket, Python was ready with functions and classes that you could use to get the job done.
Third, note that my programs decreased considerably in quality as I forced myself to use increasingly lower-level
protocols. The search2.py and search3.py listings, for example, started to hard-code things such as the form
structure and hostnames in a way that is inflexible and that might be hard to maintain later. The code in search4.py
is even worse: it includes a handwritten, unparameterized HTTP request whose structure is completely opaque to
Python. And, of course, it contains none of the actual logic that would be necessary to parse and interpret the HTTP
response and understand any network error conditions that might occur.
This illustrates a lesson that you should remember throughout every subsequent chapter of this book: that
implementing network protocols correctly is difficult and that you should use the Standard Library or third-party
libraries whenever possible. Especially when you are writing a network client, you will always be tempted to
oversimplify your code; you will tend to ignore many error conditions that might arise, to prepare for only the most
likely responses, to avoid properly escaping parameters because you fondly believe that your query strings will only
ever include simple alphabetic characters, and, in general, to write very brittle code that knows as little about the
service it is talking to as is technically possible. By instead using a third-party library that has developed a thorough
implementation of a protocol, which has had to support many different Python developers who are using the library
for a variety of tasks, you will benefit from all of the edge cases and awkward corners that the library implementer has
already discovered and learned how to handle properly.
Fourth, it needs to be emphasized that higher-level network protocols—such as the Google Geocoding API
for resolving a street address—generally work by hiding the network layers beneath them. If you only ever used the
pygeocoder library, you might not even be aware that URLs and HTTP are the lower-level mechanisms that are being
used to construct and answer your queries!
An interesting question, whose answer varies depending on how carefully a Python library has been written, is
whether the library correctly hides errors at those lower levels. Could a network error that makes Google temporarily
unreachable from your location raise a raw, low-level networking exception in the middle of code that’s just trying
to find the coordinates of a street address? Or will all errors be changed into a higher-level exception specific to
geocoding? Pay careful attention to the topic of catching network errors as you go forward throughout this book,
especially in the chapters of this first part with their emphasis on low-level networking.