Files
dive-into-python3/packaging.html
T
2009-07-30 05:39:31 -04:00

480 lines
25 KiB
HTML

<!DOCTYPE html>
<head>
<meta charset=utf-8>
<title>Packaging Python Libraries - Dive into Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 17}
mark{display:inline}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
</head>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8>&nbsp;<input name=q size=25>&nbsp;<input type=submit name=root value=Search></div></form>
<p>You are here: <a href=index.html>Home</a> <span class=u>&#8227;</span> <a href=table-of-contents.html#packaging>Dive Into Python 3</a> <span class=u>&#8227;</span>
<p id=level>Difficulty level: <span class=u title=advanced>&#x2666;&#x2666;&#x2666;&#x2666;&#x2662;</span>
<h1>Packaging Python Libraries</h1>
<blockquote class=q>
<p><span class=u>&#x275D;</span> FIXME <span class=u>&#x275E;</span><br>&mdash; FIXME
</blockquote>
<p id=toc>&nbsp;
<h2 id=divingin>Diving In</h2>
<p class=f>So you want to release a Python script, library, framework, or application. Excellent. The world needs more Python code.
<p>Python 3 comes with a packaging framework called Distutils. Distutils is many things: a build tool (for you), an installation tool (for your users), a package metadata format (for search engines), and more. It integrates with the <a href=http://pypi.python.org/>Python Package Index</a> (&#8220;PyPI&#8221;), a central repository for open source Python libraries.
<p>All of these facets of Distutils center around the <i>setup script</i>, traditionally called <code>setup.py</code>. In fact, you&#8217;ve already seen several Distutils setup scripts in this book: <a href=http-web-services.html#introducing-httplib2>you used one to install <code>httplib2</code></a> in &#8220;HTTP Web Services,&#8221; and <a href=case-study-porting-chardet-to-python-3.html>another to install <code>chardet</code></a> in &#8220;Case Study: Porting <code>chardet</code> to Python 3.&#8221;
<p>In this chapter, you&#8217;ll learn how the setup script for <code>chardet</code> works and step through the process of releasing your own Python software.
<pre><code class=pp># chardet's setup.py
from distutils.core import setup
setup(
name = "chardet",
packages = ["chardet"],
version = "1.0.1",
description = "Universal encoding detector",
author = "Mark Pilgrim",
author_email = "mark@diveintomark.org",
url = "http://chardet.feedparser.org/",
download_url = "http://chardet.feedparser.org/download/python3-chardet-1.0.1.tgz",
keywords = ["encoding", "i18n", "xml"],
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
"Environment :: Other Environment",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Text Processing :: Linguistic",
],
long_description = """\
Universal character encoding detector
-------------------------------------
Detects
- ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants)
- Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese)
- EUC-JP, SHIFT_JIS, ISO-2022-JP (Japanese)
- EUC-KR, ISO-2022-KR (Korean)
- KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic)
- ISO-8859-2, windows-1250 (Hungarian)
- ISO-8859-5, windows-1251 (Bulgarian)
- windows-1252 (English)
- ISO-8859-7, windows-1253 (Greek)
- ISO-8859-8, windows-1255 (Visual and Logical Hebrew)
- TIS-620 (Thai)
This version requires Python 3 or later; a Python 2 version is available separately.
"""
)</code></pre>
<blockquote class=note>
<p><span class=u>&#x261E;</span><code>chardet</code> and <code>httplib2</code> are open source, but there&#8217;s no requirement that you release your own Python libraries under any particular license. The process described in this chapter will work for any Python software, regardless of license.
</blockquote>
<p class=a>&#x2042;
<h2 id=cantdo>Things Distutils Can&#8217;t Do For You</h2>
<p>Releasing your first Python package is a daunting process. (Releasing your second one is easier.) Distutils tries to automate as much of it as possible, but there are some things you simply must do yourself.
<ul>
<li><b>Choose a license</b>. This is a complicated topic, fraught with politics and peril. If you wish to release your software as open source, I humbly offer five pieces of advice:
<ol>
<li>Don&#8217;t write your own license.
<li>Don&#8217;t write your own license.
<li>Don&#8217;t write your own license.
<li>It doesn&#8217;t need to be <abbr>GPL</abbr>, but <a href=http://www.dwheeler.com/essays/gpl-compatible.html>it needs to be <abbr>GPL</abbr>-compatible</a>.
<li>Don&#8217;t write your own license.
</ol>
<li><b>Classify your software</b> using the PyPI classification system. I&#8217;ll explain what this means later in this chapter.
<li><b>Write a &#8220;read me&#8221; file</b>. Don&#8217;t skimp on this. At a minimum, it should give your users an overview of what your software does and how to install it.
</ul>
<p class=a>&#x2042;
<h2 id=structure>Directory Structure</h2>
<p>To start packaging your Python software, you need to get your files and directories in order. The <code>httplib2</code> directory looks like this:
<pre class=screen>
<a>httplib2/ <span class=u>&#x2460;</span></a>
|
<a>+--README.txt <span class=u>&#x2461;</span></a>
|
<a>+--setup.py <span class=u>&#x2462;</span></a>
|
<a>+--httplib2/ <span class=u>&#x2463;</span></a>
|
+--__init__.py
|
+--iri2uri.py</pre>
<ol>
<li>Make a root directory to hold everything. Give it the same name as your Python module.
<li>To accomodate Windows users, your &#8220;read me&#8221; file should include a <code>.txt</code> extension, and it should use Windows-style carriage returns. Just because <em>you</em> use a fancy text editor that runs from the command line and includes its own macro language, that doesn&#8217;t mean you need to make life difficult for your users. (Your users use Notepad. Sad but true.) Even if you&#8217;re on Linux or Mac OS X, your fancy text editor undoubtedly has an option to save files with Windows-style carriage returns.
<li>Your Distutils setup script should be named <code>setup.py</code> unless you have a good reason not to. You do not have a good reason not to.
<li>If your Python software is a single <code>.py</code> file, you should put it in the root directory along with your &#8220;read me&#8221; file and your setup script. If it&#8217;s a multi-file module (<i>i.e.</i> a directory with a main <code>__init__.py</code> script), like <code>httplib2</code>, you should put the entire directory here. Yes, that means you&#8217;ll have an <code>httplib2/</code> directory within an <code>httplib2/</code> directory. Trust me, that&#8217;s not a problem. In fact, any other arrangement would be a problem.
</ol>
<p>The <code>chardet</code> directory looks slightly different. In addition to the <code>README.txt</code> file, it has <abbr>HTML</abbr>-formatted documentation in the <code>docs/</code> directory. The <code>docs/</code> directory contains several <code>.html</code> files and an <code>images/</code> subdirectory, which contains several <code>.png</code> and <code>.gif</code> files. (This will be important later.) Also, in keeping with the convention for <abbr>(L)GPL</abbr>-licensed software, it has a separate file called <code>COPYING</code> which contains the complete text of the <abbr>LGPL</abbr>.
<pre class=nd><code>
chardet/
|
+--COPYING
|
+--setup.py
|
+--README.txt
|
+--docs/
| |
| +--index.html
| |
| +--usage.html
| |
| +--images/ ...
|
+--chardet/
|
+--__init__.py
|
+--big5freq.py
|
+--...
</code></pre>
<p class=a>&#x2042;
<h2 id=setuppy>Writing Your Setup Script</h2>
<p>The Distutils setup script is a Python script. In theory, it can do anything Python can do. In practice, it should do as little as possible, in as standard a way as possible. Setup scripts should be boring. The more exotic your installation process is, the more exotic your bug reports will be.
<p>The first line of every Distutils setup script is always the same:
<pre class=nd><code class=pp>from distutils.core import setup</code></pre>
<p>This imports the <code>setup()</code> function, which is the main entry point into Distutils. 95% of all Distutils setup scripts consist of a single call to <code>setup()</code> and nothing else. (I totally just made up that statistic, but if your Distutils setup script is doing more than calling the Distutils <code>setup()</code> function, you should have a good reason.)
<p>The <code>setup()</code> function <a href=http://docs.python.org/3.1/distutils/apiref.html#distutils.core.setup>can take dozens of parameters</a>. For the sanity of everyone involved, you must use <a href=your-first-python-program.html#optional-arguments>named arguments</a> for every parameter. This is not merely a convention; it&#8217;s a hard requirement. Your setup script will crash if you try to call the <code>setup()</code> function with non-named arguments.
<p>The following named arguments are required:
<ul>
<li><b>name</b>, the name of the package.
<li><b>version</b>, the version number of the package.
<li><b>author</b>, your full name.
<li><b>author_email</b>, your email address.
<li><b>url</b>, the home page of your project. This can be your <a href=http://pypi.python.org/>PyPI</a> package page if you don&#8217;t have a separate project website.
</ul>
<p>Although not required, I recommend that you also include the following in your setup script:
<ul>
<li><b>description</b>, a one-line summary of the project.
<li><b>long_description</b>, a multi-line string in <a href=http://docutils.sourceforge.net/rst.html>reStructuredText format</a>. <a href=http://pypi.python.org/>PyPI</a> converts this to <abbr>HTML</abbr> and displays it on your package page.
<li><b>classifiers</b>, a list of specially-formatted strings described in the next section.
</ul>
<blockquote class=note>
<p><span class=u>&#x261E;</span>Setup script metadata is defined in <a href=http://www.python.org/dev/peps/pep-0314/><abbr>PEP</abbr> 314</a>.
</blockquote>
<p>Now let&#8217;s look at the <code>chardet</code> setup script. It has all of these required and recommended parameters, plus one I haven&#8217;t mentioned yet: <code>packages</code>.
<pre class=nd><code class=pp>from distutils.core import setup
setup(
name = 'chardet',
<mark>packages = ['chardet']</mark>,
version = '1.0.2',
description = 'Universal encoding detector',
author='Mark Pilgrim',
...
)</code></pre>
<p>FIXME
<p class=a>&#x2042;
<h2 id=trove>Classifying Your Package</h2>
<p>The Python Package Index (&#8220;PyPI&#8221;) contains thousands of Python libraries. Proper classification metadata will allow people to find yours more easily.
<p>The <code>classifiers</code> parameter to the Distutils <code>setup()</code> function is a list of strings. These strings are <em>not</em> freeform. All of your classifier strings should come from <a href='http://pypi.python.org/pypi?:action=list_classifiers'>this master list on PyPI</a>.
<blockquote class=note>
<p><span class=u>&#x261E;</span>The Python Package Index lets you <a href='http://pypi.python.org/pypi?:action=browse'>browse packages by classifier</a>. You can even select multiple classifiers to narrow your search. Classifiers are not invisible metadata that you can just ignore!
</blockquote>
<p>You should <em>always</em> include at least these four classifiers:
<ul>
<li><b>Programming Language</b>. In particular, you should include both <code>"Programming Language :: Python"</code> and <code>"Programming Language :: Python :: 3"</code>. If you do not include these, your package will not show up in <a href='http://pypi.python.org/pypi?:action=browse&amp;c=533&amp;show=all'>this list of Python 3-compatible libraries</a>, which linked from the sidebar of every single page of <code>pypi.python.org</code>.
<li><b>License</b>. This is <em>the absolute first thing I look for</em> when I&#8217;m evaluating third-party libraries. Don&#8217;t make me hunt for this vital information. Don&#8217;t include more than one license classifier unless your software is explicitly available under multiple licenses (and don&#8217;t release software under multiple licenses unless you&#8217;re forced to do so).
<li><b>Operating System</b>. If your software only runs on Windows (or Mac OS X, or Linux), I want to know sooner rather than later. If your software runs anywhere without any platform-specific code, use the classifier <code>"Operating System :: OS Independent"</code>. Multiple <code>Operating System</code> classifiers are only necessary if your software requires specific support for each platform. (This is not common.)
</ul>
<p>I <em>strongly</em> recommend that you also include the following classifications:
<ul>
<li><b>Development Status</b>. Is your software beta quality? Alpha quality? Pre-alpha? Pick one. Be honest.
<li><b>Framework</b>. Does your software rely on a larger Python framework like <a href=http://www.djangoproject.com/>Django</a> or <a href=http://www.zope.org/>Zope</a>? If not, you can omit this classifier.
<li><b>Intended Audience</b>. Who would download your software? The most common choices are <code>Developers</code>, <code>End Users/Desktop</code>, <code>Science/Research</code>, and <code>System Administrators</code>. Pick one.
<li><b>Topic</b>. There are <a href='http://pypi.python.org/pypi?:action=list_classifiers'>a large number of topics to choose from</a>; choose all that apply.
</ul>
<h3 id=trove-examples>Examples of Good Package Classifiers</h3>
<p>By way of example, here are the classifiers for <a href=http://pypi.python.org/pypi/Django/>Django</a>, a production-ready, cross-platform, <abbr>BSD</abbr>-licensed content management system that runs on your web server. (Django is not yet compatible with Python 3, so the <code>Programming Language :: Python :: 3</code> classifier is not listed.)
<pre><code>Programming Language :: Python
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Framework :: Django
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Internet :: WWW/HTTP :: WSGI
Topic :: Software Development :: Libraries :: Python Modules</code></pre>
<p>Here are the classifiers for <a href=http://pypi.python.org/pypi/chardet><code>chardet</code></a>, the character encoding detection library covered in <a href=case-study-porting-chardet-to-python-3.html>Case Study: Porting <code>chardet</code> to Python 3</a>. <code>chardet</code> is beta quality, cross-platform, Python 3-compatible, <abbr>LGPL</abbr>-licensed, and intended for developers to integrate into their own products.
<pre><code>Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Other Environment
Intended Audience :: Developers
Topic :: Text Processing :: Linguistic
Topic :: Software Development :: Libraries :: Python Modules</code></pre>
<p>And here are the classifiers for <a href=http://pypi.python.org/pypi/httplib2><code>httplib2</code></a>, the <abbr>HTTP</abbr> module I mentioned at the beginning of this chapter. <code>httplib2</code> is beta quality, cross-platform, <abbr>MIT</abbr>-licensed, and intended for Python developers.
<pre><code>Programming Language :: Python
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Development Status :: 4 - Beta
Environment :: Web Environment
Intended Audience :: Developers
Topic :: Internet :: WWW/HTTP
Topic :: Software Development :: Libraries :: Python Modules</code></pre>
<h2 id=manifest>Specifying Additional Files With A Manifest</h2>
<p>FIXME
<pre class=nd><code>include COPYING
recursive-include docs *.html *.png *.gif</code></pre>
<h2 id=check>Checking Your Setup Script for Errors</h2>
<p>FIXME
<pre class=screen>
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd>c:\python31\python.exe setup.py check</kbd>
<samp>running check
warning: check: missing required meta-data: version</samp></pre>
<p>FIXME
<pre class=screen>
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd>c:\python31\python.exe setup.py check</kbd>
<samp>running check</samp></pre>
<p class=a>&#x2042;
<h2 id=sdist>Creating Source Distributions</h2>
<pre>
archive formats?
</pre>
<pre class=screen>
FIXME need to redo this now that we have a MANIFEST.in file
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd><mark>c:\python31\python.exe setup.py sdist</mark></kbd>
<samp>running sdist
running check
reading manifest file 'MANIFEST'
creating chardet-1.0.2
creating chardet-1.0.2\chardet
copying files to chardet-1.0.2...
copying setup.py -> chardet-1.0.2
copying chardet\__init__.py -> chardet-1.0.2\chardet
copying chardet\big5freq.py -> chardet-1.0.2\chardet
...
copying chardet\universaldetector.py -> chardet-1.0.2\chardet
copying chardet\utf8prober.py -> chardet-1.0.2\chardet
creating dist
creating 'dist\chardet-1.0.2.zip' and adding 'chardet-1.0.2' to it
adding 'chardet-1.0.2\PKG-INFO'
adding 'chardet-1.0.2\setup.py'
adding 'chardet-1.0.2\chardet\big5freq.py'
adding 'chardet-1.0.2\chardet\big5prober.py'
...
adding 'chardet-1.0.2\chardet\universaldetector.py'
adding 'chardet-1.0.2\chardet\utf8prober.py'
adding 'chardet-1.0.2\chardet\__init__.py'
removing 'chardet-1.0.2' (and everything under it)</samp>
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd><mark>dir dist</mark></kbd>
<samp> Volume in drive C has no label.
Volume Serial Number is DED5-B4F8
Directory of c:\Users\pilgrim\chardet\dist
07/30/2009 04:47 AM &lt;DIR> .
07/30/2009 04:47 AM &lt;DIR> ..
07/30/2009 04:47 AM 175,367 <mark>chardet-1.0.2.zip</mark>
1 File(s) 175,367 bytes
2 Dir(s) 62,235,222,016 bytes free</samp></pre>
<p class=a>&#x2042;
<h2 id=bdist>Creating Binary Distributions</h2>
<pre>
python3 setup.py bdist --help-formats
</pre>
<pre>
http://docs.python.org/3.1/distutils/builtdist.html#creating-windows-installers
</pre>
<h3 id=wininst>Building A Windows Installer</h3>
<pre class=screen>
FIXME probably need to redo this too
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd><mark>c:\python31\python.exe setup.py bdist_wininst</mark></kbd>
<samp>running bdist_wininst
running build
running build_py
installing to build\bdist.win32\wininst
running install_lib
creating build\bdist.win32\wininst
creating build\bdist.win32\wininst\PURELIB
creating build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5freq.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\big5prober.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\chardistribution.py -> build\bdist.win32\wininst\PURELIB\chardet
...
copying build\lib\chardet\universaldetector.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\utf8prober.py -> build\bdist.win32\wininst\PURELIB\chardet
copying build\lib\chardet\__init__.py -> build\bdist.win32\wininst\PURELIB\chardet
running install_egg_info
Writing build\bdist.win32\wininst\PURELIB\chardet-1.0.2-py3.1.egg-info
creating 'c:\users\pilgrim\appdata\local\temp\tmp58b9n5.zip' and adding '.' to it
adding 'PURELIB\chardet-1.0.2-py3.1.egg-info'
adding 'PURELIB\chardet\big5freq.py'
adding 'PURELIB\chardet\big5prober.py'
adding 'PURELIB\chardet\chardistribution.py'
...
adding 'PURELIB\chardet\universaldetector.py'
adding 'PURELIB\chardet\utf8prober.py'
adding 'PURELIB\chardet\__init__.py'
removing 'build\bdist.win32\wininst' (and everything under it)</samp>
<samp class=p>c:\Users\pilgrim\chardet> </samp><kbd><mark>dir dist</mark></kbd>
<samp> Volume in drive C has no label.
Volume Serial Number is DED5-B4F8
Directory of c:\Users\pilgrim\chardet\dist
07/30/2009 03:52 AM &lt;DIR> .
07/30/2009 03:52 AM &lt;DIR> ..
07/30/2009 03:52 AM 371,236 <mark>chardet-1.0.2.win32.exe</mark>
2 File(s) 546,603 bytes
2 Dir(s) 62,235,119,616 bytes free</samp></pre>
<pre>
works on non-Windows (as long as the package is pure-Python)
</pre>
<pre>
UAC?
</pre>
<h3 id=rpm>Building a Linux RPM Package</h3>
<p class=a>&#x2042;
<h2 id=pypi>Adding Your Package to The Python Package Index</h2>
<h3 id=register-user>Registering Yourself</h3>
<h3 id=register-package>Registering Your Package</h3>
<h3 id=upload>Uploading New Versions</h3>
<pre>
@jessenoller sez:
* distutils, how to make a setup.py (and include data files, tests, docs, etc for a project)
* how to upload it to pypi properly (label it for python 3 for the love of pete)
* how to build build bdist/RPMs/DEBs.
* If you can - and I've forgotten how much distutils supports of this, cover dependency management.
* Oh, I almost forgot - cover the Per-User Site packages stuff, PEP 370
</pre>
<p class=a>&#x2042;
<h2 id=future>The Many Possible Futures of Python Packaging</h2>
<p>Distutils is not the be-all and end-all of Python packaging, but as of this writing (August 2009), it&#8217;s the only packaging framework that works in Python 3. There are a number of other frameworks for Python 2; some focus on installation, others on testing and deployment. Some or all of these may end up being ported to Python 3 in the future.
<p>These frameworks focus on installation:
<ul>
<li><a href=http://pypi.python.org/pypi/setuptools>Setuptools</a>
<li><a href=http://pypi.python.org/pypi/pip>Pip</a>
<li><a href=http://bitbucket.org/tarek/distribute/src/tip/README.txt>Distribute</a>
</ul>
<p>These focus on testing and deployment:
<ul>
<li><a href=http://pypi.python.org/pypi/virtualenv><code>virtualenv</code></a>
<li><a href=http://pypi.python.org/pypi/zc.buildout><code>zc.buildout</code></a>
<li><a href=http://www.blueskyonmars.com/projects/paver/>Paver</a>
<li><a href=http://fabfile.org/>Fabric</a>
<li><a href=http://www.py2exe.org/><code>py2exe</code></a>
</ul>
<p class=a>&#x2042;
<h2 id=furtherreading>Further Reading</h2>
<p>On Distutils:
<ul>
<li><a href=http://docs.python.org/3.1/distutils/>Distributing Python Modules with Distutils</a>
<li><a href=http://docs.python.org/3.1/distutils/apiref.html#module-distutils.core>Core Distutils functionality</a> lists all the possible arguments to the <code>setup()</code> function
<li><a href=http://wiki.python.org/moin/Distutils/Cookbook>Distutils Cookbook</a>
<li><a href=http://www.python.org/dev/peps/pep-0370/><abbr>PEP</abbr> 370: Per user <code>site-packages</code> directory</a>
<li><a href=http://jessenoller.com/2009/07/19/pep-370-per-user-site-packages-and-environment-stew/><abbr>PEP</abbr> 370 and &#8220;environment stew&#8221;</a>
</ul>
<p>On other packaging frameworks:
<ul>
<li><a href=http://groups.google.com/group/django-developers/msg/5407cdb400157259>The Python packaging ecosystem</a>
<li><a href=http://www.b-list.org/weblog/2008/dec/14/packaging/>On packaging</a>
<li><a href=http://blog.ianbicking.org/2008/12/14/a-few-corrections-to-on-packaging/>A few corrections to &#8220;On packaging&#8221;</a>
<li><a href=http://www.b-list.org/weblog/2008/dec/15/pip/>Why I like Pip</a>
<li><a href=http://cournape.wordpress.com/2009/04/01/python-packaging-a-few-observations-cabal-for-a-solution/>Python packaging: a few observations</a>
<li><a href=http://jacobian.org/writing/nobody-expects-python-packaging/>Nobody expects Python packaging!</a>
</ul>
<p class=v><a rel=prev href=case-study-porting-chardet-to-python-3.html title='back to &#8220;Case Study: Porting chardet to Python 3&#8221;'><span class=u>&#x261C;</span></a> <a rel=next href=where-to-go-from-here.html title='onward to &#8220;Where to Go From Here&#8221;'><span class=u>&#x261E;</span></a>
<p class=c>&copy; 2001&ndash;9 <a href=about.html>Mark Pilgrim</a>
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>