another section of unit-testing

This commit is contained in:
Mark Pilgrim
2009-02-28 16:21:16 -05:00
parent d79d77aacc
commit c8080fdbd1
13 changed files with 4383 additions and 5059 deletions
+4 -3
View File
@@ -12,7 +12,7 @@ body{counter-reset:h1 20}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=sa value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#case-study-porting-chardet-to-python-3>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Case study: porting <code>chardet</code> to Python 3</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Words, words. They&#8217;re all we have to go on. <span>&#x275E;</span><br>&mdash; <cite>Rosencrantz and Guildenstern are Dead</cite>
@@ -98,6 +98,7 @@ body{counter-reset:h1 20}
<h2 id=running2to3>Running <code class=filename>2to3</code></h2>
<p>We&#8217;re going to migrate the <code class=filename>chardet</code> module from Python 2 to Python 3. Python 3 comes with a utility script called <code class=filename>2to3</code>, which takes your actual Python 2 source code as input and auto-converts as much as it can to Python 3. In some cases this is easy -- a function was renamed or moved to a different modules -- but in other cases it can get pretty complex. To get a sense of all that it <em>can</em> do, refer to the appendix, <a href=porting-code-to-python-3-with-2to3.html>Porting code to Python 3 with <code class=filename>2to3</code></a>. In this chapter, we&#8217;ll start by running <code class=filename>2to3</code> on the <code class=filename>chardet</code> package, but as you&#8217;ll see, there will still be a lot of work to do after the automated tools have performed their magic.
<p>The main <code class=filename>chardet</code> package is split across several different files, all in the same directory. The <code class=filename>2to3</code> script makes it easy to convert multiple files at once: just pass a directory as a command line argument, and <code class=filename>2to3</code> will convert each of the files in turn.
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<p class=skip><a href=#skip2to3output>skip over this</a>
<pre class=screen><samp class=prompt>C:\home\chardet> </samp><kbd>python c:\Python30\Tools\Scripts\2to3.py -w chardet\</kbd>
<samp>RefactoringTool: Skipping implicit fixer: buffer
@@ -717,5 +718,5 @@ for line in open(f, 'rb'):
TypeError: Can't convert 'bytes' object to str implicitly</samp></pre>
<p id=skipcantconvertbytesobject>...
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>
-913
View File
@@ -5801,660 +5801,6 @@ def fetch(source, etag=None, last_modified=None, agent=USER_AGENT):
<li>Supporting <a href="#oa.gzip" title="11.8. Handling compressed data">gzip compression</a> to reduce bandwidth even when data <em>has</em> changed.
</ul>
<div class=chapter>
<h2 id="soap">Chapter 12. <acronym>SOAP</acronym> Web Services</h2>
<p><a href="#oa">Chapter 11</a> focused on document-oriented web services over HTTP. The &#8220;input parameter&#8221; was the <acronym>URL</acronym>, and the &#8220;return value&#8221; was an actual XML document which it was your responsibility to parse.
<p>This chapter will focus on <acronym>SOAP</acronym> web services, which take a more structured approach. Rather than dealing with HTTP requests and XML documents directly,
<acronym>SOAP</acronym> allows you to simulate calling functions that return native data types. As you will see, the illusion is almost perfect;
you can &#8220;call&#8221; a function through a <acronym>SOAP</acronym> library, with the standard Python calling syntax, and the function appears to return Python objects and values. But under the covers, the <acronym>SOAP</acronym> library has actually performed a complex transaction involving multiple XML documents and a remote server.
<p><acronym>SOAP</acronym> is a complex specification, and it is somewhat misleading to say that <acronym>SOAP</acronym> is all about calling remote functions. Some people would pipe up to add that <acronym>SOAP</acronym> allows for one-way asynchronous message passing, and document-oriented web services. And those people would be correct;
<acronym>SOAP</acronym> can be used that way, and in many different ways. But this chapter will focus on so-called &#8220;RPC-style&#8221; <acronym>SOAP</acronym> -- calling a remote function and getting results back.
<h2 id="soap.divein">12.1. Diving In</h2>
<p>You use Google, right? It's a popular search engine. Have you ever wished you could programmatically access Google search
results? Now you can. Here is a program to search Google from Python.
<div class=example><h3>Example 12.1. <code>search.py</code></h3><pre><code>from SOAPpy import WSDL
# you'll need to configure these two values;
# see http://www.google.com/apis/
WSDLFILE = '/path/to/copy/of/GoogleSearch.wsdl'
APIKEY = 'YOUR_GOOGLE_API_KEY'
_server = WSDL.Proxy(WSDLFILE)
def search(q):
"""Search Google and return list of {title, link, description}"""
results = _server.doGoogleSearch(
APIKEY, q, 0, 10, False, "", False, "", "utf-8", "utf-8")
return [{"title": r.title.encode("utf-8"),
"link": r.URL.encode("utf-8"),
"description": r.snippet.encode("utf-8")}
for r in results.resultElements]
if __name__ == '__main__':
import sys
for r in search(sys.argv[1])[:5]:
print r['title']
print r['link']
print r['description']
print</pre><p>You can import this as a module and use it from a larger program, or you can run the script from the command line. On the
command line, you give the search query as a command-line argument, and it prints out the URL, title, and description of the
top five Google search results.
<p>Here is the sample output for a search for the word &#8220;python&#8221;.
<div class=example><h3>Example 12.2. Sample Usage of <code>search.py</code></h3><pre class=screen>
<samp class=prompt>C:\diveintopython3\common\py></samp> python search.py "python"
<samp>&lt;b>Python&lt;/b> Programming Language
http://www.python.org/
Home page for &lt;b>Python&lt;/b>, an interpreted, interactive, object-oriented,
extensible&lt;br> programming language. &lt;b>...&lt;/b> &lt;b>Python&lt;/b>
is OSI Certified Open Source: OSI Certified.
&lt;b>Python&lt;/b> Documentation Index
http://www.python.org/doc/
&lt;b>...&lt;/b> New-style classes (aka descrintro). Regular expressions. Database
API. Email Us.&lt;br> docs@&lt;b>python&lt;/b>.org. (c) 2004. &lt;b>Python&lt;/b>
Software Foundation. &lt;b>Python&lt;/b> Documentation. &lt;b>...&lt;/b>
Download &lt;b>Python&lt;/b> Software
http://www.python.org/download/
Download Standard &lt;b>Python&lt;/b> Software. &lt;b>Python&lt;/b> 2.3.3 is the
current production&lt;br> version of &lt;b>Python&lt;/b>. &lt;b>...&lt;/b>
&lt;b>Python&lt;/b> is OSI Certified Open Source:
Pythonline
http://www.pythonline.com/
Dive Into &lt;b>Python&lt;/b>
http://diveintopython3.org/
Dive Into &lt;b>Python&lt;/b>. &lt;b>Python&lt;/b> from novice to pro. Find:
&lt;b>...&lt;/b> It is also available in multiple&lt;br> languages. Read
Dive Into &lt;b>Python&lt;/b>. This book is still being written. &lt;b>...&lt;/b></span>
</pre><div class=itemizedlist>
<h3>Further Reading on <acronym>SOAP</acronym></h3>
<ul>
<li><a href="http://www.xmethods.net/">http://www.xmethods.net/</a> is a repository of public access <acronym>SOAP</acronym> web services.
<li>The <a href="http://www.w3.org/TR/soap/"><acronym>SOAP</acronym> specification</a> is surprisingly readable, if you like that sort of thing.
</ul>
<h2 id="soap.install">12.2. Installing the SOAP Libraries</h2>
<p>Unlike the other code in this book, this chapter relies on libraries that do not come pre-installed with Python.
<p>Before you can dive into <acronym>SOAP</acronym> web services, you'll need to install three libraries: PyXML, fpconst, and SOAPpy.
<h3>12.2.1. Installing PyXML</h3>
<p>The first library you need is PyXML, an advanced set of <acronym>XML</acronym> libraries that provide more functionality than the built-in <acronym>XML</acronym> libraries we studied in <a href="#kgp">Chapter 9</a>.
<div class=procedure>
<h3>Procedure 12.1. </h3>
<p>Here is the procedure for installing PyXML:
<ol>
<li>
<p>Go to <a href="http://pyxml.sourceforge.net/">http://pyxml.sourceforge.net/</a>, click Downloads, and download the latest version for your operating system.
<li>
<p>If you are using Windows, there are several choices. Make sure to download the version of PyXML that matches the version of Python you are using.
<li>
<p>Double-click the installer. If you download PyXML 0.8.3 for Windows and Python 2.3, the installer program will be <code>PyXML-0.8.3.win32-py2.3.exe</code>.
<li>
<p>Step through the installer program.
<li>
<p>After the installation is complete, close the installer. There will not be any visible indication of success (no programs
installed on the Start Menu or shortcuts installed on the desktop). PyXML is simply a collection of <acronym>XML</acronym> libraries used by other programs.
</ol>
<p>To verify that you installed PyXML correctly, run your Python <acronym>IDE</acronym> and check the version of the <acronym>XML</acronym> libraries you have installed, as shown here.
<div class=example><h3>Example 12.3. Verifying PyXML Installation</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>import xml</kbd>
<samp class=prompt>>>> </samp><kbd>xml.__version__</kbd>
'0.8.3'
</pre><p>This version number should match the version number of the PyXML installer program you downloaded and ran.
<h3>12.2.2. Installing fpconst</h3>
<p>The second library you need is fpconst, a set of constants and functions for working with IEEE754 double-precision special values. This provides support for the
special values Not-a-Number (NaN), Positive Infinity (Inf), and Negative Infinity (-Inf), which are part of the <acronym>SOAP</acronym> datatype specification.
<div class=procedure>
<h3>Procedure 12.2. </h3>
<p>Here is the procedure for installing fpconst:
<ol>
<li>
<p>Download the latest version of fpconst from <a href="http://www.analytics.washington.edu/statcomp/projects/rzope/fpconst/">http://www.analytics.washington.edu/statcomp/projects/rzope/fpconst/</a>.
<li>
<p>There are two downloads available, one in <code>.tar.gz</code> format, the other in <code>.zip</code> format. If you are using Windows, download the <code>.zip</code> file; otherwise, download the <code>.tar.gz</code> file.
<li>
<p>Decompress the downloaded file. On Windows XP, you can right-click on the file and choose Extract All; on earlier versions
of Windows, you will need a third-party program such as WinZip. On Mac OS X, you can double-click the compressed file to decompress it with Stuffit Expander.
<li>
<p>Open a command prompt and navigate to the directory where you decompressed the fpconst files.
<li>
<p>Type <kbd>python setup.py install</kbd> to run the installation program.
</ol>
<p>To verify that you installed fpconst correctly, run your Python <acronym>IDE</acronym> and check the version number.
<div class=example><h3>Example 12.4. Verifying fpconst Installation</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>import fpconst</kbd>
<samp class=prompt>>>> </samp><kbd>fpconst.__version__</kbd>
'0.6.0'
</pre><p>This version number should match the version number of the fpconst archive you downloaded and installed.
<h3>12.2.3. Installing SOAPpy</h3>
<p>The third and final requirement is the <acronym>SOAP</acronym> library itself: SOAPpy.
<div class=procedure>
<h3>Procedure 12.3. </h3>
<p>Here is the procedure for installing SOAPpy:
<ol>
<li>
<p>Go to <a href="http://pywebsvcs.sourceforge.net/">http://pywebsvcs.sourceforge.net/</a> and select Latest Official Release under the SOAPpy section.
<li>
<p>There are two downloads available. If you are using Windows, download the <code>.zip</code> file; otherwise, download the <code>.tar.gz</code> file.
<li>
<p>Decompress the downloaded file, just as you did with fpconst.
<li>
<p>Open a command prompt and navigate to the directory where you decompressed the SOAPpy files.
<li>
<p>Type <kbd>python setup.py install</kbd> to run the installation program.
</ol>
<p>To verify that you installed SOAPpy correctly, run your Python <acronym>IDE</acronym> and check the version number.
<div class=example><h3>Example 12.5. Verifying SOAPpy Installation</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>import SOAPpy</kbd>
<samp class=prompt>>>> </samp><kbd>SOAPpy.__version__</kbd>
'0.11.4'
</pre><p>This version number should match the version number of the SOAPpy archive you downloaded and installed.
<h2 id="soap.firststeps">12.3. First Steps with <acronym>SOAP</acronym></h2>
<p>The heart of <acronym>SOAP</acronym> is the ability to call remote functions. There are a number of public access <acronym>SOAP</acronym> servers that provide simple functions for demonstration purposes.
<p>The most popular public access <acronym>SOAP</acronym> server is <a href="http://www.xmethods.net/">http://www.xmethods.net/</a>. This example uses a demonstration function that takes a United States zip code and returns the current temperature in that
region.
<div class=example><h3>Example 12.6. Getting the Current Temperature</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import SOAPProxy</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>url = 'http://services.xmethods.net:80/soap/servlet/rpcrouter'</kbd>
<samp class=prompt>>>> </samp><kbd>namespace = 'urn:xmethods-Temperature'</kbd> <span>&#x2461;</span>
<samp class=prompt>>>> </samp><kbd>server = SOAPProxy(url, namespace)</kbd> <span>&#x2462;</span>
<samp class=prompt>>>> </samp><kbd>server.getTemp('27502')</kbd> <span>&#x2463;</span>
80.0
</pre><div class=calloutlist>
<ol>
<li>You access the remote <acronym>SOAP</acronym> server through a proxy class, <code>SOAPProxy</code>. The proxy handles all the internals of <acronym>SOAP</acronym> for you, including creating the XML request document out of the function name and argument list, sending the request over
HTTP to the remote <acronym>SOAP</acronym> server, parsing the XML response document, and creating native Python values to return. You'll see what these XML documents look like in the next section.
<li>Every <acronym>SOAP</acronym> service has a <acronym>URL</acronym> which handles all the requests. The same <acronym>URL</acronym> is used for all function calls. This particular service only has a single function, but later in this chapter you'll see
examples of the Google <acronym>API</acronym>, which has several functions. The service <acronym>URL</acronym> is shared by all functions.Each <acronym>SOAP</acronym> service also has a namespace, which is defined by the server and is completely arbitrary. It's simply part of the configuration
required to call <acronym>SOAP</acronym> methods. It allows the server to share a single service <acronym>URL</acronym> and route requests between several unrelated services. It's like dividing Python modules into <a href="#kgp.packages" title="9.2. Packages">packages</a>.
<li>You're creating the <code>SOAPProxy</code> with the service <acronym>URL</acronym> and the service namespace. This doesn't make any connection to the <acronym>SOAP</acronym> server; it simply creates a local Python object.
<li>Now with everything configured properly, you can actually call remote <acronym>SOAP</acronym> methods as if they were local functions. You pass arguments just like a normal function, and you get a return value just
like a normal function. But under the covers, there's a heck of a lot going on.
<p>Let's peek under those covers.
<h2 id="soap.debug">12.4. Debugging <acronym>SOAP</acronym> Web Services</h2>
<p>The <acronym>SOAP</acronym> libraries provide an easy way to see what's going on behind the scenes.
<p>Turning on debugging is a simple matter of setting two flags in the <code>SOAPProxy</code>'s configuration.
<div class=example><h3>Example 12.7. Debugging <acronym>SOAP</acronym> Web Services</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import SOAPProxy</kbd>
<samp class=prompt>>>> </samp><kbd>url = 'http://services.xmethods.net:80/soap/servlet/rpcrouter'</kbd>
<samp class=prompt>>>> </samp><kbd>n = 'urn:xmethods-Temperature'</kbd>
<samp class=prompt>>>> </samp><kbd>server = SOAPProxy(url, namespace=n)</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>server.config.dumpSOAPOut = 1</kbd> <span>&#x2461;</span>
<samp class=prompt>>>> </samp><kbd>server.config.dumpSOAPIn = 1</kbd>
<samp class=prompt>>>> </samp><kbd>temperature = server.getTemp('27502')</kbd> <span>&#x2462;</span>
<samp>*** Outgoing SOAP ******************************************************
&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
&lt;SOAP-ENV:Body>
&lt;ns1:getTemp xmlns:ns1="urn:xmethods-Temperature" SOAP-ENC:root="1">
&lt;v1 xsi:type="xsd:string">27502&lt;/v1>
&lt;/ns1:getTemp>
&lt;/SOAP-ENV:Body>
&lt;/SOAP-ENV:Envelope>
************************************************************************
*** Incoming SOAP ******************************************************
&lt;?xml version='1.0' encoding='UTF-8'?>
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
&lt;SOAP-ENV:Body>
&lt;ns1:getTempResponse xmlns:ns1="urn:xmethods-Temperature"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
&lt;return xsi:type="xsd:float">80.0&lt;/return>
&lt;/ns1:getTempResponse>
&lt;/SOAP-ENV:Body>
&lt;/SOAP-ENV:Envelope>
************************************************************************
</samp>
<samp class=prompt>>>> </samp><kbd>temperature</kbd>
80.0
</pre><div class=calloutlist>
<ol>
<li>First, create the <code>SOAPProxy</code> like normal, with the service <acronym>URL</acronym> and the namespace.
<li>Second, turn on debugging by setting <var>server.config.dumpSOAPIn</var> and <var>server.config.dumpSOAPOut</var>.
<li>Third, call the remote <acronym>SOAP</acronym> method as usual. The <acronym>SOAP</acronym> library will print out both the outgoing XML request document, and the incoming XML response document. This is all the hard
work that <code>SOAPProxy</code> is doing for you. Intimidating, isn't it? Let's break it down.
<p>Most of the XML request document that gets sent to the server is just boilerplate. Ignore all the namespace declarations;
they're going to be the same (or similar) for all <acronym>SOAP</acronym> calls. The heart of the &#8220;function call&#8221; is this fragment within the <code>&lt;Body></code> element:
<pre><code>
&lt;ns1:getTemp <span>&#x2460;</span>
xmlns:ns1="urn:xmethods-Temperature" <span>&#x2461;</span>
SOAP-ENC:root="1">
&lt;v1 xsi:type="xsd:string">27502&lt;/v1> <span>&#x2462;</span>
&lt;/ns1:getTemp>
</pre><div class=calloutlist>
<ol>
<li>The element name is the function name, <code>getTemp</code>. <code>SOAPProxy</code> uses <a href="#kgp.handler" title="10.5. Creating separate handlers by node type"><code>getattr</code> as a dispatcher</a>. Instead of calling separate local methods based on the method name, it actually uses the method name to construct the XML
request document.
<li>The function's XML element is contained in a specific namespace, which is the namespace you specified when you created the
<code>SOAPProxy</code> object. Don't worry about the <code>SOAP-ENC:root</code>; that's boilerplate too.
<li>The arguments of the function also got translated into XML. <code>SOAPProxy</code> introspects each argument to determine its datatype (in this case it's a string). The argument datatype goes into the <code>xsi:type</code> attribute, followed by the actual string value.
<p>The XML return document is equally easy to understand, once you know what to ignore. Focus on this fragment within the <code>&lt;Body></code>:
<pre><code>
&lt;ns1:getTempResponse <span>&#x2460;</span>
xmlns:ns1="urn:xmethods-Temperature" <span>&#x2461;</span>
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
&lt;return xsi:type="xsd:float">80.0&lt;/return> <span>&#x2462;</span>
&lt;/ns1:getTempResponse>
</pre><div class=calloutlist>
<ol>
<li>The server wraps the function return value within a <code>&lt;getTempResponse></code> element. By convention, this wrapper element is the name of the function, plus <code>Response</code>. But it could really be almost anything; the important thing that <code>SOAPProxy</code> notices is not the element name, but the namespace.
<li>The server returns the response in the same namespace we used in the request, the same namespace we specified when we first
create the <code>SOAPProxy</code>. Later in this chapter we'll see what happens if you forget to specify the namespace when creating the <code>SOAPProxy</code>.
<li>The return value is specified, along with its datatype (it's a float). <code>SOAPProxy</code> uses this explicit datatype to create a Python object of the correct native datatype and return it.
<h2 id="soap.wsdl">12.5. Introducing <acronym>WSDL</acronym></h2>
<p>The <code>SOAPProxy</code> class proxies local method calls and transparently turns then into invocations of remote <acronym>SOAP</acronym> methods. As you've seen, this is a lot of work, and <code>SOAPProxy</code> does it quickly and transparently. What it doesn't do is provide any means of method introspection.
<p>Consider this: the previous two sections showed an example of calling a simple remote <acronym>SOAP</acronym> method with one argument and one return value, both of simple data types. This required knowing, and keeping track of, the
service <acronym>URL</acronym>, the service namespace, the function name, the number of arguments, and the datatype of each argument. If any of these is
missing or wrong, the whole thing falls apart.
<p>That shouldn't come as a big surprise. If I wanted to call a local function, I would need to know what package or module
it was in (the equivalent of service <acronym>URL</acronym> and namespace). I would need to know the correct function name and the correct number of arguments. Python deftly handles datatyping without explicit types, but I would still need to know how many argument to pass, and how many
return values to expect.
<p>The big difference is introspection. As you saw in <a href="#apihelper">Chapter 4</a>, Python excels at letting you discover things about modules and functions at runtime. You can list the available functions within
a module, and with a little work, drill down to individual function declarations and arguments.
<p><acronym>WSDL</acronym> lets you do that with <acronym>SOAP</acronym> web services. <acronym>WSDL</acronym> stands for &#8220;Web Services Description Language&#8221;. Although designed to be flexible enough to describe many types of web services, it is most often used to describe <acronym>SOAP</acronym> web services.
<p>A <acronym>WSDL</acronym> file is just that: a file. More specifically, it's an XML file. It usually lives on the same server you use to access the
<acronym>SOAP</acronym> web services it describes, although there's nothing special about it. Later in this chapter, we'll download the <acronym>WSDL</acronym> file for the Google API and use it locally. That doesn't mean we're calling Google locally; the <acronym>WSDL</acronym> file still describes the remote functions sitting on Google's server.
<p>A <acronym>WSDL</acronym> file contains a description of everything involved in calling a <acronym>SOAP</acronym> web service:
<div class=itemizedlist>
<ul>
<li>The service <acronym>URL</acronym> and namespace
<li>The type of web service (probably function calls using <acronym>SOAP</acronym>, although as I mentioned, <acronym>WSDL</acronym> is flexible enough to describe a wide variety of web services)
<li>The list of available functions
<li>The arguments for each function
<li>The datatype of each argument
<li>The return values of each function, and the datatype of each return value
</ul>
<p>In other words, a <acronym>WSDL</acronym> file tells you everything you need to know to be able to call a <acronym>SOAP</acronym> web service.
<h2 id="soap.introspection">12.6. Introspecting <acronym>SOAP</acronym> Web Services with <acronym>WSDL</acronym></h2>
<p>Like many things in the web services arena, <acronym>WSDL</acronym> has a long and checkered history, full of political strife and intrigue. I will skip over this history entirely, since it
bores me to tears. There were other standards that tried to do similar things, but <acronym>WSDL</acronym> won, so let's learn how to use it.
<p>The most fundamental thing that <acronym>WSDL</acronym> allows you to do is discover the available methods offered by a <acronym>SOAP</acronym> server.
<div class=example><h3>Example 12.8. Discovering The Available Methods</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import WSDL</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl')</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy(wsdlFile)</kbd> <span>&#x2461;</span>
<samp class=prompt>>>> </samp><kbd>server.methods.keys()</kbd> <span>&#x2462;</span>
[u'getTemp']
</pre><div class=calloutlist>
<ol>
<li>SOAPpy includes a <acronym>WSDL</acronym> parser. At the time of this writing, it was labeled as being in the early stages of development, but I had no problem parsing
any of the <acronym>WSDL</acronym> files I tried.
<li>To use a <acronym>WSDL</acronym> file, you again use a proxy class, <code>WSDL.Proxy</code>, which takes a single argument: the <acronym>WSDL</acronym> file. Note that in this case you are passing in the <acronym>URL</acronym> of a <acronym>WSDL</acronym> file stored on the remote server, but the proxy class works just as well with a local copy of the <acronym>WSDL</acronym> file. The act of creating the <acronym>WSDL</acronym> proxy will download the <acronym>WSDL</acronym> file and parse it, so it there are any errors in the <acronym>WSDL</acronym> file (or it can't be fetched due to networking problems), you'll know about it immediately.
<li>The <acronym>WSDL</acronym> proxy class exposes the available functions as a Python dictionary, <var>server.methods</var>. So getting the list of available methods is as simple as calling the dictionary method <code>keys()</code>.
<p>Okay, so you know that this <acronym>SOAP</acronym> server offers a single method: <code>getTemp</code>. But how do you call it? The <acronym>WSDL</acronym> proxy object can tell you that too.
<div class=example><h3>Example 12.9. Discovering A Method's Arguments</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>callInfo = server.methods['getTemp']</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>callInfo.inparams</kbd> <span>&#x2461;</span>
[&lt;SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AD0>]
<samp class=prompt>>>> </samp><kbd>callInfo.inparams[0].name</kbd> <span>&#x2462;</span>
u'zipcode'
<samp class=prompt>>>> </samp><kbd>callInfo.inparams[0].type</kbd> <span>&#x2463;</span>
(u'http://www.w3.org/2001/XMLSchema', u'string')
</pre><div class=calloutlist>
<ol>
<li>The <var>server.methods</var> dictionary is filled with a SOAPpy-specific structure called <code>CallInfo</code>. A <code>CallInfo</code> object contains information about one specific function, including the function arguments.
<li>The function arguments are stored in <var>callInfo.inparams</var>, which is a Python list of <code>ParameterInfo</code> objects that hold information about each parameter.
<li>Each <code>ParameterInfo</code> object contains a <var>name</var> attribute, which is the argument name. You are not required to know the argument name to call the function through <acronym>SOAP</acronym>, but <acronym>SOAP</acronym> does support calling functions with named arguments (just like Python), and <code>WSDL.Proxy</code> will correctly handle mapping named arguments to the remote function if you choose to use them.
<li>Each parameter is also explicitly typed, using datatypes defined in XML Schema. You saw this in the wire trace in the previous
section; the XML Schema namespace was part of the &#8220;boilerplate&#8221; I told you to ignore. For our purposes here, you may continue to ignore it. The <var>zipcode</var> parameter is a string, and if you pass in a Python string to the <code>WSDL.Proxy</code> object, it will map it correctly and send it to the server.
<p><acronym>WSDL</acronym> also lets you introspect into a function's return values.
<div class=example><h3>Example 12.10. Discovering A Method's Return Values</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>callInfo.outparams</kbd> <span>&#x2460;</span>
[&lt;SOAPpy.wstools.WSDLTools.ParameterInfo instance at 0x00CF3AF8>]
<samp class=prompt>>>> </samp><kbd>callInfo.outparams[0].name</kbd> <span>&#x2461;</span>
u'return'
<samp class=prompt>>>> </samp><kbd>callInfo.outparams[0].type</kbd>
(u'http://www.w3.org/2001/XMLSchema', u'float')
</pre><div class=calloutlist>
<ol>
<li>The adjunct to <var>callInfo.inparams</var> for function arguments is <var>callInfo.outparams</var> for return value. It is also a list, because functions called through <acronym>SOAP</acronym> can return multiple values, just like Python functions.
<li>Each <code>ParameterInfo</code> object contains <var>name</var> and <var>type</var>. This function returns a single value, named <var>return</var>, which is a float.
<p>Let's put it all together, and call a <acronym>SOAP</acronym> web service through a <acronym>WSDL</acronym> proxy.
<div class=example><h3>Example 12.11. Calling A Web Service Through A <acronym>WSDL</acronym> Proxy</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import WSDL</kbd>
<samp class=prompt>>>> </samp><kbd>wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl')</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy(wsdlFile)</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>server.getTemp('90210')</kbd> <span>&#x2461;</span>
66.0
<samp class=prompt>>>> </samp><kbd>server.soapproxy.config.dumpSOAPOut = 1</kbd> <span>&#x2462;</span>
<samp class=prompt>>>> </samp><kbd>server.soapproxy.config.dumpSOAPIn = 1</kbd>
<samp class=prompt>>>> </samp><kbd>temperature = server.getTemp('90210')</kbd>
<samp>*** Outgoing SOAP ******************************************************
&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
&lt;SOAP-ENV:Body>
&lt;ns1:getTemp xmlns:ns1="urn:xmethods-Temperature" SOAP-ENC:root="1">
&lt;v1 xsi:type="xsd:string">90210&lt;/v1>
&lt;/ns1:getTemp>
&lt;/SOAP-ENV:Body>
&lt;/SOAP-ENV:Envelope>
************************************************************************
*** Incoming SOAP ******************************************************
&lt;?xml version='1.0' encoding='UTF-8'?>
&lt;SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
&lt;SOAP-ENV:Body>
&lt;ns1:getTempResponse xmlns:ns1="urn:xmethods-Temperature"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
&lt;return xsi:type="xsd:float">66.0&lt;/return>
&lt;/ns1:getTempResponse>
&lt;/SOAP-ENV:Body>
&lt;/SOAP-ENV:Envelope>
************************************************************************
</samp>
<samp class=prompt>>>> </samp><kbd>temperature</kbd>
66.0
</pre><div class=calloutlist>
<ol>
<li>The configuration is simpler than calling the <acronym>SOAP</acronym> service directly, since the <acronym>WSDL</acronym> file contains the both service <acronym>URL</acronym> and namespace you need to call the service. Creating the <code>WSDL.Proxy</code> object downloads the <acronym>WSDL</acronym> file, parses it, and configures a <code>SOAPProxy</code> object that it uses to call the actual <acronym>SOAP</acronym> web service.
<li>Once the <code>WSDL.Proxy</code> object is created, you can call a function as easily as you did with the <code>SOAPProxy</code> object. This is not surprising; the <code>WSDL.Proxy</code> is just a wrapper around the <code>SOAPProxy</code> with some introspection methods added, so the syntax for calling functions is the same.
<li>You can access the <code>WSDL.Proxy</code>'s <code>SOAPProxy</code> with <var>server.soapproxy</var>. This is useful to turning on debugging, so that when you can call functions through the <acronym>WSDL</acronym> proxy, its <code>SOAPProxy</code> will dump the outgoing and incoming XML documents that are going over the wire.
<h2 id="soap.google">12.7. Searching Google</h2>
<p>Let's finally turn to the sample code that you saw that the beginning of this chapter, which does something more useful and
exciting than get the current temperature.
<p>Google provides a <acronym>SOAP</acronym> <acronym>API</acronym> for programmatically accessing Google search results. To use it, you will need to sign up for Google Web Services.
<div class=procedure>
<h3>Procedure 12.4. Signing Up for Google Web Services</h3>
<ol>
<li>
<p>Go to <a href="http://www.google.com/apis/">http://www.google.com/apis/</a> and create a Google account. This requires only an email address. After you sign up you will receive your Google API license
key by email. You will need this key to pass as a parameter whenever you call Google's search functions.
<li>
<p>Also on <a href="http://www.google.com/apis/">http://www.google.com/apis/</a>, download the Google Web APIs developer kit. This includes some sample code in several programming languages (but not Python), and more importantly, it includes the <acronym>WSDL</acronym> file.
<li>
<p>Decompress the developer kit file and find <code>GoogleSearch.wsdl</code>. Copy this file to some permanent location on your local drive. You will need it later in this chapter.
</ol>
<p>Once you have your developer key and your Google <acronym>WSDL</acronym> file in a known place, you can start poking around with Google Web Services.
<div class=example><h3>Example 12.12. Introspecting Google Web Services</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import WSDL</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy('/path/to/your/GoogleSearch.wsdl')</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>server.methods.keys()</kbd> <span>&#x2461;</span>
[u'doGoogleSearch', u'doGetCachedPage', u'doSpellingSuggestion']
<samp class=prompt>>>> </samp><kbd>callInfo = server.methods['doGoogleSearch']</kbd>
<samp class=prompt>>>> </samp><kbd>for arg in callInfo.inparams:</kbd> <span>&#x2462;</span>
<samp class=prompt>... </samp>print arg.name.ljust(15), arg.type
<samp>key (u'http://www.w3.org/2001/XMLSchema', u'string')
q (u'http://www.w3.org/2001/XMLSchema', u'string')
start (u'http://www.w3.org/2001/XMLSchema', u'int')
maxResults (u'http://www.w3.org/2001/XMLSchema', u'int')
filter (u'http://www.w3.org/2001/XMLSchema', u'boolean')
restrict (u'http://www.w3.org/2001/XMLSchema', u'string')
safeSearch (u'http://www.w3.org/2001/XMLSchema', u'boolean')
lr (u'http://www.w3.org/2001/XMLSchema', u'string')
ie (u'http://www.w3.org/2001/XMLSchema', u'string')
oe (u'http://www.w3.org/2001/XMLSchema', u'string')</span>
</pre><div class=calloutlist>
<ol>
<li>Getting started with Google web services is easy: just create a <code>WSDL.Proxy</code> object and point it at your local copy of Google's <acronym>WSDL</acronym> file.
<li>According to the <acronym>WSDL</acronym> file, Google offers three functions: <code>doGoogleSearch</code>, <code>doGetCachedPage</code>, and <code>doSpellingSuggestion</code>. These do exactly what they sound like: perform a Google search and return the results programmatically, get access to the
cached version of a page from the last time Google saw it, and offer spelling suggestions for commonly misspelled search words.
<li>The <code>doGoogleSearch</code> function takes a number of parameters of various types. Note that while the <acronym>WSDL</acronym> file can tell you what the arguments are called and what datatype they are, it can't tell you what they mean or how to use
them. It could theoretically tell you the acceptable range of values for each parameter, if only specific values were allowed,
but Google's <acronym>WSDL</acronym> file is not that detailed. <code>WSDL.Proxy</code> can't work magic; it can only give you the information provided in the <acronym>WSDL</acronym> file.
<p>Here is a brief synopsis of all the parameters to the <code>doGoogleSearch</code> function:
<div class=itemizedlist>
<ul>
<li><var>key</var> - Your Google API key, which you received when you signed up for Google web services.
<li><var>q</var> - The search word or phrase you're looking for. The syntax is exactly the same as Google's web form, so if you know any
advanced search syntax or tricks, they all work here as well.
<li><var>start</var> - The index of the result to start on. Like the interactive web version of Google, this function returns 10 results at a
time. If you wanted to get the second &#8220;page&#8221; of results, you would set <var>start</var> to 10.
<li><var>maxResults</var> - The number of results to return. Currently capped at 10, although you can specify fewer if you are only interested in
a few results and want to save a little bandwidth.
<li><var>filter</var> - If <code>True</code>, Google will filter out duplicate pages from the results.
<li><var>restrict</var> - Set this to <code>country</code> plus a country code to get results only from a particular country. Example: <code>countryUK</code> to search pages in the United Kingdom. You can also specify <code>linux</code>, <code>mac</code>, or <code>bsd</code> to search a Google-defined set of technical sites, or <code>unclesam</code> to search sites about the United States government.
<li><var>safeSearch</var> - If <code>True</code>, Google will filter out porn sites.
<li><var>lr</var> (&#8220;language restrict&#8221;) - Set this to a language code to get results only in a particular language.
<li><var>ie</var> and <var>oe</var> (&#8220;input encoding&#8221; and &#8220;output encoding&#8221;) - Deprecated, both must be <code>utf-8</code>.
</ul>
<div class=example><h3>Example 12.13. Searching Google</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import WSDL</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy('/path/to/your/GoogleSearch.wsdl')</kbd>
<samp class=prompt>>>> </samp><kbd>key = 'YOUR_GOOGLE_API_KEY'</kbd>
<samp class=prompt>>>> </samp><kbd>results = server.doGoogleSearch(key, 'mark', 0, 10, False, "",</kbd>
<samp class=prompt>... </samp>False, "", "utf-8", "utf-8") <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>len(results.resultElements)</kbd><span>&#x2461;</span>
10
<samp class=prompt>>>> </samp><kbd>results.resultElements[0].URL</kbd> <span>&#x2462;</span>
'http://diveintomark.org/'
<samp class=prompt>>>> </samp><kbd>results.resultElements[0].title</kbd>
'dive into &lt;b>mark&lt;/b>'
</pre><div class=calloutlist>
<ol>
<li>After setting up the <code>WSDL.Proxy</code> object, you can call <code>server.doGoogleSearch</code> with all ten parameters. Remember to use your own Google API key that you received when you signed up for Google web services.
<li>There's a lot of information returned, but let's look at the actual search results first. They're stored in <var>results.resultElements</var>, and you can access them just like a normal Python list.
<li>Each element in the <var>resultElements</var> is an object that has a <var>URL</var>, <var>title</var>, <var>snippet</var>, and other useful attributes. At this point you can use normal Python introspection techniques like <kbd>dir(results.resultElements[0])</kbd> to see the available attributes. Or you can introspect through the <acronym>WSDL</acronym> proxy object and look through the function's <var>outparams</var>. Each technique will give you the same information.
<p>The <var>results</var> object contains more than the actual search results. It also contains information about the search itself, such as how long
it took and how many results were found (even though only 10 were returned). The Google web interface shows this information,
and you can access it programmatically too.
<div class=example><h3>Example 12.14. Accessing Secondary Information From Google</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>results.searchTime</kbd> <span>&#x2460;</span>
0.224919
<samp class=prompt>>>> </samp><kbd>results.estimatedTotalResultsCount</kbd> <span>&#x2461;</span>
29800000
<samp class=prompt>>>> </samp><kbd>results.directoryCategories</kbd> <span>&#x2462;</span>
<samp>[&lt;SOAPpy.Types.structType item at 14367400>:
{'fullViewableName':
'Top/Arts/Literature/World_Literature/American/19th_Century/Twain,_Mark',
'specialEncoding': ''}]</samp>
<samp class=prompt>>>> </samp><kbd>results.directoryCategories[0].fullViewableName</kbd>
'Top/Arts/Literature/World_Literature/American/19th_Century/Twain,_Mark'
</pre><div class=calloutlist>
<ol>
<li>This search took 0.224919 seconds. That does not include the time spent sending and receiving the actual <acronym>SOAP</acronym> XML documents. It's just the time that Google spent processing your request once it received it.
<li>In total, there were approximately 30 million results. You can access them 10 at a time by changing the <var>start</var> parameter and calling <code>server.doGoogleSearch</code> again.
<li>For some queries, Google also returns a list of related categories in the <a href="http://directory.google.com/">Google Directory</a>. You can append these URLs to <a href="http://directory.google.com/">http://directory.google.com/</a> to construct the link to the directory category page.
<h2 id="soap.troubleshooting">12.8. Troubleshooting <acronym>SOAP</acronym> Web Services</h2>
<p>Of course, the world of <acronym>SOAP</acronym> web services is not all happiness and light. Sometimes things go wrong.
<p>As you've seen throughout this chapter, <acronym>SOAP</acronym> involves several layers. There's the HTTP layer, since <acronym>SOAP</acronym> is sending XML documents to, and receiving XML documents from, an HTTP server. So all the debugging techniques you learned
in <a href="#oa" title="Chapter 11. HTTP Web Services">Chapter 11, <i>HTTP Web Services</i></a> come into play here. You can <kbd>import httplib</kbd> and then set <kbd>httplib.HTTPConnection.debuglevel = 1</kbd> to see the underlying HTTP traffic.
<p>Beyond the underlying HTTP layer, there are a number of things that can go wrong. SOAPpy does an admirable job hiding the <acronym>SOAP</acronym> syntax from you, but that also means it can be difficult to determine where the problem is when things don't work.
<p>Here are a few examples of common mistakes that I've made in using <acronym>SOAP</acronym> web services, and the errors they generated.
<div class=example><h3>Example 12.15. Calling a Method With an Incorrectly Configured Proxy</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import SOAPProxy</kbd>
<samp class=prompt>>>> </samp><kbd>url = 'http://services.xmethods.net:80/soap/servlet/rpcrouter'</kbd>
<samp class=prompt>>>> </samp><kbd>server = SOAPProxy(url)</kbd> <span>&#x2460;</span>
<samp class=prompt>>>> </samp><kbd>server.getTemp('27502')</kbd> <span>&#x2461;</span>
<samp class=traceback>&lt;Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?>
Traceback (most recent call last):
File "&lt;stdin>", line 1, in ?
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
return self.__r_call(*args, **kw)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
self.__hd, self.__ma)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server.BadTargetObjectURI:
Unable to determine object id from call: is the method element namespaced?></span>
</pre><div class=calloutlist>
<ol>
<li>Did you spot the mistake? You're creating a <code>SOAPProxy</code> manually, and you've correctly specified the service <acronym>URL</acronym>, but you haven't specified the namespace. Since multiple services may be routed through the same service <acronym>URL</acronym>, the namespace is essential to determine which service you're trying to talk to, and therefore which method you're really
calling.
<li>The server responds by sending a <acronym>SOAP</acronym> Fault, which SOAPpy turns into a Python exception of type <code>SOAPpy.Types.faultType</code>. All errors returned from any <acronym>SOAP</acronym> server will always be <acronym>SOAP</acronym> Faults, so you can easily catch this exception. In this case, the human-readable part of the <acronym>SOAP</acronym> Fault gives a clue to the problem: the method element is not namespaced, because the original <code>SOAPProxy</code> object was not configured with a service namespace.
<p>Misconfiguring the basic elements of the <acronym>SOAP</acronym> service is one of the problems that <acronym>WSDL</acronym> aims to solve. The <acronym>WSDL</acronym> file contains the service <acronym>URL</acronym> and namespace, so you can't get it wrong. Of course, there are still other things you can get wrong.
<div class=example><h3>Example 12.16. Calling a Method With the Wrong Arguments</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl'</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy(wsdlFile)</kbd>
<samp class=prompt>>>> </samp><kbd>temperature = server.getTemp(27502)</kbd> <span>&#x2460;</span>
<samp class=traceback>&lt;Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match> <span>&#x2461;</span>
Traceback (most recent call last):
File "&lt;stdin>", line 1, in ?
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
return self.__r_call(*args, **kw)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
self.__hd, self.__ma)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server: Exception while handling service request:
services.temperature.TempService.getTemp(int) -- no signature match></span>
</pre><div class=calloutlist>
<ol>
<li>Did you spot the mistake? It's a subtle one: you're calling <code>server.getTemp</code> with an integer instead of a string. As you saw from introspecting the <acronym>WSDL</acronym> file, the <code>getTemp()</code> <acronym>SOAP</acronym> function takes a single argument, <var>zipcode</var>, which must be a string. <code>WSDL.Proxy</code> will <em>not</em> coerce datatypes for you; you need to pass the exact datatypes that the server expects.
<li>Again, the server returns a <acronym>SOAP</acronym> Fault, and the human-readable part of the error gives a clue as to the problem: you're calling a <code>getTemp</code> function with an integer value, but there is no function defined with that name that takes an integer. In theory, <acronym>SOAP</acronym> allows you to <em>overload</em> functions, so you could have two functions in the same <acronym>SOAP</acronym> service with the same name and the same number of arguments, but the arguments were of different datatypes. This is why
it's important to match the datatypes exactly, and why <code>WSDL.Proxy</code> doesn't coerce datatypes for you. If it did, you could end up calling a completely different function! Good luck debugging
that one. It's much easier to be picky about datatypes and fail as quickly as possible if you get them wrong.
<p>It's also possible to write Python code that expects a different number of return values than the remote function actually returns.
<div class=example><h3>Example 12.17. Calling a Method and Expecting the Wrong Number of Return Values</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>wsdlFile = 'http://www.xmethods.net/sd/2001/TemperatureService.wsdl'</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy(wsdlFile)</kbd>
<samp class=prompt>>>> </samp><kbd>(city, temperature) = server.getTemp(27502)</kbd> <span>&#x2460;</span>
<samp class=traceback>Traceback (most recent call last):
File "&lt;stdin>", line 1, in ?
TypeError: unpack non-sequence</span>
</pre><div class=calloutlist>
<ol>
<li>Did you spot the mistake? <code>server.getTemp</code> only returns one value, a float, but you've written code that assumes you're getting two values and trying to assign them
to two different variables. Note that this does not fail with a <acronym>SOAP</acronym> fault. As far as the remote server is concerned, nothing went wrong at all. The error only occurred <em>after</em> the <acronym>SOAP</acronym> transaction was complete, <code>WSDL.Proxy</code> returned a float, and your local Python interpreter tried to accomodate your request to split it into two different variables. Since the function only returned
one value, you get a Python exception trying to split it, not a <acronym>SOAP</acronym> Fault.
<p>What about Google's web service? The most common problem I've had with it is that I forget to set the application key properly.
<div class=example><h3>Example 12.18. Calling a Method With An Application-Specific Error</h3><pre class=screen>
<samp class=prompt>>>> </samp><kbd>from SOAPpy import WSDL</kbd>
<samp class=prompt>>>> </samp><kbd>server = WSDL.Proxy(r'/path/to/local/GoogleSearch.wsdl')</kbd>
<samp class=prompt>>>> </samp><kbd>results = server.doGoogleSearch('foo', 'mark', 0, 10, False, "",</kbd> <span>&#x2460;</span>
<samp class=prompt>... </samp>False, "", "utf-8", "utf-8")
<samp class=traceback>&lt;Fault SOAP-ENV:Server: <span>&#x2461;</span>
Exception from service object: Invalid authorization key: foo:
&lt;SOAPpy.Types.structType detail at 14164616>:
{'stackTrace':
'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
QueryLimits.java:220)
at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
GoogleSearchService.java:825)
at com.google.soap.search.GoogleSearchService.doGoogleSearch(
GoogleSearchService.java:121)
at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
at org.apache.soap.providers.RPCJavaProvider.invoke(
RPCJavaProvider.java:129)
at org.apache.soap.server.http.RPCRouterServlet.doPost(
RPCRouterServlet.java:288)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
at com.google.gse.HttpConnection.run(HttpConnection.java:195)
at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
at com.google.soap.search.UserKey.&lt;init>(UserKey.java:59)
at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
QueryLimits.java:217)
... 14 more
'}>
Traceback (most recent call last):
File "&lt;stdin>", line 1, in ?
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 453, in __call__
return self.__r_call(*args, **kw)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 475, in __r_call
self.__hd, self.__ma)
File "c:\python23\Lib\site-packages\SOAPpy\Client.py", line 389, in __call
raise p
SOAPpy.Types.faultType: &lt;Fault SOAP-ENV:Server: Exception from service object:
Invalid authorization key: foo:
&lt;SOAPpy.Types.structType detail at 14164616>:
{'stackTrace':
'com.google.soap.search.GoogleSearchFault: Invalid authorization key: foo
at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
QueryLimits.java:220)
at com.google.soap.search.QueryLimits.validateKey(QueryLimits.java:127)
at com.google.soap.search.GoogleSearchService.doPublicMethodChecks(
GoogleSearchService.java:825)
at com.google.soap.search.GoogleSearchService.doGoogleSearch(
GoogleSearchService.java:121)
at sun.reflect.GeneratedMethodAccessor13.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.soap.server.RPCRouter.invoke(RPCRouter.java:146)
at org.apache.soap.providers.RPCJavaProvider.invoke(
RPCJavaProvider.java:129)
at org.apache.soap.server.http.RPCRouterServlet.doPost(
RPCRouterServlet.java:288)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at com.google.gse.HttpConnection.runServlet(HttpConnection.java:237)
at com.google.gse.HttpConnection.run(HttpConnection.java:195)
at com.google.gse.DispatchQueue$WorkerThread.run(DispatchQueue.java:201)
Caused by: com.google.soap.search.UserKeyInvalidException: Key was of wrong size.
at com.google.soap.search.UserKey.&lt;init>(UserKey.java:59)
at com.google.soap.search.QueryLimits.lookUpAndLoadFromINSIfNeedBe(
QueryLimits.java:217)
... 14 more
'}></span>
</pre><div class=calloutlist>
<ol>
<li>Can you spot the mistake? There's nothing wrong with the calling syntax, or the number of arguments, or the datatypes. The
problem is application-specific: the first argument is supposed to be my application key, but <code>foo</code> is not a valid Google key.
<li>The Google server responds with a <acronym>SOAP</acronym> Fault and an incredibly long error message, which includes a complete Java stack trace. Remember that <em>all</em> <acronym>SOAP</acronym> errors are signified by <acronym>SOAP</acronym> Faults: errors in configuration, errors in function arguments, and application-specific errors like this. Buried in there
somewhere is the crucial piece of information: <code>Invalid authorization key: foo</code>.
<div class=itemizedlist>
<h3>Further Reading on Troubleshooting <acronym>SOAP</acronym></h3>
<ul>
<li><a href="http://www-106.ibm.com/developerworks/webservices/library/ws-pyth17.html">New developments for SOAPpy</a> steps through trying to connect to another <acronym>SOAP</acronym> service that doesn't quite work as advertised.
</ul>
<h2 id="soap.summary">12.9. Summary</h2>
<p><acronym>SOAP</acronym> web services are very complicated. The specification is very ambitious and tries to cover many different use cases for web
services. This chapter has touched on some of the simpler use cases.
<div class=highlights>
<p>Before diving into the next chapter, make sure you're comfortable doing all of these things:
<div class=itemizedlist>
<ul>
<li>Connecting to a <acronym>SOAP</acronym> server and calling remote methods
<li>Loading a <acronym>WSDL</acronym> file and introspecting remote methods
<li>Debugging <acronym>SOAP</acronym> calls with wire traces
<li>Troubleshooting common <acronym>SOAP</acronym>-related errors
</ul>
<div class=chapter>
<h2 id="roman">Chapter 13. Unit Testing</h2>
@@ -6503,265 +5849,6 @@ numerals. You saw the mechanics of constructing and validating Roman numerals in
<li><a href="http://www.wilkiecollins.demon.co.uk/roman/front.htm">This site</a> has more on Roman numerals, including a fascinating <a href="http://www.wilkiecollins.demon.co.uk/roman/intro.htm">history</a> of how Romans and other civilizations really used them (short answer: haphazardly and inconsistently).
</ul>
<h2 id="roman.divein">13.2. Diving in</h2>
<p>Now that you've completely defined the behavior you expect from your conversion functions, you're going to do something a
little unexpected: you're going to write a test suite that puts these functions through their paces and makes sure that they
behave the way you want them to. You read that right: you're going to write code that tests code that you haven't written
yet.
<p>This is called unit testing, since the set of two conversion functions can be written and tested as a unit, separate from
any larger program they may become part of later. Python has a framework for unit testing, the appropriately-named <code>unittest</code> module.
<table id="note.unittest" class=note border="0" summary="">
<td rowspan="2" align="center" valign="top" width="1%"><img src="images/note.png" alt="Note" title="" width="24" height="24"><td colspan="2" align="left" valign="top" width="99%"><code>unittest</code> is included with Python 2.1 and later. Python 2.0 users can download it from <a href="http://pyunit.sourceforge.net/"><code>pyunit.sourceforge.net</code></a>.
<p>Unit testing is an important part of an overall testing-centric development strategy. If you write unit tests, it is important
to write them early (preferably before writing the code that they test), and to keep them updated as code and requirements
change. Unit testing is not a replacement for higher-level functional or system testing, but it is important in all phases
of development:
<div class=itemizedlist>
<ul>
<li>Before writing code, it forces you to detail your requirements in a useful fashion.
<li>While writing code, it keeps you from over-coding. When all the test cases pass, the function is complete.
<li>When refactoring code, it assures you that the new version behaves the same way as the old version.
<li>When maintaining code, it helps you cover your ass when someone comes screaming that your latest change broke their old code.
(&#8220;But <em>sir</em>, all the unit tests passed when I checked it in...&#8221;)
<li>When writing code in a team, it increases confidence that the code you're about to commit isn't going to break other peoples'
code, because you can run their unittests first. (I've seen this sort of thing in code sprints. A team breaks up the assignment,
everybody takes the specs for their task, writes unit tests for it, then shares their unit tests with the rest of the team.
That way, nobody goes off too far into developing code that won't play well with others.)
</ul>
<h2 id="roman.romantest">13.3. Introducing <code>romantest.py</code></h2>
<p>This is the complete test suite for your Roman numeral conversion functions, which are yet to be written but will eventually
be in <code>roman.py</code>. It is not immediately obvious how it all fits together; none of these classes or methods reference any of the others.
There are good reasons for this, as you'll see shortly.
<div class=example><h3>Example 13.1. <code>romantest.py</code></h3>
<p>If you have not already done so, you can <a href="http://diveintopython3.org/download/diveintopython3-examples-5.4.zip" title="Download example scripts">download this and other examples</a> used in this book.
<pre><code>
"""Unit test for roman.py"""
import roman
import unittest
class KnownValues(unittest.TestCase):
knownValues = ( (1, 'I'),
(2, 'II'),
(3, 'III'),
(4, 'IV'),
(5, 'V'),
(6, 'VI'),
(7, 'VII'),
(8, 'VIII'),
(9, 'IX'),
(10, 'X'),
(50, 'L'),
(100, 'C'),
(500, 'D'),
(1000, 'M'),
(31, 'XXXI'),
(148, 'CXLVIII'),
(294, 'CCXCIV'),
(312, 'CCCXII'),
(421, 'CDXXI'),
(528, 'DXXVIII'),
(621, 'DCXXI'),
(782, 'DCCLXXXII'),
(870, 'DCCCLXX'),
(941, 'CMXLI'),
(1043, 'MXLIII'),
(1110, 'MCX'),
(1226, 'MCCXXVI'),
(1301, 'MCCCI'),
(1485, 'MCDLXXXV'),
(1509, 'MDIX'),
(1607, 'MDCVII'),
(1754, 'MDCCLIV'),
(1832, 'MDCCCXXXII'),
(1993, 'MCMXCIII'),
(2074, 'MMLXXIV'),
(2152, 'MMCLII'),
(2212, 'MMCCXII'),
(2343, 'MMCCCXLIII'),
(2499, 'MMCDXCIX'),
(2574, 'MMDLXXIV'),
(2646, 'MMDCXLVI'),
(2723, 'MMDCCXXIII'),
(2892, 'MMDCCCXCII'),
(2975, 'MMCMLXXV'),
(3051, 'MMMLI'),
(3185, 'MMMCLXXXV'),
(3250, 'MMMCCL'),
(3313, 'MMMCCCXIII'),
(3408, 'MMMCDVIII'),
(3501, 'MMMDI'),
(3610, 'MMMDCX'),
(3743, 'MMMDCCXLIII'),
(3844, 'MMMDCCCXLIV'),
(3888, 'MMMDCCCLXXXVIII'),
(3940, 'MMMCMXL'),
(3999, 'MMMCMXCIX'))
def testToRomanKnownValues(self):
"""to_roman should give known result with known input"""
for integer, numeral in self.knownValues:
result = roman.to_roman(integer)
self.assertEqual(numeral, result)
def testFromRomanKnownValues(self):
"""from_roman should give known result with known input"""
for integer, numeral in self.knownValues:
result = roman.from_roman(numeral)
self.assertEqual(integer, result)
class ToRomanBadInput(unittest.TestCase):
def testTooLarge(self):
"""to_roman should fail with large input"""
self.assertRaises(roman.OutOfRangeError, roman.to_roman, 4000)
def testZero(self):
"""to_roman should fail with 0 input"""
self.assertRaises(roman.OutOfRangeError, roman.to_roman, 0)
def testNegative(self):
"""to_roman should fail with negative input"""
self.assertRaises(roman.OutOfRangeError, roman.to_roman, -1)
def testNonInteger(self):
"""to_roman should fail with non-integer input"""
self.assertRaises(roman.NotIntegerError, roman.to_roman, 0.5)
class FromRomanBadInput(unittest.TestCase):
def testTooManyRepeatedNumerals(self):
"""from_roman should fail with too many repeated numerals"""
for s in ('MMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'):
self.assertRaises(roman.InvalidRomanNumeralError, roman.from_roman, s)
def testRepeatedPairs(self):
"""from_roman should fail with repeated pairs of numerals"""
for s in ('CMCM', 'CDCD', 'XCXC', 'XLXL', 'IXIX', 'IVIV'):
self.assertRaises(roman.InvalidRomanNumeralError, roman.from_roman, s)
def testMalformedAntecedent(self):
"""from_roman should fail with malformed antecedents"""
for s in ('IIMXCC', 'VX', 'DCM', 'CMM', 'IXIV',
'MCMC', 'XCX', 'IVI', 'LM', 'LD', 'LC'):
self.assertRaises(roman.InvalidRomanNumeralError, roman.from_roman, s)
class SanityCheck(unittest.TestCase):
def testSanity(self):
"""from_roman(to_roman(n))==n for all n"""
for integer in range(1, 4000):
numeral = roman.to_roman(integer)
result = roman.from_roman(numeral)
self.assertEqual(integer, result)
class CaseCheck(unittest.TestCase):
def testToRomanCase(self):
"""to_roman should always return uppercase"""
for integer in range(1, 4000):
numeral = roman.to_roman(integer)
self.assertEqual(numeral, numeral.upper())
def testFromRomanCase(self):
"""from_roman should only accept uppercase input"""
for integer in range(1, 4000):
numeral = roman.to_roman(integer)
roman.from_roman(numeral.upper())
self.assertRaises(roman.InvalidRomanNumeralError,
roman.from_roman, numeral.lower())
if __name__ == "__main__":
unittest.main() </pre><div class=itemizedlist>
<h3>Further reading</h3>
<ul>
<li><a href="http://pyunit.sourceforge.net/">The PyUnit home page</a> has an in-depth discussion of <a href="http://pyunit.sourceforge.net/pyunit.html">using the <code>unittest</code> framework</a>, including advanced features not covered in this chapter.
<li><a href="http://pyunit.sourceforge.net/pyunit.html">The PyUnit <acronym>FAQ</acronym></a> explains <a href="http://pyunit.sourceforge.net/pyunit.html#WHERE">why test cases are stored separately</a> from the code they test.
<li><a href="http://www.python.org/doc/current/lib/"><i class=citetitle>Python Library Reference</i></a> summarizes the <a href="http://www.python.org/doc/current/lib/module-unittest.html"><code>unittest</code></a> module.
<li><a href="http://www.extremeprogramming.org/">ExtremeProgramming.org</a> discusses <a href="http://www.extremeprogramming.org/rules/unittests.html">why you should write unit tests</a>.
<li><a href="http://www.c2.com/cgi/wiki">The Portland Pattern Repository</a> has an ongoing discussion of <a href="http://www.c2.com/cgi/wiki?UnitTests">unit tests</a>, including a <a href="http://www.c2.com/cgi/wiki?StandardDefinitionOfUnitTest">standard definition</a>, why you should <a href="http://www.c2.com/cgi/wiki?CodeUnitTestFirst">code unit tests first</a>, and several in-depth <a href="http://www.c2.com/cgi/wiki?UnitTestTrial">case studies</a>.
</ul>
<h2 id="roman.success">13.4. Testing for success</h2>
<p>A test case answers a single question about the code it is testing. A test case should be able to...
<ul>
<li>...run completely by itself, without any human input. Unit testing is about automation.
<li>...determine by itself whether the function it is testing has passed or failed, without a human interpreting the results.
<li>...run in isolation, separate from any other test cases (even if they test the same functions). Each test case is an island.
</ul>
<p>Given that, let's build the first test case. You have the following <a href="#roman.requirements">requirement</a>:
<div class=orderedlist>
<ol>
<li><code>to_roman()</code> should return the Roman numeral representation for all integers <code>1</code> to <code>3999</code>.
</ol>
<div class=example><h3 id="roman.testtoromanknownvalues.example">Example 13.2. <code>testToRomanKnownValues</code></h3><pre><code>
class KnownValues(unittest.TestCase): <span>&#x2460;</span>
knownValues = ( (1, 'I'),
(2, 'II'),
(3, 'III'),
(4, 'IV'),
(5, 'V'),
(6, 'VI'),
(7, 'VII'),
(8, 'VIII'),
(9, 'IX'),
(10, 'X'),
(50, 'L'),
(100, 'C'),
(500, 'D'),
(1000, 'M'),
(31, 'XXXI'),
(148, 'CXLVIII'),
(294, 'CCXCIV'),
(312, 'CCCXII'),
(421, 'CDXXI'),
(528, 'DXXVIII'),
(621, 'DCXXI'),
(782, 'DCCLXXXII'),
(870, 'DCCCLXX'),
(941, 'CMXLI'),
(1043, 'MXLIII'),
(1110, 'MCX'),
(1226, 'MCCXXVI'),
(1301, 'MCCCI'),
(1485, 'MCDLXXXV'),
(1509, 'MDIX'),
(1607, 'MDCVII'),
(1754, 'MDCCLIV'),
(1832, 'MDCCCXXXII'),
(1993, 'MCMXCIII'),
(2074, 'MMLXXIV'),
(2152, 'MMCLII'),
(2212, 'MMCCXII'),
(2343, 'MMCCCXLIII'),
(2499, 'MMCDXCIX'),
(2574, 'MMDLXXIV'),
(2646, 'MMDCXLVI'),
(2723, 'MMDCCXXIII'),
(2892, 'MMDCCCXCII'),
(2975, 'MMCMLXXV'),
(3051, 'MMMLI'),
(3185, 'MMMCLXXXV'),
(3250, 'MMMCCL'),
(3313, 'MMMCCCXIII'),
(3408, 'MMMCDVIII'),
(3501, 'MMMDI'),
(3610, 'MMMDCX'),
(3743, 'MMMDCCXLIII'),
(3844, 'MMMDCCCXLIV'),
(3888, 'MMMDCCCLXXXVIII'),
(3940, 'MMMCMXL'),
(3999, 'MMMCMXCIX')) <span>&#x2461;</span>
def testToRomanKnownValues(self): <span>&#x2462;</span>
"""to_roman should give known result with known input"""
for integer, numeral in self.knownValues:
result = roman.to_roman(integer) <span>&#x2463;</span> <span>&#x2464;</span>
self.assertEqual(numeral, result) <span>&#x2465;</span></pre><div class=calloutlist>
<h2 id="roman.failure">13.5. Testing for failure</h2>
<p>It is not enough to test that functions succeed when given good input; you must also test that they fail when given bad input. And not just any sort of failure; they must fail in the way you expect.
<p>Remember the <a href="#roman.requirements">other requirements</a> for <code>to_roman()</code>:
+5 -7
View File
@@ -37,14 +37,9 @@ pre a:hover{border:0}
kbd{font-weight:bold}
.prompt{color:#667}
/* headers */
h1,h2,h3,p,ul,ol{margin:1.75em 0;font-size:medium}
h1{background:papayawhip;width:100%}
h1,h2,h3{clear:both}
/* tables */
table{width:100%;border-collapse:collapse}
th,td{width:45%;margin:0;padding:0 0.5em}
th,td{width:45%;margin:0;padding:0 0.5em;border:1px solid #bbb}
th{text-align:left;vertical-align:baseline}
td{vertical-align:top}
th:first-child{width:10%;text-align:center}
@@ -52,7 +47,10 @@ th:first-child{width:10%;text-align:center}
.hover{background:#eee;color:inherit;cursor:default}
td pre{margin:0;padding:0;border:0}
/* section counters */
/* headers */
h1,h2,h3,p,ul,ol{margin:1.75em 0;font-size:medium}
h1,#noscript{background:papayawhip;width:100%}
h1,h2,h3{clear:both}
body{counter-reset:h1}
h1:before{content:"Chapter " counter(h1) ". "}
h1{counter-reset:h2}
+1
View File
@@ -4,6 +4,7 @@ var LANGS = {'python2': 'Python 2', 'java': 'Java', 'perl5': 'Perl 5', 'clang':
//google.load("jquery", "1.3");
//google.setOnLoadCallback(function() {
$(document).ready(function() {
$("#noscript").hide();
var HS = {'visible': 'hide', 'hidden': 'show'};
/*
// toggle-able language comparisons
Vendored
+4232 -4097
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -12,7 +12,7 @@ body{counter-reset:h1 2}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=root value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#native-datatypes>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Native datatypes</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Wonder is the foundation of all philosophy, research its progress, ignorance its end. <span>&#x275E;</span><br>&mdash; <cite>Michel de Montaigne</cite>
@@ -59,6 +59,7 @@ body{counter-reset:h1 2}
<h2 id=booleans>Booleans</h2>
<p>Booleans are either true or false. Python has two constants, <code>True</code> and <code>False</code>, which can be used to assign boolean values directly. Expressions can also evaluate to a boolean value. In certain places (like <code>if</code> statements), Python expects an expression to evaluate to a boolean value. These places are called <i>boolean contexts</i>. You can use virtually any expression in a boolean context, and Python will try to determine its truth value. Different datatypes have different rules about which values are true or false in a boolean context. (This will make more sense once you see some concrete examples later in this chapter.)
<p>For example, take this snippet from <a href=your-first-python-program.html#divingin><code>humansize.py</code></a>:
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<pre><code>if size &lt; 0:
raise ValueError('number must be non-negative')</code></pre>
<p><var>size</var> is an integer, <code>0</code> is an integer, and <code>&lt;</code> is a numerical operator. The result of the expression <code>size &lt; 0</code> is always a boolean. You can test this yourself in the Python interactive shell:
@@ -365,5 +366,5 @@ KeyError: 'db.diveintopython3.org'</samp></pre>
<li>...etc...
</ul>
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>
+4 -3
View File
@@ -14,7 +14,7 @@ h3:before{counter-increment:h3;content:"A." counter(h2) "." counter(h3) ". "}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=sa value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#porting-code-to-python-3-with-2to3>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Porting code to Python 3 with <code>2to3</code></h1>
<blockquote class=q>
<p><span>&#x275D;</span> Life is pleasant. Death is peaceful. It&#8217;s the transition that&#8217;s troublesome. <span>&#x275E;</span><br>&mdash; Isaac Asimov (attributed)
@@ -81,6 +81,7 @@ h3:before{counter-increment:h3;content:"A." counter(h2) "." counter(h3) ". "}
<p class=fancy>Virtually all Python 2 programs will need at least some tweaking to run properly under Python 3. To help with this transition, Python 3 comes with a utility script called <code>2to3</code>, which takes your actual Python 2 source code as input and auto-converts as much as it can to Python 3. <a href=case-study-porting-chardet-to-python-3.html#running2to3>Case study: porting <code>chardet</code> to Python 3</a> describes how to run the <code>2to3</code> script, then shows some things it can't fix automatically. This appendix documents what it <em>can</em> fix automatically.
<h2 id=print><code>print</code> statement</h2>
<p>In Python 2, <code>print</code> was a statement. Whatever you wanted to print simply followed the <code>print</code> keyword. In Python 3, <code>print()</code> is a function &mdash; whatever you want to print is passed to <code>print()</code> like any other function.
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<p class=skip><a href=#skipcompareprint>skip over this table</a>
<table id=compareprint>
<tr>
@@ -1272,5 +1273,5 @@ do_stuff(a_list)</code></pre></td></tr>
<p id=skipcompareidioms>
<p>FIXME: once the rest of the book is written, this appendix should contain copious links back to any chapter or section that touches on these features.
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
ssh diveintomark.org "hg -R /home/mark/db/diveintopython3/ serve --stdio" &
pid=$!
hg push ssh://mark@diveintomark.org//home/mark/db/diveintopython3/
kill "$pid"
+4 -3
View File
@@ -12,7 +12,7 @@ body{counter-reset:h1 4}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=root value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#regular-expressions>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Regular expressions</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Some people, when confronted with a problem, think &#8220;I know, I&#8217;ll use regular expressions.&#8221; Now they have two problems. <span>&#x275E;</span><br>&mdash; <cite>Jamie Zawinski</cite>
@@ -42,6 +42,7 @@ body{counter-reset:h1 4}
</blockquote>
<h2 id=streetaddresses>Case study: street addresses</h2>
<p>This series of examples was inspired by a real-life problem I had in my day job several years ago, when I needed to scrub and standardize street addresses exported from a legacy system before importing them into a newer system. (See, I don&#8217;t just make this stuff up; it&#8217;s actually useful.) This example shows how I approached the problem.
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<pre class=screen>
<samp class=prompt>>>> </samp><kbd>s = '100 NORTH MAIN ROAD'</kbd>
<a><samp class=prompt>>>> </samp><kbd>s.replace('ROAD', 'RD.')</kbd> <span>&#x2460;</span></a>
@@ -431,5 +432,5 @@ body{counter-reset:h1 4}
</ul>
<p>Regular expressions are extremely powerful, but they are not the correct solution for every problem. You should learn enough about them to know when they are appropriate, when they will solve your problems, and when they will cause more problems than they solve.
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>
+4 -3
View File
@@ -4,7 +4,8 @@ This program is part of "Dive Into Python 3", a free Python book for
experienced programmers. Visit http://diveintopython3.org/ for the
latest version.
"""
class OutOfRangeError(ValueError): pass
class OutOfRangeError(ValueError):
pass
roman_numeral_map = (('M', 1000),
('CM', 900),
@@ -22,8 +23,8 @@ roman_numeral_map = (('M', 1000),
def to_roman(n):
"""convert integer to Roman numeral"""
# if n > 3999:
# raise OutOfRangeError("number out of range (must be less than 3999)")
if n > 3999:
raise OutOfRangeError("number out of range (must be less than 3999)")
result = ""
for numeral, integer in roman_numeral_map:
+9 -8
View File
@@ -26,7 +26,7 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
<li>Python from source
<li>The interactive shell
</ol>
<li><a href=your-first-python-program.html>Your first Python program</a>
<li id=your-first-python-program><a href=your-first-python-program.html>Your first Python program</a>
<ol>
<li><a href=your-first-python-program.html#divingin>Diving in</a>
<li><a href=your-first-python-program.html#declaringfunctions>Declaring functions</a>
@@ -45,7 +45,7 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
<li><a href=your-first-python-program.html#runningscripts>Running scripts</a>
<li><a href=your-first-python-program.html#furtherreading>Further reading</a>
</ol>
<li><a href=native-datatypes.html>Native Python datatypes</a>
<li id=native-python-datatypes><a href=native-datatypes.html>Native Python datatypes</a>
<ol>
<li><a href=native-datatypes.html#divingin>Diving in</a>
<li><a href=native-datatypes.html#booleans>Booleans</a>
@@ -74,7 +74,7 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
<li>Byte streams
<li>Summary
</ol>
<li><a href=regular-expressions.html>Regular expressions</a>
<li id=regular-expressions><a href=regular-expressions.html>Regular expressions</a>
<ol>
<li><a href=regular-expressions.html#divingin>Diving in</a>
<li><a href=regular-expressions.html#streetaddresses>Case study: street addresses</a>
@@ -123,11 +123,12 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
<li>...mention why from module import * is only allowed at module level
</ol>
</ol>
<li><a href=unit-testing.html>Unit testing</a>
<li id=unit-testing><a href=unit-testing.html>Unit testing</a>
<ol>
<li><a href=unit-testing.html#divingin>(Not) diving in</a>
<li><a href=unit-testing.html#romantest1><code>romantest1.py</code></a>
<li><a href=unit-testing.html#romantest2><code>romantest2.py</code></a>
<li><a href=unit-testing.html#romantest1>A single question</a>
<li><a href=unit-testing.html#romantest2>&#8220;Halt and catch fire&#8221;</a>
<li><a href=unit-testing.html#romantest3>More halting, more fire</a>
<li>...
</ol>
<li>Test-first programming
@@ -248,7 +249,7 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
<li>PyPy
<li>Stackless Python
</ol>
<li><a href=case-study-porting-chardet-to-python-3.html>Case study: porting <code>chardet</code> to Python 3</a>
<li id=case-study-porting-chardet-to-python-3><a href=case-study-porting-chardet-to-python-3.html>Case study: porting <code>chardet</code> to Python 3</a>
<ol>
<li><a href=case-study-porting-chardet-to-python-3.html#divingin>Introducing <code class=filename>chardet</code>: a mini-<abbr>FAQ</abbr></a>
<ol>
@@ -278,7 +279,7 @@ ul li ol{margin:0;padding:0 0 0 2.5em}
</ol>
</ol>
<ul>
<li><a href=porting-code-to-python-3-with-2to3.html>Appendix A. Porting code to Python 3 with <code class=filename>2to3</code></a>
<li id=porting-code-to-python-3-with-2to3><a href=porting-code-to-python-3-with-2to3.html>Appendix A. Porting code to Python 3 with <code class=filename>2to3</code></a>
<ol>
<li><a href=porting-code-to-python-3-with-2to3.html#divingin>Diving in</a>
<li><a href=porting-code-to-python-3-with-2to3.html#print><code>print</code> statement</a>
+106 -16
View File
@@ -12,15 +12,16 @@ body{counter-reset:h1 7}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=root value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#unit-testing>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Unit testing</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Certitude is not the test of certainty. We have been cocksure of many things that were not so. <span>&#x275E;</span><br>&mdash; <cite>Oliver Wendell Holmes, Jr.</cite>
</blockquote>
<ol>
<li><a href=#divingin>(Not) diving in</a>
<li><a href=#romantest1><code>romantest1.py</code></a>
<li><a href=#romantest2><code>romantest2.py</code></a>
<li><a href=#romantest1>A single question</a>
<li><a href=#romantest2>&#8220;Halt and catch fire&#8221;</a>
<li><a href=#romantest3>More halting, more fire</a>
<li>...
</ol>
<h2 id=divingin>(Not) diving in</h2>
@@ -46,7 +47,7 @@ body{counter-reset:h1 7}
<li>When maintaining code, it helps you cover your ass when someone comes screaming that your latest change broke their old code. (&#8220;But <em>sir</em>, all the unit tests passed when I checked it in...&#8221;)
<li>When writing code in a team, it increases confidence that the code you're about to commit isn't going to break someone else's code, because you can run their unit tests first. (I've seen this sort of thing in code sprints. A team breaks up the assignment, everybody takes the specs for their task, writes unit tests for it, then shares their unit tests with the rest of the team. That way, nobody goes off too far into developing code that doesn't play well with others.)
</ul>
<h2 id=romantest1><code>romantest1.py</code></h2>
<h2 id=romantest1>A single question</h2>
<p>A test case answers a single question about the code it is testing. A test case should be able to...
<ul>
<li>...run completely by itself, without any human input. Unit testing is about automation.
@@ -58,6 +59,7 @@ body{counter-reset:h1 7}
<li>The <code>to_roman()</code> function should return the Roman numeral representation for all integers <code>1</code> to <code>3999</code>.
</ol>
<p>It is not immediately obvious how this code does&hellip; well, <em>anything</em>. It defines a class which has no <code>__init__()</code> method. The class <em>does</em> have another method, but it is never called. The entire script has a <code>__main__</code> block, but it doesn't reference the class or its method. But it does do something, I promise.
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<p class=download>[<a href=romantest1.py>download <code>romantest1.py</code></a>]
<pre><code>import roman1
import unittest
@@ -224,30 +226,118 @@ OK</samp></pre>
<li>Hooray! The <code>to_roman()</code> function passes the &#8220;known values&#8221; test case. It's not comprehensive, but it does put the function through its paces with a variety of inputs, including inputs that produce every single-character Roman numeral, the largest possible input (<code>3999</code>), and the input that produces the longest possible Roman numeral (<code>3888</code>). At this point, you can be reasonably confident that the function works for any good input value you could throw at it.
</ol>
<p>&#8220;Good&#8221; input? Hmm. What about bad input?
<h2 id=romantest2><code>romantest2.py</code></h2>
<h2 id=romantest2>&#8220;Halt and catch fire&#8221;</h2>
<p>It is not enough to test that functions succeed when given good input; you must also test that they fail when given bad input. And not just any sort of failure; they must fail in the way you expect.
<pre class=screen>
<samp class=prompt>>>> </samp><kbd>import roman1</kbd>
<a><samp class=prompt>>>> </samp><kbd>roman1.to_roman(4000)</kbd> <span>&#x2460;</span></a>
<samp class=prompt>>>> </samp><kbd>roman1.to_roman(4000)</kbd>
<samp>'MMMM'</samp>
<samp class=prompt>>>> </samp><kbd>roman1.to_roman(5000)</kbd>
<samp>'MMMMM'</samp>
<samp class=prompt>>>> </samp><kbd>roman1.to_roman(9999)</kbd>
<samp>'MMMMMMMMMCMXCIX'</samp></pre>
<a><samp class=prompt>>>> </samp><kbd>roman1.to_roman(9000)</kbd> <span>&#x2460;</span></a>
<samp>'MMMMMMMMM'</samp></pre>
<ol>
<li>FIXME
<li>That's definitely not what you wanted &mdash; that's not even a valid Roman numeral! In fact, each of these numbers is outside the range of acceptable input, but the function returns a bogus value anyway. Silently returning bad values is <em>baaaaaaad</em>; if a program is going to fail, it is far better that it fail quickly and noisily. &#8220;Halt and catch fire,&#8221; as the saying goes. The Pythonic way to halt and catch fire is to raise an exception.
</ol>
<p>The question to ask yourself is, &#8220;How can I express this as a testable requirement?&#8221; How's this for starters:
<blockquote>
<p>The <code>to_roman()</code> function should fail when given an integer greater than <code>3999</code>.
<p>The <code>to_roman()</code> function should raise an <code>OutOfRangeError</code> when given an integer greater than <code>3999</code>.
</blockquote>
<p>What would that test look like?
<p class=download>[<a href=romantest2.py>download <code>romantest2.py</code></a>]
<pre><code>class ToRomanBadInput(unittest.TestCase):
def test_too_large(self):
<pre><code>
<a>class ToRomanBadInput(unittest.TestCase): <span>&#x2460;</span></a>
<a> def test_too_large(self): <span>&#x2461;</span></a>
"""to_roman should fail with large input"""
self.assertRaises(roman2.OutOfRangeError, roman2.to_roman, 4000)</code></pre>
<!-- FIXME callouts -->
<a> self.assertRaises(roman2.OutOfRangeError, roman2.to_roman, 4000) <span>&#x2462;</span></a></code></pre>
<ol>
<li>Like the previous test case, you create a class that inherits from <code>unittest.TestCase</code>. You can have more than one test per class (as you'll see later in this chapter), but I chose to create a new class here because this test is something different than the last one. We'll keep all the good input tests together in one class, and all the bad input tests together in another.
<li>Like the previous test case, the test itself is a method of the class, with a name starting with <code>test</code>.
<li>The <code>unittest.TestCase</code> class provides the <code>assertRaises</code> method, which takes the following arguments: the exception you're expecting, the function you're testing, and the arguments you're passing to that function. (If the function you're testing takes more than one argument, pass them all to <code>assertRaises</code>, in order, and it will pass them right along to the function you're testing.)
</ol>
<p>Pay close attention to this last line of code. Instead of calling <code>to_roman()</code> directly and manually checking that it raises a particular exception (by wrapping it in a <code>try...except</code> block [FIXME xref]), the <code>assertRaises</code> method has encapsulated all of that for us. All you do is tell it what exception you're expecting (<code>roman2.OutOfRangeError</code>), the function (<code>to_roman()</code>), and the function's arguments (<code>4000</code>). The <code>assertRaises</code> method takes care of calling <code>to_roman()</code> and checking that it raises <code>roman2.OutOfRangeError</code>.
<p>Also note that you're passing the <code>to_roman()</code> function itself as an argument; you're not calling it, and you're not passing the name of it as a string. Have I mentioned recently how handy it is that <a href="your-first-python-program.html#everythingisanobject">everything in Python is an object</a>?
<p>So what happens when you run the test suite with this new test?
<pre class=screen>
<samp class=prompt>you@localhost:~$ </samp><kbd>python3 romantest2.py -v</kbd>
<samp>to_roman should give known result with known input ... ok
<a>to_roman should fail with large input ... ERROR <span>&#x2460;</span></a>
======================================================================
ERROR: to_roman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last):
File "romantest2.py", line 78, in test_too_large
self.assertRaises(roman2.OutOfRangeError, roman2.to_roman, 4000)
<a>AttributeError: 'module' object has no attribute 'OutOfRangeError' <span>&#x2461;</span></a>
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=1)</samp></pre>
<ol>
<li>You should have expected this to fail (since you haven't written any code to pass it yet), but... it didn't actually &#8220;fail,&#8221; it had an &#8220;error&#8221; instead. This is a subtle but important distinction. A unit test actually has <em>three</em> return values: pass, fail, and error. Pass, of course, means that the test passed &mdash; the code did what you expected. &#8220;Fail&#8221; is what the previous test case did (until you wrote code to make it pass) &mdash; it executed the code but the result was not what you expected. &#8220;Error&#8221; means that the code didn't even execute properly.
<li>Why didn't the code execute properly? The traceback gives the answer: the module you're testing doesn't have an exception called <code>OutOfRangeError</code>. Remember, you passed this exception to the <code>assertRaises()</code> method, because it's the exception you want the function to raise given an out-of-range input. But the exception doesn't exist, so the call to the <code>assertRaises()</code> method failed. It never got a chance to test the <code>to_roman()</code> function; it didn't get that far.
</ol>
<p>To solve this problem, you need to define the <code>OutOfRangeError</code> exception in <code>roman2.py</code>.
<pre><code><a>class OutOfRangeError(ValueError): <span>&#x2460;</span></a>
<a> pass <span>&#x2461;</span></a></code></pre>
<ol>
<li>Exceptions are classes. An &#8220;out of range&#8221; error is a kind of value error &mdash; the argument value is out of its acceptable range. So this exception inherits from the built-in <code>ValueError</code> exception. This is not strictly necessary (it could just inherit from the base <code>Exception</code> class), but it feels right.
<li>Exceptions don't actually do anything, but you need at least one line of code to make a class. Calling <code>pass</code> does precisely nothing, but it's a line of Python code, so that makes it a class.
</ol>
<p>Now run the test suite again.
<pre class=screen>
<samp class=prompt>you@localhost:~$ </samp><kbd>python3 romantest2.py -v</kbd>
to_roman should give known result with known input ... ok
<a>to_roman should fail with large input ... FAIL <span>&#x2460;</span></a>
======================================================================
FAIL: to_roman should fail with large input
----------------------------------------------------------------------
Traceback (most recent call last):
File "romantest2.py", line 78, in test_too_large
self.assertRaises(roman2.OutOfRangeError, roman2.to_roman, 4000)
<a>AssertionError: OutOfRangeError not raised by to_roman <span>&#x2461;</span></a>
----------------------------------------------------------------------
Ran 2 tests in 0.016s
FAILED (failures=1)</samp></pre>
<ol>
<li>The new test is still not passing, but it's not returning an error either. Instead, the test is failing. That's progress! It means the call to the <code>assertRaises()</code> method succeeded this time, and the unit test framework actually tested the <code>to_roman()</code> function.
<li>Of course, the <code>to_roman()</code> function isn't raising the <code>OutOfRangeError</code> exception you just defined, because you haven't told it to do that yet. That's excellent news! It means this is a valid test case &mdash; it fails before you write the code to make it pass.
</ol>
<p>Now you can write the code to make this test pass.
<p class=download>[<a href=roman2.py>download <code>roman2.py</code></a>]
<pre><code>def to_roman(n):
"""convert integer to Roman numeral"""
if n > 3999:
<a> raise OutOfRangeError("number out of range (must be less than 3999)") <span>&#x2460;</span></a>
result = ""
for numeral, integer in roman_numeral_map:
while n >= integer:
result += numeral
n -= integer
return result</code></pre>
<ol>
<li>This is straightforward: if the given input (<var>n</var>) is greater than <code>3999</code>, raise an <code>OutOfRangeError</code> exception. The unit test does not check the human-readable string that accompanies the exception, although you could write another test that did check it (but watch out for internationalization issues for strings that vary by the user's language or environment).
</ol>
<p>Does this make the test pass? Let's find out.
<pre class=screen>
<samp class=prompt>you@localhost:~$ </samp><kbd>python3 romantest2.py -v</kbd>
<samp>to_roman should give known result with known input ... ok
<a>to_roman should fail with large input ... ok <span>&#x2460;</span></a>
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK</samp></pre>
<ol>
<li>Hooray! Both tests pass. Because you worked iteratively, bouncing back and forth between testing and coding, you can be sure that the two lines of code you just wrote were the cause of that one test going from &#8220;fail&#8221; to &#8220;pass.&#8221; That kind of confidence doesn't come cheap, but it will pay for itself over the lifetime of your code.
</ol>
<h2 id=romantest3>More halting, more fire</h2>
<p>...
<!--
For instance, the <code>testFromRomanCase</code> method (&#8220;<code>from_roman()</code> should only accept uppercase input&#8221;) was an error, because the call to <code>numeral.upper()</code> raised an <code>AttributeError</code> exception, because <code>to_roman()</code> was supposed to return a string but didn't. But <code>testZero</code> (&#8220;<code>to_roman()</code> should fail with 0 input&#8221;) was a failure, because the call to <code>from_roman()</code> did not raise the <code>InvalidRomanNumeral</code> exception that <code>assertRaises</code> was looking for.
@@ -274,5 +364,5 @@ For instance, the <code>testFromRomanCase</code> method (&#8220;<code>from_roman
</ol>
-->
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>
+4 -3
View File
@@ -12,7 +12,7 @@ body{counter-reset:h1 1}
</head>
<p class=skip><a href=#divingin>skip to main content</a>
<form action=http://www.google.com/cse id=search><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=31>&nbsp;<input type=submit name=sa value=Search></div></form>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html>Dive Into Python 3</a> <span>&#8227;</span>
<p class=nav>You are here: <a href=/>Home</a> <span>&#8227;</span> <a href=table-of-contents.html#your-first-python-program>Dive Into Python 3</a> <span>&#8227;</span>
<h1>Your first Python program</h1>
<blockquote class=q>
<p><span>&#x275D;</span> Don&#8217;t bury your burden in saintly silence. You have a problem? Great. Rejoice, dive in, and investigate. <span>&#x275E;</span><br>&mdash; <cite>Ven. Henepola Gunararatana</cite>
@@ -40,6 +40,7 @@ body{counter-reset:h1 1}
</ol>
<h2 id=divingin>Diving in</h2>
<p class=fancy>Books about programming usually start with a bunch of boring chapters about fundamentals and eventually work up to building something useful. Let's skip all that. Here is a complete, working Python program. It probably makes absolutely no sense to you. Don't worry about that, because you're going to dissect it line by line. But read through it first and see what, if anything, you can make of it.
<p id=noscript>[The code examples will be easier to follow if you enable Javascript, but whatever.]
<p class=download>[<a href=humansize.py>download <code>humansize.py</code></a>]</p>
<pre><code>SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
@@ -239,5 +240,5 @@ if __name__ == "__main__":
<li><a href=http://docs.python.org/3.0/reference/><cite>Python Reference Manual</cite></a> explains what it means to say that <a href=http://docs.python.org/3.0/reference/datamodel.html#objects-values-and-types>everything in Python is an object</a>, because some people are pedantic and like to discuss that sort of thing at great length.
</ul>
<p class=c>&copy; 2001&ndash;4, 2009 <span>&#x2133;</span>ark Pilgrim, <a href=http://creativecommons.org/licenses/by-sa/3.0/ rel=license>CC-BY-SA-3.0</a>
<script type=text/javascript src=jquery.js></script>
<script type=text/javascript src=dip3.js></script>
<script src=jquery.js></script>
<script src=dip3.js></script>