[Python-checkins] distutils2: Backported packaging from cpython default, overwriting previous version.

vinay.sajip python-checkins at python.org
Thu Aug 18 11:12:56 CEST 2011


http://hg.python.org/distutils2/rev/e3ec249ee8bc
changeset:   1120:e3ec249ee8bc
user:        Vinay Sajip <vinay_sajip at yahoo.co.uk>
date:        Thu Aug 18 10:11:00 2011 +0100
summary:
  Backported packaging from cpython default, overwriting previous version.

files:
  distutils2/README                                                                         |    13 -
  distutils2/__init__.py                                                                    |     6 +-
  distutils2/_trove.py                                                                      |  1088 +++---
  distutils2/command/__init__.py                                                            |    10 +-
  distutils2/command/bdist.py                                                               |    29 +-
  distutils2/command/bdist_dumb.py                                                          |    39 +-
  distutils2/command/bdist_msi.py                                                           |   121 +-
  distutils2/command/bdist_wininst.py                                                       |   142 +-
  distutils2/command/build.py                                                               |    10 +-
  distutils2/command/build_clib.py                                                          |    61 +-
  distutils2/command/build_ext.py                                                           |   161 +-
  distutils2/command/build_py.py                                                            |    55 +-
  distutils2/command/build_scripts.py                                                       |    93 +-
  distutils2/command/check.py                                                               |    44 +-
  distutils2/command/clean.py                                                               |    11 +-
  distutils2/command/cmd.py                                                                 |    96 +-
  distutils2/command/command_template                                                       |    13 +-
  distutils2/command/config.py                                                              |   100 +-
  distutils2/command/install_data.py                                                        |    48 +-
  distutils2/command/install_dist.py                                                        |    74 +-
  distutils2/command/install_distinfo.py                                                    |    62 +-
  distutils2/command/install_headers.py                                                     |    12 +-
  distutils2/command/install_lib.py                                                         |    38 +-
  distutils2/command/install_scripts.py                                                     |    23 +-
  distutils2/command/register.py                                                            |   111 +-
  distutils2/command/sdist.py                                                               |   109 +-
  distutils2/command/test.py                                                                |    30 +-
  distutils2/command/upload.py                                                              |   140 +-
  distutils2/command/upload_docs.py                                                         |   110 +-
  distutils2/command/wininst-10.0-amd64.exe                                                 |   Bin 
  distutils2/command/wininst-10.0.exe                                                       |   Bin 
  distutils2/compat.py                                                                      |    20 +-
  distutils2/compiler/__init__.py                                                           |    75 +-
  distutils2/compiler/bcppcompiler.py                                                       |   181 +-
  distutils2/compiler/ccompiler.py                                                          |   192 +-
  distutils2/compiler/cygwinccompiler.py                                                    |    73 +-
  distutils2/compiler/extension.py                                                          |    43 +-
  distutils2/compiler/msvc9compiler.py                                                      |   166 +-
  distutils2/compiler/msvccompiler.py                                                       |   282 +-
  distutils2/compiler/unixccompiler.py                                                      |    45 +-
  distutils2/config.py                                                                      |   200 +-
  distutils2/create.py                                                                      |   689 ++++
  distutils2/database.py                                                                    |   647 ++++
  distutils2/depgraph.py                                                                    |   125 +-
  distutils2/dist.py                                                                        |   190 +-
  distutils2/errors.py                                                                      |    52 +-
  distutils2/fancy_getopt.py                                                                |   180 +-
  distutils2/install.py                                                                     |   391 +-
  distutils2/manifest.py                                                                    |   141 +-
  distutils2/markers.py                                                                     |    69 +-
  distutils2/metadata.py                                                                    |   160 +-
  distutils2/mkcfg.py                                                                       |   657 ----
  distutils2/index/__init__.py                                                              |     6 +-
  distutils2/index/base.py                                                                  |     4 +-
  distutils2/index/dist.py                                                                  |    87 +-
  distutils2/index/errors.py                                                                |    22 +-
  distutils2/index/mirrors.py                                                               |     8 +-
  distutils2/index/simple.py                                                                |   183 +-
  distutils2/index/wrapper.py                                                               |    18 +-
  distutils2/index/xmlrpc.py                                                                |    41 +-
  distutils2/pysetup                                                                        |     5 -
  distutils2/resources.py                                                                   |    25 -
  distutils2/run.py                                                                         |   568 ++-
  distutils2/tests/__init__.py                                                              |    54 +-
  distutils2/tests/__main__.py                                                              |    23 +
  distutils2/tests/fake_dists/babar-0.1.dist-info/INSTALLER                                 |   Bin 
  distutils2/tests/fake_dists/babar-0.1.dist-info/METADATA                                  |     4 +
  distutils2/tests/fake_dists/babar-0.1.dist-info/RECORD                                    |   Bin 
  distutils2/tests/fake_dists/babar-0.1.dist-info/REQUESTED                                 |   Bin 
  distutils2/tests/fake_dists/babar-0.1.dist-info/RESOURCES                                 |     2 +
  distutils2/tests/fake_dists/babar.cfg                                                     |     1 +
  distutils2/tests/fake_dists/babar.png                                                     |   Bin 
  distutils2/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO                                   |     6 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO                              |    18 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt                           |   Bin 
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt                  |     1 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt                      |     3 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe                          |     1 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt                          |     6 +
  distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt                         |   Bin 
  distutils2/tests/fake_dists/cheese-2.0.2.egg-info                                         |     5 +
  distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER                            |   Bin 
  distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA                             |     9 +
  distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD                               |   Bin 
  distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED                            |   Bin 
  distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py                             |     1 +
  distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py                            |    10 +
  distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py                                    |     5 +
  distutils2/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO                         |     5 +
  distutils2/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER                             |   Bin 
  distutils2/tests/fake_dists/grammar-1.0a4.dist-info/METADATA                              |     5 +
  distutils2/tests/fake_dists/grammar-1.0a4.dist-info/RECORD                                |   Bin 
  distutils2/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED                             |   Bin 
  distutils2/tests/fake_dists/grammar-1.0a4/grammar/__init__.py                             |     1 +
  distutils2/tests/fake_dists/grammar-1.0a4/grammar/utils.py                                |     8 +
  distutils2/tests/fake_dists/nut-funkyversion.egg-info                                     |     3 +
  distutils2/tests/fake_dists/strawberry-0.6.egg                                            |   Bin 
  distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER                           |   Bin 
  distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA                            |     7 +
  distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD                              |   Bin 
  distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED                           |   Bin 
  distutils2/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py                       |    18 +
  distutils2/tests/fake_dists/truffles-5.0.egg-info                                         |     3 +
  distutils2/tests/fixer/fix_idioms.py                                                      |    18 +-
  distutils2/tests/pypi_server.py                                                           |    96 +-
  distutils2/tests/pypi_test_server.py                                                      |    59 +
  distutils2/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz |   Bin 
  distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz            |   Bin 
  distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html                   |     2 +-
  distutils2/tests/support.py                                                               |   433 ++-
  distutils2/tests/test_ccompiler.py                                                        |     8 +-
  distutils2/tests/test_command_bdist.py                                                    |     6 +-
  distutils2/tests/test_command_bdist_dumb.py                                               |    36 +-
  distutils2/tests/test_command_bdist_msi.py                                                |     7 +-
  distutils2/tests/test_command_bdist_wininst.py                                            |     9 +-
  distutils2/tests/test_command_build.py                                                    |     6 +-
  distutils2/tests/test_command_build_clib.py                                               |    43 +-
  distutils2/tests/test_command_build_ext.py                                                |   272 +-
  distutils2/tests/test_command_build_py.py                                                 |    34 +-
  distutils2/tests/test_command_build_scripts.py                                            |    23 +-
  distutils2/tests/test_command_check.py                                                    |    78 +-
  distutils2/tests/test_command_clean.py                                                    |    15 +-
  distutils2/tests/test_command_cmd.py                                                      |    31 +-
  distutils2/tests/test_command_config.py                                                   |    23 +-
  distutils2/tests/test_command_install_data.py                                             |    45 +-
  distutils2/tests/test_command_install_dist.py                                             |    52 +-
  distutils2/tests/test_command_install_distinfo.py                                         |    52 +-
  distutils2/tests/test_command_install_headers.py                                          |     5 +-
  distutils2/tests/test_command_install_lib.py                                              |    47 +-
  distutils2/tests/test_command_install_scripts.py                                          |    29 +-
  distutils2/tests/test_command_register.py                                                 |    95 +-
  distutils2/tests/test_command_sdist.py                                                    |   177 +-
  distutils2/tests/test_command_test.py                                                     |   105 +-
  distutils2/tests/test_command_upload.py                                                   |    54 +-
  distutils2/tests/test_command_upload_docs.py                                              |   116 +-
  distutils2/tests/test_compiler.py                                                         |    16 +-
  distutils2/tests/test_config.py                                                           |   248 +-
  distutils2/tests/test_create.py                                                           |   243 +
  distutils2/tests/test_cygwinccompiler.py                                                  |    27 +-
  distutils2/tests/test_database.py                                                         |   674 ++++
  distutils2/tests/test_depgraph.py                                                         |   124 +-
  distutils2/tests/test_dist.py                                                             |   198 +-
  distutils2/tests/test_extension.py                                                        |     2 +-
  distutils2/tests/test_index_dist.py                                                       |   282 -
  distutils2/tests/test_index_simple.py                                                     |   322 --
  distutils2/tests/test_index_xmlrpc.py                                                     |   109 -
  distutils2/tests/test_install.py                                                          |   177 +-
  distutils2/tests/test_manifest.py                                                         |    80 +-
  distutils2/tests/test_markers.py                                                          |    16 +-
  distutils2/tests/test_metadata.py                                                         |    75 +-
  distutils2/tests/test_mixin2to3.py                                                        |    90 +-
  distutils2/tests/test_mkcfg.py                                                            |   221 -
  distutils2/tests/test_msvc9compiler.py                                                    |    43 +-
  distutils2/tests/test_pypi_dist.py                                                        |   285 +
  distutils2/tests/test_pypi_server.py                                                      |    80 +-
  distutils2/tests/test_pypi_simple.py                                                      |   351 ++
  distutils2/tests/test_pypi_xmlrpc.py                                                      |   101 +
  distutils2/tests/test_resources.py                                                        |   174 -
  distutils2/tests/test_run.py                                                              |    40 +-
  distutils2/tests/test_uninstall.py                                                        |    99 +-
  distutils2/tests/test_unixccompiler.py                                                    |    25 +-
  distutils2/tests/test_util.py                                                             |   551 +++-
  distutils2/tests/test_version.py                                                          |    19 +-
  distutils2/tests/xxmodule.c                                                               |   379 --
  distutils2/util.py                                                                        |  1450 ++++++++-
  distutils2/version.py                                                                     |    16 +-
  sysconfig.cfg                                                                             |   111 +
  sysconfig.py                                                                              |   766 +++++
  sysconfig.pyc                                                                             |   Bin 
  sysconfig.pyo                                                                             |   Bin 
  test_distutils2.py                                                                        |     5 +
  171 files changed, 10808 insertions(+), 7160 deletions(-)


diff --git a/distutils2/README b/distutils2/README
deleted file mode 100644
--- a/distutils2/README
+++ /dev/null
@@ -1,13 +0,0 @@
-This directory contains the Distutils2 package.
-
-There's a full documentation available at:
-
-    http://docs.python.org/distutils/
-
-The Distutils-SIG web page is also a good starting point:
-
-    http://www.python.org/sigs/distutils-sig/
-
-WARNING: Distutils2 must remain compatible with Python 2.4
-
-$Id: README 70017 2009-02-27 12:53:34Z tarek.ziade $
diff --git a/distutils2/__init__.py b/distutils2/__init__.py
--- a/distutils2/__init__.py
+++ b/distutils2/__init__.py
@@ -1,12 +1,14 @@
-"""distutils
+"""Support for distutils2, distribution and installation of Python projects.
 
-Third-party tools can use parts of Distutils2 as building blocks
+Third-party tools can use parts of distutils2 as building blocks
 without causing the other modules to be imported:
 
     import distutils2.version
+    import distutils2.metadata
     import distutils2.pypi.simple
     import distutils2.tests.pypi_server
 """
+
 from logging import getLogger
 
 __all__ = ['__version__', 'logger']
diff --git a/distutils2/_trove.py b/distutils2/_trove.py
--- a/distutils2/_trove.py
+++ b/distutils2/_trove.py
@@ -1,4 +1,4 @@
-# Temporary helper for mkcfg.
+"""Temporary helper for create."""
 
 # XXX get the list from PyPI and cache it instead of hardcoding
 
@@ -6,547 +6,547 @@
 # than a list of strings
 
 all_classifiers = [
-    'Development Status :: 1 - Planning',
-    'Development Status :: 2 - Pre-Alpha',
-    'Development Status :: 3 - Alpha',
-    'Development Status :: 4 - Beta',
-    'Development Status :: 5 - Production/Stable',
-    'Development Status :: 6 - Mature',
-    'Development Status :: 7 - Inactive',
-    'Environment :: Console',
-    'Environment :: Console :: Curses',
-    'Environment :: Console :: Framebuffer',
-    'Environment :: Console :: Newt',
-    'Environment :: Console :: svgalib',
-    "Environment :: Handhelds/PDA's",
-    'Environment :: MacOS X',
-    'Environment :: MacOS X :: Aqua',
-    'Environment :: MacOS X :: Carbon',
-    'Environment :: MacOS X :: Cocoa',
-    'Environment :: No Input/Output (Daemon)',
-    'Environment :: Other Environment',
-    'Environment :: Plugins',
-    'Environment :: Web Environment',
-    'Environment :: Web Environment :: Buffet',
-    'Environment :: Web Environment :: Mozilla',
-    'Environment :: Web Environment :: ToscaWidgets',
-    'Environment :: Win32 (MS Windows)',
-    'Environment :: X11 Applications',
-    'Environment :: X11 Applications :: Gnome',
-    'Environment :: X11 Applications :: GTK',
-    'Environment :: X11 Applications :: KDE',
-    'Environment :: X11 Applications :: Qt',
-    'Framework :: BFG',
-    'Framework :: Buildout',
-    'Framework :: Chandler',
-    'Framework :: CubicWeb',
-    'Framework :: Django',
-    'Framework :: IDLE',
-    'Framework :: Paste',
-    'Framework :: Plone',
-    'Framework :: Pylons',
-    'Framework :: Setuptools Plugin',
-    'Framework :: Trac',
-    'Framework :: TurboGears',
-    'Framework :: TurboGears :: Applications',
-    'Framework :: TurboGears :: Widgets',
-    'Framework :: Twisted',
-    'Framework :: ZODB',
-    'Framework :: Zope2',
-    'Framework :: Zope3',
-    'Intended Audience :: Customer Service',
-    'Intended Audience :: Developers',
-    'Intended Audience :: Education',
-    'Intended Audience :: End Users/Desktop',
-    'Intended Audience :: Financial and Insurance Industry',
-    'Intended Audience :: Healthcare Industry',
-    'Intended Audience :: Information Technology',
-    'Intended Audience :: Legal Industry',
-    'Intended Audience :: Manufacturing',
-    'Intended Audience :: Other Audience',
-    'Intended Audience :: Religion',
-    'Intended Audience :: Science/Research',
-    'Intended Audience :: System Administrators',
-    'Intended Audience :: Telecommunications Industry',
-    'License :: Aladdin Free Public License (AFPL)',
-    'License :: DFSG approved',
-    'License :: Eiffel Forum License (EFL)',
-    'License :: Free For Educational Use',
-    'License :: Free For Home Use',
-    'License :: Free for non-commercial use',
-    'License :: Freely Distributable',
-    'License :: Free To Use But Restricted',
-    'License :: Freeware',
-    'License :: Netscape Public License (NPL)',
-    'License :: Nokia Open Source License (NOKOS)',
-    'License :: OSI Approved',
-    'License :: OSI Approved :: Academic Free License (AFL)',
-    'License :: OSI Approved :: Apache Software License',
-    'License :: OSI Approved :: Apple Public Source License',
-    'License :: OSI Approved :: Artistic License',
-    'License :: OSI Approved :: Attribution Assurance License',
-    'License :: OSI Approved :: BSD License',
-    'License :: OSI Approved :: Common Public License',
-    'License :: OSI Approved :: Eiffel Forum License',
-    'License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)',
-    'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)',
-    'License :: OSI Approved :: GNU Affero General Public License v3',
-    'License :: OSI Approved :: GNU Free Documentation License (FDL)',
-    'License :: OSI Approved :: GNU General Public License (GPL)',
-    'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
-    'License :: OSI Approved :: IBM Public License',
-    'License :: OSI Approved :: Intel Open Source License',
-    'License :: OSI Approved :: ISC License (ISCL)',
-    'License :: OSI Approved :: Jabber Open Source License',
-    'License :: OSI Approved :: MIT License',
-    'License :: OSI Approved :: MITRE Collaborative Virtual Workspace License (CVW)',
-    'License :: OSI Approved :: Motosoto License',
-    'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)',
-    'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
-    'License :: OSI Approved :: Nethack General Public License',
-    'License :: OSI Approved :: Nokia Open Source License',
-    'License :: OSI Approved :: Open Group Test Suite License',
-    'License :: OSI Approved :: Python License (CNRI Python License)',
-    'License :: OSI Approved :: Python Software Foundation License',
-    'License :: OSI Approved :: Qt Public License (QPL)',
-    'License :: OSI Approved :: Ricoh Source Code Public License',
-    'License :: OSI Approved :: Sleepycat License',
-    'License :: OSI Approved :: Sun Industry Standards Source License (SISSL)',
-    'License :: OSI Approved :: Sun Public License',
-    'License :: OSI Approved :: University of Illinois/NCSA Open Source License',
-    'License :: OSI Approved :: Vovida Software License 1.0',
-    'License :: OSI Approved :: W3C License',
-    'License :: OSI Approved :: X.Net License',
-    'License :: OSI Approved :: zlib/libpng License',
-    'License :: OSI Approved :: Zope Public License',
-    'License :: Other/Proprietary License',
-    'License :: Public Domain',
-    'License :: Repoze Public License',
-    'Natural Language :: Afrikaans',
-    'Natural Language :: Arabic',
-    'Natural Language :: Bengali',
-    'Natural Language :: Bosnian',
-    'Natural Language :: Bulgarian',
-    'Natural Language :: Catalan',
-    'Natural Language :: Chinese (Simplified)',
-    'Natural Language :: Chinese (Traditional)',
-    'Natural Language :: Croatian',
-    'Natural Language :: Czech',
-    'Natural Language :: Danish',
-    'Natural Language :: Dutch',
-    'Natural Language :: English',
-    'Natural Language :: Esperanto',
-    'Natural Language :: Finnish',
-    'Natural Language :: French',
-    'Natural Language :: German',
-    'Natural Language :: Greek',
-    'Natural Language :: Hebrew',
-    'Natural Language :: Hindi',
-    'Natural Language :: Hungarian',
-    'Natural Language :: Icelandic',
-    'Natural Language :: Indonesian',
-    'Natural Language :: Italian',
-    'Natural Language :: Japanese',
-    'Natural Language :: Javanese',
-    'Natural Language :: Korean',
-    'Natural Language :: Latin',
-    'Natural Language :: Latvian',
-    'Natural Language :: Macedonian',
-    'Natural Language :: Malay',
-    'Natural Language :: Marathi',
-    'Natural Language :: Norwegian',
-    'Natural Language :: Panjabi',
-    'Natural Language :: Persian',
-    'Natural Language :: Polish',
-    'Natural Language :: Portuguese',
-    'Natural Language :: Portuguese (Brazilian)',
-    'Natural Language :: Romanian',
-    'Natural Language :: Russian',
-    'Natural Language :: Serbian',
-    'Natural Language :: Slovak',
-    'Natural Language :: Slovenian',
-    'Natural Language :: Spanish',
-    'Natural Language :: Swedish',
-    'Natural Language :: Tamil',
-    'Natural Language :: Telugu',
-    'Natural Language :: Thai',
-    'Natural Language :: Turkish',
-    'Natural Language :: Ukranian',
-    'Natural Language :: Urdu',
-    'Natural Language :: Vietnamese',
-    'Operating System :: BeOS',
-    'Operating System :: MacOS',
-    'Operating System :: MacOS :: MacOS 9',
-    'Operating System :: MacOS :: MacOS X',
-    'Operating System :: Microsoft',
-    'Operating System :: Microsoft :: MS-DOS',
-    'Operating System :: Microsoft :: Windows',
-    'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier',
-    'Operating System :: Microsoft :: Windows :: Windows 95/98/2000',
-    'Operating System :: Microsoft :: Windows :: Windows CE',
-    'Operating System :: Microsoft :: Windows :: Windows NT/2000',
-    'Operating System :: OS/2',
-    'Operating System :: OS Independent',
-    'Operating System :: Other OS',
-    'Operating System :: PalmOS',
-    'Operating System :: PDA Systems',
-    'Operating System :: POSIX',
-    'Operating System :: POSIX :: AIX',
-    'Operating System :: POSIX :: BSD',
-    'Operating System :: POSIX :: BSD :: BSD/OS',
-    'Operating System :: POSIX :: BSD :: FreeBSD',
-    'Operating System :: POSIX :: BSD :: NetBSD',
-    'Operating System :: POSIX :: BSD :: OpenBSD',
-    'Operating System :: POSIX :: GNU Hurd',
-    'Operating System :: POSIX :: HP-UX',
-    'Operating System :: POSIX :: IRIX',
-    'Operating System :: POSIX :: Linux',
-    'Operating System :: POSIX :: Other',
-    'Operating System :: POSIX :: SCO',
-    'Operating System :: POSIX :: SunOS/Solaris',
-    'Operating System :: Unix',
-    'Programming Language :: Ada',
-    'Programming Language :: APL',
-    'Programming Language :: ASP',
-    'Programming Language :: Assembly',
-    'Programming Language :: Awk',
-    'Programming Language :: Basic',
-    'Programming Language :: C',
-    'Programming Language :: C#',
-    'Programming Language :: C++',
-    'Programming Language :: Cold Fusion',
-    'Programming Language :: Cython',
-    'Programming Language :: Delphi/Kylix',
-    'Programming Language :: Dylan',
-    'Programming Language :: Eiffel',
-    'Programming Language :: Emacs-Lisp',
-    'Programming Language :: Erlang',
-    'Programming Language :: Euler',
-    'Programming Language :: Euphoria',
-    'Programming Language :: Forth',
-    'Programming Language :: Fortran',
-    'Programming Language :: Haskell',
-    'Programming Language :: Java',
-    'Programming Language :: JavaScript',
-    'Programming Language :: Lisp',
-    'Programming Language :: Logo',
-    'Programming Language :: ML',
-    'Programming Language :: Modula',
-    'Programming Language :: Objective C',
-    'Programming Language :: Object Pascal',
-    'Programming Language :: OCaml',
-    'Programming Language :: Other',
-    'Programming Language :: Other Scripting Engines',
-    'Programming Language :: Pascal',
-    'Programming Language :: Perl',
-    'Programming Language :: PHP',
-    'Programming Language :: Pike',
-    'Programming Language :: Pliant',
-    'Programming Language :: PL/SQL',
-    'Programming Language :: PROGRESS',
-    'Programming Language :: Prolog',
-    'Programming Language :: Python',
-    'Programming Language :: Python :: 2',
-    'Programming Language :: Python :: 2.3',
-    'Programming Language :: Python :: 2.4',
-    'Programming Language :: Python :: 2.5',
-    'Programming Language :: Python :: 2.6',
-    'Programming Language :: Python :: 2.7',
-    'Programming Language :: Python :: 3',
-    'Programming Language :: Python :: 3.0',
-    'Programming Language :: Python :: 3.1',
-    'Programming Language :: Python :: 3.2',
-    'Programming Language :: REBOL',
-    'Programming Language :: Rexx',
-    'Programming Language :: Ruby',
-    'Programming Language :: Scheme',
-    'Programming Language :: Simula',
-    'Programming Language :: Smalltalk',
-    'Programming Language :: SQL',
-    'Programming Language :: Tcl',
-    'Programming Language :: Unix Shell',
-    'Programming Language :: Visual Basic',
-    'Programming Language :: XBasic',
-    'Programming Language :: YACC',
-    'Programming Language :: Zope',
-    'Topic :: Adaptive Technologies',
-    'Topic :: Artistic Software',
-    'Topic :: Communications',
-    'Topic :: Communications :: BBS',
-    'Topic :: Communications :: Chat',
-    'Topic :: Communications :: Chat :: AOL Instant Messenger',
-    'Topic :: Communications :: Chat :: ICQ',
-    'Topic :: Communications :: Chat :: Internet Relay Chat',
-    'Topic :: Communications :: Chat :: Unix Talk',
-    'Topic :: Communications :: Conferencing',
-    'Topic :: Communications :: Email',
-    'Topic :: Communications :: Email :: Address Book',
-    'Topic :: Communications :: Email :: Email Clients (MUA)',
-    'Topic :: Communications :: Email :: Filters',
-    'Topic :: Communications :: Email :: Mailing List Servers',
-    'Topic :: Communications :: Email :: Mail Transport Agents',
-    'Topic :: Communications :: Email :: Post-Office',
-    'Topic :: Communications :: Email :: Post-Office :: IMAP',
-    'Topic :: Communications :: Email :: Post-Office :: POP3',
-    'Topic :: Communications :: Fax',
-    'Topic :: Communications :: FIDO',
-    'Topic :: Communications :: File Sharing',
-    'Topic :: Communications :: File Sharing :: Gnutella',
-    'Topic :: Communications :: File Sharing :: Napster',
-    'Topic :: Communications :: Ham Radio',
-    'Topic :: Communications :: Internet Phone',
-    'Topic :: Communications :: Telephony',
-    'Topic :: Communications :: Usenet News',
-    'Topic :: Database',
-    'Topic :: Database :: Database Engines/Servers',
-    'Topic :: Database :: Front-Ends',
-    'Topic :: Desktop Environment',
-    'Topic :: Desktop Environment :: File Managers',
-    'Topic :: Desktop Environment :: Gnome',
-    'Topic :: Desktop Environment :: GNUstep',
-    'Topic :: Desktop Environment :: K Desktop Environment (KDE)',
-    'Topic :: Desktop Environment :: K Desktop Environment (KDE) :: Themes',
-    'Topic :: Desktop Environment :: PicoGUI',
-    'Topic :: Desktop Environment :: PicoGUI :: Applications',
-    'Topic :: Desktop Environment :: PicoGUI :: Themes',
-    'Topic :: Desktop Environment :: Screen Savers',
-    'Topic :: Desktop Environment :: Window Managers',
-    'Topic :: Desktop Environment :: Window Managers :: Afterstep',
-    'Topic :: Desktop Environment :: Window Managers :: Afterstep :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: Applets',
-    'Topic :: Desktop Environment :: Window Managers :: Blackbox',
-    'Topic :: Desktop Environment :: Window Managers :: Blackbox :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: CTWM',
-    'Topic :: Desktop Environment :: Window Managers :: CTWM :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: Enlightenment',
-    'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Epplets',
-    'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR15',
-    'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR16',
-    'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR17',
-    'Topic :: Desktop Environment :: Window Managers :: Fluxbox',
-    'Topic :: Desktop Environment :: Window Managers :: Fluxbox :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: FVWM',
-    'Topic :: Desktop Environment :: Window Managers :: FVWM :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: IceWM',
-    'Topic :: Desktop Environment :: Window Managers :: IceWM :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: MetaCity',
-    'Topic :: Desktop Environment :: Window Managers :: MetaCity :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: Oroborus',
-    'Topic :: Desktop Environment :: Window Managers :: Oroborus :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: Sawfish',
-    'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes 0.30',
-    'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes pre-0.30',
-    'Topic :: Desktop Environment :: Window Managers :: Waimea',
-    'Topic :: Desktop Environment :: Window Managers :: Waimea :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: Window Maker',
-    'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Applets',
-    'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Themes',
-    'Topic :: Desktop Environment :: Window Managers :: XFCE',
-    'Topic :: Desktop Environment :: Window Managers :: XFCE :: Themes',
-    'Topic :: Documentation',
-    'Topic :: Education',
-    'Topic :: Education :: Computer Aided Instruction (CAI)',
-    'Topic :: Education :: Testing',
-    'Topic :: Games/Entertainment',
-    'Topic :: Games/Entertainment :: Arcade',
-    'Topic :: Games/Entertainment :: Board Games',
-    'Topic :: Games/Entertainment :: First Person Shooters',
-    'Topic :: Games/Entertainment :: Fortune Cookies',
-    'Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)',
-    'Topic :: Games/Entertainment :: Puzzle Games',
-    'Topic :: Games/Entertainment :: Real Time Strategy',
-    'Topic :: Games/Entertainment :: Role-Playing',
-    'Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games',
-    'Topic :: Games/Entertainment :: Simulation',
-    'Topic :: Games/Entertainment :: Turn Based Strategy',
-    'Topic :: Home Automation',
-    'Topic :: Internet',
-    'Topic :: Internet :: File Transfer Protocol (FTP)',
-    'Topic :: Internet :: Finger',
-    'Topic :: Internet :: Log Analysis',
-    'Topic :: Internet :: Name Service (DNS)',
-    'Topic :: Internet :: Proxy Servers',
-    'Topic :: Internet :: WAP',
-    'Topic :: Internet :: WWW/HTTP',
-    'Topic :: Internet :: WWW/HTTP :: Browsers',
-    'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
-    'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
-    'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards',
-    'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary',
-    'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters',
-    'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
-    'Topic :: Internet :: WWW/HTTP :: Indexing/Search',
-    'Topic :: Internet :: WWW/HTTP :: Site Management',
-    'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking',
-    'Topic :: Internet :: WWW/HTTP :: WSGI',
-    'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
-    'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware',
-    'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
-    'Topic :: Internet :: Z39.50',
-    'Topic :: Multimedia',
-    'Topic :: Multimedia :: Graphics',
-    'Topic :: Multimedia :: Graphics :: 3D Modeling',
-    'Topic :: Multimedia :: Graphics :: 3D Rendering',
-    'Topic :: Multimedia :: Graphics :: Capture',
-    'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera',
-    'Topic :: Multimedia :: Graphics :: Capture :: Scanners',
-    'Topic :: Multimedia :: Graphics :: Capture :: Screen Capture',
-    'Topic :: Multimedia :: Graphics :: Editors',
-    'Topic :: Multimedia :: Graphics :: Editors :: Raster-Based',
-    'Topic :: Multimedia :: Graphics :: Editors :: Vector-Based',
-    'Topic :: Multimedia :: Graphics :: Graphics Conversion',
-    'Topic :: Multimedia :: Graphics :: Presentation',
-    'Topic :: Multimedia :: Graphics :: Viewers',
-    'Topic :: Multimedia :: Sound/Audio',
-    'Topic :: Multimedia :: Sound/Audio :: Analysis',
-    'Topic :: Multimedia :: Sound/Audio :: Capture/Recording',
-    'Topic :: Multimedia :: Sound/Audio :: CD Audio',
-    'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing',
-    'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping',
-    'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing',
-    'Topic :: Multimedia :: Sound/Audio :: Conversion',
-    'Topic :: Multimedia :: Sound/Audio :: Editors',
-    'Topic :: Multimedia :: Sound/Audio :: MIDI',
-    'Topic :: Multimedia :: Sound/Audio :: Mixers',
-    'Topic :: Multimedia :: Sound/Audio :: Players',
-    'Topic :: Multimedia :: Sound/Audio :: Players :: MP3',
-    'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis',
-    'Topic :: Multimedia :: Sound/Audio :: Speech',
-    'Topic :: Multimedia :: Video',
-    'Topic :: Multimedia :: Video :: Capture',
-    'Topic :: Multimedia :: Video :: Conversion',
-    'Topic :: Multimedia :: Video :: Display',
-    'Topic :: Multimedia :: Video :: Non-Linear Editor',
-    'Topic :: Office/Business',
-    'Topic :: Office/Business :: Financial',
-    'Topic :: Office/Business :: Financial :: Accounting',
-    'Topic :: Office/Business :: Financial :: Investment',
-    'Topic :: Office/Business :: Financial :: Point-Of-Sale',
-    'Topic :: Office/Business :: Financial :: Spreadsheet',
-    'Topic :: Office/Business :: Groupware',
-    'Topic :: Office/Business :: News/Diary',
-    'Topic :: Office/Business :: Office Suites',
-    'Topic :: Office/Business :: Scheduling',
-    'Topic :: Other/Nonlisted Topic',
-    'Topic :: Printing',
-    'Topic :: Religion',
-    'Topic :: Scientific/Engineering',
-    'Topic :: Scientific/Engineering :: Artificial Intelligence',
-    'Topic :: Scientific/Engineering :: Astronomy',
-    'Topic :: Scientific/Engineering :: Atmospheric Science',
-    'Topic :: Scientific/Engineering :: Bio-Informatics',
-    'Topic :: Scientific/Engineering :: Chemistry',
-    'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
-    'Topic :: Scientific/Engineering :: GIS',
-    'Topic :: Scientific/Engineering :: Human Machine Interfaces',
-    'Topic :: Scientific/Engineering :: Image Recognition',
-    'Topic :: Scientific/Engineering :: Information Analysis',
-    'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator',
-    'Topic :: Scientific/Engineering :: Mathematics',
-    'Topic :: Scientific/Engineering :: Medical Science Apps.',
-    'Topic :: Scientific/Engineering :: Physics',
-    'Topic :: Scientific/Engineering :: Visualization',
-    'Topic :: Security',
-    'Topic :: Security :: Cryptography',
-    'Topic :: Sociology',
-    'Topic :: Sociology :: Genealogy',
-    'Topic :: Sociology :: History',
-    'Topic :: Software Development',
-    'Topic :: Software Development :: Assemblers',
-    'Topic :: Software Development :: Bug Tracking',
-    'Topic :: Software Development :: Build Tools',
-    'Topic :: Software Development :: Code Generators',
-    'Topic :: Software Development :: Compilers',
-    'Topic :: Software Development :: Debuggers',
-    'Topic :: Software Development :: Disassemblers',
-    'Topic :: Software Development :: Documentation',
-    'Topic :: Software Development :: Embedded Systems',
-    'Topic :: Software Development :: Internationalization',
-    'Topic :: Software Development :: Interpreters',
-    'Topic :: Software Development :: Libraries',
-    'Topic :: Software Development :: Libraries :: Application Frameworks',
-    'Topic :: Software Development :: Libraries :: Java Libraries',
-    'Topic :: Software Development :: Libraries :: Perl Modules',
-    'Topic :: Software Development :: Libraries :: PHP Classes',
-    'Topic :: Software Development :: Libraries :: Pike Modules',
-    'Topic :: Software Development :: Libraries :: pygame',
-    'Topic :: Software Development :: Libraries :: Python Modules',
-    'Topic :: Software Development :: Libraries :: Ruby Modules',
-    'Topic :: Software Development :: Libraries :: Tcl Extensions',
-    'Topic :: Software Development :: Localization',
-    'Topic :: Software Development :: Object Brokering',
-    'Topic :: Software Development :: Object Brokering :: CORBA',
-    'Topic :: Software Development :: Pre-processors',
-    'Topic :: Software Development :: Quality Assurance',
-    'Topic :: Software Development :: Testing',
-    'Topic :: Software Development :: Testing :: Traffic Generation',
-    'Topic :: Software Development :: User Interfaces',
-    'Topic :: Software Development :: Version Control',
-    'Topic :: Software Development :: Version Control :: CVS',
-    'Topic :: Software Development :: Version Control :: RCS',
-    'Topic :: Software Development :: Version Control :: SCCS',
-    'Topic :: Software Development :: Widget Sets',
-    'Topic :: System',
-    'Topic :: System :: Archiving',
-    'Topic :: System :: Archiving :: Backup',
-    'Topic :: System :: Archiving :: Compression',
-    'Topic :: System :: Archiving :: Mirroring',
-    'Topic :: System :: Archiving :: Packaging',
-    'Topic :: System :: Benchmark',
-    'Topic :: System :: Boot',
-    'Topic :: System :: Boot :: Init',
-    'Topic :: System :: Clustering',
-    'Topic :: System :: Console Fonts',
-    'Topic :: System :: Distributed Computing',
-    'Topic :: System :: Emulators',
-    'Topic :: System :: Filesystems',
-    'Topic :: System :: Hardware',
-    'Topic :: System :: Hardware :: Hardware Drivers',
-    'Topic :: System :: Hardware :: Mainframes',
-    'Topic :: System :: Hardware :: Symmetric Multi-processing',
-    'Topic :: System :: Installation/Setup',
-    'Topic :: System :: Logging',
-    'Topic :: System :: Monitoring',
-    'Topic :: System :: Networking',
-    'Topic :: System :: Networking :: Firewalls',
-    'Topic :: System :: Networking :: Monitoring',
-    'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog',
-    'Topic :: System :: Networking :: Time Synchronization',
-    'Topic :: System :: Operating System',
-    'Topic :: System :: Operating System Kernels',
-    'Topic :: System :: Operating System Kernels :: BSD',
-    'Topic :: System :: Operating System Kernels :: GNU Hurd',
-    'Topic :: System :: Operating System Kernels :: Linux',
-    'Topic :: System :: Power (UPS)',
-    'Topic :: System :: Recovery Tools',
-    'Topic :: System :: Shells',
-    'Topic :: System :: Software Distribution',
-    'Topic :: System :: Systems Administration',
-    'Topic :: System :: Systems Administration :: Authentication/Directory',
-    'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP',
-    'Topic :: System :: Systems Administration :: Authentication/Directory :: NIS',
-    'Topic :: System :: System Shells',
-    'Topic :: Terminals',
-    'Topic :: Terminals :: Serial',
-    'Topic :: Terminals :: Telnet',
-    'Topic :: Terminals :: Terminal Emulators/X Terminals',
-    'Topic :: Text Editors',
-    'Topic :: Text Editors :: Documentation',
-    'Topic :: Text Editors :: Emacs',
-    'Topic :: Text Editors :: Integrated Development Environments (IDE)',
-    'Topic :: Text Editors :: Text Processing',
-    'Topic :: Text Editors :: Word Processors',
-    'Topic :: Text Processing',
-    'Topic :: Text Processing :: Filters',
-    'Topic :: Text Processing :: Fonts',
-    'Topic :: Text Processing :: General',
-    'Topic :: Text Processing :: Indexing',
-    'Topic :: Text Processing :: Linguistic',
-    'Topic :: Text Processing :: Markup',
-    'Topic :: Text Processing :: Markup :: HTML',
-    'Topic :: Text Processing :: Markup :: LaTeX',
-    'Topic :: Text Processing :: Markup :: SGML',
-    'Topic :: Text Processing :: Markup :: VRML',
-    'Topic :: Text Processing :: Markup :: XML',
-    'Topic :: Utilities',
+'Development Status :: 1 - Planning',
+'Development Status :: 2 - Pre-Alpha',
+'Development Status :: 3 - Alpha',
+'Development Status :: 4 - Beta',
+'Development Status :: 5 - Production/Stable',
+'Development Status :: 6 - Mature',
+'Development Status :: 7 - Inactive',
+'Environment :: Console',
+'Environment :: Console :: Curses',
+'Environment :: Console :: Framebuffer',
+'Environment :: Console :: Newt',
+'Environment :: Console :: svgalib',
+"Environment :: Handhelds/PDA's",
+'Environment :: MacOS X',
+'Environment :: MacOS X :: Aqua',
+'Environment :: MacOS X :: Carbon',
+'Environment :: MacOS X :: Cocoa',
+'Environment :: No Input/Output (Daemon)',
+'Environment :: Other Environment',
+'Environment :: Plugins',
+'Environment :: Web Environment',
+'Environment :: Web Environment :: Buffet',
+'Environment :: Web Environment :: Mozilla',
+'Environment :: Web Environment :: ToscaWidgets',
+'Environment :: Win32 (MS Windows)',
+'Environment :: X11 Applications',
+'Environment :: X11 Applications :: Gnome',
+'Environment :: X11 Applications :: GTK',
+'Environment :: X11 Applications :: KDE',
+'Environment :: X11 Applications :: Qt',
+'Framework :: BFG',
+'Framework :: Buildout',
+'Framework :: Chandler',
+'Framework :: CubicWeb',
+'Framework :: Django',
+'Framework :: IDLE',
+'Framework :: Paste',
+'Framework :: Plone',
+'Framework :: Pylons',
+'Framework :: Setuptools Plugin',
+'Framework :: Trac',
+'Framework :: TurboGears',
+'Framework :: TurboGears :: Applications',
+'Framework :: TurboGears :: Widgets',
+'Framework :: Twisted',
+'Framework :: ZODB',
+'Framework :: Zope2',
+'Framework :: Zope3',
+'Intended Audience :: Customer Service',
+'Intended Audience :: Developers',
+'Intended Audience :: Education',
+'Intended Audience :: End Users/Desktop',
+'Intended Audience :: Financial and Insurance Industry',
+'Intended Audience :: Healthcare Industry',
+'Intended Audience :: Information Technology',
+'Intended Audience :: Legal Industry',
+'Intended Audience :: Manufacturing',
+'Intended Audience :: Other Audience',
+'Intended Audience :: Religion',
+'Intended Audience :: Science/Research',
+'Intended Audience :: System Administrators',
+'Intended Audience :: Telecommunications Industry',
+'License :: Aladdin Free Public License (AFPL)',
+'License :: DFSG approved',
+'License :: Eiffel Forum License (EFL)',
+'License :: Free For Educational Use',
+'License :: Free For Home Use',
+'License :: Free for non-commercial use',
+'License :: Freely Distributable',
+'License :: Free To Use But Restricted',
+'License :: Freeware',
+'License :: Netscape Public License (NPL)',
+'License :: Nokia Open Source License (NOKOS)',
+'License :: OSI Approved',
+'License :: OSI Approved :: Academic Free License (AFL)',
+'License :: OSI Approved :: Apache Software License',
+'License :: OSI Approved :: Apple Public Source License',
+'License :: OSI Approved :: Artistic License',
+'License :: OSI Approved :: Attribution Assurance License',
+'License :: OSI Approved :: BSD License',
+'License :: OSI Approved :: Common Public License',
+'License :: OSI Approved :: Eiffel Forum License',
+'License :: OSI Approved :: European Union Public Licence 1.0 (EUPL 1.0)',
+'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)',
+'License :: OSI Approved :: GNU Affero General Public License v3',
+'License :: OSI Approved :: GNU Free Documentation License (FDL)',
+'License :: OSI Approved :: GNU General Public License (GPL)',
+'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
+'License :: OSI Approved :: IBM Public License',
+'License :: OSI Approved :: Intel Open Source License',
+'License :: OSI Approved :: ISC License (ISCL)',
+'License :: OSI Approved :: Jabber Open Source License',
+'License :: OSI Approved :: MIT License',
+'License :: OSI Approved :: MITRE Collaborative Virtual Workspace License (CVW)',
+'License :: OSI Approved :: Motosoto License',
+'License :: OSI Approved :: Mozilla Public License 1.0 (MPL)',
+'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
+'License :: OSI Approved :: Nethack General Public License',
+'License :: OSI Approved :: Nokia Open Source License',
+'License :: OSI Approved :: Open Group Test Suite License',
+'License :: OSI Approved :: Python License (CNRI Python License)',
+'License :: OSI Approved :: Python Software Foundation License',
+'License :: OSI Approved :: Qt Public License (QPL)',
+'License :: OSI Approved :: Ricoh Source Code Public License',
+'License :: OSI Approved :: Sleepycat License',
+'License :: OSI Approved :: Sun Industry Standards Source License (SISSL)',
+'License :: OSI Approved :: Sun Public License',
+'License :: OSI Approved :: University of Illinois/NCSA Open Source License',
+'License :: OSI Approved :: Vovida Software License 1.0',
+'License :: OSI Approved :: W3C License',
+'License :: OSI Approved :: X.Net License',
+'License :: OSI Approved :: zlib/libpng License',
+'License :: OSI Approved :: Zope Public License',
+'License :: Other/Proprietary License',
+'License :: Public Domain',
+'License :: Repoze Public License',
+'Natural Language :: Afrikaans',
+'Natural Language :: Arabic',
+'Natural Language :: Bengali',
+'Natural Language :: Bosnian',
+'Natural Language :: Bulgarian',
+'Natural Language :: Catalan',
+'Natural Language :: Chinese (Simplified)',
+'Natural Language :: Chinese (Traditional)',
+'Natural Language :: Croatian',
+'Natural Language :: Czech',
+'Natural Language :: Danish',
+'Natural Language :: Dutch',
+'Natural Language :: English',
+'Natural Language :: Esperanto',
+'Natural Language :: Finnish',
+'Natural Language :: French',
+'Natural Language :: German',
+'Natural Language :: Greek',
+'Natural Language :: Hebrew',
+'Natural Language :: Hindi',
+'Natural Language :: Hungarian',
+'Natural Language :: Icelandic',
+'Natural Language :: Indonesian',
+'Natural Language :: Italian',
+'Natural Language :: Japanese',
+'Natural Language :: Javanese',
+'Natural Language :: Korean',
+'Natural Language :: Latin',
+'Natural Language :: Latvian',
+'Natural Language :: Macedonian',
+'Natural Language :: Malay',
+'Natural Language :: Marathi',
+'Natural Language :: Norwegian',
+'Natural Language :: Panjabi',
+'Natural Language :: Persian',
+'Natural Language :: Polish',
+'Natural Language :: Portuguese',
+'Natural Language :: Portuguese (Brazilian)',
+'Natural Language :: Romanian',
+'Natural Language :: Russian',
+'Natural Language :: Serbian',
+'Natural Language :: Slovak',
+'Natural Language :: Slovenian',
+'Natural Language :: Spanish',
+'Natural Language :: Swedish',
+'Natural Language :: Tamil',
+'Natural Language :: Telugu',
+'Natural Language :: Thai',
+'Natural Language :: Turkish',
+'Natural Language :: Ukranian',
+'Natural Language :: Urdu',
+'Natural Language :: Vietnamese',
+'Operating System :: BeOS',
+'Operating System :: MacOS',
+'Operating System :: MacOS :: MacOS 9',
+'Operating System :: MacOS :: MacOS X',
+'Operating System :: Microsoft',
+'Operating System :: Microsoft :: MS-DOS',
+'Operating System :: Microsoft :: Windows',
+'Operating System :: Microsoft :: Windows :: Windows 3.1 or Earlier',
+'Operating System :: Microsoft :: Windows :: Windows 95/98/2000',
+'Operating System :: Microsoft :: Windows :: Windows CE',
+'Operating System :: Microsoft :: Windows :: Windows NT/2000',
+'Operating System :: OS/2',
+'Operating System :: OS Independent',
+'Operating System :: Other OS',
+'Operating System :: PalmOS',
+'Operating System :: PDA Systems',
+'Operating System :: POSIX',
+'Operating System :: POSIX :: AIX',
+'Operating System :: POSIX :: BSD',
+'Operating System :: POSIX :: BSD :: BSD/OS',
+'Operating System :: POSIX :: BSD :: FreeBSD',
+'Operating System :: POSIX :: BSD :: NetBSD',
+'Operating System :: POSIX :: BSD :: OpenBSD',
+'Operating System :: POSIX :: GNU Hurd',
+'Operating System :: POSIX :: HP-UX',
+'Operating System :: POSIX :: IRIX',
+'Operating System :: POSIX :: Linux',
+'Operating System :: POSIX :: Other',
+'Operating System :: POSIX :: SCO',
+'Operating System :: POSIX :: SunOS/Solaris',
+'Operating System :: Unix',
+'Programming Language :: Ada',
+'Programming Language :: APL',
+'Programming Language :: ASP',
+'Programming Language :: Assembly',
+'Programming Language :: Awk',
+'Programming Language :: Basic',
+'Programming Language :: C',
+'Programming Language :: C#',
+'Programming Language :: C++',
+'Programming Language :: Cold Fusion',
+'Programming Language :: Cython',
+'Programming Language :: Delphi/Kylix',
+'Programming Language :: Dylan',
+'Programming Language :: Eiffel',
+'Programming Language :: Emacs-Lisp',
+'Programming Language :: Erlang',
+'Programming Language :: Euler',
+'Programming Language :: Euphoria',
+'Programming Language :: Forth',
+'Programming Language :: Fortran',
+'Programming Language :: Haskell',
+'Programming Language :: Java',
+'Programming Language :: JavaScript',
+'Programming Language :: Lisp',
+'Programming Language :: Logo',
+'Programming Language :: ML',
+'Programming Language :: Modula',
+'Programming Language :: Objective C',
+'Programming Language :: Object Pascal',
+'Programming Language :: OCaml',
+'Programming Language :: Other',
+'Programming Language :: Other Scripting Engines',
+'Programming Language :: Pascal',
+'Programming Language :: Perl',
+'Programming Language :: PHP',
+'Programming Language :: Pike',
+'Programming Language :: Pliant',
+'Programming Language :: PL/SQL',
+'Programming Language :: PROGRESS',
+'Programming Language :: Prolog',
+'Programming Language :: Python',
+'Programming Language :: Python :: 2',
+'Programming Language :: Python :: 2.3',
+'Programming Language :: Python :: 2.4',
+'Programming Language :: Python :: 2.5',
+'Programming Language :: Python :: 2.6',
+'Programming Language :: Python :: 2.7',
+'Programming Language :: Python :: 3',
+'Programming Language :: Python :: 3.0',
+'Programming Language :: Python :: 3.1',
+'Programming Language :: Python :: 3.2',
+'Programming Language :: REBOL',
+'Programming Language :: Rexx',
+'Programming Language :: Ruby',
+'Programming Language :: Scheme',
+'Programming Language :: Simula',
+'Programming Language :: Smalltalk',
+'Programming Language :: SQL',
+'Programming Language :: Tcl',
+'Programming Language :: Unix Shell',
+'Programming Language :: Visual Basic',
+'Programming Language :: XBasic',
+'Programming Language :: YACC',
+'Programming Language :: Zope',
+'Topic :: Adaptive Technologies',
+'Topic :: Artistic Software',
+'Topic :: Communications',
+'Topic :: Communications :: BBS',
+'Topic :: Communications :: Chat',
+'Topic :: Communications :: Chat :: AOL Instant Messenger',
+'Topic :: Communications :: Chat :: ICQ',
+'Topic :: Communications :: Chat :: Internet Relay Chat',
+'Topic :: Communications :: Chat :: Unix Talk',
+'Topic :: Communications :: Conferencing',
+'Topic :: Communications :: Email',
+'Topic :: Communications :: Email :: Address Book',
+'Topic :: Communications :: Email :: Email Clients (MUA)',
+'Topic :: Communications :: Email :: Filters',
+'Topic :: Communications :: Email :: Mailing List Servers',
+'Topic :: Communications :: Email :: Mail Transport Agents',
+'Topic :: Communications :: Email :: Post-Office',
+'Topic :: Communications :: Email :: Post-Office :: IMAP',
+'Topic :: Communications :: Email :: Post-Office :: POP3',
+'Topic :: Communications :: Fax',
+'Topic :: Communications :: FIDO',
+'Topic :: Communications :: File Sharing',
+'Topic :: Communications :: File Sharing :: Gnutella',
+'Topic :: Communications :: File Sharing :: Napster',
+'Topic :: Communications :: Ham Radio',
+'Topic :: Communications :: Internet Phone',
+'Topic :: Communications :: Telephony',
+'Topic :: Communications :: Usenet News',
+'Topic :: Database',
+'Topic :: Database :: Database Engines/Servers',
+'Topic :: Database :: Front-Ends',
+'Topic :: Desktop Environment',
+'Topic :: Desktop Environment :: File Managers',
+'Topic :: Desktop Environment :: Gnome',
+'Topic :: Desktop Environment :: GNUstep',
+'Topic :: Desktop Environment :: K Desktop Environment (KDE)',
+'Topic :: Desktop Environment :: K Desktop Environment (KDE) :: Themes',
+'Topic :: Desktop Environment :: PicoGUI',
+'Topic :: Desktop Environment :: PicoGUI :: Applications',
+'Topic :: Desktop Environment :: PicoGUI :: Themes',
+'Topic :: Desktop Environment :: Screen Savers',
+'Topic :: Desktop Environment :: Window Managers',
+'Topic :: Desktop Environment :: Window Managers :: Afterstep',
+'Topic :: Desktop Environment :: Window Managers :: Afterstep :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Applets',
+'Topic :: Desktop Environment :: Window Managers :: Blackbox',
+'Topic :: Desktop Environment :: Window Managers :: Blackbox :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: CTWM',
+'Topic :: Desktop Environment :: Window Managers :: CTWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Epplets',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR15',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR16',
+'Topic :: Desktop Environment :: Window Managers :: Enlightenment :: Themes DR17',
+'Topic :: Desktop Environment :: Window Managers :: Fluxbox',
+'Topic :: Desktop Environment :: Window Managers :: Fluxbox :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: FVWM',
+'Topic :: Desktop Environment :: Window Managers :: FVWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: IceWM',
+'Topic :: Desktop Environment :: Window Managers :: IceWM :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: MetaCity',
+'Topic :: Desktop Environment :: Window Managers :: MetaCity :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Oroborus',
+'Topic :: Desktop Environment :: Window Managers :: Oroborus :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes 0.30',
+'Topic :: Desktop Environment :: Window Managers :: Sawfish :: Themes pre-0.30',
+'Topic :: Desktop Environment :: Window Managers :: Waimea',
+'Topic :: Desktop Environment :: Window Managers :: Waimea :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Applets',
+'Topic :: Desktop Environment :: Window Managers :: Window Maker :: Themes',
+'Topic :: Desktop Environment :: Window Managers :: XFCE',
+'Topic :: Desktop Environment :: Window Managers :: XFCE :: Themes',
+'Topic :: Documentation',
+'Topic :: Education',
+'Topic :: Education :: Computer Aided Instruction (CAI)',
+'Topic :: Education :: Testing',
+'Topic :: Games/Entertainment',
+'Topic :: Games/Entertainment :: Arcade',
+'Topic :: Games/Entertainment :: Board Games',
+'Topic :: Games/Entertainment :: First Person Shooters',
+'Topic :: Games/Entertainment :: Fortune Cookies',
+'Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)',
+'Topic :: Games/Entertainment :: Puzzle Games',
+'Topic :: Games/Entertainment :: Real Time Strategy',
+'Topic :: Games/Entertainment :: Role-Playing',
+'Topic :: Games/Entertainment :: Side-Scrolling/Arcade Games',
+'Topic :: Games/Entertainment :: Simulation',
+'Topic :: Games/Entertainment :: Turn Based Strategy',
+'Topic :: Home Automation',
+'Topic :: Internet',
+'Topic :: Internet :: File Transfer Protocol (FTP)',
+'Topic :: Internet :: Finger',
+'Topic :: Internet :: Log Analysis',
+'Topic :: Internet :: Name Service (DNS)',
+'Topic :: Internet :: Proxy Servers',
+'Topic :: Internet :: WAP',
+'Topic :: Internet :: WWW/HTTP',
+'Topic :: Internet :: WWW/HTTP :: Browsers',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Message Boards',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: News/Diary',
+'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Page Counters',
+'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
+'Topic :: Internet :: WWW/HTTP :: Indexing/Search',
+'Topic :: Internet :: WWW/HTTP :: Site Management',
+'Topic :: Internet :: WWW/HTTP :: Site Management :: Link Checking',
+'Topic :: Internet :: WWW/HTTP :: WSGI',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware',
+'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
+'Topic :: Internet :: Z39.50',
+'Topic :: Multimedia',
+'Topic :: Multimedia :: Graphics',
+'Topic :: Multimedia :: Graphics :: 3D Modeling',
+'Topic :: Multimedia :: Graphics :: 3D Rendering',
+'Topic :: Multimedia :: Graphics :: Capture',
+'Topic :: Multimedia :: Graphics :: Capture :: Digital Camera',
+'Topic :: Multimedia :: Graphics :: Capture :: Scanners',
+'Topic :: Multimedia :: Graphics :: Capture :: Screen Capture',
+'Topic :: Multimedia :: Graphics :: Editors',
+'Topic :: Multimedia :: Graphics :: Editors :: Raster-Based',
+'Topic :: Multimedia :: Graphics :: Editors :: Vector-Based',
+'Topic :: Multimedia :: Graphics :: Graphics Conversion',
+'Topic :: Multimedia :: Graphics :: Presentation',
+'Topic :: Multimedia :: Graphics :: Viewers',
+'Topic :: Multimedia :: Sound/Audio',
+'Topic :: Multimedia :: Sound/Audio :: Analysis',
+'Topic :: Multimedia :: Sound/Audio :: Capture/Recording',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Playing',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping',
+'Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Writing',
+'Topic :: Multimedia :: Sound/Audio :: Conversion',
+'Topic :: Multimedia :: Sound/Audio :: Editors',
+'Topic :: Multimedia :: Sound/Audio :: MIDI',
+'Topic :: Multimedia :: Sound/Audio :: Mixers',
+'Topic :: Multimedia :: Sound/Audio :: Players',
+'Topic :: Multimedia :: Sound/Audio :: Players :: MP3',
+'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis',
+'Topic :: Multimedia :: Sound/Audio :: Speech',
+'Topic :: Multimedia :: Video',
+'Topic :: Multimedia :: Video :: Capture',
+'Topic :: Multimedia :: Video :: Conversion',
+'Topic :: Multimedia :: Video :: Display',
+'Topic :: Multimedia :: Video :: Non-Linear Editor',
+'Topic :: Office/Business',
+'Topic :: Office/Business :: Financial',
+'Topic :: Office/Business :: Financial :: Accounting',
+'Topic :: Office/Business :: Financial :: Investment',
+'Topic :: Office/Business :: Financial :: Point-Of-Sale',
+'Topic :: Office/Business :: Financial :: Spreadsheet',
+'Topic :: Office/Business :: Groupware',
+'Topic :: Office/Business :: News/Diary',
+'Topic :: Office/Business :: Office Suites',
+'Topic :: Office/Business :: Scheduling',
+'Topic :: Other/Nonlisted Topic',
+'Topic :: Printing',
+'Topic :: Religion',
+'Topic :: Scientific/Engineering',
+'Topic :: Scientific/Engineering :: Artificial Intelligence',
+'Topic :: Scientific/Engineering :: Astronomy',
+'Topic :: Scientific/Engineering :: Atmospheric Science',
+'Topic :: Scientific/Engineering :: Bio-Informatics',
+'Topic :: Scientific/Engineering :: Chemistry',
+'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
+'Topic :: Scientific/Engineering :: GIS',
+'Topic :: Scientific/Engineering :: Human Machine Interfaces',
+'Topic :: Scientific/Engineering :: Image Recognition',
+'Topic :: Scientific/Engineering :: Information Analysis',
+'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator',
+'Topic :: Scientific/Engineering :: Mathematics',
+'Topic :: Scientific/Engineering :: Medical Science Apps.',
+'Topic :: Scientific/Engineering :: Physics',
+'Topic :: Scientific/Engineering :: Visualization',
+'Topic :: Security',
+'Topic :: Security :: Cryptography',
+'Topic :: Sociology',
+'Topic :: Sociology :: Genealogy',
+'Topic :: Sociology :: History',
+'Topic :: Software Development',
+'Topic :: Software Development :: Assemblers',
+'Topic :: Software Development :: Bug Tracking',
+'Topic :: Software Development :: Build Tools',
+'Topic :: Software Development :: Code Generators',
+'Topic :: Software Development :: Compilers',
+'Topic :: Software Development :: Debuggers',
+'Topic :: Software Development :: Disassemblers',
+'Topic :: Software Development :: Documentation',
+'Topic :: Software Development :: Embedded Systems',
+'Topic :: Software Development :: Internationalization',
+'Topic :: Software Development :: Interpreters',
+'Topic :: Software Development :: Libraries',
+'Topic :: Software Development :: Libraries :: Application Frameworks',
+'Topic :: Software Development :: Libraries :: Java Libraries',
+'Topic :: Software Development :: Libraries :: Perl Modules',
+'Topic :: Software Development :: Libraries :: PHP Classes',
+'Topic :: Software Development :: Libraries :: Pike Modules',
+'Topic :: Software Development :: Libraries :: pygame',
+'Topic :: Software Development :: Libraries :: Python Modules',
+'Topic :: Software Development :: Libraries :: Ruby Modules',
+'Topic :: Software Development :: Libraries :: Tcl Extensions',
+'Topic :: Software Development :: Localization',
+'Topic :: Software Development :: Object Brokering',
+'Topic :: Software Development :: Object Brokering :: CORBA',
+'Topic :: Software Development :: Pre-processors',
+'Topic :: Software Development :: Quality Assurance',
+'Topic :: Software Development :: Testing',
+'Topic :: Software Development :: Testing :: Traffic Generation',
+'Topic :: Software Development :: User Interfaces',
+'Topic :: Software Development :: Version Control',
+'Topic :: Software Development :: Version Control :: CVS',
+'Topic :: Software Development :: Version Control :: RCS',
+'Topic :: Software Development :: Version Control :: SCCS',
+'Topic :: Software Development :: Widget Sets',
+'Topic :: System',
+'Topic :: System :: Archiving',
+'Topic :: System :: Archiving :: Backup',
+'Topic :: System :: Archiving :: Compression',
+'Topic :: System :: Archiving :: Mirroring',
+'Topic :: System :: Archiving :: Packaging',
+'Topic :: System :: Benchmark',
+'Topic :: System :: Boot',
+'Topic :: System :: Boot :: Init',
+'Topic :: System :: Clustering',
+'Topic :: System :: Console Fonts',
+'Topic :: System :: Distributed Computing',
+'Topic :: System :: Emulators',
+'Topic :: System :: Filesystems',
+'Topic :: System :: Hardware',
+'Topic :: System :: Hardware :: Hardware Drivers',
+'Topic :: System :: Hardware :: Mainframes',
+'Topic :: System :: Hardware :: Symmetric Multi-processing',
+'Topic :: System :: Installation/Setup',
+'Topic :: System :: Logging',
+'Topic :: System :: Monitoring',
+'Topic :: System :: Networking',
+'Topic :: System :: Networking :: Firewalls',
+'Topic :: System :: Networking :: Monitoring',
+'Topic :: System :: Networking :: Monitoring :: Hardware Watchdog',
+'Topic :: System :: Networking :: Time Synchronization',
+'Topic :: System :: Operating System',
+'Topic :: System :: Operating System Kernels',
+'Topic :: System :: Operating System Kernels :: BSD',
+'Topic :: System :: Operating System Kernels :: GNU Hurd',
+'Topic :: System :: Operating System Kernels :: Linux',
+'Topic :: System :: Power (UPS)',
+'Topic :: System :: Recovery Tools',
+'Topic :: System :: Shells',
+'Topic :: System :: Software Distribution',
+'Topic :: System :: Systems Administration',
+'Topic :: System :: Systems Administration :: Authentication/Directory',
+'Topic :: System :: Systems Administration :: Authentication/Directory :: LDAP',
+'Topic :: System :: Systems Administration :: Authentication/Directory :: NIS',
+'Topic :: System :: System Shells',
+'Topic :: Terminals',
+'Topic :: Terminals :: Serial',
+'Topic :: Terminals :: Telnet',
+'Topic :: Terminals :: Terminal Emulators/X Terminals',
+'Topic :: Text Editors',
+'Topic :: Text Editors :: Documentation',
+'Topic :: Text Editors :: Emacs',
+'Topic :: Text Editors :: Integrated Development Environments (IDE)',
+'Topic :: Text Editors :: Text Processing',
+'Topic :: Text Editors :: Word Processors',
+'Topic :: Text Processing',
+'Topic :: Text Processing :: Filters',
+'Topic :: Text Processing :: Fonts',
+'Topic :: Text Processing :: General',
+'Topic :: Text Processing :: Indexing',
+'Topic :: Text Processing :: Linguistic',
+'Topic :: Text Processing :: Markup',
+'Topic :: Text Processing :: Markup :: HTML',
+'Topic :: Text Processing :: Markup :: LaTeX',
+'Topic :: Text Processing :: Markup :: SGML',
+'Topic :: Text Processing :: Markup :: VRML',
+'Topic :: Text Processing :: Markup :: XML',
+'Topic :: Utilities',
 ]
diff --git a/distutils2/command/__init__.py b/distutils2/command/__init__.py
--- a/distutils2/command/__init__.py
+++ b/distutils2/command/__init__.py
@@ -1,8 +1,6 @@
-"""distutils.command
+"""Subpackage containing all standard commands."""
 
-Package containing implementation of all the standard Distutils
-commands."""
-from distutils2.errors import DistutilsModuleError
+from distutils2.errors import PackagingModuleError
 from distutils2.util import resolve_name
 
 __all__ = ['get_command_names', 'set_command', 'get_command_class',
@@ -50,9 +48,9 @@
     """Return the registered command"""
     try:
         cls = _COMMANDS[name]
-        if isinstance(cls, str):
+        if isinstance(cls, basestring):
             cls = resolve_name(cls)
             _COMMANDS[name] = cls
         return cls
     except KeyError:
-        raise DistutilsModuleError("Invalid command %s" % name)
+        raise PackagingModuleError("Invalid command %s" % name)
diff --git a/distutils2/command/bdist.py b/distutils2/command/bdist.py
--- a/distutils2/command/bdist.py
+++ b/distutils2/command/bdist.py
@@ -1,12 +1,15 @@
-"""distutils.command.bdist
+"""Create a built (binary) distribution.
 
-Implements the Distutils 'bdist' command (create a built [binary]
-distribution)."""
+If a --formats option was given on the command line, this command will
+call the corresponding bdist_* commands; if the option was absent, a
+bdist_* command depending on the current platform will be called.
+"""
+
 import os
 
 from distutils2 import util
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsPlatformError, DistutilsOptionError
+from distutils2.errors import PackagingPlatformError, PackagingOptionError
 
 
 def show_formats():
@@ -52,8 +55,10 @@
          "lists available distribution formats", show_formats),
         ]
 
-    # This won't do in reality: will need to distinguish RPM-ish Linux,
-    # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
+    # This is of course very simplistic.  The various UNIX family operating
+    # systems have their specific formats, but they are out of scope for us;
+    # bdist_dumb is, well, dumb; it's more a building block for other
+    # distutils2 tools than a real end-user binary format.
     default_format = {'posix': 'gztar',
                       'nt': 'zip',
                       'os2': 'zip'}
@@ -79,7 +84,7 @@
         self.plat_name = None
         self.formats = None
         self.dist_dir = None
-        self.skip_build = 0
+        self.skip_build = False
         self.group = None
         self.owner = None
 
@@ -104,9 +109,8 @@
             try:
                 self.formats = [self.default_format[os.name]]
             except KeyError:
-                raise DistutilsPlatformError, \
-                      "don't know how to create built distributions " + \
-                      "on platform %s" % os.name
+                raise PackagingPlatformError("don't know how to create built distributions " + \
+                      "on platform %s" % os.name)
 
         if self.dist_dir is None:
             self.dist_dir = "dist"
@@ -118,12 +122,13 @@
             try:
                 commands.append(self.format_command[format][0])
             except KeyError:
-                raise DistutilsOptionError, "invalid format '%s'" % format
+                raise PackagingOptionError("invalid format '%s'" % format)
 
         # Reinitialize and run each command.
         for i in range(len(self.formats)):
             cmd_name = commands[i]
             sub_cmd = self.get_reinitialized_command(cmd_name)
+            sub_cmd.format = self.formats[i]
 
             # passing the owner and group names for tar archiving
             if cmd_name == 'bdist_dumb':
@@ -133,5 +138,5 @@
             # If we're going to need to run this command again, tell it to
             # keep its temporary files around so subsequent runs go faster.
             if cmd_name in commands[i+1:]:
-                sub_cmd.keep_temp = 1
+                sub_cmd.keep_temp = True
             self.run_command(cmd_name)
diff --git a/distutils2/command/bdist_dumb.py b/distutils2/command/bdist_dumb.py
--- a/distutils2/command/bdist_dumb.py
+++ b/distutils2/command/bdist_dumb.py
@@ -1,22 +1,19 @@
-"""distutils.command.bdist_dumb
+"""Create a "dumb" built distribution.
 
-Implements the Distutils 'bdist_dumb' command (create a "dumb" built
-distribution -- i.e., just an archive to be unpacked under $prefix or
-$exec_prefix)."""
-
+A dumb distribution is just an archive meant to be unpacked under
+sys.prefix or sys.exec_prefix.
+"""
 
 import os
+
 from shutil import rmtree
-try:
-    from sysconfig import get_python_version
-except ImportError:
-    from distutils2._backport.sysconfig import get_python_version
+from sysconfig import get_python_version
 from distutils2.util import get_platform
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsPlatformError
+from distutils2.errors import PackagingPlatformError
 from distutils2 import logger
 
-class bdist_dumb (Command):
+class bdist_dumb(Command):
 
     description = 'create a "dumb" built distribution'
 
@@ -52,14 +49,14 @@
                        'os2': 'zip' }
 
 
-    def initialize_options (self):
+    def initialize_options(self):
         self.bdist_dir = None
         self.plat_name = None
         self.format = None
-        self.keep_temp = 0
+        self.keep_temp = False
         self.dist_dir = None
-        self.skip_build = 0
-        self.relative = 0
+        self.skip_build = False
+        self.relative = False
         self.owner = None
         self.group = None
 
@@ -72,9 +69,8 @@
             try:
                 self.format = self.default_format[os.name]
             except KeyError:
-                raise DistutilsPlatformError, \
-                      ("don't know how to create dumb built distributions " +
-                       "on platform %s") % os.name
+                raise PackagingPlatformError(("don't know how to create dumb built distributions " +
+                       "on platform %s") % os.name)
 
         self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
 
@@ -82,10 +78,11 @@
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.get_reinitialized_command('install_dist', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install_dist',
+                                                 reinit_subcommands=True)
         install.root = self.bdist_dir
         install.skip_build = self.skip_build
-        install.warn_dir = 0
+        install.warn_dir = False
 
         logger.info("installing to %s", self.bdist_dir)
         self.run_command('install_dist')
@@ -106,7 +103,7 @@
         else:
             if (self.distribution.has_ext_modules() and
                 (install.install_base != install.install_platbase)):
-                raise DistutilsPlatformError(
+                raise PackagingPlatformError(
                     "can't make a dumb built distribution where base and "
                     "platbase are different (%r, %r)" %
                     (install.install_base, install.install_platbase))
diff --git a/distutils2/command/bdist_msi.py b/distutils2/command/bdist_msi.py
--- a/distutils2/command/bdist_msi.py
+++ b/distutils2/command/bdist_msi.py
@@ -1,25 +1,33 @@
-# -*- coding: iso-8859-1 -*-
-# Copyright (C) 2005, 2006 Martin von Löwis
+"""Create a Microsoft Installer (.msi) binary distribution."""
+
+# Copyright (C) 2005, 2006 Martin von Löwis
 # Licensed to PSF under a Contributor Agreement.
-# The bdist_wininst command proper
-# based on bdist_wininst
-"""
-Implements the bdist_msi command.
-"""
-import sys, os
+
+import sys
+import os
+import msilib
+
+
 from sysconfig import get_python_version
-
-from distutils2.core import Command
-from distutils2.version import StrictVersion
-from distutils2.errors import DistutilsOptionError
-from distutils2 import log
+from shutil import rmtree
+from distutils2.command.cmd import Command
+from distutils2.version import NormalizedVersion
+from distutils2.errors import PackagingOptionError
+from distutils2 import logger as log
 from distutils2.util import get_platform
-from distutils2._backport.shutil import rmtree
-
-import msilib
 from msilib import schema, sequence, text
 from msilib import Directory, Feature, Dialog, add_data
 
+class MSIVersion(NormalizedVersion):
+    """
+    MSI ProductVersion must be strictly numeric.
+    MSIVersion disallows prerelease and postrelease versions.
+    """
+    def __init__(self, *args, **kwargs):
+        super(MSIVersion, self).__init__(*args, **kwargs)
+        if not self.is_final:
+            raise ValueError("ProductVersion must be strictly numeric")
+
 class PyDialog(Dialog):
     """Dialog class with a fixed layout: controls at the top, then a ruler,
     then a list of buttons: back, next, cancel. Optionally a bitmap at the
@@ -81,7 +89,7 @@
         Return the button, so that events can be associated"""
         return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next)
 
-class bdist_msi (Command):
+class bdist_msi(Command):
 
     description = "create a Microsoft Installer (.msi) binary distribution"
 
@@ -123,20 +131,20 @@
                     '3.5', '3.6', '3.7', '3.8', '3.9']
     other_version = 'X'
 
-    def initialize_options (self):
+    def initialize_options(self):
         self.bdist_dir = None
         self.plat_name = None
-        self.keep_temp = 0
-        self.no_target_compile = 0
-        self.no_target_optimize = 0
+        self.keep_temp = False
+        self.no_target_compile = False
+        self.no_target_optimize = False
         self.target_version = None
         self.dist_dir = None
-        self.skip_build = 0
+        self.skip_build = False
         self.install_script = None
         self.pre_install_script = None
         self.versions = None
 
-    def finalize_options (self):
+    def finalize_options(self):
         if self.bdist_dir is None:
             bdist_base = self.get_finalized_command('bdist').bdist_base
             self.bdist_dir = os.path.join(bdist_base, 'msi')
@@ -147,41 +155,39 @@
             self.versions = [self.target_version]
             if not self.skip_build and self.distribution.has_ext_modules()\
                and self.target_version != short_version:
-                raise DistutilsOptionError, \
-                      "target version can only be %s, or the '--skip-build'" \
-                      " option must be specified" % (short_version,)
+                raise PackagingOptionError("target version can only be %s, or the '--skip-build'" \
+                      " option must be specified" % (short_version,))
         else:
             self.versions = list(self.all_versions)
 
         self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
 
         if self.pre_install_script:
-            raise DistutilsOptionError, "the pre-install-script feature is not yet implemented"
+            raise PackagingOptionError("the pre-install-script feature is not yet implemented")
 
         if self.install_script:
             for script in self.distribution.scripts:
                 if self.install_script == os.path.basename(script):
                     break
             else:
-                raise DistutilsOptionError, \
-                      "install_script '%s' not found in scripts" % \
-                      self.install_script
+                raise PackagingOptionError("install_script '%s' not found in scripts" % \
+                      self.install_script)
         self.install_script_key = None
-    # finalize_options()
 
 
-    def run (self):
+    def run(self):
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.get_reinitialized_command('install_dist', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install_dist',
+                                                 reinit_subcommands=True)
         install.prefix = self.bdist_dir
         install.skip_build = self.skip_build
-        install.warn_dir = 0
+        install.warn_dir = False
 
         install_lib = self.get_reinitialized_command('install_lib')
         # we do not want to include pyc or pyo files
-        install_lib.compile = 0
+        install_lib.compile = False
         install_lib.optimize = 0
 
         if self.distribution.has_ext_modules():
@@ -223,10 +229,7 @@
             author = metadata.maintainer
         if not author:
             author = "UNKNOWN"
-        version = metadata.get_version()
-        # ProductVersion must be strictly numeric
-        # XXX need to deal with prerelease versions
-        sversion = "%d.%d.%d" % StrictVersion(version).version
+        version = MSIVersion(metadata.get_version())
         # Prefix ProductName with Python x.y, so that
         # it sorts together with the other Python packages
         # in Add-Remove-Programs (APR)
@@ -237,7 +240,7 @@
             product_name = "Python %s" % (fullname)
         self.db = msilib.init_database(installer_name, schema,
                 product_name, msilib.gen_uuid(),
-                sversion, author)
+                str(version), author)
         msilib.add_tables(self.db, sequence)
         props = [('DistVersion', version)]
         email = metadata.author_email or metadata.maintainer_email
@@ -307,7 +310,7 @@
                             key = seen[afile] = dir.add_file(file)
                             if file==self.install_script:
                                 if self.install_script_key:
-                                    raise DistutilsOptionError(
+                                    raise PackagingOptionError(
                                           "Multiple files with name %s" % file)
                                 self.install_script_key = '[#%s]' % key
                         else:
@@ -387,27 +390,27 @@
         #     entries for each version as the above code does
         if self.pre_install_script:
             scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
-            f = open(scriptfn, "w")
-            # The batch file will be executed with [PYTHON], so that %1
-            # is the path to the Python interpreter; %0 will be the path
-            # of the batch file.
-            # rem ="""
-            # %1 %0
-            # exit
-            # """
-            # <actual script>
-            f.write('rem ="""\n%1 %0\nexit\n"""\n')
-            f.write(open(self.pre_install_script).read())
-            f.close()
+            with open(scriptfn, "w") as f:
+                # The batch file will be executed with [PYTHON], so that %1
+                # is the path to the Python interpreter; %0 will be the path
+                # of the batch file.
+                # rem ="""
+                # %1 %0
+                # exit
+                # """
+                # <actual script>
+                f.write('rem ="""\n%1 %0\nexit\n"""\n')
+                with open(self.pre_install_script) as fp:
+                    f.write(fp.read())
             add_data(self.db, "Binary",
-                [("PreInstall", msilib.Binary(scriptfn))
-                ])
+                     [("PreInstall", msilib.Binary(scriptfn)),
+                     ])
             add_data(self.db, "CustomAction",
-                [("PreInstall", 2, "PreInstall", None)
-                ])
+                     [("PreInstall", 2, "PreInstall", None),
+                     ])
             add_data(self.db, "InstallExecuteSequence",
-                    [("PreInstall", "NOT Installed", 450)])
-
+                     [("PreInstall", "NOT Installed", 450),
+                     ])
 
     def add_ui(self):
         db = self.db
diff --git a/distutils2/command/bdist_wininst.py b/distutils2/command/bdist_wininst.py
--- a/distutils2/command/bdist_wininst.py
+++ b/distutils2/command/bdist_wininst.py
@@ -1,25 +1,21 @@
-"""distutils.command.bdist_wininst
+"""Create an executable installer for Windows."""
 
-Implements the Distutils 'bdist_wininst' command: create a windows installer
-exe-program."""
-
+# FIXME synchronize bytes/str use with same file in distutils
 
 import sys
 import os
-import string
+
 from shutil import rmtree
-try:
-    from sysconfig import get_python_version
-except ImportError:
-    from distutils2._backport.sysconfig import get_python_version
+from sysconfig import get_python_version
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsOptionError, DistutilsPlatformError
+from distutils2.errors import PackagingOptionError, PackagingPlatformError
 from distutils2 import logger
 from distutils2.util import get_platform
 
-class bdist_wininst (Command):
 
-    description = "create an executable installer for MS Windows"
+class bdist_wininst(Command):
+
+    description = "create an executable installer for Windows"
 
     user_options = [('bdist-dir=', None,
                      "temporary directory for creating the distribution"),
@@ -61,25 +57,23 @@
     boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
                        'skip-build']
 
-    def initialize_options (self):
+    def initialize_options(self):
         self.bdist_dir = None
         self.plat_name = None
-        self.keep_temp = 0
-        self.no_target_compile = 0
-        self.no_target_optimize = 0
+        self.keep_temp = False
+        self.no_target_compile = False
+        self.no_target_optimize = False
         self.target_version = None
         self.dist_dir = None
         self.bitmap = None
         self.title = None
-        self.skip_build = 0
+        self.skip_build = False
         self.install_script = None
         self.pre_install_script = None
         self.user_access_control = None
 
-    # initialize_options()
 
-
-    def finalize_options (self):
+    def finalize_options(self):
         if self.bdist_dir is None:
             if self.skip_build and self.plat_name:
                 # If build is skipped and plat_name is overridden, bdist will
@@ -94,9 +88,8 @@
         if not self.skip_build and self.distribution.has_ext_modules():
             short_version = get_python_version()
             if self.target_version and self.target_version != short_version:
-                raise DistutilsOptionError, \
-                      "target version can only be %s, or the '--skip-build'" \
-                      " option must be specified" % (short_version,)
+                raise PackagingOptionError("target version can only be %s, or the '--skip-build'" \
+                      " option must be specified" % (short_version,))
             self.target_version = short_version
 
         self.set_undefined_options('bdist', 'dist_dir', 'plat_name')
@@ -106,32 +99,30 @@
                 if self.install_script == os.path.basename(script):
                     break
             else:
-                raise DistutilsOptionError, \
-                      "install_script '%s' not found in scripts" % \
-                      self.install_script
-    # finalize_options()
+                raise PackagingOptionError("install_script '%s' not found in scripts" % \
+                      self.install_script)
 
-
-    def run (self):
+    def run(self):
         if (sys.platform != "win32" and
             (self.distribution.has_ext_modules() or
              self.distribution.has_c_libraries())):
-            raise DistutilsPlatformError \
+            raise PackagingPlatformError \
                   ("distribution contains extensions and/or C libraries; "
                    "must be compiled on a Windows 32 platform")
 
         if not self.skip_build:
             self.run_command('build')
 
-        install = self.get_reinitialized_command('install', reinit_subcommands=1)
+        install = self.get_reinitialized_command('install',
+                                                 reinit_subcommands=True)
         install.root = self.bdist_dir
         install.skip_build = self.skip_build
-        install.warn_dir = 0
+        install.warn_dir = False
         install.plat_name = self.plat_name
 
         install_lib = self.get_reinitialized_command('install_lib')
         # we do not want to include pyc or pyo files
-        install_lib.compile = 0
+        install_lib.compile = False
         install_lib.optimize = 0
 
         if self.distribution.has_ext_modules():
@@ -153,7 +144,7 @@
         # Use a custom scheme for the zip-file, because we have to decide
         # at installation time which scheme to use.
         for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
-            value = string.upper(key)
+            value = key.upper()
             if key == 'headers':
                 value = value + '/Include/$dist_name'
             setattr(install,
@@ -196,7 +187,7 @@
             else:
                 rmtree(self.bdist_dir)
 
-    def get_inidata (self):
+    def get_inidata(self):
         # Return data describing the installation.
 
         lines = []
@@ -211,14 +202,14 @@
 
         # Escape newline characters
         def escape(s):
-            return string.replace(s, "\n", "\\n")
+            return s.replace("\n", "\\n")
 
         for name in ["author", "author_email", "description", "maintainer",
                      "maintainer_email", "name", "url", "version"]:
             data = getattr(metadata, name, "")
             if data:
                 info = info + ("\n    %s: %s" % \
-                               (string.capitalize(name), escape(data)))
+                               (name.capitalize(), escape(data)))
                 lines.append("%s=%s" % (name, escape(data)))
 
         # The [setup] section contains entries controlling
@@ -241,11 +232,9 @@
         build_info = "Built %s with distutils2-%s" % \
                      (time.ctime(time.time()), distutils2.__version__)
         lines.append("build_info=%s" % build_info)
-        return string.join(lines, "\n")
+        return "\n".join(lines)
 
-    # get_inidata()
-
-    def create_exe (self, arcname, fullname, bitmap=None):
+    def create_exe(self, arcname, fullname, bitmap=None):
         import struct
 
         self.mkpath(self.dist_dir)
@@ -253,52 +242,48 @@
         cfgdata = self.get_inidata()
 
         installer_name = self.get_installer_filename(fullname)
-        self.announce("creating %s" % installer_name)
+        logger.info("creating %s", installer_name)
 
         if bitmap:
-            bitmapdata = open(bitmap, "rb").read()
+            with open(bitmap, "rb") as fp:
+                bitmapdata = fp.read()
             bitmaplen = len(bitmapdata)
         else:
             bitmaplen = 0
 
-        file = open(installer_name, "wb")
-        file.write(self.get_exe_bytes())
-        if bitmap:
-            file.write(bitmapdata)
+        with open(installer_name, "wb") as file:
+            file.write(self.get_exe_bytes())
+            if bitmap:
+                file.write(bitmapdata)
 
-        # Convert cfgdata from unicode to ascii, mbcs encoded
-        try:
-            unicode
-        except NameError:
-            pass
-        else:
+            # Convert cfgdata from unicode to ascii, mbcs encoded
             if isinstance(cfgdata, unicode):
                 cfgdata = cfgdata.encode("mbcs")
 
-        # Append the pre-install script
-        cfgdata = cfgdata + "\0"
-        if self.pre_install_script:
-            script_data = open(self.pre_install_script, "r").read()
-            cfgdata = cfgdata + script_data + "\n\0"
-        else:
-            # empty pre-install script
+            # Append the pre-install script
             cfgdata = cfgdata + "\0"
-        file.write(cfgdata)
+            if self.pre_install_script:
+                with open(self.pre_install_script) as fp:
+                    script_data = fp.read()
+                cfgdata = cfgdata + script_data + "\n\0"
+            else:
+                # empty pre-install script
+                cfgdata = cfgdata + "\0"
+            file.write(cfgdata)
 
-        # The 'magic number' 0x1234567B is used to make sure that the
-        # binary layout of 'cfgdata' is what the wininst.exe binary
-        # expects.  If the layout changes, increment that number, make
-        # the corresponding changes to the wininst.exe sources, and
-        # recompile them.
-        header = struct.pack("<iii",
-                             0x1234567B,       # tag
-                             len(cfgdata),     # length
-                             bitmaplen,        # number of bytes in bitmap
-                             )
-        file.write(header)
-        file.write(open(arcname, "rb").read())
-
-    # create_exe()
+            # The 'magic number' 0x1234567B is used to make sure that the
+            # binary layout of 'cfgdata' is what the wininst.exe binary
+            # expects.  If the layout changes, increment that number, make
+            # the corresponding changes to the wininst.exe sources, and
+            # recompile them.
+            header = struct.pack("<iii",
+                                 0x1234567B,       # tag
+                                 len(cfgdata),     # length
+                                 bitmaplen,        # number of bytes in bitmap
+                                 )
+            file.write(header)
+            with open(arcname, "rb") as fp:
+                file.write(fp.read())
 
     def get_installer_filename(self, fullname):
         # Factored out to allow overriding in subclasses
@@ -312,9 +297,8 @@
             installer_name = os.path.join(self.dist_dir,
                                           "%s.%s.exe" % (fullname, self.plat_name))
         return installer_name
-    # get_installer_filename()
 
-    def get_exe_bytes (self):
+    def get_exe_bytes(self):
         from distutils2.compiler.msvccompiler import get_build_version
         # If a target-version other than the current version has been
         # specified, then using the MSVC version from *this* build is no good.
@@ -354,5 +338,5 @@
             sfix = ''
 
         filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix))
-        return open(filename, "rb").read()
-# class bdist_wininst
+        with open(filename, "rb") as fp:
+            return fp.read()
diff --git a/distutils2/command/build.py b/distutils2/command/build.py
--- a/distutils2/command/build.py
+++ b/distutils2/command/build.py
@@ -1,13 +1,11 @@
-"""distutils.command.build
+"""Main build command, which calls the other build_* commands."""
 
-Implements the Distutils 'build' command.
-"""
 import sys
 import os
 
 from distutils2.util import get_platform
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 from distutils2.compiler import show_compilers
 
 
@@ -67,7 +65,7 @@
         self.compiler = None
         self.plat_name = None
         self.debug = None
-        self.force = 0
+        self.force = False
         self.executable = None
         self.use_2to3 = False
         self.convert_2to3_doctests = None
@@ -81,7 +79,7 @@
             # supported via ./configure flags, if at all).  Avoid misleading
             # other platforms.
             if os.name != 'nt':
-                raise DistutilsOptionError(
+                raise PackagingOptionError(
                             "--plat-name only supported on Windows (try "
                             "using './configure --help' on your platform)")
 
diff --git a/distutils2/command/build_clib.py b/distutils2/command/build_clib.py
--- a/distutils2/command/build_clib.py
+++ b/distutils2/command/build_clib.py
@@ -1,10 +1,8 @@
-"""distutils.command.build_clib
+"""Build C/C++ libraries.
 
-Implements the Distutils 'build_clib' command, to build a C/C++ library
-that is included in the module distribution and needed by an extension
-module."""
-
-
+This command is useful to build libraries that are included in the
+distribution and needed by extension modules.
+"""
 
 # XXX this module has *lots* of code ripped-off quite transparently from
 # build_ext.py -- not surprisingly really, as the work required to build
@@ -17,7 +15,7 @@
 
 import os
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsSetupError
+from distutils2.errors import PackagingSetupError
 from distutils2.compiler import customize_compiler
 from distutils2 import logger
 
@@ -29,7 +27,7 @@
 
 class build_clib(Command):
 
-    description = "build C/C++ libraries used by Python extensions"
+    description = "build C/C++ libraries used by extension modules"
 
     user_options = [
         ('build-clib=', 'b',
@@ -63,7 +61,7 @@
         self.define = None
         self.undef = None
         self.debug = None
-        self.force = 0
+        self.force = False
         self.compiler = None
 
 
@@ -84,7 +82,7 @@
 
         if self.include_dirs is None:
             self.include_dirs = self.distribution.include_dirs or []
-        if isinstance(self.include_dirs, str):
+        if isinstance(self.include_dirs, basestring):
             self.include_dirs = self.include_dirs.split(os.pathsep)
 
         # XXX same as for build_ext -- what about 'self.define' and
@@ -105,7 +103,7 @@
             self.compiler.set_include_dirs(self.include_dirs)
         if self.define is not None:
             # 'define' option is a list of (name,value) tuples
-            for (name,value) in self.define:
+            for name, value in self.define:
                 self.compiler.define_macro(name, value)
         if self.undef is not None:
             for macro in self.undef:
@@ -121,34 +119,29 @@
         This method checks that it is a list of 2-tuples, where the tuples
         are (library_name, build_info_dict).
 
-        Raise DistutilsSetupError if the structure is invalid anywhere;
+        Raise PackagingSetupError if the structure is invalid anywhere;
         just returns otherwise.
         """
         if not isinstance(libraries, list):
-            raise DistutilsSetupError, \
-                  "'libraries' option must be a list of tuples"
+            raise PackagingSetupError("'libraries' option must be a list of tuples")
 
         for lib in libraries:
             if not isinstance(lib, tuple) and len(lib) != 2:
-                raise DistutilsSetupError, \
-                      "each element of 'libraries' must a 2-tuple"
+                raise PackagingSetupError("each element of 'libraries' must a 2-tuple")
 
             name, build_info = lib
 
-            if not isinstance(name, str):
-                raise DistutilsSetupError, \
-                      "first element of each tuple in 'libraries' " + \
-                      "must be a string (the library name)"
+            if not isinstance(name, basestring):
+                raise PackagingSetupError("first element of each tuple in 'libraries' " + \
+                      "must be a string (the library name)")
             if '/' in name or (os.sep != '/' and os.sep in name):
-                raise DistutilsSetupError, \
-                      ("bad library name '%s': " +
+                raise PackagingSetupError(("bad library name '%s': " +
                        "may not contain directory separators") % \
-                      lib[0]
+                      lib[0])
 
             if not isinstance(build_info, dict):
-                raise DistutilsSetupError, \
-                      "second element of each tuple in 'libraries' " + \
-                      "must be a dictionary (build info)"
+                raise PackagingSetupError("second element of each tuple in 'libraries' " + \
+                      "must be a dictionary (build info)")
 
     def get_library_names(self):
         # Assume the library list is valid -- 'check_library_list()' is
@@ -157,7 +150,7 @@
             return None
 
         lib_names = []
-        for (lib_name, build_info) in self.libraries:
+        for lib_name, build_info in self.libraries:
             lib_names.append(lib_name)
         return lib_names
 
@@ -165,25 +158,23 @@
     def get_source_files(self):
         self.check_library_list(self.libraries)
         filenames = []
-        for (lib_name, build_info) in self.libraries:
+        for lib_name, build_info in self.libraries:
             sources = build_info.get('sources')
             if sources is None or not isinstance(sources, (list, tuple)):
-                raise DistutilsSetupError, \
-                      ("in 'libraries' option (library '%s'), "
+                raise PackagingSetupError(("in 'libraries' option (library '%s'), "
                        "'sources' must be present and must be "
-                       "a list of source filenames") % lib_name
+                       "a list of source filenames") % lib_name)
 
             filenames.extend(sources)
         return filenames
 
     def build_libraries(self, libraries):
-        for (lib_name, build_info) in libraries:
+        for lib_name, build_info in libraries:
             sources = build_info.get('sources')
             if sources is None or not isinstance(sources, (list, tuple)):
-                raise DistutilsSetupError, \
-                      ("in 'libraries' option (library '%s'), " +
+                raise PackagingSetupError(("in 'libraries' option (library '%s'), " +
                        "'sources' must be present and must be " +
-                       "a list of source filenames") % lib_name
+                       "a list of source filenames") % lib_name)
             sources = list(sources)
 
             logger.info("building '%s' library", lib_name)
diff --git a/distutils2/command/build_ext.py b/distutils2/command/build_ext.py
--- a/distutils2/command/build_ext.py
+++ b/distutils2/command/build_ext.py
@@ -1,30 +1,26 @@
-"""distutils.command.build_ext
+"""Build extension modules."""
 
-Implements the Distutils 'build_ext' command, for building extension
-modules (currently limited to C extensions, should accommodate C++
-extensions ASAP)."""
+# FIXME Is this module limited to C extensions or do C++ extensions work too?
+# The docstring of this module said that C++ was not supported, but other
+# comments contradict that.
 
-
-import sys, os, re
-from warnings import warn
+import os
+import re
+import sys
+import logging
+import sysconfig
 
 from distutils2.util import get_platform
 from distutils2.command.cmd import Command
-from distutils2.errors import (CCompilerError, CompileError, DistutilsError,
-                               DistutilsPlatformError, DistutilsSetupError)
+from distutils2.errors import (CCompilerError, CompileError, PackagingError,
+                               PackagingPlatformError, PackagingSetupError)
 from distutils2.compiler import customize_compiler, show_compilers
 from distutils2.util import newer_group
 from distutils2.compiler.extension import Extension
 from distutils2 import logger
-from distutils2._backport import sysconfig
 
-# this keeps compatibility from 2.3 to 2.5
-if sys.version < "2.6":
-    USER_BASE = None
-    HAS_USER_SITE = False
-else:
-    from site import USER_BASE
-    HAS_USER_SITE = True
+import site
+HAS_USER_SITE = True
 
 if os.name == 'nt':
     from distutils2.compiler.msvccompiler import get_build_version
@@ -38,11 +34,11 @@
 
 class build_ext(Command):
 
-    description = "build C/C++ extensions (compile/link to build directory)"
+    description = "build C/C++ extension modules (compile/link to build directory)"
 
     # XXX thoughts on how to deal with complex command-line options like
     # these, i.e. how to make it so fancy_getopt can suck them off the
-    # command line and make it look like setup.py defined the appropriate
+    # command line and turn them into the appropriate
     # lists of tuples of what-have-you.
     #   - each command needs a callback to process its command-line options
     #   - Command.__init__() needs access to its share of the whole
@@ -90,15 +86,13 @@
          "forcibly build everything (ignore file timestamps)"),
         ('compiler=', 'c',
          "specify the compiler type"),
-        ('swig-cpp', None,
-         "make SWIG create C++ files (default is C)"),
         ('swig-opts=', None,
          "list of SWIG command-line options"),
         ('swig=', None,
          "path to the SWIG executable"),
         ]
 
-    boolean_options = ['inplace', 'debug', 'force', 'swig-cpp']
+    boolean_options = ['inplace', 'debug', 'force']
 
     if HAS_USER_SITE:
         user_options.append(('user', None,
@@ -110,46 +104,12 @@
          "list available compilers", show_compilers),
         ]
 
-
-    # making 'compiler' a property to deprecate
-    # its usage as something else than a compiler type
-    # e.g. like a compiler instance
-    def __init__(self, dist):
-        self._compiler = None
-        Command.__init__(self, dist)
-
-    def __setattr__(self, name, value):
-        # need this to make sure setattr() (used in distutils)
-        # doesn't kill our property
-        if name == 'compiler':
-            self._set_compiler(value)
-        else:
-            self.__dict__[name] = value
-
-    def _set_compiler(self, compiler):
-        if not isinstance(compiler, str) and compiler is not None:
-            # we don't want to allow that anymore in the future
-            warn("'compiler' specifies the compiler type in build_ext. "
-                 "If you want to get the compiler object itself, "
-                 "use 'compiler_obj'", DeprecationWarning)
-        self._compiler = compiler
-
-    def _get_compiler(self):
-        if not isinstance(self._compiler, str) and self._compiler is not None:
-            # we don't want to allow that anymore in the future
-            warn("'compiler' specifies the compiler type in build_ext. "
-                 "If you want to get the compiler object itself, "
-                 "use 'compiler_obj'", DeprecationWarning)
-        return self._compiler
-
-    compiler = property(_get_compiler, _set_compiler)
-
     def initialize_options(self):
         self.extensions = None
         self.build_lib = None
         self.plat_name = None
         self.build_temp = None
-        self.inplace = 0
+        self.inplace = False
         self.package = None
 
         self.include_dirs = None
@@ -163,7 +123,6 @@
         self.force = None
         self.compiler = None
         self.swig = None
-        self.swig_cpp = None
         self.swig_opts = None
         if HAS_USER_SITE:
             self.user = None
@@ -183,7 +142,7 @@
             if not isinstance(self.extensions, (list, tuple)):
                 type_name = (self.extensions is None and 'None'
                             or type(self.extensions).__name__)
-                raise DistutilsSetupError(
+                raise PackagingSetupError(
                     "'ext_modules' must be a sequence of Extension instances,"
                     " not %s" % (type_name,))
             for i, ext in enumerate(self.extensions):
@@ -191,7 +150,7 @@
                     continue                # OK! (assume type-checking done
                                             # by Extension constructor)
                 type_name = (ext is None and 'None' or type(ext).__name__)
-                raise DistutilsSetupError(
+                raise PackagingSetupError(
                     "'ext_modules' item %d must be an Extension instance,"
                     " not %s" % (i, type_name))
 
@@ -201,7 +160,7 @@
         plat_py_include = sysconfig.get_path('platinclude')
         if self.include_dirs is None:
             self.include_dirs = self.distribution.include_dirs or []
-        if isinstance(self.include_dirs, str):
+        if isinstance(self.include_dirs, basestring):
             self.include_dirs = self.include_dirs.split(os.pathsep)
 
         # Put the Python "system" include dir at the end, so that
@@ -210,7 +169,7 @@
         if plat_py_include != py_include:
             self.include_dirs.append(plat_py_include)
 
-        if isinstance(self.libraries, str):
+        if isinstance(self.libraries, basestring):
             self.libraries = [self.libraries]
 
         # Life is easier if we're not forever checking for None, so
@@ -219,12 +178,12 @@
             self.libraries = []
         if self.library_dirs is None:
             self.library_dirs = []
-        elif isinstance(self.library_dirs, str):
+        elif isinstance(self.library_dirs, basestring):
             self.library_dirs = self.library_dirs.split(os.pathsep)
 
         if self.rpath is None:
             self.rpath = []
-        elif isinstance(self.rpath, str):
+        elif isinstance(self.rpath, basestring):
             self.rpath = self.rpath.split(os.pathsep)
 
         # for extensions under windows use different directories
@@ -317,8 +276,8 @@
 
         # Finally add the user include and library directories if requested
         if HAS_USER_SITE and self.user:
-            user_include = os.path.join(USER_BASE, "include")
-            user_lib = os.path.join(USER_BASE, "lib")
+            user_include = os.path.join(site.USER_BASE, "include")
+            user_lib = os.path.join(site.USER_BASE, "lib")
             if os.path.isdir(user_include):
                 self.include_dirs.append(user_include)
             if os.path.isdir(user_lib):
@@ -328,18 +287,6 @@
     def run(self):
         from distutils2.compiler import new_compiler
 
-        # 'self.extensions', as supplied by setup.py, is a list of
-        # Extension instances.  See the documentation for Extension (in
-        # distutils.extension) for details.
-        #
-        # For backwards compatibility with Distutils 0.8.2 and earlier, we
-        # also allow the 'extensions' list to be a list of tuples:
-        #    (ext_name, build_info)
-        # where build_info is a dictionary containing everything that
-        # Extension instances do except the name, with a few things being
-        # differently named.  We convert these 2-tuples to Extension
-        # instances as needed.
-
         if not self.extensions:
             return
 
@@ -351,24 +298,17 @@
             self.libraries.extend(build_clib.get_library_names() or [])
             self.library_dirs.append(build_clib.build_clib)
 
+        # Temporary kludge until we remove the verbose arguments and use
+        # logging everywhere
+        verbose = logger.getEffectiveLevel() >= logging.DEBUG
+
         # Setup the CCompiler object that we'll use to do all the
         # compiling and linking
-
-        # used to prevent the usage of an existing compiler for the
-        # compiler option when calling new_compiler()
-        # this will be removed in 3.3 and 2.8
-        if not isinstance(self._compiler, str):
-            self._compiler = None
-
-        self.compiler_obj = new_compiler(compiler=self._compiler,
-                                         verbose=self.verbose,
+        self.compiler_obj = new_compiler(compiler=self.compiler,
+                                         verbose=verbose,
                                          dry_run=self.dry_run,
                                          force=self.force)
 
-        # used to keep the compiler object reachable with
-        # "self.compiler". this will be removed in 3.3 and 2.8
-        self._compiler = self.compiler_obj
-
         customize_compiler(self.compiler_obj)
         # If we are cross-compiling, init the compiler now (if we are not
         # cross-compiling, init would not hurt, but people may rely on
@@ -384,7 +324,7 @@
             self.compiler_obj.set_include_dirs(self.include_dirs)
         if self.define is not None:
             # 'define' option is a list of (name,value) tuples
-            for (name, value) in self.define:
+            for name, value in self.define:
                 self.compiler_obj.define_macro(name, value)
         if self.undef is not None:
             for macro in self.undef:
@@ -423,19 +363,19 @@
         for ext in self.extensions:
             try:
                 self.build_extension(ext)
-            except (CCompilerError, DistutilsError, CompileError), e:
+            except (CCompilerError, PackagingError, CompileError):
                 if not ext.optional:
                     raise
-                self.warn('building extension "%s" failed: %s' %
-                          (ext.name, e))
+                logger.warning('%s: building extension %r failed: %s',
+                               self.get_command_name(), ext.name,
+                               sys.exc_info()[1])
 
     def build_extension(self, ext):
         sources = ext.sources
         if sources is None or not isinstance(sources, (list, tuple)):
-            raise DistutilsSetupError, \
-                  ("in 'ext_modules' option (extension '%s'), " +
+            raise PackagingSetupError(("in 'ext_modules' option (extension '%s'), " +
                    "'sources' must be present and must be " +
-                   "a list of source filenames") % ext.name
+                   "a list of source filenames") % ext.name)
         sources = list(sources)
 
         ext_path = self.get_ext_fullpath(ext.name)
@@ -484,7 +424,7 @@
         # The setup.py script for Python on Unix needs to be able to
         # get this list so it can perform all the clean up needed to
         # avoid keeping object files around when cleaning out a failed
-        # build of an extension module.  Since Distutils does not
+        # build of an extension module.  Since Packaging does not
         # track dependencies, we have to get rid of intermediates to
         # ensure all the intermediates will be properly re-built.
         #
@@ -527,17 +467,13 @@
         # source -- but there should be an option to put SWIG output in
         # the temp dir.
 
-        if self.swig_cpp:
-            logger.warn("--swig-cpp is deprecated - use --swig-opts=-c++")
-
-        if self.swig_cpp or ('-c++' in self.swig_opts) or \
-           ('-c++' in extension.swig_opts):
+        if ('-c++' in self.swig_opts or '-c++' in extension.swig_opts):
             target_ext = '.cpp'
         else:
             target_ext = '.c'
 
         for source in sources:
-            (base, ext) = os.path.splitext(source)
+            base, ext = os.path.splitext(source)
             if ext == ".i":             # SWIG interface file
                 new_sources.append(base + '_wrap' + target_ext)
                 swig_sources.append(source)
@@ -551,8 +487,6 @@
         swig = self.swig or self.find_swig()
         swig_cmd = [swig, "-python"]
         swig_cmd.extend(self.swig_opts)
-        if self.swig_cpp:
-            swig_cmd.append("-c++")
 
         # Do not override commandline arguments
         if not self.swig_opts:
@@ -591,9 +525,8 @@
             return "swig.exe"
 
         else:
-            raise DistutilsPlatformError, \
-                  ("I don't know how to find (much less run) SWIG "
-                   "on platform '%s'") % os.name
+            raise PackagingPlatformError(("I don't know how to find (much less run) SWIG "
+                   "on platform '%s'") % os.name)
 
     # -- Name generators -----------------------------------------------
     # (extension names, filenames, whatever)
@@ -654,7 +587,7 @@
         provided, "init" + module_name.  Only relevant on Windows, where
         the .pyd file (DLL) must export the module "init" function.
         """
-        initfunc_name = "init" + ext.name.split('.')[-1]
+        initfunc_name = "PyInit_" + ext.name.split('.')[-1]
         if initfunc_name not in ext.export_symbols:
             ext.export_symbols.append(initfunc_name)
         return ext.export_symbols
@@ -723,9 +656,9 @@
 
         else:
             if sysconfig.get_config_var('Py_ENABLE_SHARED'):
-                template = "python%d.%d"
-                pythonlib = (template %
-                             (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+                pythonlib = 'python{}.{}{}'.format(
+                    sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff,
+                    sys.abiflags)
                 return ext.libraries + [pythonlib]
             else:
                 return ext.libraries
diff --git a/distutils2/command/build_py.py b/distutils2/command/build_py.py
--- a/distutils2/command/build_py.py
+++ b/distutils2/command/build_py.py
@@ -1,15 +1,12 @@
-"""distutils.command.build_py
-
-Implements the Distutils 'build_py' command."""
-
+"""Build pure Python modules (just copy to build directory)."""
 
 import os
 import sys
-import logging
 from glob import glob
 
+from distutils2 import logger
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsOptionError, DistutilsFileError
+from distutils2.errors import PackagingOptionError, PackagingFileError
 from distutils2.util import convert_path
 from distutils2.compat import Mixin2to3
 
@@ -18,10 +15,10 @@
 
 class build_py(Command, Mixin2to3):
 
-    description = "\"build\" pure Python modules (copy to build directory)"
+    description = "build pure Python modules (copy to build directory)"
 
     user_options = [
-        ('build-lib=', 'd', "directory to \"build\" (copy) to"),
+        ('build-lib=', 'd', "directory to build (copy) to"),
         ('compile', 'c', "compile .py to .pyc"),
         ('no-compile', None, "don't compile .py files [default]"),
         ('optimize=', 'O',
@@ -45,7 +42,7 @@
         self.package = None
         self.package_data = None
         self.package_dir = None
-        self.compile = 0
+        self.compile = False
         self.optimize = 0
         self.force = None
         self._updated_files = []
@@ -77,7 +74,7 @@
                 self.optimize = int(self.optimize)
                 assert 0 <= self.optimize <= 2
             except (ValueError, AssertionError):
-                raise DistutilsOptionError("optimize must be 0, 1, or 2")
+                raise PackagingOptionError("optimize must be 0, 1, or 2")
 
     def run(self):
         # XXX copy_file by default preserves atime and mtime.  IMHO this is
@@ -111,7 +108,7 @@
             self.run_2to3(self._updated_files, self._doctests_2to3,
                                             self.use_2to3_fixers)
 
-        self.byte_compile(self.get_outputs(include_bytecode=0))
+        self.byte_compile(self.get_outputs(include_bytecode=False))
 
     # -- Top-level worker functions ------------------------------------
 
@@ -154,7 +151,7 @@
             # Each pattern has to be converted to a platform-specific path
             filelist = glob(os.path.join(src_dir, convert_path(pattern)))
             # Files that match more than one pattern are only added once
-            files.extend([fn for fn in filelist if fn not in files])
+            files.extend(fn for fn in filelist if fn not in files)
         return files
 
     def build_package_data(self):
@@ -179,6 +176,7 @@
         """Return the directory, relative to the top of the source
            distribution, where package 'package' should be found
            (at least according to the 'package_dir' option, if any)."""
+
         path = package.split('.')
         if self.package_dir is not None:
             path.insert(0, self.package_dir)
@@ -197,10 +195,10 @@
         # circumvent them.
         if package_dir != "":
             if not os.path.exists(package_dir):
-                raise DistutilsFileError(
+                raise PackagingFileError(
                       "package directory '%s' does not exist" % package_dir)
             if not os.path.isdir(package_dir):
-                raise DistutilsFileError(
+                raise PackagingFileError(
                        "supposed package directory '%s' exists, "
                        "but is not a directory" % package_dir)
 
@@ -210,8 +208,8 @@
             if os.path.isfile(init_py):
                 return init_py
             else:
-                logging.warning(("package init file '%s' not found " +
-                                 "(or not a regular file)"), init_py)
+                logger.warning(("package init file '%s' not found " +
+                                "(or not a regular file)"), init_py)
 
         # Either not in a package at all (__init__.py not expected), or
         # __init__.py doesn't exist -- so don't return the filename.
@@ -219,8 +217,8 @@
 
     def check_module(self, module, module_file):
         if not os.path.isfile(module_file):
-            logging.warning("file %s (for module %s) not found",
-                            module_file, module)
+            logger.warning("file %s (for module %s) not found",
+                           module_file, module)
             return False
         else:
             return True
@@ -240,7 +238,7 @@
                 module = os.path.splitext(os.path.basename(f))[0]
                 modules.append((package, module, f))
             else:
-                self.debug_print("excluding %s" % setup_script)
+                logger.debug("excluding %s", setup_script)
         return modules
 
     def find_modules(self):
@@ -273,10 +271,10 @@
             module_base = path[-1]
 
             try:
-                (package_dir, checked) = packages[package]
+                package_dir, checked = packages[package]
             except KeyError:
                 package_dir = self.get_package_dir(package)
-                checked = 0
+                checked = False
 
             if not checked:
                 init_py = self.check_package(package, package_dir)
@@ -323,10 +321,10 @@
         outfile_path = [build_dir] + list(package) + [module + ".py"]
         return os.path.join(*outfile_path)
 
-    def get_outputs(self, include_bytecode=1):
+    def get_outputs(self, include_bytecode=True):
         modules = self.find_all_modules()
         outputs = []
-        for (package, module, module_file) in modules:
+        for package, module, module_file in modules:
             package = package.split('.')
             filename = self.get_module_outfile(self.build_lib, package, module)
             outputs.append(filename)
@@ -344,7 +342,7 @@
         return outputs
 
     def build_module(self, module, module_file, package):
-        if isinstance(package, str):
+        if isinstance(package, basestring):
             package = package.split('.')
         elif not isinstance(package, (list, tuple)):
             raise TypeError(
@@ -356,11 +354,11 @@
         outfile = self.get_module_outfile(self.build_lib, package, module)
         dir = os.path.dirname(outfile)
         self.mkpath(dir)
-        return self.copy_file(module_file, outfile, preserve_mode=0)
+        return self.copy_file(module_file, outfile, preserve_mode=False)
 
     def build_modules(self):
         modules = self.find_modules()
-        for (package, module, module_file) in modules:
+        for package, module, module_file in modules:
 
             # Now "build" the module -- ie. copy the source file to
             # self.build_lib (the build directory for Python source).
@@ -385,13 +383,14 @@
 
             # Now loop over the modules we found, "building" each one (just
             # copy it to self.build_lib).
-            for (package_, module, module_file) in modules:
+            for package_, module, module_file in modules:
                 assert package == package_
                 self.build_module(module, module_file, package)
 
     def byte_compile(self, files):
         if hasattr(sys, 'dont_write_bytecode') and sys.dont_write_bytecode:
-            self.warn('byte-compiling is disabled, skipping.')
+            logger.warning('%s: byte-compiling is disabled, skipping.',
+                           self.get_command_name())
             return
 
         from distutils2.util import byte_compile
diff --git a/distutils2/command/build_scripts.py b/distutils2/command/build_scripts.py
--- a/distutils2/command/build_scripts.py
+++ b/distutils2/command/build_scripts.py
@@ -1,26 +1,24 @@
-"""distutils.command.build_scripts
+"""Build scripts (copy to build dir and fix up shebang line)."""
 
-Implements the Distutils 'build_scripts' command."""
-
-
-import os, re
-from stat import ST_MODE
+import os
+import re
+import sysconfig
 
 from distutils2.command.cmd import Command
-from distutils2.util import convert_path, newer
+from distutils2.util import convert_path, newer, detect_encoding, fsencode
 from distutils2 import logger
-from distutils2._backport import sysconfig
 from distutils2.compat import Mixin2to3
 
+
 # check if Python is called on the first line with this expression
-first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$')
+first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$')
 
-class build_scripts (Command, Mixin2to3):
+class build_scripts(Command, Mixin2to3):
 
-    description = "\"build\" scripts (copy and fixup #! line)"
+    description = "build scripts (copy and fix up shebang line)"
 
     user_options = [
-        ('build-dir=', 'd', "directory to \"build\" (copy) to"),
+        ('build-dir=', 'd', "directory to build (copy) to"),
         ('force', 'f', "forcibly build everything (ignore file timestamps"),
         ('executable=', 'e', "specify final destination interpreter path"),
         ]
@@ -28,7 +26,7 @@
     boolean_options = ['force']
 
 
-    def initialize_options (self):
+    def initialize_options(self):
         self.build_dir = None
         self.scripts = None
         self.force = None
@@ -38,7 +36,7 @@
         self.convert_2to3_doctests = None
         self.use_2to3_fixers = None
 
-    def finalize_options (self):
+    def finalize_options(self):
         self.set_undefined_options('build',
                                    ('build_scripts', 'build_dir'),
                                    'use_2to3', 'use_2to3_fixers',
@@ -49,14 +47,14 @@
     def get_source_files(self):
         return self.scripts
 
-    def run (self):
+    def run(self):
         if not self.scripts:
             return
         copied_files = self.copy_scripts()
-        if self.use_2to3 and self.copied_files:
-            self._run_2to3(self.copied_files, fixers=self.use_2to3_fixers)
+        if self.use_2to3 and copied_files:
+            self._run_2to3(copied_files, fixers=self.use_2to3_fixers)
 
-    def copy_scripts (self):
+    def copy_scripts(self):
         """Copy each script listed in 'self.scripts'; if it's marked as a
         Python script in the Unix way (first line matches 'first_line_re',
         ie. starts with "\#!" and contains "python"), then adjust the first
@@ -65,7 +63,7 @@
         self.mkpath(self.build_dir)
         outfiles = []
         for script in self.scripts:
-            adjust = 0
+            adjust = False
             script = convert_path(script)
             outfile = os.path.join(self.build_dir, os.path.basename(script))
             outfiles.append(outfile)
@@ -78,40 +76,62 @@
             # that way, we'll get accurate feedback if we can read the
             # script.
             try:
-                f = open(script, "r")
+                f = open(script, "rb")
             except IOError:
                 if not self.dry_run:
                     raise
                 f = None
             else:
+                encoding, lines = detect_encoding(f.readline)
+                f.seek(0)
                 first_line = f.readline()
                 if not first_line:
-                    self.warn("%s is an empty file (skipping)" % script)
+                    logger.warning('%s: %s is an empty file (skipping)',
+                                   self.get_command_name(),  script)
                     continue
 
                 match = first_line_re.match(first_line)
                 if match:
-                    adjust = 1
-                    post_interp = match.group(1) or ''
+                    adjust = True
+                    post_interp = match.group(1) or b''
 
             if adjust:
                 logger.info("copying and adjusting %s -> %s", script,
                          self.build_dir)
                 if not self.dry_run:
-                    outf = open(outfile, "w")
                     if not sysconfig.is_python_build():
-                        outf.write("#!%s%s\n" %
-                                   (self.executable,
-                                    post_interp))
+                        executable = self.executable
                     else:
-                        outf.write("#!%s%s\n" %
-                                   (os.path.join(
+                        executable = os.path.join(
                             sysconfig.get_config_var("BINDIR"),
                            "python%s%s" % (sysconfig.get_config_var("VERSION"),
-                                           sysconfig.get_config_var("EXE"))),
-                                    post_interp))
-                    outf.writelines(f.readlines())
-                    outf.close()
+                                           sysconfig.get_config_var("EXE")))
+                    executable = fsencode(executable)
+                    shebang = b"#!" + executable + post_interp + b"\n"
+                    # Python parser starts to read a script using UTF-8 until
+                    # it gets a #coding:xxx cookie. The shebang has to be the
+                    # first line of a file, the #coding:xxx cookie cannot be
+                    # written before. So the shebang has to be decodable from
+                    # UTF-8.
+                    try:
+                        shebang.decode('utf-8')
+                    except UnicodeDecodeError:
+                        raise ValueError(
+                            "The shebang ({!r}) is not decodable "
+                            "from utf-8".format(shebang))
+                    # If the script is encoded to a custom encoding (use a
+                    # #coding:xxx cookie), the shebang has to be decodable from
+                    # the script encoding too.
+                    try:
+                        shebang.decode(encoding)
+                    except UnicodeDecodeError:
+                        raise ValueError(
+                            "The shebang ({!r}) is not decodable "
+                            "from the script encoding ({})"
+                            .format(shebang, encoding))
+                    with open(outfile, "wb") as outf:
+                        outf.write(shebang)
+                        outf.writelines(f.readlines())
                 if f:
                     f.close()
             else:
@@ -124,13 +144,10 @@
                 if self.dry_run:
                     logger.info("changing mode of %s", file)
                 else:
-                    oldmode = os.stat(file)[ST_MODE] & 07777
-                    newmode = (oldmode | 0555) & 07777
+                    oldmode = os.stat(file).st_mode & 0o7777
+                    newmode = (oldmode | 0o555) & 0o7777
                     if newmode != oldmode:
                         logger.info("changing mode of %s from %o to %o",
                                  file, oldmode, newmode)
                         os.chmod(file, newmode)
         return outfiles
-    # copy_scripts ()
-
-# class build_scripts
diff --git a/distutils2/command/check.py b/distutils2/command/check.py
--- a/distutils2/command/check.py
+++ b/distutils2/command/check.py
@@ -1,16 +1,14 @@
-"""distutils.command.check
+"""Check PEP compliance of metadata."""
 
-Implements the Distutils 'check' command.
-"""
-
+from distutils2 import logger
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsSetupError
+from distutils2.errors import PackagingSetupError
 from distutils2.util import resolve_name
 
 class check(Command):
-    """This command checks the metadata of the package.
-    """
-    description = ("perform some checks on the package")
+
+    description = "check PEP compliance of metadata"
+
     user_options = [('metadata', 'm', 'Verify metadata'),
                     ('all', 'a',
                      ('runs extended set of checks')),
@@ -21,18 +19,20 @@
 
     def initialize_options(self):
         """Sets default values for options."""
-        self.all = 0
-        self.metadata = 1
-        self.strict = 0
+        self.all = False
+        self.metadata = True
+        self.strict = False
         self._warnings = []
 
     def finalize_options(self):
         pass
 
-    def warn(self, msg):
-        """Counts the number of warnings that occurs."""
-        self._warnings.append(msg)
-        return Command.warn(self, msg)
+    def warn(self, msg, *args):
+        """Wrapper around logging that also remembers messages."""
+        # XXX we could use a special handler for this, but would need to test
+        # if it works even if the logger has a too high level
+        self._warnings.append((msg, args))
+        return logger.warning('%s: %s' % (self.get_command_name(), msg), *args)
 
     def run(self):
         """Runs the command."""
@@ -46,8 +46,8 @@
         # let's raise an error in strict mode, if we have at least
         # one warning
         if self.strict and len(self._warnings) > 0:
-            msg = '\n'.join(self._warnings)
-            raise DistutilsSetupError(msg)
+            msg = '\n'.join(msg % args for msg, args in self._warnings)
+            raise PackagingSetupError(msg)
 
     def check_metadata(self):
         """Ensures that all required elements of metadata are supplied.
@@ -58,7 +58,7 @@
         """
         missing, warnings = self.distribution.metadata.check(strict=True)
         if missing != []:
-            self.warn("missing required metadata: %s"  % ', '.join(missing))
+            self.warn('missing required metadata: %s', ', '.join(missing))
         for warning in warnings:
             self.warn(warning)
 
@@ -74,15 +74,15 @@
                     warning = '%s (line %s)' % (warning[1], line)
                 self.warn(warning)
         elif self.strict:
-            raise DistutilsSetupError('The docutils package is needed.')
+            raise PackagingSetupError('The docutils package is needed.')
 
     def check_hooks_resolvable(self):
-        for options in self.distribution.command_options.itervalues():
+        for options in self.distribution.command_options.values():
             for hook_kind in ("pre_hook", "post_hook"):
                 if hook_kind not in options:
                     break
-                for hook_name in options[hook_kind][1].itervalues():
+                for hook_name in options[hook_kind][1].values():
                     try:
                         resolve_name(hook_name)
                     except ImportError:
-                        self.warn("Name '%s' cannot be resolved." % hook_name)
+                        self.warn('name %r cannot be resolved', hook_name)
diff --git a/distutils2/command/clean.py b/distutils2/command/clean.py
--- a/distutils2/command/clean.py
+++ b/distutils2/command/clean.py
@@ -1,9 +1,6 @@
-"""distutils.command.clean
+"""Clean up temporary files created by the build command."""
 
-Implements the Distutils 'clean' command."""
-
-# contributed by Bastian Kleineidam <calvin at cs.uni-sb.de>, added 2000-03-18
-
+# Contributed by Bastian Kleineidam <calvin at cs.uni-sb.de>
 
 import os
 from shutil import rmtree
@@ -66,7 +63,7 @@
                     else:
                         rmtree(directory)
                 else:
-                    logger.warn("'%s' does not exist -- can't clean it",
+                    logger.warning("'%s' does not exist -- can't clean it",
                                 directory)
 
         # just for the heck of it, try to remove the base build directory:
@@ -77,5 +74,3 @@
                 logger.info("removing '%s'", self.build_base)
             except OSError:
                 pass
-
-# class clean
diff --git a/distutils2/command/cmd.py b/distutils2/command/cmd.py
--- a/distutils2/command/cmd.py
+++ b/distutils2/command/cmd.py
@@ -1,21 +1,16 @@
-"""distutils.cmd
+"""Base class for commands."""
 
-Provides the Command class, the base class for the command classes
-in the distutils.command package.
-"""
 import os
 import re
-import logging
-
-from distutils2.errors import DistutilsOptionError
+from shutil import copyfile, move
 from distutils2 import util
 from distutils2 import logger
-from distutils2._backport.shutil import copytree, copyfile, move, make_archive
+from distutils2.errors import PackagingOptionError
 
 
 class Command(object):
     """Abstract base class for defining command classes, the "worker bees"
-    of the Distutils.  A useful analogy for command classes is to think of
+    of the Packaging.  A useful analogy for command classes is to think of
     them as subroutines with local variables called "options".  The options
     are "declared" in 'initialize_options()' and "defined" (given their
     final values, aka "finalized") in 'finalize_options()', both of which
@@ -62,7 +57,8 @@
         from distutils2.dist import Distribution
 
         if not isinstance(dist, Distribution):
-            raise TypeError("dist must be a Distribution instance")
+            raise TypeError("dist must be an instance of Distribution, not %r"
+                            % type(dist))
         if self.__class__ is Command:
             raise RuntimeError("Command is an abstract class")
 
@@ -70,7 +66,7 @@
         self.initialize_options()
 
         # Per-command versions of the global flags, so that the user can
-        # customize Distutils' behaviour command-by-command and let some
+        # customize Packaging' behaviour command-by-command and let some
         # commands fall back on the Distribution's behaviour.  None means
         # "not defined, check self.distribution's copy", while 0 or 1 mean
         # false and true (duh).  Note that this means figuring out the real
@@ -80,10 +76,6 @@
         #     "fix" it?]
         self._dry_run = None
 
-        # verbose is largely ignored, but needs to be set for
-        # backwards compatibility (I think)?
-        self.verbose = dist.verbose
-
         # Some commands define a 'self.force' option to ignore file
         # timestamps, but methods defined *here* assume that
         # 'self.force' exists for all commands.  So define it here
@@ -92,13 +84,13 @@
 
         # The 'help' flag is just used for command line parsing, so
         # none of that complicated bureaucracy is needed.
-        self.help = 0
+        self.help = False
 
         # 'finalized' records whether or not 'finalize_options()' has been
         # called.  'finalize_options()' itself should not pay attention to
         # this flag: it is the business of 'ensure_finalized()', which
         # always calls 'finalize_options()', to respect/update it.
-        self.finalized = 0
+        self.finalized = False
 
     # XXX A more explicit way to customize dry_run would be better.
     @property
@@ -111,7 +103,7 @@
     def ensure_finalized(self):
         if not self.finalized:
             self.finalize_options()
-        self.finalized = 1
+        self.finalized = True
 
     # Subclasses must define:
     #   initialize_options()
@@ -156,18 +148,17 @@
     def dump_options(self, header=None, indent=""):
         if header is None:
             header = "command options for '%s':" % self.get_command_name()
-        self.announce(indent + header, level=logging.INFO)
+        logger.info(indent + header)
         indent = indent + "  "
         negative_opt = getattr(self, 'negative_opt', ())
-        for (option, _, _) in self.user_options:
+        for option, _, _ in self.user_options:
             if option in negative_opt:
                 continue
             option = option.replace('-', '_')
             if option[-1] == "=":
                 option = option[:-1]
             value = getattr(self, option)
-            self.announce(indent + "%s = %s" % (option, value),
-                          level=logging.INFO)
+            logger.info(indent + "%s = %s", option, value)
 
     def run(self):
         """A command's raison d'etre: carry out the action it exists to
@@ -182,13 +173,6 @@
         raise RuntimeError(
             "abstract method -- subclass %s must override" % self.__class__)
 
-    # TODO remove this method, just use logging.info
-    def announce(self, msg, level=logging.INFO):
-        """If the current verbosity level is of greater than or equal to
-        'level' print 'msg' to stdout.
-        """
-        logger.log(level, msg)
-
     # -- External interface --------------------------------------------
     # (called by outsiders)
 
@@ -221,7 +205,7 @@
     # value meets certain type and value constraints.  If not, we try to
     # force it into conformance (eg. if we expect a list but have a string,
     # split the string on comma and/or whitespace).  If we can't force the
-    # option into conformance, raise DistutilsOptionError.  Thus, command
+    # option into conformance, raise PackagingOptionError.  Thus, command
     # classes need do nothing more than (eg.)
     #   self.ensure_string_list('foo')
     # and they can be guaranteed that thereafter, self.foo will be
@@ -232,8 +216,8 @@
         if val is None:
             setattr(self, option, default)
             return default
-        elif not isinstance(val, str):
-            raise DistutilsOptionError("'%s' must be a %s (got `%s`)" %
+        elif not isinstance(val, basestring):
+            raise PackagingOptionError("'%s' must be a %s (got `%s`)" %
                                        (option, what, val))
         return val
 
@@ -252,28 +236,28 @@
         val = getattr(self, option)
         if val is None:
             return
-        elif isinstance(val, str):
+        elif isinstance(val, basestring):
             setattr(self, option, re.split(r',\s*|\s+', val))
         else:
             if isinstance(val, list):
                 # checks if all elements are str
-                ok = 1
+                ok = True
                 for element in val:
-                    if not isinstance(element, str):
-                        ok = 0
+                    if not isinstance(element, basestring):
+                        ok = False
                         break
             else:
-                ok = 0
+                ok = False
 
             if not ok:
-                raise DistutilsOptionError(
+                raise PackagingOptionError(
                     "'%s' must be a list of strings (got %r)" % (option, val))
 
     def _ensure_tested_string(self, option, tester,
                               what, error_fmt, default=None):
         val = self._ensure_stringlike(option, what, default)
         if val is not None and not tester(val):
-            raise DistutilsOptionError(
+            raise PackagingOptionError(
                 ("error in '%s' option: " + error_fmt) % (option, val))
 
     def ensure_filename(self, option):
@@ -324,7 +308,7 @@
                 setattr(self, dst_option,
                         getattr(src_cmd_obj, src_option))
 
-    def get_finalized_command(self, command, create=1):
+    def get_finalized_command(self, command, create=True):
         """Wrapper around Distribution's 'get_command_obj()' method: find
         (create if necessary and 'create' is true) the command object for
         'command', call its 'ensure_finalized()' method, and return the
@@ -334,7 +318,7 @@
         cmd_obj.ensure_finalized()
         return cmd_obj
 
-    def get_reinitialized_command(self, command, reinit_subcommands=0):
+    def get_reinitialized_command(self, command, reinit_subcommands=False):
         return self.distribution.get_reinitialized_command(
             command, reinit_subcommands)
 
@@ -364,14 +348,10 @@
 
     # -- External world manipulation -----------------------------------
 
-    # TODO remove this method, just use logging.warn
-    def warn(self, msg):
-        logger.warning("warning: %s: %s\n", self.get_command_name(), msg)
-
     def execute(self, func, args, msg=None, level=1):
         util.execute(func, args, msg, dry_run=self.dry_run)
 
-    def mkpath(self, name, mode=0777, dry_run=None, verbose=0):
+    def mkpath(self, name, mode=0o777, dry_run=None, verbose=0):
         if dry_run is None:
             dry_run = self.dry_run
         name = os.path.normpath(name)
@@ -386,7 +366,7 @@
         os.makedirs(name, mode)
 
     def copy_file(self, infile, outfile,
-                   preserve_mode=1, preserve_times=1, link=None, level=1):
+                  preserve_mode=True, preserve_times=True, link=None, level=1):
         """Copy a file respecting verbose, dry-run and force flags.  (The
         former two default to whatever is in the Distribution object, and
         the latter defaults to false for commands that don't define it.)"""
@@ -398,32 +378,34 @@
         copyfile(infile, outfile)
         return outfile, None  # XXX
 
-    def copy_tree(self, infile, outfile,
-                   preserve_mode=1, preserve_times=1, preserve_symlinks=0,
-                   level=1):
+    def copy_tree(self, infile, outfile, preserve_mode=True,
+                  preserve_times=True, preserve_symlinks=False, level=1):
         """Copy an entire directory tree respecting verbose, dry-run,
         and force flags.
         """
         if self.dry_run:
             return  # see if we want to display something
-        return copytree(infile, outfile, preserve_symlinks)
+
+
+        return util.copy_tree(infile, outfile, preserve_mode, preserve_times,
+            preserve_symlinks, not self.force, dry_run=self.dry_run)
 
     def move_file(self, src, dst, level=1):
-        """Move a file respectin dry-run flag."""
+        """Move a file respecting the dry-run flag."""
         if self.dry_run:
             return  # XXX log ?
         return move(src, dst)
 
-    def spawn(self, cmd, search_path=1, level=1):
+    def spawn(self, cmd, search_path=True, level=1):
         """Spawn an external command respecting dry-run flag."""
         from distutils2.util import spawn
         spawn(cmd, search_path, dry_run=self.dry_run)
 
     def make_archive(self, base_name, format, root_dir=None, base_dir=None,
                      owner=None, group=None):
-        return make_archive(base_name, format, root_dir,
-                            base_dir, dry_run=self.dry_run,
-                            owner=owner, group=group)
+        return util.make_archive(base_name, format, root_dir,
+                                 base_dir, dry_run=self.dry_run,
+                                 owner=owner, group=group)
 
     def make_file(self, infiles, outfile, func, args,
                   exec_msg=None, skip_msg=None, level=1):
@@ -439,7 +421,7 @@
             skip_msg = "skipping %s (inputs unchanged)" % outfile
 
         # Allow 'infiles' to be a single string
-        if isinstance(infiles, str):
+        if isinstance(infiles, basestring):
             infiles = (infiles,)
         elif not isinstance(infiles, (list, tuple)):
             raise TypeError(
diff --git a/distutils2/command/command_template b/distutils2/command/command_template
--- a/distutils2/command/command_template
+++ b/distutils2/command/command_template
@@ -1,7 +1,7 @@
-"""Implementation of the 'x' command."""
+"""Do X and Y."""
 
-import logging
-from distutils2.command.cmd import Command
+from packaging import logger
+from packaging.command.cmd import Command
 
 
 class x(Command):
@@ -12,11 +12,10 @@
     # List of option tuples: long name, short name (None if no short
     # name), and help string.
     user_options = [
-        ('', '', # long option, short option (one letter) or None
-         ""), # help text
+        ('', '',  # long option, short option (one letter) or None
+         ""),  # help text
         ]
 
-
     def initialize_options(self):
         self. = None
         self. = None
@@ -28,7 +27,7 @@
 
     def run(self):
         ...
-        logging.info(...)
+        logger.info(...)
 
         if not self.dry_run:
             ...
diff --git a/distutils2/command/config.py b/distutils2/command/config.py
--- a/distutils2/command/config.py
+++ b/distutils2/command/config.py
@@ -1,6 +1,6 @@
-"""distutils.command.config
+"""Prepare the build.
 
-Implements the Distutils 'config' command, a (mostly) empty command class
+This module provides config, a (mostly) empty command class
 that exists mainly to be sub-classed by specific module distributions and
 applications.  The idea is that while every "config" command is different,
 at least they're all named the same, and users always see "config" in the
@@ -8,11 +8,12 @@
 configure-like tasks: "try to compile this C code", or "figure out where
 this header file lives".
 """
+
 import os
 import re
 
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsExecError
+from distutils2.errors import PackagingExecError
 from distutils2.compiler import customize_compiler
 from distutils2 import logger
 
@@ -20,7 +21,7 @@
 
 class config(Command):
 
-    description = "prepare to build"
+    description = "prepare the build"
 
     user_options = [
         ('compiler=', None,
@@ -56,8 +57,8 @@
         self.library_dirs = None
 
         # maximal output for now
-        self.noisy = 1
-        self.dump_source = 1
+        self.noisy = True
+        self.dump_source = True
 
         # list of temporary files generated along-the-way that we have
         # to clean at some point
@@ -66,17 +67,17 @@
     def finalize_options(self):
         if self.include_dirs is None:
             self.include_dirs = self.distribution.include_dirs or []
-        elif isinstance(self.include_dirs, str):
+        elif isinstance(self.include_dirs, basestring):
             self.include_dirs = self.include_dirs.split(os.pathsep)
 
         if self.libraries is None:
             self.libraries = []
-        elif isinstance(self.libraries, str):
+        elif isinstance(self.libraries, basestring):
             self.libraries = [self.libraries]
 
         if self.library_dirs is None:
             self.library_dirs = []
-        elif isinstance(self.library_dirs, str):
+        elif isinstance(self.library_dirs, basestring):
             self.library_dirs = self.library_dirs.split(os.pathsep)
 
     def run(self):
@@ -97,7 +98,7 @@
         from distutils2.compiler import new_compiler
         if not isinstance(self.compiler, CCompiler):
             self.compiler = new_compiler(compiler=self.compiler,
-                                         dry_run=self.dry_run, force=1)
+                                         dry_run=self.dry_run, force=True)
             customize_compiler(self.compiler)
             if self.include_dirs:
                 self.compiler.set_include_dirs(self.include_dirs)
@@ -109,36 +110,35 @@
 
     def _gen_temp_sourcefile(self, body, headers, lang):
         filename = "_configtest" + LANG_EXT[lang]
-        file = open(filename, "w")
-        if headers:
-            for header in headers:
-                file.write("#include <%s>\n" % header)
-            file.write("\n")
-        file.write(body)
-        if body[-1] != "\n":
-            file.write("\n")
-        file.close()
+        with open(filename, "w") as file:
+            if headers:
+                for header in headers:
+                    file.write("#include <%s>\n" % header)
+                file.write("\n")
+            file.write(body)
+            if body[-1] != "\n":
+                file.write("\n")
         return filename
 
     def _preprocess(self, body, headers, include_dirs, lang):
         src = self._gen_temp_sourcefile(body, headers, lang)
         out = "_configtest.i"
-        self.temp_files.extend([src, out])
+        self.temp_files.extend((src, out))
         self.compiler.preprocess(src, out, include_dirs=include_dirs)
-        return (src, out)
+        return src, out
 
     def _compile(self, body, headers, include_dirs, lang):
         src = self._gen_temp_sourcefile(body, headers, lang)
         if self.dump_source:
             dump_file(src, "compiling '%s':" % src)
-        (obj,) = self.compiler.object_filenames([src])
-        self.temp_files.extend([src, obj])
+        obj = self.compiler.object_filenames([src])[0]
+        self.temp_files.extend((src, obj))
         self.compiler.compile([src], include_dirs=include_dirs)
-        return (src, obj)
+        return src, obj
 
     def _link(self, body, headers, include_dirs, libraries, library_dirs,
               lang):
-        (src, obj) = self._compile(body, headers, include_dirs, lang)
+        src, obj = self._compile(body, headers, include_dirs, lang)
         prog = os.path.splitext(os.path.basename(src))[0]
         self.compiler.link_executable([obj], prog,
                                       libraries=libraries,
@@ -149,7 +149,7 @@
             prog = prog + self.compiler.exe_extension
         self.temp_files.append(prog)
 
-        return (src, obj, prog)
+        return src, obj, prog
 
     def _clean(self, *filenames):
         if not filenames:
@@ -182,11 +182,11 @@
         """
         from distutils2.compiler.ccompiler import CompileError
         self._check_compiler()
-        ok = 1
+        ok = True
         try:
             self._preprocess(body, headers, include_dirs, lang)
         except CompileError:
-            ok = 0
+            ok = False
 
         self._clean()
         return ok
@@ -203,20 +203,19 @@
         self._check_compiler()
         src, out = self._preprocess(body, headers, include_dirs, lang)
 
-        if isinstance(pattern, str):
+        if isinstance(pattern, basestring):
             pattern = re.compile(pattern)
 
-        file = open(out)
-        match = 0
-        while 1:
-            line = file.readline()
-            if line == '':
-                break
-            if pattern.search(line):
-                match = 1
-                break
+        with open(out) as file:
+            match = False
+            while True:
+                line = file.readline()
+                if line == '':
+                    break
+                if pattern.search(line):
+                    match = True
+                    break
 
-        file.close()
         self._clean()
         return match
 
@@ -228,9 +227,9 @@
         self._check_compiler()
         try:
             self._compile(body, headers, include_dirs, lang)
-            ok = 1
+            ok = True
         except CompileError:
-            ok = 0
+            ok = False
 
         logger.info(ok and "success!" or "failure.")
         self._clean()
@@ -247,9 +246,9 @@
         try:
             self._link(body, headers, include_dirs,
                        libraries, library_dirs, lang)
-            ok = 1
+            ok = True
         except (CompileError, LinkError):
-            ok = 0
+            ok = False
 
         logger.info(ok and "success!" or "failure.")
         self._clean()
@@ -267,9 +266,9 @@
             src, obj, exe = self._link(body, headers, include_dirs,
                                        libraries, library_dirs, lang)
             self.spawn([exe])
-            ok = 1
-        except (CompileError, LinkError, DistutilsExecError):
-            ok = 0
+            ok = True
+        except (CompileError, LinkError, PackagingExecError):
+            ok = False
 
         logger.info(ok and "success!" or "failure.")
         self._clean()
@@ -281,7 +280,7 @@
     # when implementing a real-world config command!)
 
     def check_func(self, func, headers=None, include_dirs=None,
-                   libraries=None, library_dirs=None, decl=0, call=0):
+                   libraries=None, library_dirs=None, decl=False, call=False):
 
         """Determine if function 'func' is available by constructing a
         source file that refers to 'func', and compiles and links it.
@@ -312,8 +311,6 @@
         return self.try_link(body, headers, include_dirs,
                              libraries, library_dirs)
 
-    # check_func ()
-
     def check_lib(self, library, library_dirs=None, headers=None,
                   include_dirs=None, other_libraries=[]):
         """Determine if 'library' is available to be linked against,
@@ -348,8 +345,5 @@
         logger.info(filename)
     else:
         logger.info(head)
-    file = open(filename)
-    try:
+    with open(filename) as file:
         logger.info(file.read())
-    finally:
-        file.close()
diff --git a/distutils2/command/install_data.py b/distutils2/command/install_data.py
--- a/distutils2/command/install_data.py
+++ b/distutils2/command/install_data.py
@@ -1,20 +1,18 @@
-"""distutils.command.install_data
+"""Install platform-independent data files."""
 
-Implements the Distutils 'install_data' command, for installing
-platform-independent data files."""
+# Contributed by Bastian Kleineidam
 
-# contributed by Bastian Kleineidam
+import os, sys
+from shutil import Error
+from sysconfig import get_paths, format_value
+from distutils2 import logger
+from distutils2.util import convert_path
+from distutils2.command.cmd import Command
 
 
-import os
-from distutils2.command.cmd import Command
-from distutils2.util import change_root, convert_path
-from distutils2._backport.sysconfig import get_paths, format_value
-from distutils2._backport.shutil import Error
-
 class install_data(Command):
 
-    description = "install data files"
+    description = "install platform-independent data files"
 
     user_options = [
         ('install-dir=', 'd',
@@ -32,9 +30,9 @@
         self.outfiles = []
         self.data_files_out = []
         self.root = None
-        self.force = 0
+        self.force = False
         self.data_files = self.distribution.data_files
-        self.warn_dir = 1
+        self.warn_dir = True
 
     def finalize_options(self):
         self.set_undefined_options('install_dist',
@@ -43,19 +41,20 @@
 
     def run(self):
         self.mkpath(self.install_dir)
-        for file in self.data_files.items():
-            destination = convert_path(self.expand_categories(file[1]))
+        for _file in self.data_files.items():
+            destination = convert_path(self.expand_categories(_file[1]))
             dir_dest = os.path.abspath(os.path.dirname(destination))
-            
+
             self.mkpath(dir_dest)
             try:
-                (out, _) = self.copy_file(file[0], dir_dest)
-            except Error, e:
-                self.warn(e)
+                out = self.copy_file(_file[0], dir_dest)[0]
+            except Error:
+                e = sys.exc_info()[1]
+                logger.warning('%s: %s', self.get_command_name(), e)
                 out = destination
 
             self.outfiles.append(out)
-            self.data_files_out.append((file[0], destination))
+            self.data_files_out.append((_file[0], destination))
 
     def expand_categories(self, path_with_categories):
         local_vars = get_paths()
@@ -63,15 +62,16 @@
         expanded_path = format_value(path_with_categories, local_vars)
         expanded_path = format_value(expanded_path, local_vars)
         if '{' in expanded_path and '}' in expanded_path:
-            self.warn("Unable to expand %s, some categories may missing." %
-                path_with_categories)
+            logger.warning(
+                '%s: unable to expand %s, some categories may be missing',
+                self.get_command_name(), path_with_categories)
         return expanded_path
 
     def get_source_files(self):
-        return self.data_files.keys()
+        return list(self.data_files)
 
     def get_inputs(self):
-        return self.data_files.keys()
+        return list(self.data_files)
 
     def get_outputs(self):
         return self.outfiles
diff --git a/distutils2/command/install_dist.py b/distutils2/command/install_dist.py
--- a/distutils2/command/install_dist.py
+++ b/distutils2/command/install_dist.py
@@ -1,27 +1,20 @@
-"""distutils.command.install
-
-Implements the Distutils 'install_dist' command."""
-
+"""Main install command, which calls the other install_* commands."""
 
 import sys
 import os
 
-from distutils2._backport import sysconfig
-from distutils2._backport.sysconfig import (get_config_vars, get_paths,
-                                            get_path, get_config_var)
+import sysconfig
+from sysconfig import get_config_vars, get_paths, get_path, get_config_var
 
 from distutils2 import logger
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsPlatformError
+from distutils2.errors import PackagingPlatformError
 from distutils2.util import write_file
 from distutils2.util import convert_path, change_root, get_platform
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 
-# compatibility with 2.4 and 2.5
-if sys.version < '2.6':
-    HAS_USER_SITE = False
-else:
-    HAS_USER_SITE = True
+
+HAS_USER_SITE = True
 
 
 class install_dist(Command):
@@ -123,7 +116,7 @@
         self.exec_prefix = None
         self.home = None
         if HAS_USER_SITE:
-            self.user = 0
+            self.user = False
 
         # These select only the installation base; it's up to the user to
         # specify the installation scheme (currently, that means supplying
@@ -158,7 +151,7 @@
         # 'install_path_file' is always true unless some outsider meddles
         # with it.
         self.extra_path = None
-        self.install_path_file = 1
+        self.install_path_file = True
 
         # 'force' forces installation, even if target files are not
         # out-of-date.  'skip_build' skips running the "build" command,
@@ -166,9 +159,9 @@
         # a user option, it's just there so the bdist_* commands can turn
         # it off) determines whether we warn about installing to a
         # directory not in sys.path.
-        self.force = 0
-        self.skip_build = 0
-        self.warn_dir = 1
+        self.force = False
+        self.skip_build = False
+        self.warn_dir = True
 
         # These are only here as a conduit from the 'build' command to the
         # 'install_*' commands that do the real work.  ('build_base' isn't
@@ -218,25 +211,27 @@
 
         if ((self.prefix or self.exec_prefix or self.home) and
             (self.install_base or self.install_platbase)):
-            raise DistutilsOptionError(
+            raise PackagingOptionError(
                 "must supply either prefix/exec-prefix/home or "
                 "install-base/install-platbase -- not both")
 
         if self.home and (self.prefix or self.exec_prefix):
-            raise DistutilsOptionError(
+            raise PackagingOptionError(
                 "must supply either home or prefix/exec-prefix -- not both")
 
         if HAS_USER_SITE and self.user and (
                 self.prefix or self.exec_prefix or self.home or
                 self.install_base or self.install_platbase):
-            raise DistutilsOptionError(
+            raise PackagingOptionError(
                 "can't combine user with prefix/exec_prefix/home or "
                 "install_base/install_platbase")
 
         # Next, stuff that's wrong (or dubious) only on certain platforms.
         if os.name != "posix":
             if self.exec_prefix:
-                self.warn("exec-prefix option ignored on this platform")
+                logger.warning(
+                    '%s: exec-prefix option ignored on this platform',
+                    self.get_command_name())
                 self.exec_prefix = None
 
         # Now the interesting logic -- so interesting that we farm it out
@@ -355,14 +350,14 @@
                 self.install_headers is None or
                 self.install_scripts is None or
                 self.install_data is None):
-                raise DistutilsOptionError(
+                raise PackagingOptionError(
                     "install-base or install-platbase supplied, but "
                     "installation scheme is incomplete")
             return
 
         if HAS_USER_SITE and self.user:
             if self.install_userbase is None:
-                raise DistutilsPlatformError(
+                raise PackagingPlatformError(
                     "user base directory is not specified")
             self.install_base = self.install_platbase = self.install_userbase
             self.select_scheme("posix_user")
@@ -372,7 +367,7 @@
         else:
             if self.prefix is None:
                 if self.exec_prefix is not None:
-                    raise DistutilsOptionError(
+                    raise PackagingOptionError(
                         "must not supply exec-prefix without prefix")
 
                 self.prefix = os.path.normpath(sys.prefix)
@@ -390,7 +385,7 @@
         """Finalize options for non-posix platforms"""
         if HAS_USER_SITE and self.user:
             if self.install_userbase is None:
-                raise DistutilsPlatformError(
+                raise PackagingPlatformError(
                     "user base directory is not specified")
             self.install_base = self.install_platbase = self.install_userbase
             self.select_scheme(os.name + "_user")
@@ -405,7 +400,7 @@
             try:
                 self.select_scheme(os.name)
             except KeyError:
-                raise DistutilsPlatformError(
+                raise PackagingPlatformError(
                     "no support for installation on '%s'" % os.name)
 
     def dump_dirs(self, msg):
@@ -428,7 +423,7 @@
         """Set the install directories by applying the install schemes."""
         # it's the caller's problem if they supply a bad name!
         scheme = get_paths(name, expand=False)
-        for key, value in scheme.iteritems():
+        for key, value in scheme.items():
             if key == 'platinclude':
                 key = 'headers'
                 value = os.path.join(value, self.distribution.metadata['Name'])
@@ -469,7 +464,7 @@
             self.extra_path = self.distribution.extra_path
 
         if self.extra_path is not None:
-            if isinstance(self.extra_path, str):
+            if isinstance(self.extra_path, basestring):
                 self.extra_path = self.extra_path.split(',')
 
             if len(self.extra_path) == 1:
@@ -477,7 +472,7 @@
             elif len(self.extra_path) == 2:
                 path_file, extra_dirs = self.extra_path
             else:
-                raise DistutilsOptionError(
+                raise PackagingOptionError(
                     "'extra_path' option must be a list, tuple, or "
                     "comma-separated string with 1 or 2 elements")
 
@@ -504,9 +499,9 @@
         if HAS_USER_SITE and not self.user:
             return
         home = convert_path(os.path.expanduser("~"))
-        for name, path in self.config_vars.iteritems():
+        for name, path in self.config_vars.items():
             if path.startswith(home) and not os.path.isdir(path):
-                os.makedirs(path, 0700)
+                os.makedirs(path, 0o700)
 
     # -- Command execution methods -------------------------------------
 
@@ -521,7 +516,7 @@
             # internally, and not to sys.path, so we don't check the platform
             # matches what we are running.
             if self.warn_dir and build_plat != get_platform():
-                raise DistutilsPlatformError("Can't install when "
+                raise PackagingPlatformError("Can't install when "
                                              "cross-compiling")
 
         # Run all sub-commands (at least those that need to be run)
@@ -536,16 +531,16 @@
             outputs = self.get_outputs()
             if self.root:               # strip any package prefix
                 root_len = len(self.root)
-                for counter in xrange(len(outputs)):
+                for counter in range(len(outputs)):
                     outputs[counter] = outputs[counter][root_len:]
             self.execute(write_file,
                          (self.record, outputs),
                          "writing list of installed files to '%s'" %
                          self.record)
 
-        sys_path = map(os.path.normpath, sys.path)
-        sys_path = map(os.path.normcase, sys_path)
-        install_lib = os.path.normcase(os.path.normpath(self.install_lib))
+        normpath, normcase = os.path.normpath, os.path.normcase
+        sys_path = [normcase(normpath(p)) for p in sys.path]
+        install_lib = normcase(normpath(self.install_lib))
         if (self.warn_dir and
             not (self.path_file and self.install_path_file) and
             install_lib not in sys_path):
@@ -563,7 +558,8 @@
                          (filename, [self.extra_dirs]),
                          "creating %s" % filename)
         else:
-            self.warn("path file '%s' not created" % filename)
+            logger.warning('%s: path file %r not created',
+                           self.get_command_name(),  filename)
 
     # -- Reporting methods ---------------------------------------------
 
diff --git a/distutils2/command/install_distinfo.py b/distutils2/command/install_distinfo.py
--- a/distutils2/command/install_distinfo.py
+++ b/distutils2/command/install_distinfo.py
@@ -1,27 +1,16 @@
-"""
-distutils.command.install_distinfo
-==================================
+"""Create the PEP 376-compliant .dist-info directory."""
 
-:Author: Josip Djolonga
+# Forked from the former install_egg_info command by Josip Djolonga
 
-This module implements the ``install_distinfo`` command that creates the
-``.dist-info`` directory for the distribution, as specified in :pep:`376`.
-Usually, you do not have to call this command directly, it gets called
-automatically by the ``install_dist`` command.
-"""
-
-# This file was created from the code for the former command install_egg_info
-
+import codecs
 import csv
-from distutils2 import logger
-from distutils2._backport.shutil import rmtree
-from distutils2.command.cmd import Command
 import os
 import re
-try:
-    import hashlib
-except ImportError:
-    from distutils2._backport import hashlib
+import hashlib
+
+from distutils2.command.cmd import Command
+from distutils2 import logger
+from shutil import rmtree
 
 
 class install_distinfo(Command):
@@ -72,13 +61,11 @@
         if self.no_resources is None:
             self.no_resources = False
 
-
         metadata = self.distribution.metadata
 
         basename = "%s-%s.dist-info" % (
-                                        to_filename(safe_name(metadata['Name'])),
-                                        to_filename(safe_version(metadata['Version'])),
-                                        )
+            to_filename(safe_name(metadata['Name'])),
+            to_filename(safe_version(metadata['Version'])))
 
         self.distinfo_dir = os.path.join(self.distinfo_dir, basename)
         self.outputs = []
@@ -105,18 +92,14 @@
 
             installer_path = os.path.join(self.distinfo_dir, 'INSTALLER')
             logger.info('creating %s', installer_path)
-            f = open(installer_path, 'w')
-            try:
+            with open(installer_path, 'w') as f:
                 f.write(self.installer)
-            finally:
-                f.close()
             self.outputs.append(installer_path)
 
             if self.requested:
                 requested_path = os.path.join(self.distinfo_dir, 'REQUESTED')
                 logger.info('creating %s', requested_path)
-                f = open(requested_path, 'w')
-                f.close()
+                open(requested_path, 'wb').close()
                 self.outputs.append(requested_path)
 
 
@@ -126,25 +109,21 @@
                     resources_path = os.path.join(self.distinfo_dir,
                                                   'RESOURCES')
                     logger.info('creating %s', resources_path)
-                    f = open(resources_path, 'wb')
-                    try:
+                    with open(resources_path, 'wb') as f:
                         writer = csv.writer(f, delimiter=',',
-                                            lineterminator=os.linesep,
+                                            lineterminator='\n',
                                             quotechar='"')
                         for tuple in install_data.get_resources_out():
                             writer.writerow(tuple)
 
                         self.outputs.append(resources_path)
-                    finally:
-                        f.close()
 
             if not self.no_record:
                 record_path = os.path.join(self.distinfo_dir, 'RECORD')
                 logger.info('creating %s', record_path)
-                f = open(record_path, 'wb')
-                try:
+                with codecs.open(record_path, 'w', encoding='utf-8') as f:
                     writer = csv.writer(f, delimiter=',',
-                                        lineterminator=os.linesep,
+                                        lineterminator='\n',
                                         quotechar='"')
 
                     install = self.get_finalized_command('install_dist')
@@ -155,18 +134,15 @@
                             writer.writerow((fpath, '', ''))
                         else:
                             size = os.path.getsize(fpath)
-                            fd = open(fpath, 'r')
-                            hash = hashlib.md5()
-                            hash.update(fd.read())
+                            with open(fpath, 'rb') as fp:
+                                hash = hashlib.md5()
+                                hash.update(fp.read())
                             md5sum = hash.hexdigest()
                             writer.writerow((fpath, md5sum, size))
 
                     # add the RECORD file itself
                     writer.writerow((record_path, '', ''))
                     self.outputs.append(record_path)
-                finally:
-                    f.close()
-
 
     def get_outputs(self):
         return self.outputs
diff --git a/distutils2/command/install_headers.py b/distutils2/command/install_headers.py
--- a/distutils2/command/install_headers.py
+++ b/distutils2/command/install_headers.py
@@ -1,8 +1,4 @@
-"""distutils.command.install_headers
-
-Implements the Distutils 'install_headers' command, to install C/C++ header
-files to the Python include directory."""
-
+"""Install C/C++ header files to the Python include directory."""
 
 from distutils2.command.cmd import Command
 
@@ -22,7 +18,7 @@
 
     def initialize_options(self):
         self.install_dir = None
-        self.force = 0
+        self.force = False
         self.outfiles = []
 
     def finalize_options(self):
@@ -37,7 +33,7 @@
 
         self.mkpath(self.install_dir)
         for header in headers:
-            (out, _) = self.copy_file(header, self.install_dir)
+            out = self.copy_file(header, self.install_dir)[0]
             self.outfiles.append(out)
 
     def get_inputs(self):
@@ -45,5 +41,3 @@
 
     def get_outputs(self):
         return self.outfiles
-
-# class install_headers
diff --git a/distutils2/command/install_lib.py b/distutils2/command/install_lib.py
--- a/distutils2/command/install_lib.py
+++ b/distutils2/command/install_lib.py
@@ -1,14 +1,12 @@
-"""distutils.command.install_lib
-
-Implements the Distutils 'install_lib' command
-(install all Python modules)."""
-
+"""Install all modules (extensions and pure Python)."""
 
 import os
 import sys
+import logging
 
+from distutils2 import logger
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 
 
 # Extension for Python source files.
@@ -19,7 +17,7 @@
 
 class install_lib(Command):
 
-    description = "install all Python modules (extensions and pure Python)"
+    description = "install all modules (extensions and pure Python)"
 
     # The byte-compilation options are a tad confusing.  Here are the
     # possible scenarios:
@@ -55,7 +53,7 @@
         # let the 'install_dist' command dictate our installation directory
         self.install_dir = None
         self.build_dir = None
-        self.force = 0
+        self.force = False
         self.compile = None
         self.optimize = None
         self.skip_build = None
@@ -70,7 +68,7 @@
                                    'force', 'compile', 'optimize', 'skip_build')
 
         if self.compile is None:
-            self.compile = 1
+            self.compile = True
         if self.optimize is None:
             self.optimize = 0
 
@@ -80,7 +78,7 @@
                 if self.optimize not in (0, 1, 2):
                     raise AssertionError
             except (ValueError, AssertionError):
-                raise DistutilsOptionError, "optimize must be 0, 1, or 2"
+                raise PackagingOptionError("optimize must be 0, 1, or 2")
 
     def run(self):
         # Make sure we have built everything we need first
@@ -109,14 +107,19 @@
         if os.path.isdir(self.build_dir):
             outfiles = self.copy_tree(self.build_dir, self.install_dir)
         else:
-            self.warn("'%s' does not exist -- no Python modules to install" %
-                      self.build_dir)
+            logger.warning(
+                '%s: %r does not exist -- no Python modules to install',
+                self.get_command_name(), self.build_dir)
             return
         return outfiles
 
     def byte_compile(self, files):
-        if hasattr(sys, 'dont_write_bytecode') and sys.dont_write_bytecode:
-            self.warn('byte-compiling is disabled, skipping.')
+        if getattr(sys, 'dont_write_bytecode'):
+            # XXX do we want this?  because a Python runs without bytecode
+            # doesn't mean that the *dists should not contain bytecode
+            #--or does it?
+            logger.warning('%s: byte-compiling is disabled, skipping.',
+                           self.get_command_name())
             return
 
         from distutils2.util import byte_compile
@@ -127,6 +130,10 @@
         # should at least generate usable bytecode in RPM distributions.
         install_root = self.get_finalized_command('install_dist').root
 
+        # Temporary kludge until we remove the verbose arguments and use
+        # logging everywhere
+        verbose = logger.getEffectiveLevel() >= logging.DEBUG
+
         if self.compile:
             byte_compile(files, optimize=0,
                          force=self.force, prefix=install_root,
@@ -134,7 +141,8 @@
         if self.optimize > 0:
             byte_compile(files, optimize=self.optimize,
                          force=self.force, prefix=install_root,
-                         verbose=self.verbose, dry_run=self.dry_run)
+                         verbose=verbose,
+                         dry_run=self.dry_run)
 
 
     # -- Utility methods -----------------------------------------------
diff --git a/distutils2/command/install_scripts.py b/distutils2/command/install_scripts.py
--- a/distutils2/command/install_scripts.py
+++ b/distutils2/command/install_scripts.py
@@ -1,17 +1,12 @@
-"""distutils.command.install_scripts
+"""Install scripts."""
 
-Implements the Distutils 'install_scripts' command, for installing
-Python scripts."""
-
-# contributed by Bastian Kleineidam
-
+# Contributed by Bastian Kleineidam
 
 import os
 from distutils2.command.cmd import Command
 from distutils2 import logger
-from stat import ST_MODE
 
-class install_scripts (Command):
+class install_scripts(Command):
 
     description = "install scripts (Python or otherwise)"
 
@@ -25,19 +20,19 @@
     boolean_options = ['force', 'skip-build']
 
 
-    def initialize_options (self):
+    def initialize_options(self):
         self.install_dir = None
-        self.force = 0
+        self.force = False
         self.build_dir = None
         self.skip_build = None
 
-    def finalize_options (self):
+    def finalize_options(self):
         self.set_undefined_options('build', ('build_scripts', 'build_dir'))
         self.set_undefined_options('install_dist',
                                    ('install_scripts', 'install_dir'),
                                    'force', 'skip_build')
 
-    def run (self):
+    def run(self):
         if not self.skip_build:
             self.run_command('build_scripts')
 
@@ -53,11 +48,11 @@
                 if self.dry_run:
                     logger.info("changing mode of %s", file)
                 else:
-                    mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777
+                    mode = (os.stat(file).st_mode | 0o555) & 0o7777
                     logger.info("changing mode of %s to %o", file, mode)
                     os.chmod(file, mode)
 
-    def get_inputs (self):
+    def get_inputs(self):
         return self.distribution.scripts or []
 
     def get_outputs(self):
diff --git a/distutils2/command/register.py b/distutils2/command/register.py
--- a/distutils2/command/register.py
+++ b/distutils2/command/register.py
@@ -1,26 +1,20 @@
-"""distutils.command.register
+"""Register a release with a project index."""
 
-Implements the Distutils 'register' command (register with the repository).
-"""
+# Contributed by Richard Jones
 
-# created 2002/10/21, Richard Jones
-
-
-import urllib2
+import sys
 import getpass
 import urlparse
-import StringIO
-import logging
+import urllib2
 
+from distutils2 import logger
+from distutils2.util import (read_pypirc, generate_pypirc, DEFAULT_REPOSITORY,
+                            DEFAULT_REALM, get_pypirc_path, encode_multipart)
 from distutils2.command.cmd import Command
-from distutils2 import logger
-from distutils2.metadata import metadata_to_dict
-from distutils2.util import (read_pypirc, generate_pypirc, DEFAULT_REPOSITORY,
-                             DEFAULT_REALM, get_pypirc_path)
 
 class register(Command):
 
-    description = "register the distribution with the Python package index"
+    description = "register a release with PyPI"
     user_options = [
         ('repository=', 'r',
          "repository URL [default: %s]" % DEFAULT_REPOSITORY),
@@ -37,9 +31,9 @@
     def initialize_options(self):
         self.repository = None
         self.realm = None
-        self.show_response = 0
-        self.list_classifiers = 0
-        self.strict = 0
+        self.show_response = False
+        self.list_classifiers = False
+        self.strict = False
 
     def finalize_options(self):
         if self.repository is None:
@@ -48,14 +42,17 @@
             self.realm = DEFAULT_REALM
 
     def run(self):
-        self.finalize_options()
         self._set_config()
 
         # Check the package metadata
         check = self.distribution.get_command_obj('check')
-        check.strict = self.strict
-        check.all = 1
-        self.run_command('check')
+        if check.strict != self.strict and not check.all:
+            # If check was already run but with different options,
+            # re-run it
+            check.strict = self.strict
+            check.all = True
+            self.distribution.have_run.pop('check', None)
+            self.run_command('check')
 
         if self.dry_run:
             self.verify_metadata()
@@ -123,6 +120,9 @@
              3. set the password to a random string and email the user.
 
         '''
+        # TODO factor registration out into another method
+        # TODO use print to print, not logging
+
         # see if we can short-cut and get the username/password from the
         # config
         if self.has_config:
@@ -136,24 +136,24 @@
         # get the user's login info
         choices = '1 2 3 4'.split()
         while choice not in choices:
-            self.announce('''\
+            logger.info('''\
 We need to know who you are, so please choose either:
  1. use your existing login,
  2. register as a new user,
  3. have the server generate a new password for you (and email it to you), or
  4. quit
-Your selection [default 1]: ''', logging.INFO)
+Your selection [default 1]: ''')
 
-            choice = raw_input()
+            choice = input()
             if not choice:
                 choice = '1'
             elif choice not in choices:
-                print 'Please choose one of the four options!'
+                print('Please choose one of the four options!')
 
         if choice == '1':
             # get the username and password
             while not username:
-                username = raw_input('Username: ')
+                username = input('Username: ')
             while not password:
                 password = getpass.getpass('Password: ')
 
@@ -164,8 +164,7 @@
             # send the info to the server and report the result
             code, result = self.post_to_server(self.build_post_data('submit'),
                 auth)
-            self.announce('Server response (%s): %s' % (code, result),
-                          logging.INFO)
+            logger.info('Server response (%s): %s', code, result)
 
             # possibly save the login
             if code == 200:
@@ -174,14 +173,13 @@
                     # so the upload command can reuse it
                     self.distribution.password = password
                 else:
-                    self.announce(('I can store your PyPI login so future '
-                                   'submissions will be faster.'),
-                                   logging.INFO)
-                    self.announce('(the login will be stored in %s)' % \
-                                  get_pypirc_path(), logging.INFO)
+                    logger.info(
+                        'I can store your PyPI login so future submissions '
+                        'will be faster.\n(the login will be stored in %s)',
+                        get_pypirc_path())
                     choice = 'X'
                     while choice.lower() not in 'yn':
-                        choice = raw_input('Save your login (y/N)?')
+                        choice = input('Save your login (y/N)?')
                         if not choice:
                             choice = 'n'
                     if choice.lower() == 'y':
@@ -192,7 +190,7 @@
             data['name'] = data['password'] = data['email'] = ''
             data['confirm'] = None
             while not data['name']:
-                data['name'] = raw_input('Username: ')
+                data['name'] = input('Username: ')
             while data['password'] != data['confirm']:
                 while not data['password']:
                     data['password'] = getpass.getpass('Password: ')
@@ -201,9 +199,9 @@
                 if data['password'] != data['confirm']:
                     data['password'] = ''
                     data['confirm'] = None
-                    print "Password and confirm don't match!"
+                    print("Password and confirm don't match!")
             while not data['email']:
-                data['email'] = raw_input('   EMail: ')
+                data['email'] = input('   EMail: ')
             code, result = self.post_to_server(data)
             if code != 200:
                 logger.info('server response (%s): %s', code, result)
@@ -214,14 +212,14 @@
             data = {':action': 'password_reset'}
             data['email'] = ''
             while not data['email']:
-                data['email'] = raw_input('Your email address: ')
+                data['email'] = input('Your email address: ')
             code, result = self.post_to_server(data)
             logger.info('server response (%s): %s', code, result)
 
     def build_post_data(self, action):
         # figure the data to send - the metadata plus some additional
         # information used by the package server
-        data = metadata_to_dict(self.distribution.metadata)
+        data = self.distribution.metadata.todict()
         data[':action'] = action
         return data
 
@@ -230,33 +228,13 @@
         ''' Post a query to the server, and return a string response.
         '''
         if 'name' in data:
-            self.announce('Registering %s to %s' % (data['name'],
-                                                   self.repository),
-                                                   logging.INFO)
+            logger.info('Registering %s to %s', data['name'], self.repository)
         # Build up the MIME payload for the urllib2 POST data
-        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
-        sep_boundary = '\n--' + boundary
-        end_boundary = sep_boundary + '--'
-        body = StringIO.StringIO()
-        for key, value in data.iteritems():
-            # handle multiple entries for the same name
-            if not isinstance(value, (tuple, list)):
-                value = [value]
-
-            for value in value:
-                body.write(sep_boundary)
-                body.write('\nContent-Disposition: form-data; name="%s"'%key)
-                body.write("\n\n")
-                body.write(value)
-                if value and value[-1] == '\r':
-                    body.write('\n')  # write an extra newline (lurve Macs)
-        body.write(end_boundary)
-        body.write("\n")
-        body = body.getvalue()
+        content_type, body = encode_multipart(data.items(), [])
 
         # build the Request
         headers = {
-            'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
+            'Content-type': content_type,
             'Content-length': str(len(body))
         }
         req = urllib2.Request(self.repository, body, headers)
@@ -268,18 +246,19 @@
         data = ''
         try:
             result = opener.open(req)
-        except urllib2.HTTPError, e:
+        except urllib2.HTTPError:
+            e = sys.exc_info()[1]
             if self.show_response:
                 data = e.fp.read()
             result = e.code, e.msg
-        except urllib2.URLError, e:
-            result = 500, str(e)
+        except urllib2.URLError:
+            result = 500, str(sys.exc_info()[1])
         else:
             if self.show_response:
                 data = result.read()
             result = 200, 'OK'
         if self.show_response:
             dashes = '-' * 75
-            self.announce('%s%s%s' % (dashes, data, dashes))
+            logger.info('%s%s%s', dashes, data, dashes)
 
         return result
diff --git a/distutils2/command/sdist.py b/distutils2/command/sdist.py
--- a/distutils2/command/sdist.py
+++ b/distutils2/command/sdist.py
@@ -1,34 +1,27 @@
-"""distutils.command.sdist
+"""Create a source distribution."""
 
-Implements the Distutils 'sdist' command (create a source distribution)."""
 import os
+import re
 import sys
+from StringIO import StringIO
 from shutil import rmtree
-import re
-from StringIO import StringIO
 
-try:
-    from shutil import get_archive_formats
-except ImportError:
-    from distutils2._backport.shutil import get_archive_formats
-
+from distutils2 import logger
+from distutils2.util import resolve_name, get_archive_formats
+from distutils2.errors import (PackagingPlatformError, PackagingOptionError,
+                              PackagingModuleError, PackagingFileError)
 from distutils2.command import get_command_names
 from distutils2.command.cmd import Command
-from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError,
-                               DistutilsModuleError, DistutilsFileError)
 from distutils2.manifest import Manifest
-from distutils2 import logger
-from distutils2.util import resolve_name
+
 
 def show_formats():
     """Print all possible values for the 'formats' option (used by
     the "--help-formats" command-line option).
     """
     from distutils2.fancy_getopt import FancyGetopt
-    formats = []
-    for name, desc in get_archive_formats():
-        formats.append(("formats=" + name, None, desc))
-    formats.sort()
+    formats = sorted(('formats=' + name, None, desc)
+                     for name, desc in get_archive_formats())
     FancyGetopt(formats).print_help(
         "List of available source distribution formats:")
 
@@ -36,6 +29,7 @@
 _COLLAPSE_PATTERN = re.compile('\\\w\n', re.M)
 _COMMENTED_LINE = re.compile('^#.*\n$|^\w*\n$', re.M)
 
+
 class sdist(Command):
 
     description = "create a source distribution (tarball, zip file, etc.)"
@@ -84,26 +78,24 @@
         ]
 
     negative_opt = {'no-defaults': 'use-defaults',
-                    'no-prune': 'prune' }
+                    'no-prune': 'prune'}
 
     default_format = {'posix': 'gztar',
-                      'nt': 'zip' }
-
+                      'nt': 'zip'}
 
     def initialize_options(self):
         self.manifest = None
-
         # 'use_defaults': if true, we will include the default file set
         # in the manifest
-        self.use_defaults = 1
-        self.prune = 1
-        self.manifest_only = 0
+        self.use_defaults = True
+        self.prune = True
+        self.manifest_only = False
         self.formats = None
-        self.keep_temp = 0
+        self.keep_temp = False
         self.dist_dir = None
 
         self.archive_files = None
-        self.metadata_check = 1
+        self.metadata_check = True
         self.owner = None
         self.group = None
         self.filelist = None
@@ -125,14 +117,13 @@
             try:
                 self.formats = [self.default_format[os.name]]
             except KeyError:
-                raise DistutilsPlatformError, \
-                      "don't know how to create source distributions " + \
-                      "on platform %s" % os.name
+                raise PackagingPlatformError("don't know how to create source "
+                       "distributions on platform %s" % os.name)
 
         bad_format = self._check_archive_formats(self.formats)
         if bad_format:
-            raise DistutilsOptionError, \
-                  "unknown archive format '%s'" % bad_format
+            raise PackagingOptionError("unknown archive format '%s'" \
+                        % bad_format)
 
         if self.dist_dir is None:
             self.dist_dir = "dist"
@@ -143,7 +134,7 @@
         if self.manifest_builders is None:
             self.manifest_builders = []
         else:
-            if isinstance(self.manifest_builders, str):
+            if isinstance(self.manifest_builders, basestring):
                 self.manifest_builders = self.manifest_builders.split(',')
             builders = []
             for builder in self.manifest_builders:
@@ -152,14 +143,13 @@
                     continue
                 try:
                     builder = resolve_name(builder)
-                except ImportError, e:
-                    raise DistutilsModuleError(e)
+                except ImportError:
+                    raise PackagingModuleError(sys.exc_info()[1])
 
                 builders.append(builder)
 
             self.manifest_builders = builders
 
-
     def run(self):
         # 'filelist' contains the list of files that will make up the
         # manifest
@@ -191,7 +181,8 @@
         """
         template_exists = len(self.distribution.extra_files) > 0
         if not template_exists:
-            self.warn('Using default file list')
+            logger.warning('%s: using default file list',
+                           self.get_command_name())
         self.filelist.findall()
 
         if self.use_defaults:
@@ -210,20 +201,24 @@
         self.filelist.write(self.manifest)
 
     def add_defaults(self):
-        """Add all the default files to self.filelist:
-          - all pure Python modules mentioned in setup script
-          - all files pointed by package_data (build_py)
-          - all files defined in data_files.
-          - all files defined as scripts.
-          - all C sources listed as part of extensions or C libraries
-            in the setup script (doesn't catch C headers!)
-        Warns if (README or README.txt) or setup.py are missing; everything
-        else is optional.
+        """Add all default files to self.filelist.
+
+        In addition to the setup.cfg file, this will include all files returned
+        by the get_source_files of every registered command.  This will find
+        Python modules and packages, data files listed in package_data_,
+        data_files and extra_files, scripts, C sources of extension modules or
+        C libraries (headers are missing).
         """
+        if os.path.exists('setup.cfg'):
+            self.filelist.append('setup.cfg')
+        else:
+            logger.warning("%s: standard 'setup.cfg' file not found",
+                           self.get_command_name())
+
         for cmd_name in get_command_names():
             try:
                 cmd_obj = self.get_finalized_command(cmd_name)
-            except DistutilsOptionError:
+            except PackagingOptionError:
                 pass
             else:
                 self.filelist.extend(cmd_obj.get_source_files())
@@ -252,8 +247,7 @@
         vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
                     '_darcs']
         vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
-        self.filelist.exclude_pattern(vcs_ptrn, is_regex=1)
-
+        self.filelist.exclude_pattern(vcs_ptrn, is_regex=True)
 
     def make_release_tree(self, base_dir, files):
         """Create the directory tree that will become the source
@@ -285,18 +279,19 @@
             msg = "copying files to %s..." % base_dir
 
         if not files:
-            logger.warn("no files to distribute -- empty manifest?")
+            logger.warning("no files to distribute -- empty manifest?")
         else:
             logger.info(msg)
 
         for file in self.distribution.metadata.requires_files:
             if file not in files:
-                msg = "'%s' must be included explicitly in 'extra_files'" % file
-                raise DistutilsFileError(msg)
+                msg = "'%s' must be included explicitly in 'extra_files'" \
+                        % file
+                raise PackagingFileError(msg)
 
         for file in files:
             if not os.path.isfile(file):
-                logger.warn("'%s' not a regular file -- skipping", file)
+                logger.warning("'%s' not a regular file -- skipping", file)
             else:
                 dest = os.path.join(base_dir, file)
                 self.copy_file(file, dest, link=link)
@@ -342,12 +337,12 @@
         """
         return self.archive_files
 
-    def create_tree(self, base_dir, files, mode=0777, verbose=1, dry_run=0):
-        need_dir = {}
+    def create_tree(self, base_dir, files, mode=0o777, verbose=1,
+                    dry_run=False):
+        need_dir = set()
         for file in files:
-            need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1
-        need_dirs = sorted(need_dir)
+            need_dir.add(os.path.join(base_dir, os.path.dirname(file)))
 
         # Now create them
-        for dir in need_dirs:
+        for dir in sorted(need_dir):
             self.mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
diff --git a/distutils2/command/test.py b/distutils2/command/test.py
--- a/distutils2/command/test.py
+++ b/distutils2/command/test.py
@@ -1,20 +1,20 @@
+"""Run the project's test suite."""
+
 import os
 import sys
+import logging
 import unittest
 
+from distutils2 import logger
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsOptionError
+from distutils2.database import get_distribution
+from distutils2.errors import PackagingOptionError
 from distutils2.util import resolve_name
 
-try:
-    from pkgutil import get_distribution
-except ImportError:
-    from distutils2._backport.pkgutil import get_distribution
-
 
 class test(Command):
 
-    description = "run the distribution's test suite"
+    description = "run the project's test suite"
 
     user_options = [
         ('suite=', 's',
@@ -34,11 +34,11 @@
         self.build_lib = self.get_finalized_command("build").build_lib
         for requirement in self.tests_require:
             if get_distribution(requirement) is None:
-                self.announce("test dependency %s is not installed, "
-                              "tests may fail" % requirement)
+                logger.warning("test dependency %s is not installed, "
+                               "tests may fail", requirement)
         if (not self.suite and not self.runner and
             self.get_ut_with_discovery() is None):
-            raise DistutilsOptionError(
+            raise PackagingOptionError(
                 "no test discovery available, please give a 'suite' or "
                 "'runner' option or install unittest2")
 
@@ -60,16 +60,22 @@
             self.run_command('build')
             sys.path.insert(0, build.build_lib)
 
+            # Temporary kludge until we remove the verbose arguments and use
+            # logging everywhere
+            logger = logging.getLogger('distutils2')
+            verbose = logger.getEffectiveLevel() >= logging.DEBUG
+            verbosity = verbose + 1
+
             # run the tests
             if self.runner:
                 resolve_name(self.runner)()
             elif self.suite:
-                runner = unittest.TextTestRunner(verbosity=self.verbose + 1)
+                runner = unittest.TextTestRunner(verbosity=verbosity)
                 runner.run(resolve_name(self.suite)())
             elif self.get_ut_with_discovery():
                 ut = self.get_ut_with_discovery()
                 test_suite = ut.TestLoader().discover(os.curdir)
-                runner = ut.TextTestRunner(verbosity=self.verbose + 1)
+                runner = ut.TextTestRunner(verbosity=verbosity)
                 runner.run(test_suite)
         finally:
             sys.path[:] = prev_syspath
diff --git a/distutils2/command/upload.py b/distutils2/command/upload.py
--- a/distutils2/command/upload.py
+++ b/distutils2/command/upload.py
@@ -1,27 +1,21 @@
-"""distutils.command.upload
+"""Upload a distribution to a project index."""
 
-Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
-import os
+import os, sys
 import socket
+import logging
 import platform
-import logging
-from urllib2 import urlopen, Request, HTTPError
+import urlparse
+from io import BytesIO
 from base64 import standard_b64encode
-import urlparse
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-try:
-    from hashlib import md5
-except ImportError:
-    from distutils2._backport.hashlib import md5
+from hashlib import md5
+from urllib2 import HTTPError
+from urllib2 import urlopen, Request
 
-from distutils2.errors import DistutilsOptionError
-from distutils2.util import spawn
+from distutils2 import logger
+from distutils2.errors import PackagingOptionError
+from distutils2.util import (spawn, read_pypirc, DEFAULT_REPOSITORY,
+                            DEFAULT_REALM, encode_multipart)
 from distutils2.command.cmd import Command
-from distutils2.metadata import metadata_to_dict
-from distutils2.util import read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM
 
 
 class upload(Command):
@@ -46,10 +40,10 @@
     def initialize_options(self):
         self.repository = None
         self.realm = None
-        self.show_response = 0
+        self.show_response = False
         self.username = ''
         self.password = ''
-        self.show_response = 0
+        self.show_response = False
         self.sign = False
         self.identity = None
         self.upload_docs = False
@@ -60,9 +54,8 @@
         if self.realm is None:
             self.realm = DEFAULT_REALM
         if self.identity and not self.sign:
-            raise DistutilsOptionError(
-                "Must use --sign for --identity to have meaning"
-            )
+            raise PackagingOptionError(
+                "Must use --sign for --identity to have meaning")
         config = read_pypirc(self.repository, self.realm)
         if config != {}:
             self.username = config['username']
@@ -77,7 +70,8 @@
 
     def run(self):
         if not self.distribution.dist_files:
-            raise DistutilsOptionError("No dist file created in earlier command")
+            raise PackagingOptionError(
+                "No dist file created in earlier command")
         for command, pyversion, filename in self.distribution.dist_files:
             self.upload_file(command, pyversion, filename)
         if self.upload_docs:
@@ -90,13 +84,13 @@
     # XXX to be refactored with register.post_to_server
     def upload_file(self, command, pyversion, filename):
         # Makes sure the repository URL is compliant
-        schema, netloc, url, params, query, fragments = \
+        scheme, netloc, url, params, query, fragments = \
             urlparse.urlparse(self.repository)
         if params or query or fragments:
             raise AssertionError("Incompatible url %s" % self.repository)
 
-        if schema not in ('http', 'https'):
-            raise AssertionError("unsupported schema " + schema)
+        if scheme not in ('http', 'https'):
+            raise AssertionError("unsupported scheme " + scheme)
 
         # Sign if requested
         if self.sign:
@@ -108,99 +102,69 @@
 
         # Fill in the data - send all the metadata in case we need to
         # register a new release
-        content = open(filename,'rb').read()
+        with open(filename, 'rb') as f:
+            content = f.read()
 
-        data = metadata_to_dict(self.distribution.metadata)
+        data = self.distribution.metadata.todict()
 
         # extra upload infos
         data[':action'] = 'file_upload'
         data['protcol_version'] = '1'
-        data['content'] = [os.path.basename(filename), content]
+        data['content'] = (os.path.basename(filename), content)
         data['filetype'] = command
         data['pyversion'] = pyversion
         data['md5_digest'] = md5(content).hexdigest()
 
-        comment = ''
         if command == 'bdist_dumb':
-            comment = 'built for %s' % platform.platform(terse=1)
-        data['comment'] = comment
+            data['comment'] = 'built for %s' % platform.platform(terse=True)
 
         if self.sign:
-            data['gpg_signature'] = [(os.path.basename(filename) + ".asc",
-                                      open(filename+".asc").read())]
+            with open(filename + '.asc') as fp:
+                sig = fp.read()
+            data['gpg_signature'] = [
+                (os.path.basename(filename) + ".asc", sig)]
 
         # set up the authentication
-        auth = "Basic " + standard_b64encode(self.username + ":" +
-                                             self.password)
+        # The exact encoding of the authentication string is debated.
+        # Anyway PyPI only accepts ascii for both username or password.
+        user_pass = (self.username + ":" + self.password).encode('ascii')
+        auth = b"Basic " + standard_b64encode(user_pass)
 
         # Build up the MIME payload for the POST data
-        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
-        sep_boundary = '\n--' + boundary
-        end_boundary = sep_boundary + '--'
-        body = StringIO()
-        file_fields = ('content', 'gpg_signature')
+        files = []
+        for key in ('content', 'gpg_signature'):
+            if key in data:
+                filename_, value = data.pop(key)
+                files.append((key, filename_, value))
 
-        for key, values in data.iteritems():
-            # handle multiple entries for the same name
-            if not isinstance(values, (tuple, list)):
-                values = [values]
+        content_type, body = encode_multipart(data.items(), files)
 
-            content_dispo = 'Content-Disposition: form-data; name="%s"' % key
-
-            if key in file_fields:
-                filename_, content = values
-                filename_ = ';filename="%s"' % filename_
-                body.write(sep_boundary)
-                body.write("\n")
-                body.write(content_dispo)
-                body.write(filename_)
-                body.write("\n\n")
-                body.write(content)
-            else:
-                for value in values:
-                    body.write(sep_boundary)
-                    body.write("\n")
-                    body.write(content_dispo)
-                    body.write("\n\n")
-                    body.write(value)
-                    if value and value[-1] == '\r':
-                        # write an extra newline (lurve Macs)
-                        body.write('\n')
-
-        body.write(end_boundary)
-        body.write("\n")
-        body = body.getvalue()
-
-        self.announce("Submitting %s to %s" % (filename, self.repository),
-                      logging.INFO)
+        logger.info("Submitting %s to %s", filename, self.repository)
 
         # build the Request
-        headers = {'Content-type':
-                        'multipart/form-data; boundary=%s' % boundary,
+        headers = {'Content-type': content_type,
                    'Content-length': str(len(body)),
                    'Authorization': auth}
 
-        request = Request(self.repository, data=body,
-                          headers=headers)
+        request = Request(self.repository, body, headers)
         # send the data
         try:
             result = urlopen(request)
             status = result.code
             reason = result.msg
-        except socket.error, e:
-            self.announce(str(e), logging.ERROR)
+        except socket.error:
+            logger.error(sys.exc_info()[1])
             return
-        except HTTPError, e:
+        except HTTPError:
+            e = sys.exc_info()[1]
             status = e.code
             reason = e.msg
 
         if status == 200:
-            self.announce('Server response (%s): %s' % (status, reason),
-                          logging.INFO)
+            logger.info('Server response (%s): %s', status, reason)
         else:
-            self.announce('Upload failed (%s): %s' % (status, reason),
-                          logging.ERROR)
+            logger.error('Upload failed (%s): %s', status, reason)
 
-        if self.show_response:
-            msg = '\n'.join(('-' * 75, result.read(), '-' * 75))
-            self.announce(msg, logging.INFO)
+        if self.show_response and logger.isEnabledFor(logging.INFO):
+            sep = '-' * 75
+            logger.info('%s\n%s\n%s', sep, result.read().decode(), sep)
diff --git a/distutils2/command/upload_docs.py b/distutils2/command/upload_docs.py
--- a/distutils2/command/upload_docs.py
+++ b/distutils2/command/upload_docs.py
@@ -1,65 +1,38 @@
-import os
+"""Upload HTML documentation to a project index."""
+
+import os, sys
 import base64
-import httplib
 import socket
-import urlparse
 import zipfile
 import logging
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
+import httplib
+import urlparse
+from io import BytesIO
 
 from distutils2 import logger
-from distutils2.command.upload import upload
+from distutils2.util import (read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM,
+                            encode_multipart)
+from distutils2.errors import PackagingFileError
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsFileError
-from distutils2.util import read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM
+
 
 def zip_dir(directory):
-    """Compresses recursively contents of directory into a StringIO object"""
-    destination = StringIO()
-    zip_file = zipfile.ZipFile(destination, "w")
-    for root, dirs, files in os.walk(directory):
-        for name in files:
-            full = os.path.join(root, name)
-            relative = root[len(directory):].lstrip(os.path.sep)
-            dest = os.path.join(relative, name)
-            zip_file.write(full, dest)
-    zip_file.close()
+    """Compresses recursively contents of directory into a BytesIO object"""
+    destination = BytesIO()
+    with zipfile.ZipFile(destination, "w") as zip_file:
+        for root, dirs, files in os.walk(directory):
+            for name in files:
+                full = os.path.join(root, name)
+                relative = root[len(directory):].lstrip(os.path.sep)
+                dest = os.path.join(relative, name)
+                zip_file.write(full, dest)
     return destination
 
-# grabbed from
-#    http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/
-def encode_multipart(fields, files, boundary=None):
-    """
-    fields is a sequence of (name, value) elements for regular form fields.
-    files is a sequence of (name, filename, value) elements for data to be uploaded as files
-    Return (content_type, body) ready for httplib.HTTP instance
-    """
-    if boundary is None:
-        boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
-    l = []
-    for (key, value) in fields:
-        l.extend([
-            '--' + boundary,
-            'Content-Disposition: form-data; name="%s"' % key,
-            '',
-            value])
-    for (key, filename, value) in files:
-        l.extend([
-            '--' + boundary,
-            'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename),
-            '',
-            value])
-    l.append('--' + boundary + '--')
-    l.append('')
-    body =  '\r\n'.join(l)
-    content_type = 'multipart/form-data; boundary=%s' % boundary
-    return content_type, body
 
 class upload_docs(Command):
 
+    description = "upload HTML documentation to PyPI"
+
     user_options = [
         ('repository=', 'r',
          "repository URL [default: %s]" % DEFAULT_REPOSITORY),
@@ -72,7 +45,7 @@
     def initialize_options(self):
         self.repository = None
         self.realm = None
-        self.show_response = 0
+        self.show_response = False
         self.upload_dir = None
         self.username = ''
         self.password = ''
@@ -87,7 +60,7 @@
             self.upload_dir = os.path.join(build.build_base, "docs")
             if not os.path.isdir(self.upload_dir):
                 self.upload_dir = os.path.join(build.build_base, "doc")
-        self.announce('Using upload directory %s' % self.upload_dir)
+        logger.info('Using upload directory %s', self.upload_dir)
         self.verify_upload_dir(self.upload_dir)
         config = read_pypirc(self.repository, self.realm)
         if config != {}:
@@ -101,30 +74,31 @@
         index_location = os.path.join(upload_dir, "index.html")
         if not os.path.exists(index_location):
             mesg = "No 'index.html found in docs directory (%s)"
-            raise DistutilsFileError(mesg % upload_dir)
+            raise PackagingFileError(mesg % upload_dir)
 
     def run(self):
         name = self.distribution.metadata['Name']
         version = self.distribution.metadata['Version']
         zip_file = zip_dir(self.upload_dir)
 
-        fields = [(':action', 'doc_upload'), ('name', name), ('version', version)]
+        fields = [(':action', 'doc_upload'),
+                  ('name', name), ('version', version)]
         files = [('content', name, zip_file.getvalue())]
         content_type, body = encode_multipart(fields, files)
 
         credentials = self.username + ':' + self.password
-        auth = "Basic " + base64.encodestring(credentials).strip()
+        auth = b"Basic " + base64.encodebytes(credentials.encode()).strip()
 
-        self.announce("Submitting documentation to %s" % (self.repository))
+        logger.info("Submitting documentation to %s", self.repository)
 
-        schema, netloc, url, params, query, fragments = \
-            urlparse.urlparse(self.repository)
-        if schema == "http":
+        scheme, netloc, url, params, query, fragments = urlparse.urlparse(
+            self.repository)
+        if scheme == "http":
             conn = httplib.HTTPConnection(netloc)
-        elif schema == "https":
+        elif scheme == "https":
             conn = httplib.HTTPSConnection(netloc)
         else:
-            raise AssertionError("unsupported schema "+schema)
+            raise AssertionError("unsupported scheme %r" % scheme)
 
         try:
             conn.connect()
@@ -134,23 +108,23 @@
             conn.putheader('Authorization', auth)
             conn.endheaders()
             conn.send(body)
-        except socket.error, e:
-            self.announce(str(e), logging.ERROR)
+
+        except socket.error:
+            logger.error(sys.exc_info()[1])
             return
 
         r = conn.getresponse()
 
         if r.status == 200:
-            self.announce('Server response (%s): %s' % (r.status, r.reason))
+            logger.info('Server response (%s): %s', r.status, r.reason)
         elif r.status == 301:
             location = r.getheader('Location')
             if location is None:
                 location = 'http://packages.python.org/%s/' % name
-            self.announce('Upload successful. Visit %s' % location)
+            logger.info('Upload successful. Visit %s', location)
         else:
-            self.announce('Upload failed (%s): %s' % (r.status, r.reason),
-                          logging.ERROR)
+            logger.error('Upload failed (%s): %s', r.status, r.reason)
 
-        if self.show_response:
-            msg = '\n'.join(('-' * 75, r.read(), '-' * 75))
-            self.announce(msg)
+        if self.show_response and logger.isEnabledFor(logging.INFO):
+            sep = '-' * 75
+            logger.info('%s\n%s\n%s', sep, r.read().decode('utf-8'), sep)
diff --git a/distutils2/command/wininst-10.0-amd64.exe b/distutils2/command/wininst-10.0-amd64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..11f98cd2adf1075b7ff7be7f02ebc8e743bb6b9e
GIT binary patch
literal 222208
zc%1CLd3Y36);L_fkp>d0SsEg1kVXT|5(sKSK)axtR6{oe*`mS_jYb?m8R!-r5rdUz
znrkcbj<dY8Iy1i+9mi2;-WdqEr4z`;P6PtD1aZb{8Z?3?VXfae=T>(TP-ouneZN1x
z=lOWZ?W$XMIrp4%&vtL!3f#3>un2-+g+E;vgi3+`^9cXOKa(I_H+=hb!ryxC8BuBS
z?-{XR`GadSR<C*Zfi?H6$|%0)p@$w0X54#!#+uMW84o^`A<vzkvFhRb?jN6)*2`ry
z at tc&fZ~UWV`pLxKBbmoeehj~x9I=y8`hEZ8XYhM^SoGwl^t<!qC-i&Z<SD8%<D^XG
zw3Ex}x0#-Aa(qO;cRX0U9NYb-AryE8;l8JBLbZEiAd%N9j1+pCdkMl!vmgvIQQqzo
zkT^JMC<m|Mu?-WU^H*rG5HiL8W5Jw_iAsr|Ywx>V3a5u#gctFm$|AJ(gWne|!sfx(
z!d-R0Ss3!I#JTEUky-HAzWMR^;QfyV;n(jqbAWK{*6w98pq24!?z<;=k02~Lz}plK
z!tX3|w{jl%Kb{v8%9daT;3OS>hghyIS2cb$FGypAvC^O>UsKLAe$Co7#c)kH0o(}v
z;kV%0a{meq|Np=4|CI8E;Goi6A?Oshm`ZaeY)5*Da&?7C(W9Y%o~zltd$$r^;<78@
zl`e-84!Y78E9&VCAL~%qF@=4hlphq!eh8IH^8~SM6I at Ue3fraqbUpqyE9F(ekiz!X
zf`oBSzQZgqP3_P_6S5CNrtTaAIptNM0ZO?Z?4v}Svlg0#h+^}zTK!18m%<Kj!}+Ek
z at wDx9ghp)JiZ{;7^UOk`!u3!AN^i4Kv0BR#MXwRdSLwR0uqCc6g{^euC at kpmRANJl
zx+7y*%{60^7Dx+WTm=gIh3iEq=4G(}Yf;z*7NctAXSPFHCCZwj-sj2^yx9%j>?(!r
ztCL-srSJ%v%Fvxd5iZuMzyQVaUlOQ&AxrJBg!=ngjgP$tRkS}{(RCQ|AL8rOj)Gt>
zR<Aq9LaF#nrTloXSYb!D;e^tg0CBUsc1Iv|=@Wi4sHF4v1<8Ixqp_8tfEZ-~BBqWb
zrU`L*WLpYdXII4z5%G@)Dc3@|;?@^jKSlCyeb%)Ze*F>I<<XsYLfig`-{qluC9(h#
zB%F`>-0Lkr6SC{h8E_YW38BqIXtNJ`Ba&6-Sed{}DooRzgYjCutcRS?yI8gc$rN#x
z&o>LP0=$L>GK}F$k$Gl??a`g>bFuy&7 at +Pvj@RXFM)ho7eLPn0Llm#|N0zt>eC%#u
z(G{6Kwl>ShPD)FqCCh3$X}(z5cp4EXvGc^mF-h&Pi{<Y at 7H}nuHA9X}FVw{E3qmB{
zg!MAsL at o#e;#ryN4ML911HBrBXD^P-Z-u8h*nE~YX(4j{TQ1b5K*Z~iRVM+QZNTgC
z;Z$ck3RmsH#e$Gm6HHI=^4m~AW>wor9UsX)PDM4gikeB5dh+U0h at _v*K{EZqh2ug_
zyW0T>NQc2MtMLM}9rYJ;z_nZ$6^$ld$LF?V;UeOHX_2&8iM;A6LPTyn{h|p6Vf+4g
zi=brhP{QTJ6YS5Z_I<3`%T7p49&*9!zHdmImz|g0Eq+$5gcrCTmDJM%q>bx`pDLJb
z>!F1GE at gV8e}*4WQxr at A-rE99>&-rc+~Fzl=B*t5r5O1o<SE5x70WrLc*B_K5$jF<
z;#R-q+{W7qW}D2R1_>`L)=-uB?wky at dc0CxtyqAhva3LRcjholJ)}b{`+e{;$Z|l5
zq~-x6GTbg2qCj@>4#m=_L>87R5!pV>$Lf9V{aAVRCR2sA7s$G at s?e8;+MvUvjyzF%
z_BAT2H!AH1O4s+%m*BVxgHC*grOv)4ZnY^eXG_BQz<_~L-PtPu+)!K(?WdJu%gYre
zyA(yu5rVh-p+A<Zum!FH-Uqbbe!w4*bRX-f<z%1)6wBHGzdn6ufbH=`PgwmHz?)Ca
z6+$}#I4E8ly3&;ONQafKLb{uB0cTAYz}b4}yZylQ()FYDC0MQ~SSW6tnJTk6=}3dD
z5 at ch@xemt&0wTKsb22myTLL~C0|mX%r5$SY$8J|+wM?L6hQjK#zh6S>1#=IiSfKn&
z at OqhnaDWQ(@Gn7_NP2Wz51d>2A<UO3Uynj<PhPzk`8%>FP{t3~c@&r+mU0EB?E;q)
zu$=L!jruG$_0$|DpR#9H`B)<o0#J^~^67M+eNftDbtrmug*@;IQ0)ss1;RUsqX0_z
ziVr9jK-byVyzVICVv{^tpSmPCXzGOEfMh8mqB at rov2@CkpsjSugisG?TGHzgw}Ij+
z?KsSq*bf0lu02=;XMvKvTU$fg?H4W$r^&`0I9sG;(sxMId}k@@&Dgt7g5e0iI{&4)
zh5~}CPzhCNwZe{q3JtDP_voAKWevfHRNef at gNj~NdVWc0VQEg*`o*O=*&9Zd<~$Tk
zE6rIGQs7ywB`uy;QfEm6&aG0^8naq=B|ZT~bCy(TCphc1KB^#0?G>^^VbF+{%M}*n
z$gH at zqBq?GsYB1AcJ$Q33<p)2W9UZDd;|{xV`<MS7P1j++8UK>^;!E&f;wu~D$p59
z*tQa%L$$kMz#uG^Qt_%#iW0Fk0A{(&U2L?s4Gl|e8*fNq9om(>Cb&$Spl|@Pl<>5X
zcpaV#ayrETXSbid?+f2;_k~wDeBrf(JkaW203CttM*uJTM$FI at cIE7AU<QEvZa*Ni
z1|@Q*p5353SNOo-sArmDY0z$`o;wggYj#!K4RYH=Rk9n9!Ex4tw#xO9LDCB~^%GBl
zm;{|&hv0b-SqhpGFpGC&Elb))s7?yFwDgZQq-B{4{RBmwCf$yhV#h+$l}Oq+z=`gx
zQ_O^=>tXE0tD!=~d1($lxNV6=i0wrBMVtrcpwa`=;5S%0Y`buqg*0*oJo_s>+od~g
zPyy%S56H5ab_mJ{!9Swjm^S7nMCt;Zv!WJy(1aRfuwJ96bq at 7nuQi__-d9rJ1<+bN
zA1*$>5$=m?K>I3|s_X`tS*wYsbmtSdVL!qCbPMB{NZ>yFkVS|OhK7FiFg5gf01Xa8
zV(@N at fcDX|91SpEra#8#OA((hh_8elwh*<B=L%_^Dl_5Hnwii#Zx9;qFBQ-<SS~<P
zNSBXNT0m)$03fqtx^v+yq}^jf0GPgHG^AMoG|-j0^W(b!x9?(OfW$(7B(T{Z at m3(s
zJ(#l`s_a9wk`{-IN0CS228Qb;fu4!Es|_Z3_AW$72(?+F%>q)Y{zzK=vuGEj{pPP`
zuqI?yV^sMI at zrCg(9vzsQEVC6D}Gl2Qnd(jvwnjRn{<a!suj=~%T1J0*un~Yy7@&i
zDyYP3SYlw85}UC^L5}hG0esv+@)eXBNSVP#IVe+Tlo^R--ZNzIax65G3dumnLh7%g
zv%kZBDEyL9_|dCcRE$DlEc6r<LRMG}*n3ez^#qd)?N%#diz8&e7su;|u`?5>q89;G
zdO;SnlFw_hw3(}juTuPI at 4!?TcBjRt0V1gf_3GdNbl&UELYOeI;wv00MO{l71#VS{
zK<TrQiC<ZPZARWO*!1U*a5mj<CN at QOT*_q-h#kcjnVk+oI}n+j;S0}h4n#okM4Y?)
z##DWzkhlVc%Q)THIupSL5O6rl5oK9as%0j1{{yJS_Uq2kD at cSYH89{+H^8+%>XGFZ
zA*nCcyV7Zb6z~c12A?1&U5N?8?X;s{kcuk^U8 at nC2q`M`5D??K^X{3+Ozq)~0v8{*
z$s~xx^4~zV%+hZ8E>0HPxd*77j at jJ4h50{pCiej{{5Tm%2-HQPU9fvDNXXW~k=Hyn
z)8pg(F{0#ll-P|rDRe{o+K>{g5fyO4e#vgwWCaBkt&l}j?`OGn^}-A+7<tj4(pN!&
zj9o+qn7Ux*J`VQ&$)#N1bhT$u^F*AZh(bqo=K^de;=DZDZ0I1}De<g>Jj;(x#(19f
zdZH9$4dz+TCQ3m?JI}f&QA#7T59mIf@^ceKTXg3KJnOnd7C1tUkOhJoZBVf5#j-kb
z$lAoRci<v`7k{S<c=RegIwO|-j&eQ`%YH@|XAHp~N&7v>J^cvRZNa;tr?Od`B at ZJ%
z(DgphA-9!S1k4)u`w__WUqRN%2T|tl0<{@EVN)!7By45?6!)_#Nk5DVqc%gm*kjEx
z)P7{tf(#rN1P=9wN0l!`ZEdT)AEmuNJnjj&kJ}7WX$ed|P~qLIBG1D4U5K;s^iK)?
zm8hj`4rKo9ol4kSgkD+1sRQ5vb|O&Rfq{l_0ND5ii4b{c7WXK_UT{52dEOs+9wB!X
z&wAP at 26zd3LE<dp*>ygnWGiH+>(1LTn+5RETl5I%?e5Gg#k{Z=1jm5R%)5DJrtUOB
zCa9m>fL$#6vB{uI1Q``+S7tJ(`MAlT?;5<w4P=UC%P9-g_cEU45X%-)7I?N^xX9@(
zmQ6E}PYQ-t=#oibt?1!rX#Y^jM%ds(g<GRUlp^f{OzOx?Yb>3_LE6a at G#6|#l-PdS
zf)Zc^u&ch%j__-TE}`F?_S!8#w}Vop05+pGVg>Yc+*_`1$nr;S{vkXHxc}*kSl^$;
zQnOINrf6&0bUkhc!#r&L5Nt?a-bd>{^s`X9KQgKq%1dmYkG%(mKW}D$kEOoPjmMi&
z`SS{=K-6cky>DX7LHofIs9Qn-nR#U?;<1qfC5cL2bX_l{vQ?*lqN296YB!Wa6YdtE
zW at J6G%Ms0LN71suq5KrswWQ9P(+kFz!J2_>*<S6~d_Y%CJhc)dBuSnO6e-zzv at NR<
zefK}w$(naB{rZ at YAwvp$>IjgGXZ`FvDl#AYBEYn_u}STrQDz|-q><T9m~|AVF at iMU
zV6}9{X)fPp7T*1e6m~P)r#q)jGYi|k#CtFpe at aEhOhX=P!ud+!m%jX-O6gAPG}0Zm
zc@{x?|2(!sQ+^mYjb2vkV at J^P{|Qo;?Zb(%dn(v=PVMg-iC^By$J)Rwd!9sO at Nmzz
zuc+l$rkcrK^cX$;Y^s?v9_UNx^j(Y!=hdxEGxA149_zs9NO8UP&voR`E8!jpjLgF~
zR8S7TYK&_;jbng6 at +@V at XjAX<^8nEwrlF?%1wBw$4{ya-3w81t<onoe-5HsN`e%u&
zfb9fvpXFmKa(ry92SYmv5(51gsKY{_;Bp|~>U1UiXa*-CDgi$OP2&7B>M?dgclrsB
z&JSi%2P89cdDa~~3kak;Z{%5X6Imei`|+$75?N4DppmtrFPw)VwOcSwGZ6%Ka4e$Z
zCZN}$A`rYyFf*WzRtR+SvAy6QH^XBy2-zdRs1~(ezuL<HSH6%$C=Icun-7@~qaZ|E
z2yM3gLz2G7e##+U)%M)bg+aX$$z+5P>%W#m?*_8Ji5y|*y`N7p3vVMwuswt&HU$TJ
z*ie)q<8F`c{O%Ou%oTHhGpDx`hog at Jc93H#p{&8n&4~gs$9GLL?zG4kx(;KHohF!x
zfhUpiwhQ~~Jp~bc$Y4?%-5oYqv^^ID00`S(@wgoZC7+#wY*~rSs)FQ at fkVe3hsvR3
zEcyY!g$Dt$BS7xzO}#2oF`iJ?5c;x{bD=GJ9!FdT6rMkpLZHO8 at aU^4$go7LU6NWe
zR;slQ7POjH0$a-m>m&}QaN3W-zS90oxlk!1&*0{F5y+kB9PKxP38PkWdj=^93=Yh_
zf|%QoPFyynqrxn*60JWOl%#b5<e;l{;Z0ny0deakw<Y1R0L)RDfr8&A^huiDhjNU`
zZV;qKvye^bM~U+WJd9f_WDGaKQ{6dX3I&o;IJ=KRUMg^AlXik+NZR(Lq-X}CDG8Qe
zfka+D*o81!?g1izI7rV5Zd#XG$+X6ikPl7%{d`9tJR?02o}IA>hnd5NdHMR}Ff$;>
z7g?Lm&SRL7LZ~=iKZyEYgO{%F6U&?+kk!x4V%dKQJfnhsgR9`r1^4Csxo&JPqM(LY
z_9yOYzXX~w_8%CS0v{snkIT>^+F+N(t at RjmH$g8_^aQB?BYB92XI+H^Tv{m<1hi~7
zTS>mb!x(k!9G=T at kpZkc16Vos3(9UmG)8RWW*AR_)1_@$^k@$}ia!QzF5#e|r%iUG
zpOsN}+TibFpS<tw<5BVay_Bqn;B;{Iqti(QQAGIMA3_rnCAIaRfyT~2KRD5FqDAOn
z9n{jQK{Xr7 at mWGLlIlkf5{Zxs$bGx7lC2r26FrJrq at hev*x5Qy3&x!j(xtS{(@bj$
z&QxNwW?Gm)AK*b8c at eomCvM@>D^p0s{7GX#8)lsN7jEq|(f^6pFrR~OU$s79RZxo_
zxs at BZD6-##>+pC5j3Stx1qy{pL6$BDG3rUg8+Zgsoz^ysLNn)s^Wf%- at 4!tcAK=PG
zwA+rsL%y29lwQ6_!F2$1z)~+&S_Ljj!V7RF&A*w{Gq<l_gEn;M%Se6_4m?WanZK}$
zurA_(qLBkTC$I_G_*GXG{IY6qcAI;F{Q}s%@Y5CuhlD_QqI&8ErTCa)Il6JZoucYu
z`9LVWG4Jt3llunyp&w6HsP^7PV7Dviu^$vGzC=BnyVf2|SBhs?5t;6KxU%C<IPJX8
zBDm+<mB{1Px6wy*4`h3!utO<MQQXa1KQxiRKt}VF{106BK{@}*WFD<n`P})Phs$vf
zOhllG?tF@#2Op4`=tZ9(=*p5JiW#&-i3~THlxAp;e}Ym`Lk2GJLzjrcdhH1es`6kq
zyG%iH#j;B~vK_;OdwgN9;0sT7>CV>x>BO_~0lM=qH<Nm~a61P2w5AKXZipSjS8OQZ
z;syh>2}k5^;%W8t^7sJ5!)>}X>Fn-LTqd*uYDZdxbiREn#Rhi)WtZ0wU4f#_T>Z{-
zCMjA)IWhHu80 at V(3!zf%Mia;3YTa1?_uKf40~I<8?w}*Kzw&{Sk`m~z{S>RtJ{avV
zv-)UkxFrgDWW(*~Z1`Qvvl}2Sg1<ueE5MZ%jH|<64*X?tm2NWp{<q8su{z0cf%fap
zC*gWK?|3gwmPhcRv34-+FEdb(r at aC{XJ3mHh!jVyR`doQ_y+|x7wd!K75MsJvT~%L
z7u5eAum4 at Dk2QgKT`KpTWK0yeZkuQXOwuNCS-6^(u<~z7sEjH at FQ(m3v_lMYsu}hV
znCA- at OFV7}>b0^8bdDxGMjir?qPAZ$dMF~=ZS%;WIGWu?L8u~>O?-_8S^|oE2=Htd
zK%apy3x_D$_o(iC6c}q82s8BaDs<=fCZhNrH~luw9Yy}|su!X3PvPliE(c10HHLw#
zeQ6P?L_3vqo|r}i)}7j9x{s3O6L<q0H{H2oGEQ6Fxda;D)`v?L-FXZ4jA|5x-)Q7-
zfU^+CUVv8$CZ6zmpgJgef5iKS7k!dLz#TBbaC(7#HJMPn-I8dtda at BSj?zU9#=Jz1
z<(G=N^?_If>!WkkblK9R&AFJbj at wrBHBf7;1!sZ$1V6?Y#uJkb*2W%JPevmNihx-$
z6$4>8Bu`~<ePq{Uue(upFL33!>)o<zs${9*oU-pf-PH#=z(kxLvG${D<lKK`^H8A&
z!R@%0M;B107m$3d^bq~mCF$QMITf=U6dhA9%n{3e3xI9B-~jl<^0!fies&(CltbGK
z!Pf=DeF&hDtNTiF($POzg2<eiZQ#yJ7xh{U!<!M?6^R06`BRg!<I72AiqdCo*DOYm
z?T^`{_Wwh+2-`0wGGE}C9{{J28AF**LT3CXY2#y#9GGRHp<t&$a}w<apxp-b+gFpV
zQ9CIyQD}I8lTYrUiGp~zJ3+H_L+xOE<;70T^GAn)9B+Z2eT*ie;$t0t7K4#^r(%EC
z3&^om=-;5ZnB4|?d^|~YjCX$zn;8*w<zSM9NhT&4FtI!ZWQoT{V92pHk%8{exYFB&
zV5Xt`>lF79aSIq&TdWSrT_3V5dKIow$H68m-(~DPz=-&Bc{EjE#J`wmPUb2+_YK|o
z^hCogR3Z!C=nA(%w?*SfTtB`IX|8!5B*7@#Oi5p05swQ)!|1e9l%L!Gy#95(K1{Iy
zHoU!*mcBt!?ZJ-OuI)eL8UA8WbicDxp!Kg4Ije%*{Q6jK;p7<p7q at n}3BwL!Atkx7
zq6p;8Sgu*CjnK8CJ6DZ0CycW|0n+)>GHye^XzEmIiP&0lo at Ob0r-t*!R11*PYOoIv
z$r>I&-dGGjvJMztZ^P~vuzy{Ap at thzN@9^Ku^wpK*Bdyet_I`2N?uKHwlWPH4E5EX
z(rH7>6eT?LPf#X45m<XV`Gd}WQ;?&$OYrFgus0V$+4g2-aD&o*6qGv{B9Hd&TtigP
zn>T<-VzUC|H6ie!Y!~3EdcphzE-5|-*Iji02FDE_zp?<Nr*?TwLIl*LbuPrg6ZO;_
zkOE at a%P;}6^^K>h0ifVmy-_U3;MhA?ASsdaYQ^#o;K~=(91>XnI=Be8!f0dlG;MAB
z7SQTu at cOId1jpzI7#%f)p6g-V*>eKvi|qx7v}!^<SdeU|R&)ux>BBVR|1lmToIByU
zi9gq7z(Z(Ws$^&l9;QX8_1Jv%cx1U2=zatk)4?GTMVBg{rb7PWhR$+7gL0c}GW1$j
z6`Twdc#OOez7Pj}f&GCkMfd_`r<gKNz<Q}r&s+XUXa-ci0V<!)xvFx%@tE-fWyFCd
z&Y$IwBvW$|bf(rWjWZ-BmY3eLBqf-a?KXs%X*}sgsj}j#kv5M at M9I->SgJesL53G5
z%9U<HFglJpz5{jl=ZR5FrLozjVP<P0^Cm=zb2Q{tIOFpyknWsj5n^X)zOEUEB-q`p
z=@QH<J|k8BX9BbU+hagCg}S}@ck?iUUSTHza2UAwuXf0A at 84vvNHv2z!)QW))pkPm
zTk2<zRJrM@;l^Ov=~Wn{`1O702((a;f{KPaixP{yGZy<!d%l3|3;!I_K408IVFm5Z
zAh3CdIdp@}(pDAFLe3ao;@w*)G_!vydM9^`<;8znj6sS`w!T?_7Te$88FStO)mYk>
z0gIrmr5NH7KPci&sgrQKg}?s<7?iQ>jzQQg+?;_6q_{`MbY)2}9qmZSPwtSpaXjB#
z;bR5%ZuM}Hr&pIn9<DNz1bYNvj4w(mLVq?+iS0PIIJX#>d)-Zpf?SdcdKT{F$Xrt#
zu7Bx9ZZx=#h)U+>bMA!RM45o{^wqn#>4Vav)AV^fCmDRHN%+3WnBqbZH;a|X%Y}SB
zZZltxo8L2Ga+dP-IIshM-2>5Y%fdOd at jOX@$h2NGU&ft8b`j+c#fV1XYAY>0ciHa;
zCLJxA!Z1i@=+4lMq=xg=4qNC(7|$4zv5Rl*oO|`;ggr3^2O7(z!2j|wG$*d-j)sDJ
zkq+t3yC`>BUM{i7C%14DW_18permLiXUV(UnaI|jp$tEo%(ks_fkDPBH8Qm3C5fxO
zOK4-<vP3OE%qY~h+Rcn2CL88jxVf+SAEb;`bBU7&a3BUkJ#7+yGM|b%Pv1<TCv6Di
zGUty2$ku7|XIljHzLiL%lzX9nzZoZ2ohCiYqgaI$ib5Bi7Wctw_))20^LN8AqPGWF
z4F-A#j3V>`YfT<aIP;Qk$?GC&!Nk2`G}rHiAeUSq at liYH23-ZAp#b$S=iqn}P<=j`
zq`S6kIjycX`)N}CXclqB^jm0vZ%#IFMN?cWWl0AZX3A=roN7MLRs-{(eT{w<JEFMv
zTK0m;oXHotxqKM!BKU{t&R{0NfAi#|f5oA{FOzmfd=;<|&#g`X%cF&4XQMvC`K7)5
z0F5G=-JLZ)*hJZXNMvg(F{9Yoa5GB8w0r#+6|48NTHN82x)w3FPjh^aDtWuN<%fPU
z$=1 at I*>v^5J4dZgqEVBG{`!I6JTfux$8Vy6PtCq+;45xQ at MMxa9h10M4K1a80#GLh
z{(1>88-O1vH9`naFXB>Sv-w&nu}oTuw%5kfh1|TJW$V5Jh<0#ZzB6(4r{vXd?xe{w
z#$bS~<Yci|&<vR8=aVH%8(u;g9)DslR`tU?T$k8e^UsB34I%_zBd?Um@(O@&HHYsN
zYr_05O87;`O-a9~Y&BieD-7kQFrGTCCEtP%UPlXMT0EO>eoLEj?sF-~$>`_7Kyu-S
zn-hHD>Mu9a0#d~GC@}mctC_a!^`8skKAzYJupT3HxW|v#;^bL1DxfVuage_kac+R#
zK!2ogrYN{pZva-t%;s20M^m~0;E3H!6YD8xLi-13)a_<+>~-hMql~D-1EctEhAdP&
zIT+=NB!b=B%?UA~ojD0+S_kgC)gCD!hCx2W6#!g8WA`C8QQ6_9Tnq!BPH3-6YTmYS
zlDUg{^*ic(pXOd7 at bJmUFjuvmmvuc}2*VG~`Zm+D7RsvA7SF^8<pAx4W?fJ8=Et>^
z{88rQ4)KI9`8!u4L^issLs{lBcku|422aSv6(cT>?$&-XpR at aFQ(||;+6-X#Oxj^V
zaWjw=Y4>E}_L++#%zVcm(sV~=4emFXWZ?Nzd=|D9X7I6nfCUu21$gmg9x at wdPR<qF
zm{L0JSuYL|1I2d$2A}j2B at 4$HAx7Q#qmkU<%c5{((&IB+8^h}>8ctr at b`%icl8+v4
z?hJ4zc5nXe4w^9tM@?r4?>STV5FXA`45ht5?n~8h406Cg2CC~_F59}Ep;WlYG_A4m
zkVmv@<ZAvM$;cJ?;~P)c^FGFO_QCg&r)|EG_@&Pz8ZG*?e5(|HU4d(+XK%o_2r{0W
z03|Xf8WMvR(w at 43YWJSV1zr|61d!h!#f3ir=?w#<s(A!bJ&l=}71oFB)hVftP%mz=
zp3SbR8C<0{>ouMG@>(yGnd9It&`Kd;=HTm1;C8o2l^8t6{+fp&B;FPPF^!;!v|^Cu
z&t)`u+Oj&crtqvIS;Q#di)&Nw5fXL(Lb<!N(VbZvjI05aRo=?wcZHEF+(SICz4~22
zcyqZyH_#w8*#|ZMEP)hV0gQ)Ktb&^(jeUGz8`#v5q~TkW)&Ueu1b&GMGKN#VuOcx@
z!b{)aEcTC~q;D3IArP7Wh3?!n6lcTRkVfW<#QobS=Xa29H|z)985x=k4)h(Kv?CrK
zn!LO;3@;;uT#SDfaCbk$LZm|?EbV-cAswCIjKqFe?Tz1=gzb0pt`w$e={~&dpb$Q1
zXT`LADdPW%l8AtaWq(LK=>-?H at bCmaguz*AN$VWDXeJCQN at TG~+SbFEaa=HcH8`Pn
zEg8gye;b=HG8OcJzk)%=Z>L~}5}9w(ov-o=;KG`NLv-h!;Z*CN<Ga;**FcS;J68_I
z%RSnciv%ILI$nS)I)&Z$;C>B0Ui at u$eAtyveC+6kkI(Wu@$pLH$ut8W2l55JOJ_6B
z0G!lb_!gX;7?i-t=QJT~FS@&p?!>UGaI$$&H=JzawN8!eR_jm0k~k?G#BtKH at SAbM
zV>a8T;OHF5(S>^OI19cZ^-gbXWCiWM`2rnG%vbbLt|4ZjYu)!|enZ_)pSh-P3&O=|
zVZ0{hA at pm{&lD0mBKAQy#D|j22Twx7O62C=eJmWwO$wM`?!(^{CoFs3kd8d8R)~hk
zz;Rr!7t%gyuFARZY#5n?-p4pq(5e0YJ0wB2j5YSdyuE~L^Fq|-BQRD8gvc67gm|nA
zOCJIIe<)Yv_b$Lt)45H(D^iC6=;-e!_q`D at ii{=4&b8Bo|CoH&=H*9m at uCQW*E5O|
zk*l6K;Yh?5h_oXs`-~SI;7=6(dW|AS*7iavrJlD0hr=kAk58z7l)_I9G1Oh`9TM4V
z>|jAlP=ws!bAJf-gepVfKE6v~iUapSz>W6O1}u#0eIYFHKA<xG$~Fw&YW4+u-jH~N
z=eXSX>Nr|%y#FIzk1eurky`*Z|90GYp`G1qOc~r)+UfWg at D)A1K`D-+ha6dNog265
zyA*w&_>QQ(P);oMxl&xiw<T2EXD!f<{mLZp_xRSka`YNs*m}E9t+OKnq`VK;yCk(1
zx9a<f4=d4+E!yv%?*^<`HkV!z>k8ch1G at 5i;gOeJ0&`4Tjpkf&4QS(!J23FEKk*&~
z--e|vc$SRy-xI?)7mY<L%4O(T$6(aWJ;d_a^j=e&QfK>3n_1xg{?NbTbj}4QUwrNd
zK$cl-YCEpJxGw@{i(C5VxEn($z}MvmL!Z$Qk+<)nf<xL3la at B2I~#bkL;K#nJkE2g
z62Uz}k!h7T;Tw~VbAJe>LDx4z*J!H8)3kVuzo|3P#JM{sX2PZBu8rPjG&+RVRCZyz
zeYA at gX_|ar((7XTgsW-{f*RVHd%6_g%!|*K^qsL6uP)v+28zFvEFL`2?ddP^>5H9>
z4bg_52a9;A?mRvak!lX!p>|k;0ZHExSGv`ljWtU;Yo=&lVa<`<3SEbVq<c8N>^ROP
z0IN^0_2&hFAkRKbk at oet-D<8wciuFRZ)Jvo#3#Z0zdjLG;kGg~xD=fK*iSF%Isl|Q
zZykupTkK%<+LHH?mG*hTg+u_n<)6?Njy+%_%i;S_EgVaS8I=ZkKOnn#S2_uom6^$H
zGaiGTqVPmp-^Q1*BL&79okig`c at bZu%i`PL9#^_-fg{@-*L_eIX5iWnhNrmK_qR54
z!s_JlDckn`79m?_$8eFJortF>%ww1IDxBb7f{OdyREr>qTdTImVRlDb9CBp(0Aq;+
z{q1xLBW!>|$_Q|osTA&ij7%S>I|tI!afR4J8*N<1jeYIM2On2Yruvx|r%==A#F|;H
z?tEvUaU<#P(U!P6EG0)~7emS4BuhGfJkT(5NtS6f$B}$#9eEbRP9`w?#d|pzn+Ia-
z$cum;f+EH?*g*u`Dm%he{xEKAt3x9>;p+c<rs3+F5_n!gZvK+5+Xr*i9z&U(0(gC-
zsgLSTtv?Bi$A)!oWI4pMJ{xLewd&4lo)u4IX}a at up4FOZ#KBv1=P!8Ho<tT at 4EN{z
zj?-HETDZ at YkJ{^YrMxXT1$Q6b8~`rN3c#%!73_^(81avyVbBL at -W=Qrt{;&Y?MSQz
z8=p1nhyJ~7h~)pVM&os0=cqx*&yU}feDz at n-$qI#$~Z<FjsJW-mbrZjE%an2e8|o=
zPSEpPuW5sJ*uH<1+IW!4T+B_jVeG5ni@%mT6Z*5^oVn}buX<{Zk9|RlKlskGvGjBA
z##3K_fDTT^B_Jz+N~{xL_1QWyj}c&=<D^&1J6-z^AR|7Ivl*^ebCw^VJ6-TV`_l);
zYl5-A at u2XprhJ=M-`bx<z_yX(_kDa*lFeW5f6XHDeNHO-`<smI+>|*NGGqDB6?*s+
zft7<<3143<pFrC`Ug+N?5Z>wUmqT_e4S@`&Z3AAYv$fxB5%?aLd-^A%3CY0u!v4l`
zj+}gmjjf=@stDXsKgdDKG?LhA^s!5%?5CHmA8}<#u%8drEPv;t&j7wC&{-~9$C4FL
z5=C_6ByNx0t(9wBdzIiKi1X*S;?l&O0HxM^lBWH{Q5NAHjvx&KKqEalxxrz8q$2RA
zjrFy01b12wD5^!@)AihG{h$T0tS{+7+px}(mi`#qNqad1qnEZ{-imSDq4yp^P~HP$
zn?eiP-fCLs#I2ogjsk)oti>j)CS#NDVjOr{71R%6?BelJ$);cKM?lP at Hf^&;C3oaM
z-;dU1a)1Fead&b*R6pDYSFioxBIfMTop#X-E#C#b1ed}u_OK8dI^jz8Fq8N2`7P9g
z=1TT3j`wi*2I%3>yAqpng9D6RK=s;K>ICCO*Im7(;0KbB*iz7C&y*1tSm|oTpn&mm
z0&WU*$uJY{(~eI`1TC_wMtI+lBc3HnB=9#}QkVHFmRUT!_SY!_k8^mI^9 at TfzJ;JW
z at 3@XHny;p50xptm$cNm5^YAS at P_$qLaq9DFMqnsWn<f_2ICcVRV7ZlvjXt{b-b7&|
z)@^$mhTgRzZ+R89d@~m2FaO#8e9GuPSs2Sb)Y-B<jUqPQH;NKL1l{>8HlW_$fr(vE
z?{C3PL{$oZe>3og5?)$a=lxp-#TJ at rM`73tl7DBanfv>Ec^~oH#ossHOW*>p?}CKD
zYyQmKeYcBk7_BXgyOos2(AHsmFqstd1W|L`W$fqo<1XY~T-Hqjh~J^@8pb=GzZr_l
z6}6hT23f?}c(a8EP0<IzwStJt+^vBxQq9S__<oVh>a?D`?sHT(?elDWTZq at 5(OH}J
zEB#k0-lyFMB_kXDg70s-%*IOsy7M{yFrP<uwULP at VOzmaT+@6Ao<}xphc5gREkf9K
z%TQd3unpwpUmlqle-~(H5T>19PBuV-x>>`pc-n&$5pv!&k^tn;IeF>Y$uy2nhjfl(
z<jBN0I^VVXv^SOhDKGt4Mxu*k_h|pzK=C3h8M%287Ku#T3VHPIJecws+Wm1Z|DHh|
zSaFIZ&j#(kAUB55oJt$f>gGYb?%Er0`y*5ws*R<J!8^41ypm7bL>EC%ysC6sY&gCn
zd^6N+8BR4PQkW2`;=0$=>qyTyZ{T*y2y=4XYs_^dO4wi!C66>CS?iUs?X3xL`zZ;L
zwB?hK7JUg-X+O{AVfh}qb3dUY?FXGX1A1L8Y+Td0+2!Hpt#Nh*hMi9?Gxloc06=E|
z^PK_52YL~dk51^CxrS$cG>C}#7F5t~Z^9RBZ1ofQ%jx}f=M#i?=WT?v7`{KioPXvu
zjtwKK9mi;Fm|khLeU48+SZ2RRXzU60M^3B+7s%CVf1QL8>%lDT>7EurJ#X6J0LpLZ
zNk$6zTdice+yYS0u{fGAp9j}Qfy);}K6o8aELHX}#PKx#Ue`k-_--q>%o{<D_N9G^
z69}MLvh&xw>@sWcvtzQj^?($OIV82i6dEgTF>m`q7X;a|zfu%*UCukO#sW89OQW3E
zAU;)Y#%J}hCs6r=(`k=PH^sh>HiX*YSUb}T-S(BlEf$|%!)r-;edsgZBs68`jY8w!
zD@`et+D}AJi7O6 at QpDN^oCK+Nb!<GewT&Kb?0CZ|2;x&Za_j4VeCRt;drS0GZ+1c2
zSXBTVx&3b3y at Y+KH4bPJE@ym!aPrHuotNi2FTd=({HpV^t at E<I^RlD!@_gszh0e>1
zotKw7FE4jqUg^Bl`6Z-iGJ4T*h)V4rvnXKT!Gjzt2RT9#w~ZLsD8CM)3^<W{nK+ik
zr|?}*iS1XSr>r=BNv&%A{}>JwM1qHp?CTijU*iT}sqpr0lyKPuc-in>fu|2jtXXD9
zrD)t?Fj)(*codw`MyT&)^-}bd!y75Hx8iwqSYjJC`QSn1Q8!G7*LNklBCd5G+m63p
z%Q4w~d`6_~dd|z$YkJF;DE`9qh)(-hHwkNOwqe2*9kR at pC^|uQ;^``Yjo#MBvpg2_
zU$6^C3`?AEfohq*s4cStUNEun?bVx2 at LXy?hzJ_lR$~bc6Swr1a6@&Cl3gFw?BbRK
zGVA|iIeWjP?+Se&sTVWEr$*pRWEYj_=QcUJ9~O^l4spwVxRP1fk7;?2>egdUcG+`e
zkVLg~IotX)kOA|a7E)n=*)yz&D^`_kq3W6zqF>WOv`Y*2|8Wbc|Kk=M|Kk?=LnUaW
zvjwb~Xyw`#6tyNJ(Tb$jv~+2y<9`gxrK_sH(7k$<oc%(V>iw}g=23N$CjSIi9azP>
zW}%nu@<n52WD~XK5<m^~uD*(6{yWY4^oG!1z2(RM1*3jU69WSS4xG5 at z~WH&8)73c
zAKn&pTpN`~9)igPlLLeV$c0$C6#du^jl)D~9%L3q?u2~Va=@4{?zU=6 at J7&O5(7~K
zLyDyXEPP1Mq2(HFu^aFlQX+R<;*&?}I(g<>ubn;V*UX;&30we$hLASmGsy8R{GWae
zy{~O0+NG7Rp9BBhR=$27{C8XF|G&~o`Zvy~^lme%f7cn+*-~;=@zyT=Z@^@^c4DPp
z!}-Y;UP!dSx!++-uG+-py3?3kIID1W!6f_I$@Smr0w>sOG*4lY-H3W0nY-rOnEP0A
zvZdo>%ixm@>W$n9>eCX1E6|4NP$3$#kKBVXJIuAve9W#YKNu9-s!c(2wn<T=)&r+P
zU!1F<*qfg<`q at E0i~8LS(wpdq&S3pbKGvw!f`XI6f={io`Tzor2irQ=uwZUKiU*P`
zuU9sn&f>A;MJ<L0>cvy7*jaWI4W8%FbJ>=79VtVtJj0 at 4e^~~YzLo|>?Z{aF1%D4R
z5_nO2fR<?%zR34R-AQ+eXS3nX(^h8+rr|AX(r!TOtNDN)ys@*&jfq0n(LI#9bcL4+
z{XP3b+|nM%Ku=?P7WXu=ObJh8ZRhbJyJP(fJ>T2V^I#%dva4!h{W|Er%e#DZFhGvN
z9?X>4_kc#ZekM8mEKsPhP?5}5O=e3yGFvG7*$L)L_v!WS19D`Wjz9&LvuYpMfBHVf
zy`Oo<N^0#zjLf4S;w1;$VVnn`FmDbI)_?<>P9A{Y(%#g<0xJWSj;4?GdW=Kb^+qK;
z*%f|)_P0R8xI0o(FNkYFmgk$0&(u0|Q;QUtWw$hoTT+@@EX|Ty?Etiltd8H%R_zGo
zw^e(@@?#)EStIp>23S*zUO%!%vebClkc|?n0d{WU{N5Co-3Pp7$3rzfy$XwZE7D^X
zW?oJGSPZ)5yn}1|D0&_A-r1wwXq-0;jaZsx_rZS)RK9r!A+BeWpNro_ at S?~Ux0bo^
z;8E5{m}&wImFm(PM>c_`nfwD*>k+pc4*$)CF9Uah9mX7d3jmFN=0Heq;*cVUMyKBj
zKLDK=f~6JazFT%V@><2G$6>H?ue&~vpK|a>n)-G#9&IZc$Mh_4rTZhZbUZY34C54c
zNBx$1DQ}mkhN<DBK0OLJ)f=QuCaFRQwZ>OUY^NJc)!5p63$G)&_wY(<3!y^5T~8He
zY_d{~v(!aid~Ke at _ITYNT+`rSqw?Ud>5bI}SiRp}4|34_$gNUj_T|byz4FQ{?I#YL
zip(-en<SG at je^ToWsxd8SKuBbX3b><8mRE-Mk7a2!iBJ`!X~?rK`OxWxoCQ%7v!1w
zy`KT!#_z;KE9w<@J(lg#uF)*ENlhX&>yBfyBd=|>f3nedPimH)=UrJlJ&;$ob{d_6
z7CQs}dRuiyuxFiD=#Bg0Mu83gqe*5V?tqIRUf?Sxv*E&rVMHi4l-#sNue&1}vq&53
zaP^c$y=dqw?hdeGS}d}=U3TwRBEY$7t<4v<x_l9j$qNpF9b0kmnZz2KTIBFT85t(*
z#rJ}9x}1uGfz|9P42|5Quz8tytX5%o`jD^$|5KH)lp#Zta#- at Ko8AXr(v`3|JRKfP
z&w#(N;pvm%$D>4^Qfw$Do}$f0;ps*2SD0N at H@&|AIdb?Z<inau5I=g&*PrQYoafsR
zyh~w^W#U at Wyz+yO^~gTcempL+Q_;&-eW!XM>j{f`QpA&E)r<D^6V!|1lVcRNI0wP*
zle;W4n5r<JM_-hqs1M}eR>$ze_VBVyZ4h)Ew^9FBoy(gz=daV3;^16R*wFww&c8++
zmgY<p%YTGd>iI&koZ%?T8$$MobR|abBCGdgK=N3Y5OTn^2RDSA9f7Ejpf4#$iKKlF
zw_KR29fDM;0YVf}22XFcM+wi!(dTCwJ0NV4nU~@dAum%=i*f`!lwz0GaK+dUDpCb@
zSf3ALEY1{I9gOQj=eUB&lN5LF_To<H>g^&f3Jsy{2F-}!S$^ir48Vm17*3NATu5XA
zG>v+}@ub_!B-tD3?*PG=v2Lg#bG+esG6+Uiuh&Z;cv>Wr<caU$6otWlF~Ka<Ny~ep
zzLi!|bTBLx!h>dfpE)cQedGEif4%F at OmFrl>=f|8yQF4vi*rIF*z8PpL}qm;RObLy
zvk+0VmxtBGP#(*<#bZOMP|P1L&T9HJ{P<Y+L+R}Cu?kz{$oqT^m at Tt&WP0OIs~U-X
zSUj1{7#pAOeta^UJ(=yyyDa8kRxGn~Koerb_~%f_{dq7Q>+`xheeV5Q8Dxi7Ope!>
zpxz9qIyNsViSmvxT+hhLi>|d<>cTU!FsRs_w`g at A*DjaTpjz#A{1iJ4BQNx3A7{t>
z#Q9=b4e4Csez9x|q2W#ke*@m1dwr;voO`F8pX|(1WcGlEt&`P`-rG_U6XB)EP2m(T
zo0A>@@B?hTmu;~7!wXyGaG=m1UZu(5CtAE<&V~a;+i_z-C7#F3MD at HVK0l);?yUYt
z4?)PQ7oX4Rfp=Ric%p|`wh0&78iGamMp_51P{8jaCjPtI`2EEAJq0R}PcwB#VYd^G
zyf`R4pdQq?7u1wb-w9l#?<2 at 8Eo!Yj at 9dgma`*v<RC>uC#JBI>$9LC at fSIKB9YTvB
zQ07aRIUA)7o11~`4DR9%P_1f<DQs%bci`5Spi8c}zgdc&1`lsxGh82KbJHa{j7fW`
z4c~D)9?nnqvXc@!SC>L(G~Gb(dA<!k^-9K?qYBH<z!<7k4i{My-Q5BpV2{`vfWnVy
zsPG<zYycu1%yxKsyuy17z|d~I1^`?kC5jkO;O4P!#ZqGE{h`#3uw+M|B(@vO at UW7a
z04npFaqzpZ5+F#C`Hln_2sBov?e|X+FB)U at hYKBbDYh=p%i585mMK!+N0ONT5gr<L
zyt4*>+u6?)M*tZJ1t%*=XUY)wVkKhjmlu6xt-CS)JsRvD)4v>Seg?3GlE8ZUzd_{c
zFT3M7b{ickB(o^4`X2IG8noAk^U+{SeW0d{=T;xmDxfA*8@)_nHM0An-=aY~1W at iW
zMJyj~!nw9gsY~f$V8ageAES|2a3t(FzR3X~d&Mo8I at +fSou17KGEhm0Y_5{0WUft-
zE5`QG^A)hgf;K?nn%xOloC;g0ad@<g=MuvpU=)*A!+jLx|4G<grNz^_k|=`%mSO<r
zKr&6lU16Y1zxyqKWpx6U^KSxn at 7DuXwhz(6Rw>{)w6di|-tY#nL}ogew@?btguc9*
zFWe)%PR?!vih6R_jSclvZyzK^*5f*F-gnZ)vNdqUntW`pw)Z?=g2GKlbt%J8iOs(b
zW&SdIXmni)O1)U_MGOM#LH2DN^kG}R-5k6_+IVR=DDIGtUb4<^(0+NAyuN16GccAC
zIedRJPEi$})TNksE{qd62>3{5UU02Ima>JdvF&(XHF4%L2N6J3JUVn_63bo^h@*FN
zxfDHNx71gbJEq&)Pq3)F`haGsj%wBwRm1b at L*IuoE^(?oH~=Ku8M6LnX4Ivi?H4PA
z9)P>8YV9SM+v*8XjoM-GnW0<5b2GV4#(^}oWH*HK`)d#UlZxA=_V;jJhiCdF^%JwC
z?(QjvZ;IUt{GT^B^N~8}CxGVWk4fOb-A&zl{q#H^SAwaskk7h~oGmS=OR*;;mcbli
z*&iT{Jq3vxvj=0S3WgdgVZQOZ(fDQb8|($w<p*%v+yiW{dLl(?I_>_#5P;h-6!NWN
z7$vK?b$9!Ikga+3q!?}kUXM+JafatA36Tc;BB&R8h-D94Mv96FttYdG6=K+_I6v|}
z_lo(Sd-G!O6Qk^$%XDt7=EWo58YT9DR*LqTX{nY#gV%l8i+)g(Jo;e;aRhkbiWZpT
zn2BbcSk`*!Uk?QNWIM(wqb-h+(APO|j}m$Vxo2cG232;zkPN!krSu}(XIZLPW=b>I
z0QbXwqU&8LWk`6cfldd?diA1BEV~P!BCO1V3uwn0%wu~AFLfz{ARSpx#E7-D)9ez<
zMt}P-ESw$(PV0M73+G$mPHnLSr|b2>;qW(5uMbTH3YAUh%4+e^;ep@>v3=TGUm<b&
zaax7?!qClX?UmSn;U0;UG>QiBoLLEgetPj+079i9Zp}}Z^IC{8z8VjVas6eqe~~K^
z%5xygcSJjSBE-^EWcfOeCpoJCof|$QP0)$I1YQxxfZN#CHax&%mBydO{tCD1`GMl|
z>&cdCf;r-D3@%2N|D$0wt+eppeOCUv$M`+igMQ1ap^4ZHDF*ZW*M)Dz`gNRnqNi<Y
z?OBPvM^`6?Bkn&QzA=jaqS_+r)g(iztl_!A at Zv|q`KvdcABHd6J<|XHsuzZd&s4!h
zWgjvkm#q({Y)3}$M{K_uYZh4k>fHSGYm1C|IpW(P0zP2*>v3K}pBDJFdP+t%mV}?p
zr?b%V*Q>Rc<FE1}sUDEFznGhN at Z|b_YyPQ3<}Ozv&!8*mk35gAWVnwnY*NDc;7L4V
zr=8wEsW9SEnY2KTTi~RA+K)w~+wriMHW&fTPmJv*)uanZDl~<ct*=Yzi&@7LbJTzS
zTL#7EE;m9(c;o^2FC*)LRSKE4g4Kt;?}No9ZfVHdyYBCJUXr6EuW{`H9_GpWV(l1e
z{!B;r=AlvO0a}N=53r-W)-{L4ErAcjt=<o5-m$ssOTuLd)Wq-*h?3leABKv>Eek)8
zBh$?WHGbIfEr3hVEK-9ty3azZs7S9+G~hjO;9xq*;#N);^<#@#ZNb at vHk*u45i^wJ
zdOr-!lp+NdqdV8Pb%%2foWsh?7{F~h4c(|8r$D>N2yq!&Nikaau>Id?<$RY`o>Z%Q
zLn|qgyUNRUSz2LCkyJCwf7Fs6%&T8F3v8EukO$n9WHY at nbcVDOFNGXDIflnNCZr=U
z$ctmTF-V+j0o$f-x$5mh6E&#0$n<uxY!{R)k#m2I;l>cM+W77*{oyS3lOIen^6SL7
z;b(Yg1X($MxxvQ<ms6OUK66ng^fcHzL#s~h!qcOnqu=9e;n!;8E`5)}Q84NWKzJ19
z(@N;E2%xDm^+ at z#Y%HOkmB<T(G&<!+E8soKOyiyQgih0;(|DMae!qk=9cBo=u7K2~
z^r!HM-eb-8q>bmzFr+oh1K~vuZx|fX at OU%@CAJ?7JGA`31k}dIKwjS78=Y0|w35SW
z__f$H;k>H at 3;35KceRS;`FLvQ^AwMY<-v7gmdfE at W2T=iE9^31C1z#usl<InUv=j`
zsK_THRR<1iYN3v<nd`}kURh?m(DpLGQAvp2k1H_Sc+~ooo$Uv?05*0tp5p?lyy*mR
zw4^t9NA8xIVzQ-v27p5i%D5UOZfTS)(fCpVFZ!hco{R#4?S<(<fI%V2(tt99WJHU^
z8UTC=X0pByfcB2ufly0LFpa7Z<mt>>n(V at h8x3mL{R*2T`4T3~;m)e#d{`#R;IaqB
z7#L7xRiWN>Dc2)Y*#KuRX9W8i2sgCd=K$)<9 at 4LShJt>59waQ_p)SRVh&&;N=N7QV
z1u!}D^UkiliAHzw+&4%<l7j0BKwj}!llv@&x22Ig03=hsV)_Zen-9Lt0MrT3wBYUB
zl|bgAtA%qHS_jWvP3x8aiq=I}(fa=l8h>#1e}%?H-%ewbLE~!(3^YTbWg5~=XpLy^
zZaOj`<Un{p%;1Ny14U65T}#yK&i+3ls-F|JE+swTNq8|*P(_p3FR?1g9nGs3pKgMx
z?)tpBS>n?#p>LV{Ym6j|Wj~|S4Qzu31MA&(j%4>t7hJyu;Ha_m;9t<J3UE(j3|@a`
zm|z&~!C#GgBjt0?=z1lXw|Z>W88U|LV%b}rp_mC@{3`?A>yCO&Gl$5g{2{n`U@`_F
zSe1_*#F%34GDk2)oBRnzpS7LMh~#dzIauW6sgV=l#h|`p;|cTyFi1y!0QUh#VV-fH
zZmGw1CDFS(Z*Drx!0eGQ^jps+EZn-3!Q4TBvGgIVEpx!gP03N++4`?WCFeabL at e9O
zUBtI;faaUOK-(KIHv}qg%2%WcvkyO3)R6FcxCV&*Ar2(WpPX0!NKdwSth?TQAZ~R>
z*?yS6ie+&Q<fp}Z!VO{Q08E>_21(3sklhXLsLy>6TMOqa(>GcB@*ebt&{=FgGS3l_
zQg{?5KaQRObvG3*uTnGQ&Kp`L7KG>0+h~@=9{9WyLHMOdiOx`u6+pJ+{!TiOrp4=?
zW0y?d8R9j~Ni{7S0>HH7uCoUxlphbeeW0j-xcK_qewo$eu0zt^`7x39)L|lRi{US*
zxAzO$)!VO=S#79?REh64uzV{!+}>P?T6b`Rdcp&$Pn7pd5v$#@-?CVi!i&HoZ3tPR
zs~+kT1=dh%<we@!c19LuO=3G^e>|n at UA~V5F3c?olQL0LkMlDrCjb{Ng%!|hND3~5
zNcw(-&CkRogT#74c2!~>gdckLb!#yaYgI57f1V!(%<jy at lf`?hkK2Xda5b-2aQHW`
zq`>3QsDumEbO_N>tFS|?UaN(hycy}i6fzRyQMNc8oxe{%rG2ewbruZq&{T~79)s_B
zYsWq^L}oL_D(o<RFwJsEyVx6L@!ezP at ZDMQf%wS17teaJfbc$~eE?6xYqRQRq#|9j
zt73N}kob+s1e*G^dF<w3ZzWuiq2BAk1&_p%)NYye^kyIQ)8>KTR7pK=U3-hTr59Y7
z2UoR&_^-;V57{I=8t-4$5VU!-8{)m`C=1EmAhQNZuU7PGZ{GejJG|lEi9Jclv*L`8
zP at ShRe=P+{zn4iLWY~tf=rOwxa;O_*``TVa^cWruq24w;xPUE^@uZ+0!_k^;R8OQd
zm3Z6-!i!|}f_=>>HpfG|;y2CosMQ at H>!k{lPmfA^Rd|kv7g~E#iOg9~TjSLcg67o8
zI<+foAm(c}xHsjej?M?eYjG;8S4^V1VZAj$q>(b4I|Qk8^d!^^FHV)ibBEOR=uP<T
zvY^&|Esd`yUJo3Yi5CtvH!bAImE=%w(gfeQ4rqJ_6A*RG&rT4o at Cy|5mCwnvg8hBI
zHgXf)OwXcF^B_77=KDFI6-3IP?cp<<FS1GO5Y7TxTI<Cn{TT%RjFS6sPVh!HHxo*D
z*-l(!!fBGlUxW#zb}qEV>*=)pCaJOz&E!zeMB8*?{9f(8p4{d%O#W?T_vbaQnF<;2
z4W{dLS$_9Ne)myd^rCrIr0iFGxpZbBYI4P0r?{g^w8N}K%APi!6d88>*FAii9+aO6
zIstlI{*;>jq2M<dc4|lIeUgWA at G}`fS7sLydAfZ-EW`M0!ES?6e116UN>RNc@`(Nh
zrLqrVsd0%?E;UCC*;s)RzfR3b5A|f5(I9gdD6AB9I_NM<bxkFH(m6rnZr^xZ>5Ac(
zglH1j{`0NG=A9JqD4z6Yx5?~8IPg+{9rcF;@A_F_ofMhlP$Ey%N#a(JBxjZI!q?%^
zR!~^K_C_|CO3wYs%LCbs;9rPaaV6heBAeEG>wNAbq0c0Cz1pBlv<eEI4>aZd2l#JU
zp;Xy&Kv6ZbH}A;W&!oI#poNZk%MXTb^s(M`Ryy8 at SzTe1PqkPfi(h!dxgc2T at P$*h
z4{AOF#+0{#x97F&Q`9PGuW=2ucT{3WBnE1B`cYPAIVf)Rd+Xc>#EJ at BVzTI}E<W;U
z`N0i+z4%F;wb)?Z5wZNcbQWYd6az<TJ4P?Xt#Voe4ap*>eF`Hg3Cn4*5GaHzIP9@>
zv~}*J;6QP!8T<IOL<+ADKsAnoPoXPCtu{+}9cuub-S}=W=xjh&<Yqb#B;uS2KN7xp
z84mp<esiUnj#hELF@#<YiQx|jUw124V^qlDV_yOKf8t|@q)PM(eaymkdD-X4hhgvY
zxC01m8!s5vSMb1X`V?y_CibxR8E at XFp`Oyl%L`zrYeh-DWDfO#+XCo!4Y-JPu?KM*
z(7sp=ttCK3X;V}hQ(GasOc+w~OL<jK^h7tcVu&k#{f0gmO!9BC&Z)4v;PI?@k6Np%
z2;VDy;(4&4rME~Fxc4%|oXZR(m6|@0!0tXJv9ILtsy|7VLz4SF$^E4iw*DmarJpT*
zSH?}@QpBf67MYqpj++mhDnAmsklhA)wO3 at iKC&({)f_joE6ne*N-Vk^KVGg!OH2#A
zb--fQ&<NlGv8*?~=g02<wZgow6Ob|_c#MWMZsqw4aA#fUCvx5{@#z61nO8j{kNjND
z1Cc0}>GXnl;Cbc+4sQc1;G7ak-RNZ}yzYN0;lT5>n_xy{@{K^(rjI2s$%g<3gn-L{
zuTu3|N#9{P2-$05d^~Ko;VsFQ4nRdJ^S&hGn;iJ+Mw+xyM}mmz34y<{jyXsZQQgNW
zoCJ4fh3jRwM|wJ at S1fti1hWIi_`D&fN~VY9KbnbhI942KEEk3XWCY51N`N8E20hF5
zU=h*@zxU>VF$M=R?=vIofWpDF0LzmZNP}G!OI#+T8Uqq4Ks6u>Qp(tFl_1X5q$$@!
zuNlLxQ=%<qp9u!w-5O4hO}%8i&0 at T697w^Pc^N$gdijmc;@P+Z#Z$L$N1>m4+yuJo
zeu1BJR(t at m#^4Dt(T^>PyINs%){-Q?zMnP5=JgQ-P#KM}O7Ly;QQtjef^-R`?;OGO
z4N8AY=?j!TP3fbQewWfEl+K~lL+K<+hfsPQr2?gA4`ccXrH3i4r}P5#C{sF<Qial4
zl=>;1Lumn}cTl>3(nXXOQhHYx_`gGG5vBK1dLN}FlrE=~k2ly%`IHt?I_M~-sgz!!
zoK{Ldru02ZYbmXy^c6~fLFsdpmQuQg()%b~M5#jQ6iUZZI*d{WrDjUMBwYQI()9N*
z?M3OOCQLu0w1v`xl<uIkjnd7O-a+XCN*|(hHKmif<j<ruhnJ_mWa at o9rCE^1Q>Ii0
z(-b|r$y8PqIya>{WK~$SCL7=Nxb|O3i8#DW5J+D}eQJk&&3tjoLEko$@y{o|egLvP
zYlbQ9N5Mi2_VB|*3_HLo4zPNk+H$)u8tW0t<u at sCV+r;tDJj{!Sqb+6&0@!`!C#{D
z(5s at 4f*bPcLv1zUaF2k6;J<I<dE8CBHVey#=YhpG&jIy3 at Y@W3El?kRH27<Uzm8CI
z`~?76#{Eq-ec%sjK*FoQMKdN;34S$9S~2Me?QbnjbqTQreDmwqj32CXa2ds?eucYd
z5fS&aMN@*ev_(z9-kYqw0C4(pd|T8U6iXOnfOanllR?lHwFc98mX&g>h>mE?7MjN|
zEa>Q1dnxpgP|}Ln0<?teK2}5cfIg at L#Lzj+EaCqW at O8mg2H<NtHt1&ydfB1L3?(eJ
z_z_J^?$wI99fUWj)sHxP4>}tj`yLFvYkQlOjTdkof6d=3Gtd at DxAqd#<z60TV-!t5
z0OPc>F at _~7J%pcB-k)Er>FGzr`<cI&1GCcM*5|;ybj-`ZyiBAB=C@$JhWQ*Yy&iL0
z{X~^ld>b{|K)$hqwiKucB&XE2D+KOU<A=Y^K1==81XA>d1dWJ5iW)O_ArcV?@~lZB
zB?wf|Ngy?5N1PJkOq~Qu;RF)8bn$P}NAA^uBW{J3kwEEwWNpX=#P#RhNOpg?Pv{Vl
zXRUtKc!@r(y1tIn2WU~+l}wyI(Ju7y8~q`EI}pFEn5SVL62YfoUJK?mW8Qv%iztNT
zo9DoMf{YUg$=v7L=(8Q@!{Y^BmQGGpgQTA9klF;$$_Ik|B<8UPiVLjm&3-V2i|hR(
z5BQ2}X0iT&gBcE{*cHocdz)ktgQ?;c$)dKjDnIpG>iyY`KGs0-gILUjdQLr^8NgkR
zxVh$l7S=mo7tOp at q;;Fmf!^T3y$;vNC?d+k;FC0kQ$lC)d$NAE-?vS_LjN`dER8<(
ztRBcdKy!4Pzy;vOK>2}S6EX+Xmhf}xp9H7C556t|ITLtJW at pY&o-+V`ct$GEu)$A!
zlj3e{t4;}y*<`M;_9?^f4(rvxQ^6F-4V{Yjom2(+$Xy^mtsp?FfhWztvi3DMc7lp%
z0?;|M-63u8sa1Bd4EK<hVN+sRIpT_U@@Uy76|O$PM~vGCImA#ZSZri1;w2UsxA~nV
zBqM7wFEPou9hEGQVdV7V1^Vz?v3x2JEIF!g(?5A>vDeH(sE-njI!fB3x*js0t5Kp2
zj#!^Lzz5(Yj-AgBz>{6e1MC2OfHpwyBnI%MgQmo3;@X}9C{Jg_Eu3%ch4iTRX9?>4
zIq;VOs_p`hafJqJ_uL~0Z&KtxT?@>kUHEvo+1*QPw(I2P<5^hRM|gA=7?dz)ZWo at o
zPIGtGitkY>^@lw_#D#4oH|5Eb+LpV~$q8g>3zy-sUt;-V<n;t{w4jl32hYgRerRO4
z=~NX_*?7M6ON+3fNGbMaD3OJ&^gW|MD{j(ly_*+Z3i!<RW{GA0hvE^tw3qmUvKR2a
z*qcN302QOXt=gMLrA(+)H-kR)6I*FAVhg*pUl@;j at -~5YA#*Wh2Kz8?hEc@=js7%l
zC7_CX`zpf~{2xH)V^vCfOo165y<k)5Euz~M%YHB>lobc3g5?^vlvh30Q>~f`Q)p_~
z!pr+3seOFW^EMyDS#vw^Gk(Sieta0*Zt%6oeAl!{%{7K+Tgw|B>!n7g`mnv(R;<w;
z3q*jww&8Am_&3HE?Xdair738-3cm{lKhT%2U4ur=T^fy~-hpqcL{ET`eIPvFUViYg
zUXfYpFyYd}v+cZ!AD9a~kIn``D{Pp^W`Gl#DpdZ^<@=BJnB at Sp%c`T=aLfJ`2UFaQ
zvS~(Ycy>Dc3;~z^kC%1*v396}*;2$Wq<l?1HY1g)LbdX$CwHn<!|P@^Fc=obI;E?C
z6407HyMO%8UB4AZvC!iIl9X5F?)=)^*D?2+J?RQNudM>E&TcbKQt5Aeu at IwL<yDI4
z{m}UI*j4+$J7dQDh at aptlfb_Stv2YsH-*yZm=SaCy`ogUJ^lOtIP}81hkceNrTybB
zlO}p<ppv&=5$7JjNU%i-_d^O9pUvoOzg9}8_xhjIM&lvT;TfPvHsfL`Rj8<>tJ!uX
z&)H|YSqKhbrFb4}xvlB%AXV&I>q>kMewwZNZ{Szu&amSrzCjaJP_eXEu~<*-kHyrs
z9Urw(3+7ONmCBfFXQZ3rDWb!CPJGg&kfh)+mOhp)fDytZhx#Ixyi8{!Mmyi3=HK&J
z*nNMt$B%LdJiS1Rm&F3CRdc{R#8a&-f$(hsa5Asgs+R~toq&=?d)+SxrB8GSu}^4o
z$(dlPq+T|!e;-oUlZU;VZt^p4OSbNdEHg=JC5EX at 8`ANZS==tT9XHi!Lsp|+^tQzN
z`dF!l?~UCpZnY|EiycUAmp6 at 6^qP1T3l&JEm&S!`>Rxla2VA;CJ at _-+z*e!CUiZIj
z;)`I|zxezN4pb-;N at oS_(%ZO=ES^)E_h>M!G_MeI5QO-R(%ZBd8G(0%(y?@?FP5 at F
zsSrR=3^sC&Ilwl1=m;M0qNVn)WLA%$dNBqgv3m7{HP}-U-`TCkvf at LejV-u4HaK32
z$gW>w_SEawS-|L0qYYBtp0xwiy>=<@{F)m=*1X0?hNye3faB00Mc?IVtF?!&hdZC>
z{U}?HF98SpvgN#ihKTeQ(BnxcWDO02)D)V^YSk8V(7q8>L+QGnU>=T^@-98%(gxj)
zbh-huv0%`q?$hJfc{bI$%x%qs_4bb>%OxVLFN`gjgXZ>+ke0|afmZc5#5AlfJ~%;r
zPtFq^_|V&Ga8z~`o(}E#<^%HZmA&jL{#Hh=?N__a0v#E|4&bSmX+5@@g}51J|AUul
zbtVVTI?Ex^08)<ELSBRkpPwkf&n8i)&D)Lp7!ySjpNyksU*BRh+c#MW>s-}jqKbC<
zQnG1^4c|JSf50}Xu(U6C;i2lfb84Llk4hV;)#15k__eK9YEAh4yNBT(=I=Ff0qR!n
zGSe$F8QN1%3Bop9>(SD|THc0q(ze`=4W<2%3LWPQ;(pPKk!Rz4*Zt7LHS+MNqMmPA
zx1L at ZfyO}so`+Tf$*u+_yZZB!iLPezt}=Jgw;gaeA#9p25RA>W<sSgabRC at q_7rK^
zwD_O24-1iZaLcjQl)O|}ZM?7c0F=_+NoFQr0_h at vyTrg3ak&DL_kKlCr%n8vtctTB
z+Cz5y4z0(`-z at 5@ttd>2l|lt(;|Ub3%jC$KX%aiEP2|Ujf=S2f-_-GAsP27A-mWL=
z at pxO-r1iv;Nb_peR^sB#g69&i9>GIA at cQTYrHZhZw$z95<EoMQkY6AFJ~ai^8=u1;
z`b>F?_V#nQgmF?-m*Y}GUX7^Yc@(oEOES#HQC_<=UuTPAS+<V*gX)aWD)yz%Z+I2y
z9X!<7TQS*+cRqcG&;7oi)%qf-R{WxCoWAT3P^d{wq7#0D17Yqdv=H<W_%hlGVBy%I
zD|GhFmTKB}oMsXP<I7g}dyK=s)LlA}7QZ~(pgaFjV-jTicoAULxfroJ23ON+Vok(0
zZ9!L;Ph-h=z+o+FVcq#^4f1)g(HptXYVsBDrbB(T?=Tvw2a&g(iq~OrJZUr2I9zWn
zRm5*w1w7gom{IYkv7g)M{x<xUI9x#05;;6s!fQpZlHDyz#3~t`cWrGH2jj1R9Z&W_
zZTiL}YCMD*-1DpzKrb=0Avpu$`M+5pcqn+8oQK;`nQNo}Ll8Fe&${1BXD{+>JU6#f
zVO}%WKo5SAeH;pu9~A#DYi|M{Rdqd%&q at YJc!OpnKq65FL?Rd)(S!lXgBf`PGZ+Ol
zDhe7!6s!?uSZaX8muM!}r_^d|TUxE!ezn at xY6}4rvp^O=6XM3=0=T_lK%z(%mc0Kt
z=f0UtLg4Gy|Bugy%zO9Ud(U0ZJ?GrBJX;05bEpk-y9x9Ec(8P29IvbH*8oDl9qS_M
zB#@x~9<s9(o}mnYMVk7cRdgSs=t?_w6qg)%f!`J_elg~3jN2!llnTCwyuKa{8=~GY
zm)bODZgdNUFU8PXEpBCMCT at tgj%b?P)de|6 at tj?AFemEXh0nkwm$eDypTn<f!o6V=
zu+%LkJe0rjvG2UqXRJPD^4(#)Gx=(SfALrOGnwVsRAH+x-6m2z7IEbJLg@>5hw;9;
z at Ha3HGg;!X>q`Td*n)QT*=|4&nb6N};w73RwZ>==Hn$3*s}6VsE`JZCqwm>^VBmIY
zj+xbjUY~=);cOF^&nIj`Ax6Zy3Az4-p&R;zt_`iQ!-`sATNs*e3C$lMh33N&nx78;
zQ^*HxXnu0|G&B(#gyTGhyAj7#79~D;gJgi$zGryjNR07$Lvk4Ky#XqOYN>uIkJ0pa
zZhy6rxEmDQ2^**PbC(933jzsAPE<ItCT&3d)zI{QIrE%>U?Tio8SID6iY%fy3P_U%
zG>1X2<W^GbnZqMu<Z6xsRXklZ(p{Kyo?F;B$9Wt4^*Qt1m~t~F+=TCWGLpBb8|+3K
zboK3LN#imdU at jrZg#ejNPmkOagS<p at Jf=hV!q5})LOs3S at PCIql|xPenD-FmMLOhp
zG0695jzoeGxIZ3h>${XT_x}p9`5A&ZhLt*zSr1K5#;FiCs{)RFXhdkfJz at sL2#a#n
zuW73eQ^(vyi{E%tbWN(?iLkivQiNNO<HblqD at L1K?|gx7fo=z~W?4L06Z0{iXO}{f
z%@@i|@r9&xvUKErRy(uWNFji4&J^|O8)FCCS`IG!1wT0T4IKE?r`+RQpenG^*R at Sg
z$rZ{Tqut)5e*8CF{90hF+|d;nB^5Md)E3}UeTuc|Ym-4fGy<5=e6Kkf&cUR#H-PdP
z3=hd~pbxm+km>J`scmZZ+TbXxWf<1tz{ujr<aVI;ytKFxiYHdJiP^=03)OG(Vr~5(
z7eK$S0a-d0TN3y^#u<&qZkvOFo}1JU{s at V{*?$74^|C(@3T#sEG*LhwdD|GWgoN?(
zp*sa~;Y!{xVB#fp(f at SI%li7hB<{OD-qEq<nWPkGiRy`F>#GjmPpe~8G at GPY`~S&g
z(9aKM-PL9?Y(cFnl(|---A}{{<bbahhLE_Iko8r_qE5mH8zF1iH#|CvQ^`a3*P*m0
z at Z$*nagqKpp%N`lBFnQHCB^Z_^=K at d4D3ZsCW`og{txsAyzhd{k#BIM6~}MZV>~{|
z>;>voaqRsP1IktDv*%@&8k|8Nx)YI4YJq|9w<}=v1L at SS-h|2wX!i!Sqk#V%iW-Aa
z1)%e4!K)YprWnuofK~nX--t at A`PxK75&r(i9=cabQtKb#i)-)F>(GAxd3 at fp;ulEq
zR7OP{QpRqn{jgMh(oBmqJVo&hwyUoLnImjooFeF;m6 at 4Q5;yH_qag-kpWK<e`a-$d
zuKucbi5amHku64&6DW?ms5v{9PJrfo{bzh`SaG?q`C%xPu_LZc?crVx3ZKG&PjYo8
zRQEDehhUS0%}Y}RfF1rEKO#VF)Z-l1dGDv>Be0;7o#?OD=Fi&iQ_$l?Mzdd-`oX<E
zmO2XAO26xaWfv_JOAZ-Y{mLIvdM?(PF*Vo<)8_P%&l!1}y~jR5%W2(W8tA+Rn3{cA
zEneJeop7eZIp%LxKgXcnust1Wra!i8(<T#!0lZtbKVU`q;K54d7=1uXL8%6qHi>;)
z?>R6PvHbKs6iH+Cp3oi5 at YA50p2J;^#fEGC=9RD(Jq>lRTn9?Z+y>yOCjO63Gv$+w
zGwofz<+6F`HxhQfK^jV&r#j#Gad~*asY{^8RG4%D at t55KVv<6C6G=o-Z7=!r@}Dbz
zE<H>B96Ve8e0NXz^X#+b&$&J2&sUu#e;(0O{%ntzKU06qJ0zG^xx>EHgZBXxY-=!N
ziF^<JOJ6!P{sJ;|V+vlY;fH14LYLA>1m9o3)5H>0Hb^oqO`u!N$Z;;*_Aw-MD)1Vw
zv?|yxX^mc2W5AL<IQV1sV4vWw25i!YuL2%{T3*n{(vzCKEUYd#6kUC9&WNqP=O^;j
zx2Svd`Ow`ve4|xJ&VpbGl5^INVfg_Yo;{VMWJS(QqR=QN+#}CMm*K^l&pBr002HvN
z<3da%M at 6s_-*}l;B9Z=ViE|NB)rESDYFv&{kKyV_ty^|*?mnKxKEl=rclYsowdf#;
z*rAW_dt-gvlEeEr4g2^F9(Z4`#ih)<xrw^zs-}p19|+T{qhYPGz0qz`7m<+BKI*&q
zFlV27L$mGv(6khPXeRA0l=v#sQb;zJwC-=@|KA&$rmADQyNBiC%lJv&0magJv0I at S
zmHDx&N15}&+Ox46*4te_8P;Dm>afb=$`Fr5?3LW4>DL{}>QX5-w)3+MhJp2w>)XT>
zIWSRec!8L0)}davgR|Xre?ldHb#Wt}$*V}Db)m#&lG^0}b<%eTt6+>lE=VyTKaSj1
zoJ_)wra-o+ECmY3(P_!*;pwq<d~&S;9gU2JcH{>7$#<m~BCYCo9h$Zd8!-WG>pSpi
z8JhEc%oRd=`c+wS7E=d6VNLll`1c`lfYA~dhDrzyKa9ea>XL5gw_Y0+tK{L at Sb%F|
z5$Q0-Xi)6$E)*6uXx{c8eXgn%TfOYGSNNAEwu>o=be(c6(9gr}cSib&?~u<m@<)M!
zfa_C6VI<waBsR+i^-K`TAE(Q%uow2^)c0VF^4VTDO27uGtG<A~Vz^Y}#g|`ilQJSc
z^YQT0$n~+$Bv)kVJ!N>C2t;=3^F$`OQ3^Z=neFXT`*HSJ#=eLNazciI>ruU}7GS^x
z_G{*J?O at gb1l9DNQbyQk{@zu)Y@$3XC0)umgEiDc*TN at IlS<e+2=Ima_6xk!7hbJS
z(%zHTICY(Nw{XKRol at D+@G!!x_2GBBP^Y4oA2p==xc?O|+v~*{{DIVNTLONZ2nkoi
ze!dzmP(Ss==A&@}AfB-}-;p+00zMLfpprJ*r6!Ti8ZCx}?f6(9G4&9Mu_CS~jKOWm
zAv57?JBs3YTn0#kl(4-d=d;fYhM-kl9-$o*?+31z|Cl`5W73|iqw2%oqUZ^rWodr)
zQa-s^^PSFCZ+rxw5>KP_%ku~xclDBayQC~hF-fwPuoOr-zfm=>!i9oCx&pt1oLOyk
z=81l0`4JuEu_h^FCt%yljo7)#EBUn?)8${^{|bK1fH3Q+i2KKEER#`-w|vT+F-A3!
z3Irw0ybUwMltF&ED`Dv$fc_~ia!Tmm1lCrrE>7-^3~`K$oGTG2QXGt4cSet7^>veU
zFQ#TShO2P_{Dkql>KlFh+{(Q`W4uLiMU^!G{e}f<;;;A>D2$;IALV)*45Ak*#&Hbp
zr!jnYMQjX%cu!SQ2W#5^qvB)Nzi{?I{<xeEq at oYxE34uL(#QJs8piFEJ?O)%t5>00
z!)=mW2hG^yl}{%weeVCsP-gz3ub~`<sW_JZ?PDzC at fr@NCrg77trXcYqDyH=_2Cp5
zYztpkw))OW>~dlkj_1Eu_OT{?%EChNokU{1Pty!ktc=aT+LduL at LqHV*pt}+LcN>v
zJ;^;J?ons+SBlt*5ay+r)G0hoq6wa7Hmc)!LS8H(4HJ~t&DA8Meoy^6NVSoO_jPnp
zQ&~k{KBn+Q^rGIn4?BC at m(k`^ewjTpR5ZT94>U6}Mj4;r)SEw2jlsApcAyvjODpCB
zNhwM&&J0Z&@5it?klXwb at zepHdyt;{@fA4h^<H_qjY at cNsD6t<%Gfa|eKqV?XAf{s
z>lIWQc?H+^09Q160c=-T9Wj7+>-m at I`7a`X+r9F(D8Ms`OP`fOTQW(%_c1=maR4vT
zD at fBT_-J`IKq!{Yi{Vc<(4)(EnGbbA$6tDu$>n8odz9I<EFK3nsSoxnBk?j)k1}Oc
zCU`wQPmbpIVSbj!^V9qT>k1vo=Kv_WNcTqY5lNbOCG3XYiS6V2NIcY6;(L81R`!+n
zQeTNrj_+%SuIw}QZcKG&16JsX6!5YRwrXv6pe4Msix+IcUU!^^y&f6juITS6ZA2G1
zCSV-g<u*w(c{SiKvJJs6k8}EOfRDAf+1^N2<$VhmE)?4%-FgERHB|>pN}}D==pMUQ
zEUwM;ln=$?P!-!(*0QD_RAgLH?`0SGL#TFEVG=9)i{-}h0mLE!i?VxZ<aqS1y2xqw
z;N`|%cg`H=n82NWX8(ymhk3RBUackuG>3o!v1|8gI}qpqvD~Bs-Qjh$ER95Nl?rOS
zF5|LU+77AUq;i`PsQha=Fs-IfcwT0RfpnLrCP-S7wi7 at G8@*7K(crz at 7%}@V8LWGk
zh2H#cv8^)Q!d75MI*IEF>r!_4<gLO}m2?MmFGUbI=@p)uj#6T>bQI;o7NPW2D?RlW
zN}uOXDMIN7RQhl#hM3zCxI|QDS>3MPLfO-pgGoRF3gye;S?(MXe&2*a5XGE)yYR%_
zRKrU9K;fxd;L%$kp{gL9BJ7!lX4-;2XJKTxyTar_8}ad&hZ()*kKN at 4zY)0M{!-4+
z;DNBis at It!JTVBKm6`p-94SS3%E)U?(QBR4klm}w{g!i7S;Ifn8Af5*Li%J2zi#R7
z&|fXoA>pa@@POi=vF+jAC-s?AEH_mjFl97U;!z{G8^WLQ0rU%{ZOFr+>i~DgZlQD+
zA39TD9{s&mL`VF$QJf}hGI`}-dVJex2+a4?J1iVerNVFc3TjktO4fczDjx)Iu4>__
zev~CJLXot{u;}1?kCrktGT)l1Cn=DgDm<|l-%3`dqPmUn)J~o&wJ5{Ga#KlZM{HL`
zb90>9P7T|)xGKS&QRQ}37nw!#=WJ)V)4-<z0Fg<Kob3{xS_4mP5XA`vcorPF#bz8i
zR~Vi10o3GYfb9{Un9JLc{L%<)!*sF-%1y2uQ5LxopjV%?#y~4GF5HZEf{Ux}fd(vY
zyhk)QgiH7eeo%PoO<J+*_<-Lgls59G1fg^+KEVK&;^M-kx&;4R$!7x>>_PH4E~R2C
zx5INuCm#`X=Acz?Mz3;_;gBIwbiFS;@e<w}b&e38`hy9>k|X28-MOOE(n6r%NmTXw
zQ<@fDM%P_SoGX3o9%muCKU?A)<HJaTIqRuNU=rRzV>A!CvcbUgIjK&e^aeVk6L@~s
zRA)%{8L#zfiB2l)V|C$g&uCg!OQbfssgg{wO@)R>ahqyRLt(Gw|EcjD%O5R;qKgL2
zePTNb!^X$$sN{xTHCCLf#)pYclk7OO58w at RCp7RbG#RfBS0M)`A1)M1_Zgyd^}0Sq
ztMHGXwgqb2sI!3lR&UJ4=H=X(&UOmVR`<~iGZ6|2ycsZJv1T0R>o6Icfigf^U+v-U
z<+Zrwo{Syg at x*jN7qj;F+Jn*gvcY_Xmz~D34==$VeYBo5#Elj%N;WG6vmQL7=dl(2
z5~jyCWbJ+UGmxOHy+UH^IMsvQ7{a>|$n9e!zlx0Yv0@^1*c{C3sj%TenhZp&-(2S|
z?@!$wAmt1TekGDV#j<N-jOK#NdNBU^Jg_aC7>o4VZ43xVI;1_^Vi%%~VXhRq1E$=X
zU5Z!6+og~PR^&gPqxJsRbwtqY_p?r4$O8%A`$7-G(c`zfS&N at F00%q-C$hhn{Rzn-
zG<f?H(ovBFcp#-L(o*o`JUfrJ at 7<)QwJ|r@$0j`Vve6))H2DP2gn96|X-NVaqtA1W
zS%@Cfm3*SIb}kHgQm}+Qt7V~Ru36{~-7%WNyupWI_#qJ<F2)DjLUe2n4;Fa16do`x
zc?vu`MeSqmtpzvXdw6YJWuJ`R8P`sho&)Yfz)OGu(xz68{t1$N5YEqPb*>7_CF7t6
zgOMXQj)D+e2=xiO0KrFwN(Bw-uRkRRe>x*Kpx%JYIItUb6hhu3F^>&f0UcrG`J at Eq
zCf72oouqltGPjbiMTUFjNlwG^p%wkSz_e&}Xq}vZ_(0Tlddl{%Js>GJw8j{HWq&k+
zA(}(_m>>QJ?LPLPjSqK)88-`Bsrd=p%%D@@l*o3HX7O>a`3KRw!)tEI?iV-?Btw-b
zciba9l}}SNn(%7JWkTt1Y1ej$fs5d;6qpNt{ehA2_b#FAm)!XAl${5r{hw;0mc5d)
z%o at 2(YYbcr|I^^VfUYCZ{r8(w!-xjoygR&xu<^+^9}EY`#YCV#{I{X~BqdAXJ9yoH
z)a#zf>$X7M<~Iw&Q!roYj=*xsypMNuu_ at ZoS@1OJvcPO%Yl9c??xwA at dOgZ)BS!A_
z=OjCq4|iwZZWKzhAZNuuF9V{hw%q174#B~OS4cDOizQ(F!I#7Bi2cm_!pBG}xf*!r
zFn8nHvzr(X_)Rg4MmKNX75<%(_vfClLQl=|;YC2~AdWn26-txop`TEC5=I%e&?rnT
z8$u}56tK&k7NM*^)^uwa6%*fF6~3M(W|eK=THJ1#(Orf(Lv#Ti#Srl{43Dix>SXIc
z9D)t+oO1}Uh6*#Sx7u=JYzTx3bL=gAGFE3R)k(v8_qpr*)X+zrlz_u(j33rl!swq@
z!$@O|0c#{3$7*)uo>+o_3I51zc$oqJy#QHc4eHl##{*9z;6o62px~_eq=fkSjAUY^
zUJN&j0(C!Y^0N244{h}Y?hO at Pqj^Zv>vL>sAPKiv_zgS`sv>5LFTwm{q^$SU<Yqii
z0DUU=X2u&qavD8GpW}UhFMZiU(i57S?_*&}@$d1V6z+R3+at1e_2UmvG;l|zADzV#
zCyR&&y_<Q;T_|ZR%^=)(Z=S#GE7YCx?D4S|NC3s^AupitX6`(pVqj^bQ2Gfx6?cvb
z+=@Otg{_V1U$zhtIZl#SHyf9ac4t2VQ}=7iFJWW at 975<~CK`fh{${}UnGp*nRG4XA
zAyc!YVa5i86`AQr2Mxca%duLK)fc-)25sE1MXRm2kVk0rH2V~&5<2T=b-tL^mU_i;
z(s~kjrL}-X!&6W#E at yS~aw~B#p7Pa(vbJCjCauU)AIXfWr$(+poO=LU<}S~XGFCUc
zvsbs09;&MAp`u)Mq$*{`j>-w?aLVYpJZcYx)Iwlzv-?y?<sl^WYsxX4aaM<P_0TPq
z;&kKGQ$6)gd0C!;I=w&cd(8I*KN04T)*~)Q4?Cc1lf~aQ6kQqX%l#wByF^i(M#zDz
zs%#T~{74;D#pO^K8y!^$lGG_w7+ncP>WZ&31TR&`#i|38o2YYV>FRBObl0te%18KE
zg9nx~S5X`O4wqR1T7KgINv)CS=ox at GlhYa^S#pKbw3t4CmsR=L4*mMZp}sY~TPg1O
zi%HYQDU#b43EiJHs)ulZLuPWY3N1-cOP~ZZ#Qd?$aoXcAzV)4zC&~9V^NTrBK=ALu
zn>jVSJ$fbQ8Xp_LF&FbIxtVk&r?1auQdIe*-m?d<<mx>tzh}JT0Q_%Ohpqyo<=?~h
z;2pOgm2K&cyE&^-y}4Y!<IY2qn?7|{s^WB4dftQK4OsNL%v-BpgZFhuens?Sw2oN$
zF;KYGD;KB+^{MVmkVpTXN#7aeQ`IHC>*>2K2e+N4E!(zr*FiW>vh~)Lk(4~Ux-G8X
z=gYUNcfQ_wH at PY5x}T6|eOw+|d%04<ULJsE)%%<UNROHoT!b;4(>Eh)f%k+{@Um$S
zUJjB`fm_&^nlF}oiJyy$oY}g(JX1#;A~&(Ry*3I*o_y?2NWK*84PF35bym5_AZ)bc
zkHYeiTd4_IE$WZ&owpI<&uv7nzAEPcgGd#??7NPGvG)KYe`)*)FyzSz(SAa4>&(Y_
zKjj0)$j#AS4mv;h+kY1L-hF&^SKmNP97x;bzJZv2QXfaIkL%-ok45`v>h9y%^MhaU
zv%vT6<My3>1F>)*7n}o#<tO!V${*wUD3?Y1XzA|b_2&own at K+heD6LU+|f6XVh&{d
zq`tQVe*S6u@|Hiu?aM!w>iZJQckj!2=WoQTuAkY6UVS~$*f$s(2Q$^x_kh}d(tJMj
z`?&dhOODQ`t$RL~pC5ef#GeDccOM6z1Bjgix$_)A>_4fG?76r;Ha!~cqrJP2PdA(w
z{1-Vt2Yl~7jyngC1dhgkmDBffN%+ZU$xFYBTeIJm=xa8i$64}9{dpU4BKv1HqE}zV
zbAY*pgL(2CU~c)zeckxmxW1k$*86%(kG{TLciu+y|GACm)z`V_05gPxdGXr5!3_Dy
zeckzNTwjG~Ux)PQYxDN=HX`lkHlkNw?>`5abiPqGT+{bPN&iXv^vh at B_UUD7qx&?y
zd!HWOc3$w;{w(mjvZ_it5h)8#SJlb~*P_Zy?|wgg4!}AIwspd}NwBQOY at ai`CvZ>M
z{-9MpH9u%2-?ywf+7zAlVSCm6Cs3-4ajPgFCxd#~26wk_ckOw%^~L9CU&7CBpRl1(
zEj<-&VsT$hjQ#VmM#;4!kdE3<!+jY$Fa)fyAyM~}AeOraYvnVvpn6;{CAoGBkG)B3
z)Dm5_jdbZk=xq;JU&#l5@@6Sl8~rToWh!uQK6bEM-`C4N^|Cgft9?Z(_m at K^ccS(Y
z8gt+9xnw>ex!RY9Njvlt$@N75b-Ds0q>L}Pe;sHaH19&*ymI%2)Vjk`#u=~c^okdK
zoO=x*AFI3n%a3{fJI>|iWRX~M#11r%pB;oI@=#R4OHd0eRsD8EjEgSWhMCZ!?2d4Q
z0NG)TzQ7=2`y~0KNqCgVkkEQ(Cf){y{^QK1-?yE)7WCo$y3>Zg$^)7Y(ojb26aID7
zXAXO^wC?dhozv}J_P#fxPHI0PDTPK!?y{{I?PoAG+c7#%MHg!DHwoot05^*TPN&ae
zub;M~B|?p~`#VYa*9ot=O=2I5+J|LTYp&snwLVu{Aj8kz_hr1_!ynAK<`*Lb;XGg!
zSsb$8bs{hZs_<vL_mj#?y~=yByzeKM|J=*YEL<nKjxQfBnLm?UpD(+R^lWzWN+a-P
z#YOAjlh^gxilm;OGR{b at uH`(ji4xaB{x4R*V7v$GC~%37)%r5(g%MHsTo1bu;M+NT
zSam^JV#mXepfar)>T6#%68-D&s#D^s4nWoIONZ<Jo^a*$1RaQY5o*1Zu at h(2Rb4b1
z*84L1tPsrIjL_ROcc{+e`b>~7?IPu&@Q-I;mR+AMv(E_4gBcFJ9?b<s1^M(D%ylgM
zKK=5QB{9CzB&M%C##f?`@zoZ;d~46PZQRFQvjzu9Y=`<=Kv9tg(AqXZJz1ln?SOpp
zz=~-SOFI6f$-o7GW&0(;y at yOceQc-ZD5;3q5I>Y-HN!y|$RoSh>q#b=A<3r>EPn at 6
zu7wo!$<%Y(3#$*O8n{*UoDiA)4NXO>>c~fGe)NNje=yJoG(+A3`0nN?0?PEVJ*dhV
zMr}^svEg{7rKY6DL`2p^@{oH8;?=6M7UB0OTKl|P<1^3#B|gW2T8WJ|0nfXxZ5o<l
zRT#LU-pq&3X^Q23qqiy6)4$S9u|mI!nqrN3<NsocHAKCAc&zc#Kxnl_az(t3nxnlB
zbGaxmU7KlJIY?q=VS`Q5sw5b*0oc-UZ=sTO$md)jcWS{we%IH*RK0j4MJ!KrdbMpN
zxh?J-5HLmlE!J6_)Ww%~aakY=|L%wyZ=J_}!%sSo8M}RKhj&ZV<N^&DMpPK5RwVbA
zwCag9!CTM-VpeHYV20FQjmEISgEN%m{tHVEX~M#ilO4i}-4Zj-V36Nc6WmU&j%3y5
z-5j;;SUy;iTQ#4##hX>*EvpSwM-rv1s#<Ayl_c-cYD-%JmzA~!MoQI(?eIJto`>kq
z0z41okCxK5V7bIy&PC5kn4h6-MuX<q^0<+#%ZEyeqx}S~>;}#8+sAmK1=f3WFB^ZL
z12$5GHO=w(<76ka3E8gj*14!N$)BH|gU>v_y83clMGdH&xM3<^P{R`V at 7-_n-=&-Q
z at 2t1zm;0N#<SkV3a_v|)u3LFt_Z7rQq-w&p)!G2zEj=2q at ZOyrcN`UfT86-W1<X1S
zwT?~N?r6zy4^{c7UUG1V`f*BZ5!PbV9DIUx&8%W6nX?5xlR`!^D;rZAU+&M{<yen$
z#y-n+cTJu={$FT6xEDRPCXIw=_2aFCQfxTb7hr;QD at nOWm&5!hN$oKH>L3koAA+}`
z|A%(~=xr$AR?>FjyegA-!B1pRUjrsjuKy<*z>R={Tj5E)p92$6nXXY(@=TiJZ;&3v
zT5)4b_|%Qk;rP4wTn{_I_PJRntpW9)vkiuI^5jc_e>V!H7^0}&ItX9jqq}^R86E)v
z=~|FUPJpC2t}ElHmF`>cs{1AWwd)`FHJ!e8kl at zG+OQRH&Z_th`1F{KV+mK+(n7WW
zWd?Nr6f{M?!G!q|6Ew$xQX{Fz9vO}FN`eLySkmiMa~=Nz at 6XWt26*?mjt8wiml}8*
z&M9s}&3`jvF!i=X{s2?5<uRI)XW)nX>`)w)Jn2<Tx*C$!p+{bUk51u@=%D4R;OKhm
znG!xI{KG9b;#F&tIve9~u_n!NDYRu5PC+3a9H_{g8>hEOnvdayr1KBYFm1+geizcf
z7%pk5F+7t#?IJ=Ll2{EnXzkw1UtCAj_{vEX4U7C{U?u8>U3wgHvRRqD3>(Sz2CeEp
zd4cJZbT1oxvmOAtyV8Ff#uBpH(={!C!OW$Cq}+50*l=b9QWp&+Ie8vHf?rh*G+|CL
zCAc at MO5TJhb<pzuIVBi!6wajRol?vl0QY5-DH{#q76bwd`?V1in<woh_zhUH_D8Og
z3al*zroJSbz#QJh!AL2=FS7Q_l~FLyaWJ(&!lLh2wRs|4&tED}R)CQ;LLYZbhR$K!
z0Cj;Y>P)2Tq^7A~Ulz at Jx1RNR%xb55xmeZ at FzaE!Y3tP1TghB!z$OgSpgx9Xsg4DY
zqJI<X(AQwoIcsn&TCGr<nqIBvrG;=Cdcm-6)sx6%dpaZ$^P_G*#T_jGWq`qs)VpBQ
zx4E55jA%4oC~l&tae`-uk5%C%Qj_WoqTvp%R$!yBSS>6_^<(%G8OA!~w`n8{K^uJe
zFC^|#cjAJ@%dz#&O3oDEOvG at cI=_ZrP=s&d_9kdv1k*(!yOgA(YjGQGEWvGM-BWC$
zOT&D!>YamnJw9i?T6>R?lJg|F$eCvdh!Wb08K?r?S3xE|<?<^_#roV at UQarzvE}*5
zHitoVS&iz$P*>zqiIqBe%rNUc2vgBt<to4p*6*=6v(BK_ShuXfw^EAr4zF*+$9n72
zkl5d1sH(R<v4(F-C~$B-2fGNKKhvKJHAhXc(GaqphJ1WVOf|T{G{=j at wCg|k4o_DF
zUgXIiqrymV0MEA%W?6ImxY}sgjG*Yey%WonY{SOV+D}>y*s-?zUUe>7*ekC)?J(^y
z$gLC-*DB4|U1XFUAWh2-_5<4sbWgyl2CK<cN+E5XKvs)biM}S(zrBhE{)kExk6lvy
zcGN?nk<QZ`{h<*)_E)rzBF_b0GI&>03pg!sE#CfSlX4e1HCTXCo{3NSJ~-Xs1#NTf
ze!S44Xl?4Jcf+KlO03LAuI$Tr&C$14Xmsffx>yLfJ0OKT#hT;(wMH07)}c9WS!<*Y
zNSedFmfWn^%d}vkd`g3rzmwmiF$Fu1-V40{;@%5dNgMa}F<!PT6Ys$1&A%g_CO|To
zC8a)!3-tXpxImkT(7s?T?Mm~bxGrjYoOvwiD4;({8};WkNbunL=YESe4}lpJ70IaP
z(ayfO$jH->-hs^lMdUTyZae<|JzYgx$-d%o0CNBYNzxhsCq at mmG{<FYjNDmGJ+WKw
z;dB6N2TR|F4k0k+L2sx3ne%Joz|9~(yU{!T9xC%I;%r3w`hE+2 at 0ED6D^_1Oka5wP
zVrNT+fLG&Z(~rZ?kv_0zy2Q54?xF3M=gDgGu<u0H<_;Cw;6D%{g(;#kD?!bjiP*Mf
z5b}WsB;^U(*J at 1?S4SR*OGM$0E>ypsc*x8X5wEF>^u$e&sJa at _2G<>$@MbakereL&
zp~<zd+2H4O+;d8jH3DaP$Ge6oJ(5e*3M!zuvonR#a!$6aC?@Yz7^#JPc at 24&YteG^
zl%#z9vsGR5ALKro)ZZd!jmzoYsf3lTp&U at +F(bG82)eO@^%9)WP{9y9HU2E}N*AF<
z4ccAC&C^X3+cfFeS`H{tbpRk=Xu{`fsTY?KmcASoE%0SR=_N#?ZC=YUA4{^q)8fWS
zAwHPQ;e&TLawlA~7TspCn#EO<%nkZiOkaxWBbjw2{31tD&t3OmWf_<`R7ztVfm!<x
zeZgOTzLz=)9UxVOeaPH8#Xx8QHnGLYz1FZM;3QYALAPW`4ljO$oTntabN)kRg99+D
zyS#7;>XhMA=GoQq8r`_-VpaYsR%HsW(h;k2AmybxZTe#->GQy$NbBpiL|m}&Mhk0F
z^o>^3HT1?^AU!&r91WE{hjLD3jTs at cdgW%ov58LA`L^yB)QC!YTJe-|!34MROCuVI
z!JPkl3kga+_wTo3Oym%U{Z?k~hMbCoNf|<^3a=Aui~8qItP`HZ(A>pSCI;`(OE24k
z`skC~dXwGeJ<%E>HYl(bIMw=!go(AQf51}X^-_}w=Kop*EnWwT?fawOg{=v?za|*R
z at qX&%=3Ekg0(qdixYkx$?`8TL&`Z0b0~{UOt^x65dw|{|w*lyIKUA*jRmGx{Xr9_D
zarVixGfwFvEdvXag<y9S`)<9aCC(Jx!%ZgO!8}0Bd)(}xe9|bCEynp|`%$Wn-XI5v
z+D=j4d0NzJx1#zsimV;14lQTmw76aSf?1+cj>^LDEnIyvLH~At3)%*_b_9}vzTKIu
zY2Jdcn?3Wdz+AH-asi}m>`t2mX`<GkFjS;tb)r_&4(U6@>dpj_RYP at da|0yD+b4*u
z%AK*}_wpgLxu&|qBw}dITG8Bif7OGuEK6;p)&r5~a~Ae(Sy8>XC4o#UYT)EqDP~le
zYuv7ywFw?&j;S+ZcAJ~!PODJXVnEI&uH!qZ!z0YQdB-nP*m?ThpOyY=C3ZFe9&+Kq
z?P?aDEHQA^`^Y1#b`*_sg4Q56oYtynkkLKa&m80XF2wPyhVgtnHpa8N({PO4=4YIE
z52I6-i1Iki$e(a+-e09ctIQm^iSlV<aI`z*c>GoqkcVaXH*LkONK?#1a&?LBt__x0
zEs{2QkaY=VeE|Nd2m29!F^whN4zI8y^7PspDh=~STSG~1NM1F-+ at K|015c4eS3}XD
zj0Vj*2J1)~P2f`S%Ts3^+Mf0vwyY^qs4#Ysu>h$eMY3n at 59Qm{jD8#;6#&_qvz|I0
zOywGe3xKjp_p-(4_A(o(D`G?0iezq$?wY{zO@?5iYr=2fS1A1pB<7?oUWLyW7?BxE
zn}IJIlWw3ND}QrLfgd-dUb%`KW#)y at 8yOIqB6}~G2_GD(JVTnD|5^v&FXYHZhMaWn
zQ67;lvX3H8{C;F5<#QZbX(EkvH~}y_Iod$odpE07Q%|E4v!sW4R_nbhsYUnDkK<OJ
zn&e&Cvqf2r(1%~(N2-%ePY#YmKqGnnv^305ZJL8^0#M;+h#A}HFzIcHYer-ZMX>X*
zua!mp-Ei{DU5i%>@L&?zc2T*%pGUdN2pRK`PRF|@WRWO at J%WG@&OU-6qS}bgJ4GSC
zlFwCwkJCNa{r9pOp|F|)C75I1&Cqye{StoI4XL85dG)2P9l{g+(fq~LAUtJ&2iJ}@
zBjIKB#TcLN`!n>V=(uag+GFsr#)kR#qKm=lHryK1E%-NquU^-VpxtF%1+@!*s%5p|
zw;-SXzP>i{UH2Nr2B)j*f8<dikj|DT=d3{H>2k7#Qg0PWs5FYw2CDl`z)@gLfj9nn
zSZ-8n4$Jbi(O5)%19Gn82`8rLnYw)?Qu>)(^5V5q-E*2eB~oI;lx|OneZ|kq(#WMJ
zDTJn|FIu`4LZ5_QKwlq46RbGj<s;XV^O(Q~J~qf((wPhN_3A<D{oMs4JEY2{(IPox
z_PH9?ekpE2G$&PVD!gFl3F)fxsDth5j+AaM*dm~Ex$1hrd3Ydrlsv6H44Ahwfer%<
zCs`}DAC5l^YU0iU#b-PwD!H36(oI2wbbsS-P;oV52Rs$u2wb7Lan&Ly;Vr2y&N^uH
zuo}X{+*D(Ae=(y5(6MN)L7#5~NbQ05;gkrb>)04R_l1swcVXQCDl!)Xf>OmC*PpBQ
z7^?p%wVOqDQhMi>%Wr)Dg$7TGJ2WjB2)|eFMy$NuOMbGF;@prI)P!cIimXMtzpD73
zT^jmBSFCSA?lR3MxU1N-RIeggA_=0o1*41P7>iDb87)%5j?iqom|-aoO-m#nAminN
zbOXOCNs_p4bqNqT9<ETVr at L{f(pq%<fkU_MmdNWkG%7SR1==;BSG#P`sMhZ_YE;bg
zdtV;fL$fW5TPDGQpH<}!4eT?1V;?@pJ1u?q9Jib~zt8b*+4qaanC$y0Xe5_?4`9|8
zZvvzk6MA>&U)r62etiDr=g2>`JO7pO`LFAl{~W$A(#g#cs-xqT_Y~Bao`O&2I?f*b
z!h=pqM7h%#ybQ=JpE3&D0VMhn^~^PB2~Z`4`k at B)#aUI630S;ZMX_0Q$FFb29UmDk
zxoVduRFZnX;AfU2bl2acp2#v7dd~wZ0+wBOk%DojR^5nlAH^EffA7_QkK(USNjh;O
z3go?Pw_C}zxdsF>v??#qT^-|={-B6Q<&MEC`c)>O6se at zt?2_KcMcMT>#J&|=S1eN
zw(#*(kC9)TD)bv|O_&V?gGG|7?A#Y1es7BV9=S>Xo*aiafnR?uE>C?d4~)dqcGLiH
z{Ocxu<6-eBd3HdoE{ydaGDR8uSUm-LJqcu$92u_O6j$*B(TeS%l&BZKcz at G)m#bcK
zOw*KU7e&(4A#p{oj1^^ukU3U31~($~g1b-XF5GrwcZbdi_to#BaG#GWx+PY$2i#u!
z4fol}*wJwxVBF|Jn*ALe9cXYEe{`tN@~{^lxLJZ&(xD$6<GTw+c2uq!XC6!#a!9vT
za`Lm+6Pa3kU{#0PTvdrU-0i9=8s*NY(rWN&I6PGv$_^R%YXbaaR7JS$N@#X+WrAmX
z4XnNucbE)Ow!j`mT#EHMNCu4QX1Ax_-+~y^4am9;P-O$*aj)XFMEYqph%4PWLyJym
zJE%gAFg+QmA$REZ#P%!ALl9p^YgT+FOCMnwD!0G&;;#usLPG#WQd<T_KO#Z|9C?2W
zqDW{(w6W3SB8n7i5Gk5_qcZ)z@}ne!?)xMtsLenNL~rjS7fbTWm4 at Jjn`k<!Gf}iK
zZ6&U7z?0Kfp3|Rlr}`K4*4!Jf9{FGJYDxHII06GB<*+4?B8QU#Nm?V_t5jMn7+UoL
z`fIV_- at z6V9rqdIS5j_Gl~{PIk%UM)-RO2&{o`-A)u*!BE}~hmu(8%1x+BpSy2G;Y
z_-nujot%Z?AkS1-MwFw?l%%~MJT55;dOF#RGMx%b4n at e4v>ouF&-10H4cS~td1WOR
zU>9=%_Ty4ifL%z69N#mqg&gE+j`jB&4O<Wlinb<^>H at M$h2=)T!dkVfcIk-<xAW#Y
zx3e%EO<Qk{TrO<5#po6`Q~^P-;vz8v`P~_c&r*ojU~i_Ye}5Xqll7*Ggwm?u2kiPp
z5@(}IS|Q#vu<I>uwv*p9logtEFX~8=B4PdS%_L9HH9$6Yb0XdTDV{>4sp)nCjbjSk
ztTwBWDA<b1KC+h;8aWl-T|y+!s(PWhl_&|DTj=9CrHqe#%AX3!l5|#@FQZOuGaCc_
zi~HLv+ylOh41!lvz+T)xU9521zAP`n!HOi*mGEl|t=cQsnj~#!upu%fs|Bload;kx
zymYx^=n5NDqfAZ5xDW6)2x?KL9_UpEhB>ZweXz7%VoISdWB>YhT*h9^W$ZwJE`w9`
zkukst-chUH+2I~?3?$KLG at 8fffDi*0N^)ftUhklv*XQt3DY9e}9#*P6fhh0idC(9X
zAy?XKc~E339<sw*wShqlWWUH~BHJ$b+z_y;-se%cX?-*guVfyt<d-#pJbpow*{bow
zmP0UJO^Q50n=&(9HGGK$YkT;v<GPDHYzk_1CjU{><WbunKu-#%x`LzXts5TTa#77;
zzt5;EsLrP5=!SX`k9_J(@Pc~l6S171F9eGAL;2L{U@~b!)>{kpZ!Fgm+0us!XjB7~
ze522e>G=SqV7=9&?-sgx{z3I3PT9{o&@2uu+l=TI{v!0}pp09618{OXB^L1l8%beN
zbFy{AX!9<w`3Rg%nR2IfX}CmF&!EjTYK4|)VR~*yN#4dL&2i;I?iTwS1HTeV$NOlL
zp17S7XQkrh5 at HtB`;?}I)>uM!Cc1Ng#S|WULchuzojuPvIxsFf-xwGpY{(WiNJdxZ
z3P(m0)Ol?hG^luh#cTf1o3Sf+0;B3?rvyKcl<P0v)P=H4o6T$9M&H;hi;fa6LzFli
zQKA4S(SjbX2`5G_B%~NgB&-;*5?TyhpXd%Pv_;IJJ8hD(#txW{csoFI<c&Hf?Tbag
ziI!miWb{z|QSXiwK8cHd&Vw+pX{mq}z14>- at 1W_(4T^iL-OC!p?3BP&*|!=4R|p#h
z3mZhEt9ivx*x<iN6D!Q3`9slW#8kg|m#caC5sXooJv7*aUQ7MvPrVsOf?HtRN{(UI
zu%&?Uk68jk<YP&Jfm%~=*cRTkExc=6c-OXI*L)ct!*D}m?K734u|IC^M>A&;^hk)_
zj1PmYxHOiY<iP at buySA*k9Y<*0QVO``UKI)r$oz3(LG>)jw|qvLDvI0`L;j`tXelr
zW_Dm?kA?5xi9HrFQ9V-aS$+r?q;thJ)+x&GY3h-FxNTe2Dm)n2pK%uhsr<{+D91S9
znr0D7gD?e~($V2q9w`#-0hSxr-`#YY{g&_h2C<T|Y*%0nx-fn>+eNXGxXXmH0hIYk
z%nW4x2sn3Iwf at 1udXm?*3-C$jAJABnn$W75+a9AJLqZuEkhfWf`%#Uh48w_UMT`Q2
z`W~GB>)13)$okeo*lp8nA?rWsAu(iq6(2qUCexyIX?RB`pR}%a0L3G0bkDRe6hoy_
z`F&<Uv5`aU6Z~Fs5|`TQ3n5E|duZy^@={~-49aj2wOUFv#2TwFk_wtCN{8YZj5!X>
zgdFt|ARnwnGqFoH>{3!8Jo7G%rY at z;r;lc6bLV(iI~j6^790)ZFl0Jr{4{_ryVaAw
z*EIFuaU^$$t+Hmp;2jfDtClVoNx8TP at 7?dwjb!hrjml^qtU!58 at +J%<uA<ba-g-*~
zM|3(<)~XpLR$@02ksL4+b}mWp`I^%6H8cKI5%a~$ezY?k&&<S#QF&wlZ6^k<RRgAC
zWe4HXJX?2CRXWD+1ctB#ZOdD=;sx+iOOZ5wN5a}ht<`PpAJ;MqyxZcM7JX0Y^`6<|
zJ-&5qGxXLa>aD{p19<b2qRrFOc>8ul+vhfxFQA25eKv1iGKRkFMWCxBC22A8R<7#6
zgA$XYjlT<hl+BYE<E?KDTqNfhf`io&w2>Lsp#Ez+R>M+y<~Z}a?i{OYC<j(b=Y2GN
zFFniAo=-<$Q{f}Id?^VdvR41g^P>ZmcL}>>G^u~#Y24XdllnA&#cI^Y_%ruBX>IV5
zGxNYOc at qu;w(D}T)}{>Vt(aMPB!4R(Xsd$RbEdZjE<iVl=mi)1VpXrgeDc~3q&V0O
zwzw}t=u3S%(z07o&#))W9d7?^oX!)f3d2l^Sbma^4feTO0#~BTX|_Whe+DCiwFE|Z
z;pHDF3za8gtVG4S4c4a5)eL)YB6;emC7-DAe5cw;6S*f!8`S_Z3Z%qnqe1Fk`u-fg
zMlv>0AfmgkKmti&RfOyu?*bvD-i+7nY!A<HNk$xflfIABH_>}^AY!#qS$A?h1Z~t^
z_{6MFLnXDlP%9MD=3*M|WtQ|SNJk}-B+W4$QspEhyT~hRIBRToth#u0OvO6+GgO`^
z!5a|yF1w*#OvMSTFTpP2Q(Z{_ex}uxpd*rLP+<L3q|-_e*>VbPZ3fYRGDp99;tT-D
zJGg*8hqllNlxe|1fb%L54}@esC^R<ev!_tZOC<)4jfz|3xOg+X-J0X8+l>a6^d!`P
ziYBw}Fh*HEynf%@fM>~FBcps^Ad4jxATZXfImS}no#<f$&d65M`xuRB@=Ti+GDi~B
zH=ZHF3}~gTw;OvYljv*UVLo{X(56Us^}#)HQ%3mdawDgOyW?LmZ#LH!$fM(Xro`W-
z^0$2cb~S%n#NS5qw?h7w!rxZ%w*mCVXqQq#=Wi(C1-`o$?>8DaoqZ6}-%a8N^WMv8
zmKdkhkraobsd$zSK^ahP9D&|XedRZ(Le`{~j)~6VA9(TTECw>jm*q(eH8uG#Z0Eo*
zsHFtCLRUv%kURy$nn8|ZCu6gkJ%(!v`>3(q1bj>`!;&_6`2u$0h7dHz!!*?|A+`YG
zp+$2nqFLFgKEvH#aGo9Z#qGEU;5R=On?cR_01mXu(R6LS0I=>|G~H8fN7b(z5l)8|
zkZ8GZ<*sp;#MaDJEa^(GzGm8PBh7+~c_gN{Ph%xN+!k9iJVbqh`Uv at F>DGTi^iQf^
z<Q8IqRM6>WhcG~)mo=+Pfhu)lbitBSxW!fghv7LOv2wJf0vd1?#V2O%z)>Ek?y}_y
z8{StJFW1>xXv+n;<-zRS^3^$Yo{0vxGQ$`d2yfYSo*aW4U0nDN;O&}E at nkbGnHXfz
zf}lBv>`CW*@$X-w`^w~OPw)76krZL0CtV!5?X19hXt=@E^0qA?<t%yfYIK-{w2O5c
zkR(z;BSt|SSySuDstUJ}t`V~yDnWb&>*F(gK-4 at gP8Js!j^<n!iIsSAs9R+q<ww#a
z%k8mAj>-L25MfB+Bt_O-e8|+cZ+IhWm0bhuW{!Ny7)X~-nF0>A<#3c7Qs3Q2+(9Hw
zln<PVSVXzO<PK34kpwyEG at vt05l!85$C8F(SM+lmXv7)xmdoEfh<HlSEG}gA&NS&X
z33@$>-ANGwyni%pn3;5n4aa?U5xpta+b8qGvY#rh!`(sIhmFEvbr}Ma_-+}?iw{)$
zlY?&3eIJ5n;RR5`P*1dmr`lutYmd`ls7z}8$7Xa}LV-7Tst2Cx-Y4PNJY;(}E>$ll
z(!GNgmNG+7FTXfWezq<gCqKJg?d*X4^x-kI=6N2GfG;#$Vw`seV7v*zQF@~rPQ;oe
zLbIYcXfFRg%?8H=ypDDndg0jceBMSp+dO4eYrO85Ba_*{QgaU=MdU({{Q&ep2P)m6
zdu39t^RWbVM-46O!CI|rp0lv%5URBSgLM1Z{-awz(eEnHapwT!#XhkT6$Wtcf3G@=
zHLciX2qwVFhC{<p>>3{weabYWSe$JLTuav7lJX?(UD%YGYI}}aVk>{_DO_reXEop<
zY(UmgAUqozIa&+V7`W2dN#b^#DPqMWXW7x<G)eBr738lVHMdwB&wUyyb8~$dzn~V`
z#6SLy%iD-??+vIQ0+P=dgSot(^}L?>Lg~|-Va`?NPT_T_H&I1_>1S0^{V`Qy3f={k
z*q7a&TdD>6K`mDV^YmWx8fYBUfmBU!1ZEIQcVLL~QcWmD2j^a8p3|sp4_o?DPrRL=
zG-$omBIDfq^rWvdV>d(U4Z{fLd`X#>i&K(@a_<JCL9FCHPSJ at F$9;bxEq at ss9nCnS
z&x|Y2Xj&W-#g#LAxA}iQ>fTpz^4mD=M_5Xoo5&d{33OC(7^>`Rx+z-P*#R{&<ANqA
zi!57i#Ilv>E7V=S+TacWnwh at H$C93_<BHg*WBp9Mk)lDq$D97nX%u~oTMjvV%8Et6
z&^}ae<i at M-sV{SN#xg`>UN*?9+zB;as9yD3oW<iDQdyP6%mkSrcm&V40A)N>;+40j
zP(6O;*L_OeK+w__0W`lWQuJ#ykeCfLe7?L>L)3L4t8EpkjpkY;3vS3)r!ySb=?n0<
zy^!sMZ#MHz`nEvLV5sM{b^-j)lR_)<rO-pL3byl*k{FYL%AT|8BNg;{!P(yysLk~4
z at p8gn90L5$hyPM|c^8V+dwQ_m^#R8bxwGS)SC$;<;3CVCt74H34^YZ~oC|0~_xjNZ
z@?pF`*nR at nS~<#veM%*&dU~?HLINxoQUjFwN;@flQj`^x^PtDI7l>Co3f!&{P1f3k
z(kBt;Oqmv(<<7p(7?fZ_|F#|Gf-s*tdkC%lG?cp9+ at X;R-Jt>tDl3 at Xq0u;aHyL|+
z2ZuGRe%C^3ML-)4<;u5{W$4ASF|Y%?Q0K^JI8J$)x`qv7pQwZM#HS40xC6H4C$N;&
z4JYwNL?}}!^BiX;isva at UY>fWj+fa*-_SMyYRjbz=rMCMLJb)aWI7)2*;4|8yd|fp
zEml7}9Z8Us$xqHT@<#ZT8;q!%h at aJ$pQdgv at G%@b==)&K%zJrQ_J<$g8XFgq>bqEJ
zp$5FK-U{h{wD_g58*&j<V)e4^k at 08k?#A6XNWD|v!c>&YJ;7%_YnNJDj29%^aB0N3
z<Kyz+^!VkYyzWHa11-AA6R&mRmC?%-1FfDtzJf!zbSjSHrt$&eQRbixQ7ejM-D0I|
z;I~2De(dHUp2Enu*dzsO!500nmJ16PA()bF2rb at GpqG707c(%#QuUWs7$cXYj^`tq
z0Q2OMB5zU#b<+ at CIa{)-We$8+apOKsnf%HMGiF;vh9Xm$Wh0*hc$+g*ajyk}#WEde
zY5crE{d^TF8d<#1BO~45con||wY1cA$s8xu8u at Zt;8i?yr-W7xE(rrGO1$Q&A<IVf
zS*XavnnYPM31xWI3Jc3k(-WF*k=T>yJ&BcmT!e~n(=CBZB>hDSJ;8sq8d2lg{wJ+P
zA4Nt0y%8AZMGM(z#>e9Gi*kpR+tsq^$sT3FuTVS7$orWIJ%auCXtmLx6WIn48vdw<
z?TTJQjeUX6)rt6RlZN_qm)KBZ?$ve$k4el?HwpE+{eb038uLGOc)0|_ at l18-$zl_c
zs(&jc$4=8N&~4r}_82CG9!K*GQhrFlK?qH^%~ULFfv$MCii{+*<Qf2d8E(CNFP#rC
z(c8<AlAh<BgQ|+xhf+7fu!Y~DgE8sowJ4wQIrFj599Mp;HULj4pDSN|5{h}Nzk$t?
zc%52OMn||S&mh)&NLt_vEpg?C-#ydE(O&%iS-2Fo0Qbpmz4nS2Cat3V3x|3qFbe6o
zsRxieIGlwGqo78rJ^*XgZIi;6;^ktSTUlr%$CQI`6pFKbLYYWUD~;2HvTOLGX}(aF
zL9!}Gpo$Y)iFF8BC3>sWNQ>H+5fMsPBA(5538fEVQMyR?DWk@}i?}w?=|`#R2kORJ
zGTj_RTHt?3Mtv_8(2QbTi23AgDWX>Aa~)U%T<Laz`Qy6?^F8&I${2w?pm?>x#l_pA
z++|&RHJQp04Yi#kw|a%P;$65+GDWi1FL$SAs;`}*%^omeaGKA6Bn>1=S}ptvW$5q_
z&x-#TxY5BPVjiBLF-44U;1R-6<f}XmL-n4jk9NUST at rwm41dyb4`#yG7~)ZncJLOw
zjJMDa;S`ON*j#SxryikZ2ZmC!x&BP&NsgYZaP&o>*&2Y{4vd<0%{Cwugt8l<*<m+@
zMGpJ5^TfabD&+?jmOHI0GLVfN&FLwTeJBEGLA`%)Y<S10&7XGjFXvI- at v;v|W~p9*
zg~{V%kPOmaepJ&UoA5K;Pb3ZIoR0o|pj!GU52NKmamXl`WEppj>ttXkx>exZY-<2m
z&&y~k{;Lun=VNv3Beml+AHY;J?`5-XOtPrkz9hrIQOG5#mpw|k)q!FD`#<!%QxzUX
z0Tr4BPqPsjsQ=W4hvK+}YrEoU(E!q-<kvGAqrt}xyV=34HW<R?L)3rlk1@%A*h!;*
zFSh!Eqg=#A2-_HeE->Vl?ZhYY!O{e}C8o`DnK3FP3JxLu?l)XW)Djqh!!fLTI1Vn0
z4aeWUjd6%0E<(R1+hG>*!)L^kk|A+ARlm`I1klmI<uJr^vQVq*J++*nm5a%b)++iM
z5Inm=Nv%GUA<~bh at X8Neq{q1CB3N}_nA_{T3Gmxwx+n_z+ at +)1eqk~IpQX25NxBFo
zQ~gI6+1<1O*CJF|7YDpNk=}H1z%?$k_ma6hAO~JJ*qIJ)I^LvuzvTeG#tzFbAw<CU
zb-KUw0BV-^bd`>>T-b=4G5lFpBb+jsi1szddWo#u96qj at b-Ctv6jCPjTLuH1q@nPd
z at WdtyajcqS9(?6)FyidoS<*){(Q-(bjv}m4^)pS~-<fsOOwv`eUcSvpx at yg?0f9jj
ziq$T6T(tCiGz at -DGQ$^ll`SflwfBk}2S`1S(=~UGjv at 72Jgqv_7vhv@gwpSzb;>NW
zdUzW;sW9>jLrmDajWA!5Jo&rJ$p(EM-Ep5b;2k%aU at 7Aq&{py7Wb{F<SQl+4{0d7*
zy=t3&a+W0%aXs#0%vfo`BR4c*s2?ZqBhB$kH}HE(k at H)CGZt!SgD!Kjx3!k=mm^-c
z8TzJH)sv|U(8UQ{qfqXcw0t at yT#!JGcnzu$(FzR_q33OhReLkGOS?Oyj1Hun8*2Kl
zcM;YL#TRVD6XruaCLCOdA^I at x4W*cQ$}rdiM{1E%wWsqF<q&Xk;lCQ<iF;n{V63)x
z;_14$M4lAJq)k7f?|H}WsYZQQ2j)sBAiaTojH at cQVxU-VTbPp-7>rJ6pzF1!l9NBe
zkS_h2B4X#3|Hp}5&DE!pa45*S6pc;MjHrs>XpU#xSossQ6!0vX6XXghxLCN+XD`zP
zT|bpZRIRI_H4+%YUpMmcNRub;x(uzklQ-ewP?rGV!40|ZJ`;;a7fKp at gKm=>vm>Ql
zqHezc*J;uZvv5lkxw|b&tKgZkz=rz}zN(K5?k%dqxI?e<vnuJ|v&hDDqT6VQ*E0nE
zoNB(*&FygQ5<2BT<8LlDHucso?5X?3tgA%W)de+KNAW(AeNX$QB{0E@;TY?Ykx8eu
ziXP6)3*|{GCkJvC=3KEdkVU>C*oEjG=Od`+DiIaJ_DI#I%<cQTkHH at OkuuT-U^d7T
z`Sh$i%)PLJAUmA&w at Wz#f#8s<Y+AL17l}VAqLJJ;Vnl*(`}1>wOEzqZ)L-?z8xvrr
zNEu(K2K4<KO5kF<L>KB%MkG2F>dw-nBV8KwwcImyL}u1ebkg-b`;2>Y{`$k%Io7fb
zNC^_3u}l0wTToQq&6A0y1RiaBlz9<KcVH&h0im?ofYvRGOcI-5%SCI7GotWJz2ur;
zg16v9*z at mLHHM9~MQ+15cCZ0*1_e)ORk3cj+Rp0s+^yJc4^8}VDdQNb4O(mBx^B4=
z^Qthpp(`oQU1&mR3gY%Qq3o}mD!zXTYOV*oSchAkvW?c`dPr1H(fVJtN*&9wN}+o2
zgXkEL_$;O((!)2rr%WSv)?Xm#k&0wfHzZJ{+I9R?fn5TJ!~_FxNHCo{H25{obXd>y
zXP#+4zOwI-!G1zw{rnk6B-Z8KeFlBn7y1j1Vph*q9C{*}n{fgjs^Zu=2t-oC!l^k|
zFA_ at sg5y6mCvlPP#+^nd0b2-bd_oYRWOPZ at nz{W8Pu9^gO(;hxr!VJ0I1C=831|o2
z<b*-}VI{DH(8GnkoFyj=fiyr(C!i{$R!?EdL+%q8(2+g{RjJ?;sglRs(11>97Gx5J
zvLwrOcwSJyN7?v=`6H2~(2~gHSWCjo$dK`#69#{1d}JN~70OPb%uHKlYX3r+ZgSNN
zPXy3WRzX!a6rZa~kne<4zHVUuZcESwz7h%q3YvT&X9EmhgAJ;M0bd0W-8oB~D+56s
zyuU-Ktt6sU68o`_)nD~9#LzW<ub`pchDc0F?x$Y%4k3pdd7v3eoW9T<8Y`JUh6Z~D
z4;+K-*O77lAbF>zKEE4R&{&;y`0ENjm;<rq=imjJ1Z3umXL?G>mcSA6w<<arcc~Ba
zY~eqgA-q^EY_ubfyAaUOk5Wc8o`qUN?XEubY^`f15vn6BXV($k)!m5h7{~VlAiCm7
z36ld?!(T&Sd>ozwO5B<=sRy0|JklG*4Vk2)xPxc6;iuv_v-PaazG!n#94FQM|HpB#
z*4be|a$*$AX-zRKcLZ>1_(&X<`zRjEZBF0~L~j={5}HumKT(35y~qfwISFW0PYl&1
zz*{gNp at sf--1so-CnC3UWarBn-vim1Eob+C)7i+*U41sP^M$TNa<>q}Rw2HdsN*{f
z<qIT!wl8N~8KecSRF_v1UVA|8-}HaMYaCx4B)N at s>_m>Q>U^OwP;&;n28Z$- at A4ce
zdXATUgwZ;4Vi at gdi4DS~*Cl3b^A)H#vr{Cto1})j)CE;KImQyi;v%bIGRI<K_B^X0
z9*d!JORxcIem>_XW3ej&|1YrE_?#FPn{STCVvd~iVzHCGvDjmMVzI(_EY at YxvDj^W
zVzHO9qgX6}W4FYtUu8>*WKuU6V>s+){5s3rjl=Rx>LYX!5xj{mWtzMhA4=vq&O&$j
zSV^miA+fO&yOG$wUGYflz|uG*_QBF968mo-NbKMi9f`fpvxQMPNT=ms@{<rD+57+|
z)m?5>m+k7d`I!GU+I(<I&c_=484Y5EVOqJx2$XVZRltgB_D2y#4Jse3PQ-#_V6+|9
z>y9`d8k*y#93w9Bl3KF7OuNr*G6>W6M5|`jo84$BoaZ(f&KZl5s+v3wvLMm;y(sUZ
zE0aY_V at fMyU^txYJ<ah|HgG(XO_0^C{0SktU0;YSA_~)wh^`~JYI}-H;!r8Kc{&$-
z-LNu at z$u&sN!fqDH71)Gn`33uOoru`qN2?m&<wZoJa0hSvS}uY79~tSExJwztNxcp
zoVyL&LMO}z;1g`X>;H(2!IdbofmT>~E8f=}pInQr__N-Mqf at aJ$DtKJimo3~;q3fF
z(dXSCZw5JnIgARkK6a1}vyX6*UDiDs(Z;Nt8g0ys9*tQLYs?AJbt1SW{#LY>Urt8-
z#l|^4?Dltv_KWR%rB~4^H6ScpS78U{wX`KL|CNN`ac^bRUtzZ|L^y!`C>F1>`OG_{
z_7-o}Ztuts0;<IFI-uN*rEP)fx~sw*c)n79z64zu^2Y(CZNW{F;x=lv__vGxo}s^|
z>F+7}dy at Wk(%+6+3M3P-7w4eP37LvWu9~H-$7|1$XQj;LYBjm2G;}hmRg=1yWD8~c
zgW2kb4Y8xL8W2}*cCfdSOHcU=TK4B+=%l%7&Qe=2jb9&~oUa#ciPM3JY!@~RTnHE;
zOW07|=C-F<07YxHx!GBP=Y$PrVPj+ULEFFvS7$IuQl at E<t-{7?R|jH&S}2OoLfI0n
zk2awYZ=r^WsVba^h?#xr4c%_ at h8CxIL(9^=q1pBxKKuM^mCettVxq;%R%QD$+Wok|
z4#T2<Wg%C0_7*%Wc|$WB{Gn;hKzX>X732~`x8^EInOC34rgivSt-_<|FojJ!jnAYS
zAU|R;aMb|WWef~ubi0#@?s3ia$i52YA^5f?P_o)ku<XGD!7-9Nxee8T0vF at m0X(UD
z9;DNI5XbX~Z9ax%Hq*kLFHv1h>WjDHqZ=5}*%n^`ze*-2FJrvsPsk%LzsuGf&t(}6
z>PwJas{@kHjy*w|CY-}%#%~beE2d{1g`?G<QU6MMz?IdKGi3P$Nx8wsMCe=9(gbFN
zpXG^U&SrNFS(Xy{n$^HyFkihLj_H1ENqu6(>}FvcH;H}jx^~%IG at q*e(ju9^VAFnt
z5n7tVM{U_v=s?Blc4Y*v!Uxyl(}6Lv%Yb~%9Op{qdgH&1BD7s{!^ry0^`h1sZTiyx
zR`NTAdFZH%q8h{RGO1@&<FnR0$7?=i)a#c5a&ODp?`5auwKijLv{X>H`2~T-MoBD|
z6zfD}nUur^DI{o)xxXL<w$UhaueZymoJEaXD%eejvLYRa%&V0OYEix?JGY_M&;GvT
z67M?c>`rq+I~dR`8R+hMxcrFCh>@3Js`p>TMN<WfQ3Ny6fZayD-4CHq`z1(W&FsJ<
zbs#LSU_Z_A8s?`wO~EhwG)LPd6*NM>-o(OYAkv|0+Nkxvz+x!V+>OrL?togNUJb{D
z2fo&a96Uw${9UkjE0>E-uD=Gg*M_Li9Mv>sR+ at T6Bq~4J at pn8ePDy4|qR0WjUb*ND
zEO85~i>%r-2D3cU5j#)>_3mwP+EV%C#ZO&m;BE<c(cm~fOOD!1B3FhWikb3&rm3U5
z%eF<5yrFq#43U1S^8s{i!;<D*f-<CkLiZwaUz2u6PhX_y!+mmjI*!zSNuKlt>SCYh
zqT4DU)d#EZ&@q_!MK at mrV=2*;o#QO^vm4QB$<Gdm%p{?Y^xcR;*!vjlS(#U3LA{}g
z^YJ=yxSP$jkVAtnO|<t%)`-Q27P<xZ9uM$U7y4Ke5qCrp3`YkR&3#?XaJTQ>x<0++
zV*AP(SKjAJdgvCDVcC4Cpi@%Dt+)l(RlEG{2&uZuDlOIkts|i<PfE~&E_w3bE<`Ee
zC{&x*VC89zzzCqsGm^Lo9Z^KAo7zYMT0ag40!xNc6JRDUQbRR3IpoCg&+sUx8+7%g
zvsY<69Y9Tmc0TM%s=pc9c|T3exTc%2ou}k4p`9AEb3eB8jG at h)0^7#}%}vn)*U6LH
zFTmzr7T4V2eKZ%P{1@>CpMkC>nl)<iC)KeQAF1x6#d>4O{-d)C7|S}IOZ{`KV8jni
zzsi-Ah)us7eM=}w$<*{3bQxXUVMVvmz}Ddj2rcgikM>BaTBO%Br at IN_zYz;GaA9m;
zHbpn<%ht!f^j4p?`jiQOpKCJU2*K#gWLJOlE-oawC at awryjr=Tzbc at AOa49#j;1WT
zzONR;P_*wE-ZJu}=FUpwTd)?rZE68wCQJMV@(#6Yj(OvO?yc{&lh8DV-w57G^GH3u
zbZ%cghHC79t45`cHyX~u(fU0btcdqvJA4Djk#c7u?DJ*h^)r~j>cYtJpwIEs(2Fee
zu)1ZA2_r%}Wi3}w7+zWww9jCvt#qmVy at 4x}XU<@jR>^8W>rB at kL2iWfwrMt_Q1%)f
z*)SNBc$ZsIk)9GRk(8Oy!I^ahA)SG4Ws#Ahz(&m4G<}f%d=j~&qrrPHzvghkIAYa-
zME;%y?-2=mgqfz)!6zw{<x{LCX!y(wXlUh2Z2V^RMK)e)kNDUjm`0#UwsHR^2Uvae
z0jt}5fV|g4k^$4_V_nekn%O2pcmynDx9c0B3|;VP_2CF|miY!$NNrG`JC0UR+@)|}
z88Ev5^$95D4OyCH>x1x=?q!M1Qb{!*nCfN2>T+!`V3~Ef{RhYk648TbKGhKUxcXZw
zxfw2K>gB$iS*q&XHk%O`#@orTTX1)|*|(T^(?A at c)t%_kcbl%K7eGC|BGS{#jo#VC
z)^!j4(eFK3U%5jI)V3O%C~Wvt#2^}iXOFP?(4b}J;+ at 5%Evw)MhPWz&X2+p9`el&Q
z_}N>zMI&me$sMMkwRmT4#3px`gHXVxmg4P3K?k%@k~NGg_~kpe;V`zM+5wO5P<jLz
zFX+)GnB3x>M$SO%uHU^kb-R9NmWJ|qb{qZ{-($}%jy|ldhsKh<Ym<5u-RjiG6d`*?
zTg;ss at 9L2Va}4Qx?*RSs&(oTvDZj^n?0!Ji1wVvVSB6%<9a>!_pSoUnVjhM>KjpJv
zNOZgjQm)tJj)B7CZc at 3?h6s;OM7!962N#L*8JF<*74#M?6y+{>yV!)vwzn8Pt{UNq
z!DMiqv at I3*C33vuX1;%)W{Lk6GaAwT(cMueWIY_yT!fxy&T>IEl6yNlhgTY*?62 at E
zcbSB;^`yViktRI#H16x#>59|%lc9__FO;ASAgnQa%!kz1H=tgQzfzsKnXB26-I(Hk
zTYW%JI7~#Dze)}13EOZBk&iiXGYtkXBO<GDhwl9<bjPRe(CTJ)=)qm?P_Ti!pJO%L
z16^Zvc!b;B$bI#QtV(wV)YJc*&v*<eMOnQbF(r3HCz+eQ1>4BmjRzQv#GAazq$W6t
zCwa|h at JBLtz~p+(UGnOc#*k-EU?@Cj!C~_1N(1_I^EarYHgMm_Qi1<%$=r$YQ)k6r
zVWLWA&fk08Yht3auS5Y8)yB<HR*+61VC3-f at soVI9$XpcHt>$xo_u^aJ})`4j$i5`
zD{}7`qk-e62O5BgY5NX2wl>snLkbJ`>|!ok{0c7x31PGMLP<6F=4xIBXM9b6NuJTJ
zEv%oa83wpROU>|frC~tb)T{7qy1RVoG}u3X#R76ozNE}(kK+A?rQ2E3%30*gGvK^i
zD81UqH7Xa8_-!<u9qC&PHe at drS^Va&b5aCfifgB!JWf5Bd<zt&)*}Hn at jI@CvWPiG
zA~dC(z=RUaAB7x)slnCDL`lix4KBX>%Hkf4&KwZe;Doaq+sGU{{U#EHx;`i>kMv_{
zTl^@@=qlU4rU;{sYk@`h(-pW&Z1Wh5f!pxM#?`7c$KOX2>*A3Lj-h{cC`yN!#J0;{
z>qqj at e93%8HFq`YF^Cl&+ywS=j}6ZGQQX at 7el+~?Bx?0uZM#?AW^<h?D)q2$yzFy{
zO`bySo8;00gYl;;Fre7yvK5ERP?`(qTB~!rY$fe`Dv{nA-Y8d}K!qiDgXZ|_NF%9_
z;WfD*M^Pew&}{@Y;1V`eNueuTh`ST8%G}5R#NJkXw!u<zMGxlxI(ai|&d@=a7)he+
zQoY61MH=$8`J%Q>DmjHpe!>$H25?9UpkbfmZYQc~;2rupVyel#A2865kn?1IGM#<b
zqNL4$(pz(B5s>oIwGu23n6pTlyf)Dg7ywK&oJq+}?4TKDFF!h^9ZNq)Wu%HbVvL;)
z8N13p?=;A%zdfq9wO6f5!~h at ae_!Luz-Yy*=H7=jRb>K66GZv_h3a^ovH(+75TmMD
zrvj(hJ}m`xY=Q;A0au{fbwLNLoYRJ2#wJvhQNKH^X+Ra6#$A{qDbv!W&@|Y at W>V=7
zO>>5(*&;(>S)Hz*=EMT^(`@)7g=Rvofh99jOoI)E?rVA8p(5TPF5^QtQzWWwpPE8N
z**?_Y+M#|j2#&CV6b*KhT;MeDktwi8V+C{hZrjup;PUt2t<taThM-l{43SaV4BN^i
ziJ8h_7)`>4>WB>^WHUR(YzbQWn<FO89JSvmYgyy#iyuNRjJ2|TxfNmVW;*x(qwP)L
zo2s(F;iT!3ZoDECi-1%uTB&HYs4Xd)Kq4=a2nuLb&}yN=h|0i=pt7_km8Q=qF5@!e
zf-}zinQ>Gcl}&{%(uK7YfdZoxM7{Be7LYD1ecw6fzPzLXoSFakef)kj_uYHmeRn<g
zoO91owN>5BDqx{_Z&sz)iv3G|I4{3)et%ngu~HPFvZbG at YR4@ZR=b=jC5zt0(EM1V
zMVY`J{gkME<(ewDf$UIrtJ_&BE?RY?*P;Sn?LvtB;hw&{_ at kTI&&Z0ZOSZCWsmS0l
zvnxI&g5|dyY>8ep-8RbBE<G*cgO(OnV>-g`Mo7;(m+M(KbA6;1V at S`s(a5y|_oBl6
zOp%0$K>0Mg<)f(dW|tXiFWbrOK2ncUNke(vorvpOP{eo@`~4 at ni1EI6$svzDf7)v#
z(OafT^zblP4(B^iyBL+n6-ZsVzDE@*{%qiClpiFLLh2ykO)d3OHU`(Z%vAy#I<*qu
z&^v{<Z77m#?uV<3+$LXBW|hgHB)Met=rjXBY#rA8C7x_}`C@!|AjS8I<X4=gmuv8)
zjxqUVQV#p#W4+D)_{+K53=GPVc+ at 6pLCmn#UUh{7iH6BpR{UeP$t^Dbl;sZJVZrwi
zT|ZA-WBbfN`qT}gf6+<@$_HPK=h(BYuxS1TPGS9*V}$k3nyrU`>O3f55c^H2$BQzZ
z{*6%da#ArA)trolFBHj}Dp~|zAq<<^B>2Xg`R{S<`Cr=apS9oLX}=}$^jpyvzYHJr
zc1;(aM<2g}507JSmrd|pi!@u;Si!fAh-!!68%jCj1m6HkOp<1MyNU$gc6z!;y3pJ8
zfZ&U#rxL-3N*Tdnv50Boo!)xX1eLB4W!OFy`=!f;4e;T0-zCqx;B}y!(PZ9`Q at f)p
z*iNoNCTC3yevkKX@(>@;sln}N(9^jw7rF<yKv;Jq*(qOaxAfTL26QmLM6lQ3CjcbP
zjGZ6{{1y8{HH(6Mh`ldzr<;QZ4QEvqep9-_A*{ZwOAJgQCroy?T*uVE)BKX3Z&=!d
z^8{PfEnV<r$EQl9-=<Xu3!ssZw}A93%U~yZw_35lCLhPULjToBqi;Zs{NPL)<Qc-V
zaVAVWw0NhtYnI^qg+}^OSSyrxOz^!%i8)e<w`-o at OF$&m<>AUW-mV2g#YU)T%@x?B
z+to%ObNWhV^oIgiy<N=NEbi!5GI?09A%$twe<ew6wCDE)qEW2 at sAD;sVN2VCi>PgA
zi-toYtijt4+}bvZ_~Gy!d7?P>_Ii7kE7umd{aB?v%dJeZ(cLhX*&WG>a<fyu35Sv$
z`<pgkg6}{a85&q^9UC*HxKe+D65~$@jbiX_oK+Yk9>qhRoF%R?l)>~@Fe%Z^7mYFs
zS4qm;cA6xtWgz;ro&!m%l;9RN+|s2ChB<?ubjpi&mq0q!A>Yy!226EVR6k*McD7>Y
z%uv54iC1fK$ki}6Pnc~aHBpjn-tRgcmQxO4!YMnT)nfEJ8h8^6#m?42O_yy#@3=OM
z1Gl}RX-OOg$I8SAf7MRdo}EWsa&=%#uD7dTvB_qtM|WbIl^rg!QVUdIEje|fTIWa8
ziwXti(@1jv#4<}RH(~^M6$<N3xj_XUJ$-Ctryuf+)>?PH)-qeUis6D553>a}Od~D-
z?Q96Tdh$D#i^LGft#YRlZ`<$}B6Q+KIkln>%4A^-h4rV!k`4e}qo%JJjA19-Wp-e7
z(Z*eNWi4>Y^;(1rpi_3*ku^}8ZStA&A5fD(8U(|Irn#xEK1fqNPBly^BUL?W>_u96
z7M0%_Szcxt;W5(`;5`AxwKz;C1dfNjU~<+}q2d-~z;Md0#R)RcOTaZZ0_LS-qftu3
zha4IUWaX8<kQ*ZrNT$N at 0JO`~vnG?s!Jac6D$V%;KvFyxppK>0g<gce`Y4kf?Bs?*
z6y3vxD&~9GCBSe4tXax{zp=gF$YmeG8&JPJuetzGiZrW2T|7gSkK2XSeZ;^YsyTPL
zf}7i at +eL4E3~T(WwhKHt@{|=7Em9W7aha)oH-PW3e|A#;VZ=VAMk~YU#|9 at MTJl<z
zi$cxdH*5<zydAd`oE$)XKwG7YdiTy00R$)2a?KH6Ba4}YGG}}>k4_~~UbGU<TG)3*
z{7iyl@}|{2Djs5RU{&1Rjn-vT=)iL?)9&+t#TDg1qh>O-;Z|-km1!cAspcq?DOeCr
z=|=cFQ_6<Fv!zt{JKysbp<U(GvYTgnlHef3#}uU!P(hmVH2lpBJ^_fXDuGZ@;SHYt
zWlM}BJ$=xp9c2aYf#SkE%D+y}KOqFiVE%N$Hwu3NpI(Q*fJKMnFQCm!@ppmXy9j^b
z<hlTP2s}k`iQ0r!Tj=CrBW|E=c?sT4%_N0hTh$g;aze$Sf%ejI9&6;5dtrbvC&J?!
z{2O|dE;~Kkli}cKk&b|OdJv>1_&WTK4Ww+qv)3M2C^iR|qB*5}GPor4-XI&tfJj*Z
zD)nRJD;S&D&Q?W$k4z4vl;PU}yV#5%!CUx?nUW3uUIzQ8>~xNFDf}HF^@G2;LPZv@
z!5kc<RTaE|CyYVze+@&M5LC&>%hs4+#~D603-HfW{4*K<hz>M~=;23n3x-yD^&dKw
z<ohGuFXlU53-0OA;-+MCWvsbjp!@&G{R^06+hARnzLz{u^iNFFWy6_nIp~(#D9r59
zybikdrf80y3^q7iYa*{TxSy_+059{1kN-xFW8l3Q=y4nddVEIHba{|Y(sKrSy!WQ)
zK#zYnYk?k at n?r#fZ at B4y3G~>H&Ha10|F6TRx2Y8!o@{K1;TZbh#z0TSq%HLoMH$_V
z2C;i!%YS-|uC=zapJ35*b-3O_#mulBL_S&myU`}976Y*aQZM`!Cg`o6P~e+$!i<AM
zf<5`$Fi))nm^-6jC_a at 2@WF9X5uzAf=Y#p?&!l$G`uqmW1uHqsk7sn4-uCNxYG8tq
zS>WJU7P#MpwxX6Hi4$TBi)M%=$AM}2?k18 at v{R_kl=co=hF3~s-`>xsJ6>&|tAkVh
z7ha2gbx0*Qr5#A7Ts8fU{>Z_n{)J~AWi4(pXL;!+JUO$D8`L`GdG7q=jYGWzyqcx^
zS+WD!mVu(Ky;-Kh7|+d}whSaor5ogeoF~%Y1R25pjdo4)q%^zS0Qh-#uEAhw at CfqQ
z3}5vVg17CAe{6=5;2ocli5CF!_%v^c*??;RB?lo_*CF1tx4&K<t8OGV_8|(JD8=EP
z%j_JRVX#6Y((yQ%hH~mgwrYa`FM_JW3iju^r=iie%R$(JYTz~_TXP66WcFjwAU+FT
zcZ@*@Ad%oI`4$Ej{dU~yEo?JLGjEmqw*g1*mF|PD{&E+s=r{|O%PDO%MR_)KxNrfx
z0Uak>8l=nAdiZ>nG6?Tb1^`G5%;G>^XcO!zGz)f5dS at Zl(f<%sn^6R%b-_7mC~{n9
z8)C9EM#0|0=c>2<3MxcS)9y^uD5%?A5d)aOx_U5T9Q*oCYVBtBMi0i7qgbAIg=Rk_
zzo8EX>v^oGkS|yscfv1*>fI#D``C{s;ob2WVo7^P&C|f~-}Bed%^yuOATX3scGLjP
z%5I`EQhqy>r*_mBrGfsZ%pEmpg3nKAY;2nMV2|iG`NwC#nt?KJ+b`b67+5ubC*jXE
zOqikzjHEuyI3+ABN=Ccx*SX6L>{MK}0fj~+HS-0K9M8d(ne%X(7#L%OkH}-Q)JFeU
zbH$Ox1MTugc>VIL0jf*6%iEL*EePI>D78nGF7x*ENEf3|2w19t^ppZ*V>K4LsYo=X
z)S`uY>SPmOP at 6eZx-hRr-ewPsiD7S|mKOzR`h|QKj8ChwXXREJl>uLH1<1)jqHcqa
zv-d{er!8lI0Rl*26&cCne8{uITQc+hbLdVx<)frywokMqd8WGy%%0JroV*UjUhHo4
zP30$5dX%n>M^P~>n%xcDRu^p#1?DP5tT4!J+VAoY!bp7Rl#RQ>U|D;)gt!)Kk&{vR
z!k-+iJ`%c5Mwv_*8p(xZUc!0`>@GZsF=xF84|szKaEv)NQvi7Gy9v%y)k*Pyjl!xl
z^3!fq5?H1MhNGF(0J1}NKs-F&)9XV9YiH?i9~0$nH38Kf|B6D=R>Y4f!m37g-&1t?
z`t3P1!p|WyD&sPvGA=WE>lb6l(J{%4T!BZ at Tz>Z&w9=OkVO)`ks6V;@71v3Y)Zkco
z9~uKXe5HOzB}tG9QTa22-TY_$$h_)Bez at 0@>+vS(Tf{nm6s(?w?6pn`K^kX7i at wyi
zZiq1;{uqKMa)zs<`Yf?fY?ESw<A%$Eh9PX|^gc@%)W3!VK^g3mcl4&b^8(&&HUupK
z(fo~Q-MnKg<W<FH<?XMWnpI$zkHavHw95+o%<%yid>W%!1s*oy;2NJKY&_ACbHZUd
z0i$n+DH+KyO1RvLtr>t&`=6jG_!{pdH?uE3HyGTe?=Y-+7OUn(zYwQ at jwvPvuZQ|S
z8fME+$r36)Per|ISC&!mEl1-r*boy*wFDY7wQ_P+e&w_>NMbm|VrVHexCs7R;eW2R
z>Uia}751uc<x?)xkMOl=c=28~pLFcd at yOUf>?#bIYMc!32BfjgN~!nMaL&ehPu(I-
z@}6=EzPAyXopK32nG&~2h2B#$rE91W-cz%sB=4#D(xBibl9DVk&h{ib)s3c;F27@$
zGQa>Ww8tD$QtgI?S^18bALz9z;7zT*4#77aRw}Z7fWr%M9Zj at XJyM1vX!kV<6)(|7
zbF2a8mhn)Ske01o$QLV%s3xpmS!t_s7=f=C;0(B9$_hq$QeAS8ih~m8Ec)`2>pW&C
z2`(VELi)OJmbAC<F=<!f0%=EKk+gLkH{o|IGo7@?91EhKLTo8z$84&EWbhVJ83!f|
zQHiap6<7yPT3P-i$y}B{)=~9?$7HWMWEWQD+PvE`?S4<S&D-;+G#mbwNMqpdQ&J&k
zXW`FCTrH+!tmBjx;j|FfOJ7_s at 4+N#{?0smX+_y(Pqoq?9Yf57=^DvuB3=Rni;mo|
ztuV6~*AQyAWB6q>hCi81CiagB&tHbu%sw>M!uANhA4p!)0}R%`QOfq*wXyX4(BfO*
z?=0yuY`ah~D3)YCBVk5-o04z|!Wy;9?ZJ*DbPzDzpt+Fqsw0GoE8`3X7$|FS82Q<5
zU9XjR$i?mg`5S9xz7pqEb39k#Ql5znzMTfcDQ~ssd~Y{(u<uWXx`tg*1cK3^0kERl
zDy-V>5bQrFlWp>s at DlWN5>;;mrrZ^9r&%itO=7^|6HVVBH at 5)T!IkJWlUz}J)=f9W
zzCVz&k*)YeN&h0 at ONn;mUtsA0I;iCCOO4b=YgMjwR4438T<rMmgIE)pm6(pAE$na%
z(uUzl at v|EC!<JbqM^cNp=AG#qY7yCZ!<M{KYZ1D##=iaE_3PU%UdtB-y<cy1NA~NP
zd7*x7gK>1>+CU!<UUi0)=v5s;#RJC3UQN<2R~P90sgE_y5KVij1(9E#MPHWC7}%!P
zv&;3bdb>t=7WS?~?*i&ZN7ON_OG8VZNqPI}h%IpV(9?h`YXQp`gM^|gR4huQwKxZ<
zoT|xL1-=%^WS6`0r?m;b8_>&w>~7Tr8^Oy at B9D;cL2Gj870n-Y%@gO4+mycAj4kL2
zwU&ndu3n%LS&uC+9o0vlcmM3spV7%jAE_p794_vP9DVa#8vW&Kc;)T1tD#ep;FNa(
zKe%F*b^(W at 08@kB1MT at h2zlTHnytmqwpYgH3G0b4N|jF<Vb!smR at 7&jqo#jmMs{b)
za%gHJN|-jQ&9qg6x1o;3XBsw1v6+rzaIa!SvVgq^M0hR+Q9;%5;2F|8>6s(={>72O
zJi)h(=XnI*r#x?g;7h_F--ym`qzd40iOsS!2o(YN7ETQ9R9noRN}MD><(uLWakv at M
z4#6+rLH4W&eVuv at gWn-cXm^>~Y-NR6_j$}t^-Fstu*4Vwz}*h?WF~v(S`>*CW=-S0
z5h|ABHvDv&+w^U{<5Xr4Rj43cIKuOY!*@iQX{$=fD(jvm-4B`eQNej}_Npc&-chyH
zUKMn}^A!$Z)h=lIRyWWO@=ou;1ZNF1IbxKSLvGW~G3Lo}?Ep6SYXGSM&>DnkBbchj
zmP5S!x+BUEirrAf;4c&dP%j81Bnqbq6(!{KepH>{yBK)k;LEi0ay$caj^a%4aPlE9
zQB`|!%17ep%=D_co=J97&?#@hi5|%sPK1^fioQ at m4n<!<gSLF2z{Th21HPE-1%O4R
zq?~sAjwdFj)wcdIT!N_bhqw8 at Z|Gviv&^?&sQ3)8Yv9{<q!MTHdeTjyHIZ!M2#Q%A
z({?MM5R-PiR2;@j5H(*2OvMUozBdKawc2=3B0CXtf-2BXuz+X`_D9)W4x?h+7D-O|
zE7%q<d~7gm$mKl&N}-Vaq;R)180OjZGq3Mozed!@dn|Z=j=?V5EFFtXwwP~h>lYwJ
zfPVLcRpHgg*OE<`a9lCLq6B;c=km1WHo8cmmJA at VAhab8+Jfc$6&faeeEZnsjgA}z
zM}&OtnCNvnuUY!9A$2u=ShpMa2`t%hgL(RS)x?E>fm4O&cGGE@>B+&pB~&yJ0n{Mx
z0UFliJ(M7<cg3{}>+P^B8il{?5WIiWrWo-A7lr{@B|VIDWUXBE8hh#zl6>~1cBpWO
zGyic_t#rJ;2E)`MrT`@N8dB<X*ah5`u97n3HCe^SM0+i_3#+G9Vy8>(7~r@%28k~|
z06~?wy3iw>>`qf1(PoJ}$7$M+ZwB|FG?I_ml~ehcUJx47k3I?^C$g0S%F5Cz_`16_
z<R?`89(CM|KzZYc5n2&^===#gnh1?D1Rq6+N<*+TMqAZuLv)<obP$M<d?hKIjkHKS
zQYZ?Hc#cqU5)c{Rf}Ui&YN^^A`~k9z`s5WIi&$B~%_!jsj9ev}I+Rf`%$>k)4R|C(
zQJ0D}GnU25+pNO+1MI at tI69tX@+t2R2cX>h3$cHva2W)~H4}LUq?`-Ij3(foJj?h>
zinHhZDBVE&4=Ft)5WYGZ+Va6!=z>>Un5n?5V9bG)+TcMAFlJW4hd#8#&Tp{J;4$R=
zrL3vAyZ0KRceiu{?rbTScJ+*h^<Di3zC<7M^r0P%D+ at NXA_B({7->+fTGt274t4z@
zI47s_p4YUYN3*GH091!xmQP{te>f010EDUn(oM}qKvyvOQ}w>HP~Yvm at 2%~6-^Z(G
z^?mM#)OY+0^<9eZ{06I&ITpOdCfKPzD>_4`=}_1PbFi^W9IXl<8I}VYL??NUWx#%G
z<Snc-B_o{1ck`uK;Lc4LqJnO9NQhxK%+4wRW{x;F<w0cHfB^#Xz?g4#3HG)~rZ83S
z3Z0jsxA5G2)}Ue5rXk#Y6!P(3|AJC{U=nr6Mvv2mnk9G&4ihR`I`BYTm}LYqy}l#F
zv?7bT4xWNjk8-lf_m2)-ihRRa;aCX1-oeFkNyv~kva2AE0Luw*x3Y(xJp2h>O2(H;
zJMgjtXbm8@=nkn>zeC!Hk#@8 at q^ENE9a1xQyH^%<hx9+lZ(gteFXT7R4nM#AW_rVa
zFTd#}v$=QX|17haedYONHrxLBzsqd?G3)<cX7l6Mkun=cUPpw`20!VaoHd7pHZJ)f
z3T=wT2FD5%+5o>=2^iH=X2T012 at X=ki(GnBzDyUHJX*%3Hy@{+Exp<I9Z7G-{eo^+
zQ`@rs|B&8vJg+z9+a+9jlbuG=n`V^W^t%q&_V~Oed!+<)C~iD?FN&scTl_4b<YpF0
zZpO`wl-v+yf}3=3Z^_LiGr8mjsgcdh^d6pSCxlgfn3)$GMI2F#b2=w)fAH at lxykP%
zRNMfm{M(FzZ)hJ}s!$vywedl+2*cglWkvK)tFD}yHMy7M=3Py4a|}?x#qjNh8FVFs
zx4&Zp_V*38!0jlxnP?Bp{{KR9vzSY6>QQpz{;wrBE-tx&?%At6KBUFOD9O#rD9O!h
zQIeZmG|A0vA<50rXvxiOA<50r|0KC_|3}G<`!yHf!8x|7y(qc4z*aVDl9UL4b=eKj
z`NQ$b+U|b at mg<>DrQgF}lHHuqWH-e`(UR=uNA~mCvYTIK^pf4oM%m3Hja+t9OtPD`
zy=6D(pnzJ%1vpKJ<xu1u65y1i7!1B9&je~n3jCdiu|Cf{g_zQ at QnN8Nlfg22n+lIO
z4Y>O?NLGuMTH)!DDa1Wb#P2AHuF%U+*JHx-b`lQVlt|AdK=l)^cV at ca8=i>5p*#5V
zLyI4Wzq6zw92}vdFcv8a<n`owXye70cEQo`ngd&IreJDxtZD);SSYk%A`Va3PkH-k
z0|oCuQPmgIL&Bsi6edm1vIa9rn6wvZw^U-t;k?iVN!chGDyFkMEJ!+pg0<UE(vIOn
z&E-g<28>5~fh%ibNR-58J&5Mh%6+}re@*=2$|~wD0H_QJ09NP%fGkaj^8yzup#We<
zH?kUy{QE57K|&aB!GJWMN368Amssh&uvlqn_iu`oD!DWz+?ZE2Sx~GdQDRjOXxC`y
zXd?6p$@SlN&!N@$FN5B_W0CR=<qf=y!%3ghOQ!T-FPYNh-;gO)_WIr~lBY<wXy1CK
zD^jMk_?$AOvqs?ssDr#MP0 at I@!`?-e#*k2{?ix*~^fxY4ni(ZjvgglWg72+vk}9=@
zq)N|qbE(o57=`@Hb$qEDLZOmfKB)91!P_ymr}HeKQW!KOR5}(O^nIcJ^MMa_pt*w&
z`slDwX;EjSQ0e3Qe_N<D>0Mo@)P!^Mu^AoUxR9@!LwD5 at Oh0tpUYVlHmGYR;kkhKB
zKiq+HHRUyEWwRzv3R+In_mWJ<@a6|TCZg`lbElCXt2!3!BuUYkIfCyn#}@Mh-&Z`(
zBlr&Syaj^qVv-&KLLC8T8 at gGkSbaXZ(J7L*uL+X8+w(4bj!TM;xlAp9x9 at WyHRDNj
zs<kLBnoN%oBBMu#B6SFPA8SG*TmD6!Bzx6yw`nKJjL@%ZRSZgH-X-DD&cYF%ryRgq
zK5nZ at 8nsz^46>+B`>3P3{AioK>Zk*ruY$v3H}qgzl>F#;r2OdU+^_kO^BY2>ye3E0
zQF2GaMM>@F79|DIQLH0BN$_2D28RV|4f=FB(lMNlC^^zT8c)KwBu9c3q!T)Mz2rzo
zG&$0D5ptv=K3o6b>xb~ACPxxUj`RhWBLT)IY=1gJj<naL@#17O$m_{7g;vLvAvx0V
zsTxfq_yQ*FB&tAJBypWjhU7?jChe4=p1=mvJMrkR5hy;GK{BN9F3 at C1?ss$<QWMFL
z(29hgKqNytq21X<*2R0P$&f^w<pj!*zP5cqG9)vXA>nF-m56eqcV%MLr at h8KJlJxY
zVRpjOqhe at 9!*bGz9myeF@{DRa&`s`~HhYznHCZ3xbU;s{Tw}{=MCDW*o27&{fqbx;
zs9rYzcyEbP4?o6Bq_Mc^A|*<PHHp%2+;2j~u5(G0Y{L3_O`!CtHrE_SpcH`oYmJQM
z0+&ZM$<lImi4c}76_aG?+hZtMdM6}VY6f(+ywvX~qw_A{E(=MPa^4}L;5C#imEnMd
zBuji?kVJ$b2_E6XC2c at SxKs`KL_sEMel$$2_<nFBY2itsp>;&8fD1yS?|d6WNNA`K
z>m at nUVUjaZUyBj3qEy7x>L=j{5<%lTF>*(Tlm=}y2S;~m-%-}vE6u^HwMQ2c^hkWd
z!bG70<Zvm|WL?UHia~sXYEq_s!6w4LA(HTKM<dtq`{$4{t-L2h{`ZnHeROv(DO1yV
zq)bJTQYKtvac~rINlS4^%5-U1$~69LDU<mvZRk+S<oeH2rpUdIQl>K at DU({H at A*Gy
z)aQSYGJSe?sO#sDGOa8MOPQK}L&{XdrA%7i+d_TUq)eCprj#k=P3k+BGP!z5nGi>!
zl&OaL)2T0~6m(PoCo48MsQjue94yS268y&CQOGKV-yr<v_W_zDaEmeDEDHAaNM`i;
zU&5z-JP^u6CgXXzHzY55J(>35AoQyAz%PiF`si?t+|~vqM*Yfe8YWbn0D1x#J)ql_
z4rG#%B|eCfqMzC|NzqPCQiNM(z#D+-Y9RyA+~RoH0f~yqUYW=qFAMqW2);{lBo!k@
z@^=pX2`PAD-~v#_ZY{&9JIuiy@=qbD?PbB=qco}Vvf!+cC<<|PBYWphnkdTIfi%xo
zbSgUJVDP36uKwk at f}L2R3jhv1h9f?Mkvu9yq#-e<WZ-7bYsz<1nWHTnrZ_CBY98{N
zqN-kgMY^GPjbEU`RhKH=Ey{7G{xOENF@#=?o<yr}4F-eqBw4yWLprR*M(INK`WG6}
zEy}IzS$x-0CtVEnlwlgpC9nC3*u*3?Jrz=Ns}h6Z_t+<&Bh_+1vskyGGl)R?OZ`ZX
zag+Pu{Y7Ky?E}q*iv1EgSGar$SHRT`_rh`_RD(lNk>+Cp143u8-HW-GiVqftbk&08
zNHjDpm|+<*5WX^R8oGXwkE1UUS76vhkcFN_9#+v@{#Q6M^3C3ZM(IZQ_HgLQGR*C;
z=bMdEGRi4Pu}<m3Zqw`cZdak;Z(pFEoid0`(Hby92VfUHN`2sJePs1^|9!gcpe at 5I
z%6p|tAz?<FLuVR`&r;R$l8nw%6nBmyex;#z`mjIY=$IqT->K at F+(GcIlR)^3aFWPz
z)tf?+vdoJNhCEnj?JoJpOBQC?VXz$Nup+NI_#N7LRkV19vNsp0>LvN?@kRRbD|v*A
z!fNPx^OQ^eRt&`b*cT%$pN4t+VJIp at lIYeAqi3s<gnSHiQ*4dtur~Kt%X(rkh*S@(
zAK;%mn^zd6KiWF%f;m%4&1;c2lNQ~ESIA+;!rybcZWEdL$5r%2P083c08cQN0cDTw
zr;DD$J4V#2n+hCewSge-1;~QC`(sO-a*80lT&JdA803|qnGmd^L=x#>#CA137K&g5
zV5+}H{7RIao8Pwx6-aGXCFfO_btefgSL0P9eBfjnDQ&jdybCX!Xpk_h`-QGbSJneu
z3R{V0N8agBgbgcJoM_~ED4x(3FzfYVPMy=VQA7{u&%JNthUV5#EUKMM`XiZx0_TZk
zYMnAzqZ8zv1j#0a0Fd*>SPuF0Hy5w!reesLVy at 9oJ29`N!Jd^JXP~My?^^qUcwV*P
zc-*EDS`&Fp2<Y4pGP6o)M4OBv>HrR+L;g}tuVs7?F%E}(%9eA+v=HrHre_ri&&DBH
zzA($hwPm!J<Doj6gcw6Tv at 4ZXN3V)`-_-O{t?qgQ)l*~?o<+_ZjfvC-<w;yKv~prR
z7YnORfMH9$T}h>AV`39t{t_LOqrVXszvFO4fR7SZ<Dgysy1`It0dUVl-i8>w|G314
zs%F5eZz;uBt<0o at z$fv;)D^kTmb1}jsh at Xw5Oun%fbmZ6M$@1M=*%HorCo5rvOHvK
zuqTkI9&Bh=u$xxa at E;Jx1%HasauvRkKkLKwhH2r$N=A2 at dHdb+ceLU*<GF!ra0u`K
zfk~-$G}{papJv9RCuQKPwdP!bcOOpm%fcj2a^(P<+90a+=tEDfsf@>DdAHinV3 at m2
zykshd^r*o1?Q*?6r<&g?1XA<*&`j5$iIg4XnlvF=0HQx2yqdoKdp<3IK~xhO=<<rh
zoOZFq4v&_4;n|cJl6b76(WJ%+PowZLcpQZ>W#~1dHPk5)U<vSi-MY>utcHGhzl^~N
z_6Vi^0XCR0v`9j`K>T0okEh4-?jG!6-T8ZXd+LAg;gRon58XZI at 8NLc**&yi5BnOm
ze&GRYTa#+wyBQeraF4F(-J=v(ZARXeTKca=fEUY8u7K~(t&iN%xSQwXG*aLs+{aVk
z3v$ic;2DGA^_<$gmK|MoxiQpYzPZnQhojJiUHTL5{|-JFd at Gi069X@&BD6zpfC0))
z;RA#)#e7`k6tg|2UQOS1m=IR7321n`rDw?+#jK_m9mW-nkLfn~H19Cr0h^^qc-llh
z3}KAXtt@^T at a$CYsW{JgcKv+)+`AQ${*2(7ZuGvDNYyD#I4dIH;7n?QL$0Tg4U-P@
znfdrM*{+Fs`;~X$T*!tRf(uAW#b^29Wb8z24V?^DDcM%G$Z(P1yAD9uXOH8T2jiEU
zAE!e&I2!VEU_GD)d;pI at B#c(~=Y&z^AR>(L*o?lzOAFB!A-f(M9-fDQQ1YrsWz6CA
zGx2eq%6Ohr8S^ZoKTZNF<6%)g;F5oF)|^4FSDz$Bc)g1K8K{g5qB0yn4}JLwP#KuZ
z9fkF^<B%X{-w(xtt;zs)$fFNwr-wWJ+N=V=b)TYhTA(=&@?5qy4EVPhI?WMbc%#!D
zR*Vnr%F1N57xKZ)v<s{DYjJAFz#_hT7+SZMErjKX1Tc{Op*XSLFRl0LHXNaP@><w}
z|I3=zp=Mw(=4A|G&C?w(QJM(tA0yJ=@H8Ht!Ip at 0Jx^myhv2%1^ang$hUq^?q+j7_
zGzbn>MWh$=G#U{H=SHMUc^dDZg7-wEZ{z8^Fg+$BeFINli0R=G>5F+f6YIPnA{|fZ
zylQG)sb!P!Vzqu}OJ5RJ?_~#H!~-74w)j%-srx-~G$h=a-o15Eu%G at HuqF-G|3AP^
z`z=_P+~g2e8J%j4EnwZ??HV{g*5<9w6jmjPYHh$qIVNvSCdOimvs-GU9rm0Wn_T^y
zK46cEVJEN`rc=Z}Pd3{zS0_@}KL}i(7Ry+7w~GF(mnaXTmxG<e9xdo^@LbG;f3nG{
z_Qv=~&#yO$Ii13)l$$UxGdubM^mmnjX<%-?eSth60E&yXv`YJOTF;7|%<r(zayhHr
zlaIdsun}EEqN&bBEIr)z7-%~+mHW at DXps(*gUf$Ww90BqAL4d8A>SYw8d4$yCwatA
zM=-9;QSy$|;>g=yHY(E*c*5vF6U2QkIp{GvO$W*>nNnAo<pJrMGRs`)vNFqjVBenm
zz21zc9w0}nH^N at pU<5)$Ny$@f$-gM(4T(tTZH<9CZ1SEe6LQp_(cy<?S?J2)XLw&&
z-{|e0wB!ltciv#8=71rXER9o}q?^3K4CxAQFi9Hd?T+<SIp7_Nf8mh#c(<nok%1T1
z{gM!T9XbAv1}pa#@7-<<E+wb~Kn->@#CRm{4?W)RPJ6e<pxvkP2vp=Lrb;rEVsCJ=
z<n{*pNTcABSK_w^c)R0;3KW=jHW+|Y at Qk!2cPTeIU;>5%$`#g~=%ZYTPlGYaU@}t%
zYj7#h8bRqJtotElUTj{A5)02M(0%HAMG|uB=(ukcG)^&vh8B(`X_`@s;fhrKO>gmN
zhp&?DV4W!pT4ot7RG>mb_}%nEdY7nnIPw1Y24|Y#y*T*z1&SL$h7pKa1DPKNF#||n
zWjKIbKp+Vk$mBP83(|QD5&$eMvK0oQ;v|IwbY;zS$Xnoigi{!u!j+*A^W^LN?H|yp
z);Rj at eMZA2x0uNlmG^R>R!Y(TO=-5lf6HM1qZwM<gJO{sd39SgJZjFehuj|+Z)7>|
zk#|Hi*0+p4nU02d818_a<j1wdg-jw#;hb;pux5BYQ#!2~c!qdffpKTNU1{^v0u$2M
z>A&;)qm|U>^w;r4x&JXZ7e{51E#%TTj0Y8a9P7Z7rx}Xg&O{HJA{?s=)dsTMUW*>H
znEYoFT8Jg^H|{p9%AZ1xY^R{PrRL7*`+YR<fJjnv=xZt6?4JJgtwC{QcX`U7WI+X~
zytIp(YBuag{&`4OEH-EGDQ`&zy8DoZ0E>^kLOV&#rw}<8$~Jh%57x{A+g4z+>drRa
z?Da3gA9(pst#I_Y^QztGDGlgAv{=n%b2kvFgJs&w at QNMjNEgn}<g5%d;ZJim)MUoJ
zG*B^XFw4Tgy#90e6~c)HlC!uGdVCVSOE6L13%q@{7!aEQH?hW8MBC`d at S;a_oRct(
zi*@e6k@%iS2$#Igg}PRIkoZNch(S2V7&I4|B=umwt|swiw%odaE;=*ojexF@!OYBS
zVplhiv;MOpE>ENpbIXnBetYyeVh~35VITIPU6h02K6JBK>cf8M!=1DBK8&5p`*4*;
zziS*Yb!EE?cg4==uBf}t-8vT<T%t*Fzt!AH_`NFmIF2-H?pg)3 at K~O&!#_WdR0M-L
zXTwWS6zr#-%UALg@?lSB>_p^7+u*1`<*9(X)gBm|jU&)4`lmIs6jNyK?t578_xO8x
zzaMu4XB~$DH4eIDx9E}`R`PG-vK%CbntOzCHK at 1L;9z_jJ#Fy6+3#qzsk<;NM2D1D
zQN7?2)!n+lj29`D8A=+Qxy>+ at B^$X7bgbN<^eLFuI;L{!<*M*zU0`evI at g}a9iq+C
z9ijok(au}42hZk{5jh-dfmmk00zULMA=(gYxWzVqhj=1#QGS*+c{#rK`8nPr+9($m
zyPIOF1Fjf at NBHR9sLMYchHnlzY<|L2Z!&nhdOVrT^gG>jHkozI3hRH}%}IFJ^7lsc
z4X<_C5;bIUe*PHuPITwoj at pNEx4WWA@cl$LUe%s-q(layYXTCrqZrUY+HQici4t(K
zqkPS2X at 1<zrq4q4j9ZM*{-zQj_cF*Sp^N)(&*r at a%8KsvUp|A=*5jGYuAYT1z%Jzn
zI?J4eoS?FpT{w$^(3X`TO5AFfGquX<&QaMosI1;TRHb3MNc8roDBqn)^?LHymYLia
z>Xk5<+4TtoHH59288MiDpQ#V#D at 8g#WIa#p`CjXQFCg}IPk5ZqzqS(Rz!zRS{Xeg*
z9go3##pil2T9>k$d!}f8Nbk_by8s>ZL+P9?V}+M{ky-%+6snJ|h>?=LjhTL1Mi5OX
zyp2Xkax?1$*7!6=92R98EEgj>YcQcxS$P~BTz93(jqIO5JNU<$@n}VlJS2VmBP2S;
zWjyV<A75zsoNvxTeAq}2J?JfKJ6rQP1;i8s*Vmph!ikRIn&Q}>KgSQWqNpF``!3Vq
zZ<GXAU`WqKpjoQMnzNoReA)xl&SM#=<x8^Axr=8Ie9bJSL#sN+Wf<VtB2R#Vy~dC2
z6x0c3U^P$#<`<Oy=nuf#mgcQaOB|QseaIZ)6I<%-6Pxv0fDNo{=IdRGRd*-`w)+iT
zhZSLdnNKjS8cdA3gH>}h8j7=dqRj~8z!hH^DE3aQ+6<MvN#{z0ek|^H@;g4^EAkmy
z-DWiC<^I&Q_~=hv$A3QKKcB<TIp!b3SFvB;0kR3spn+mw^ujjs{@|f at yuH(42+WI7
zu;5`{Fr5mXfr9nv{E7w}+6U#(l9 at g2A=P7y+q|5gYp8)Nh~861K)Bn~o#)gdw#!Xo
zN$RRe_<>q`ff$P}-(kg8r#WldVqK<Mr=@!C4`R*1Skbfzy^F>ZP)uGm#&SvBd%%cs
z6pytauS?^+^l|uj9FcFt|6txc%3Go5og+>{F2)J&cH|uvy(eP at -z181deS8LoM@uc
z9V7VCk!|Tt7knv{7{HAWktZO(Ro-ran-+Py%~Bs=Or%6_cd}5Ch_?)nV&qC-pK$qi
zk-&aP=k3t)g4dSg4cIj>1RvgEG_ZSKL!XqstmU*y0C=+zeM!W<c!!as_pp(+pGHT2
z&vFoX2NXU5g#}N-z+u!?^wtFfCz@(ROY>aT>v3Kjg2kYtz7{TU6y<x&B6=nEkrOR%
zZLE?l`sc=CP0*&H_*ocwr%C%>V9aTl1HpR at Ko^XXz6zoPS(xz;-bN4NYK#=;Rnt8;
zc}GwD2GPRxTGXc0r_ohu`j+h!k<(-L$6X8M$^f>wgaRU{={DFqeiIMFD{uD(p+&C~
z%NHxn_68HBo4vt!sSj{s_u&G{404BvsV!gQiu|>iFGlJ}C6#Pb)34q^8{|U(Q62}_
zRRr4 at hLws5E}p}^ig339+%p8XJOVDC;J#@lxQ7t#a)7&y;1)!{r4igJ4tEU{!^lW)
zB6oM#1KdHKGQz7Gr7W*%l7 at QKROuqGDlCQ(>$^COO at 2_v!L~az|My|~91fI;A at A9G
z4Bb%JpiuZX*w4uR6z6rwJG?)}@Ea6JNDty4H~uN|{%DdWd4G(RuJ-;IFAY#PN!PQz
z59pt+za1DEt1B?<iGS0Mp9?=hiaHh;_BhXa41Sb_ at O&SCz6+mcvuEe>Aj~0`#&c<P
zE#s?n6Rgsm6JV9zO{=sfyh>})>o4o|2&?pNT%}E}zyoHtd{vf$F-+R_)7SRGPssaO
zv6_Bz8%5`I;w3j;Jf0?6<cKH97I2$b>#cx1D^$r0^N{i;uFz9vsjv4`vXtUI6)Pp6
ztT|h`$|bKy)ZikAD&D_g+5nKAes*~k;*o}D at Gp)6S5+y-!UDk8w;2uc4m^G3xFe7@
zHClEo8ZMS>qKG5UP}jZF8_fo_7I>jdIrU2bQg*Vt=aA#sENB^G%RBz0svD3Cw;b at K
z%CD at X9RRef5+`qlAIky2CeY+WXfl^Rxct;!%K<<x<h_ep89YPUKU2djKyGAk7&rzt
z0>^!x_!vN^r~YjpuICk7al(L%J_2OXhWo=Y%if!SY*3J52h1Ss<XH?mJI#%6{kR$5
z!r7NLiGlQQT{u~dYWk=xT5Z*y9QOPBwaq?x3;FdO2+w{KyJG_41}o2tgFNL{diT+-
zTnk|MWANJ2Aqmht^x=|7o6Nb%U?6cU#*f(p)%Rx?+#k}hU<r_vBi-`$c4KRuq+fKS
zt5>Qe?uYk`q0rOSl=DVbE?wFjbs#5m)O*q>R3sUBuwtv7T+NArg<00TBP8O$@4os9
z-kdOK&QxgsX2f<+dKO$Vo2YC4ic{Xk=DdPP9l)fifhk5*RzCD14h_Y$bGqb49h9ql
zQ?xltsBUSfv*d)P!oH2Ic#(@3co=$&EM!KKX<)2Im_EyUledKpfP(`a)WM^EEpcz1
zE#OeO6JS?KBYP&5M+Xj7(_%s)p0b7$r(t*L>^m21tuIhcZrSCTj4}pZDDSq(Th#QH
zCX~E%L1V9gYUEwah|Q!nO6hir at vK3vXreFv5xqoDH*qh~<irm72y|k1$>gkDVcpL|
zC|0?=+m122;k`Lc{A%Yyv|B(IIi}zwqLuuq-|acaIuFHHBc3$%&&P4qe!htgk-%~C
z%dMvWa}!^!mW{&Gf6xe=oUdJh60_5L+AKU>&et2u_yetWr{&9e6Da7qXsH#2G0o`7
z9uD19gn9V|vZL}05CeB+ik97T$6 at Ybo-2*Qh at v=cfkN01=*tvNjMU>OcHOF`OZXg^
z0L at QO(;wo6>=+hq46W%%ZbOgdrpeV2mjrIjV1Z?PY2cQ-gxB(qV-ykoPxu1_T3ldk
zh7ylM9vxTutoS4_a7USH;{k?Bj^XevYC`1O2G8T^`H?0XG8h)&=|9m>bvbiRy8 at 5F
z at STC-L!J$FuJ*p94d2FjQ_eAbeGz8LGE&RHh?%J16B<M(mK7}HgII_WDaF9xE<zVK
zH&G88kK#B1hPZ~Fp?thqO}~^E{)Gy^!5Z1kwrC-kcF1Oi{h%Z5h|xvR at o9ButJ)Z0
zlNNG~-Z>53dya00AEgg^KZlmZsLkZ*+;U*<ktqEl{k`q{y~h9bJ at mh}A&G`J8Sr2u
z?<M(F-|kU*J4Dm|iE_<a_dOW0+ANuXa289%LebQiS6yB((Sh>mb;&VUdV7ZOa`o}L
zEv^z<A#7)t+~P9T*o4*A{tjVvQT(j|mmvd#GF>d+p5n5!%uSMQW>c*T=1j0**xQ<H
zhh_8JL-+-`Ci43*jzHAA0;z{eka%rkzvB+sE_LVBI5Es~6RTADqzbY%tA-8jZ1%Pp
zHE(QT-bXf;=TiW=9PfgRSkExonmHOpi&FaNOYo=9=u7b at VYI>ey3rHQ?mq??A*zl*
z>PN&s{ZMZ(Xhq0_$h9yyX|BN2gf5Z&0KVfVF;GR%z<0#i0f5+!^Tb{U_JKV!i_|VT
zXC(NZ?L`C?6aa-7`an$@Qa(S0o`LL%g>n-N1xZZqz=H;c7ri+89B3co?TYiHy8KSF
zEnq!OF4CMtOCzy$Ieq6nVsQK)<B#`=ShG;k0jYJl^mQ*MM{3htepgJFSbh>eyYPqd
zg69l|YWXvyAUr~Y7k|A-P2hb>dBHL$3}qws3qD8LFJN{G&qfih@??3zOA&b(3qdI@
zF9<~BVZ>VHj`D)%Bl1vtRk^9W;KhhM3`wmFFE98sCY*=P*pva~1)o5k+^`NCA=qpE
zR3^WKf5{KCO>Op5Pn0AaDA^ecIijD&CHPej>Ajtc9$15G%F^gbkQ;-U1Qxl+^VEXT
zPaZKEJaO#QgNQSRxQ^h~jT?bTg*;2QmRU0ACze?ZQXUX>SC&~W^(5w1SE#U^<St=V
zG8^*(sX~}ggG#ManhOCO5_kdRIWG1xRy{{-P$cfXqfh*VEL9wPoHt at a4e%GlW)H$8
z at Co#>a5P|$2E3^m^(+d+rkafOg&zXr!m+65S8-CqA2+(jNRkE^lp()|G`4>tzI|F&
z)iTTIz#OA=wOI0VC?q2*tO^+yOY_J*|1M-mll)}hGM?fWm(+_U7!8AGHB>qnKLGVT
zdkQEI at 352_;xVr<G{p6RzL0L&IS#X2KzPyQ#^ZJpucua31Cu<OSGNfL2L}y^H~gtD
zRO6X{E}3Qc{F;sKMbUKHh6F4sUdVfG4MrmuP69jNkHKgkc}H;m`ojDjv97ulrVu7d
zW=erg#c0()vsala6GXK^Se1t*+t`ffX>$8T17M6#UxzdZNa^>jWj&dsPL}9-2VoNH
z$EgMdonz;9;KiBmTWAGdqs5h35<L|9^C;zSCXVK?WOn)sQ3c3sQ{!nu@|qL`Q)|jB
zQ>6}U@(sRCo*@+-QXere)>xK*sfTW)l5q$tI&dkr?+7Y=)h7IYe0{=HbUO at 9#z^D1
zOr!}F9UgOGrZlcFQ5p*aa-*=mkySotpiT`!Nlz6nVCoybsC82z^?WG6JQ at SFE_gSg
zbNY@!11EPSB;0p;v$6m({ybt+{QxF?T-*Yg+7>t#<Xd1KauR{jvD@%=GL}RTqP(X*
zt^+cF5P6qE<qr945Q&}9kFO_U2S?W60ix>wszixI#nbZAO~<~V5Lt*7 at WKKA4HYd%
z=S>w$P^-z?t<Jk$4D>%LT6WA8(WmbF&*(&ZzAF&h9<G;k0<>Bm!s|}zsDJclK+oG?
z%B$`EA at 4z2G3g4tbjcs`p?V(KyH!n}wH|L~4)f0nzQ1!SwbWjaDPf$n7)V{_>CjU@
zp(1U*nA3{Cx#EsfKzQf1*aPE at z>h#yHs)ul=}Fay92hsW+sbxUgqV5BMm5mu_!_VZ
zxFIndoTe?deFG8j$;gcSr4x_$Uqy`4-0%f9tzJo{r?p>%Hv2 at J@FKDb#tENEG10gW
z($Gm7ckf%qkoww&>;8OT#OpbDL^en%VkhA9IQv^9(!m(BYWkA(nov<qANd8cNUhGi
z{c3vIhHyIT3$9d`AIcc?1 at 8Z;&@Uh at wJ_z7b6KmnCQtjE%W}X4h~4EMvh7VE8asDF
zYRj8OgC0H^CdxZ(;CHa#>TpEIBn3~L=uz<wO?w>B0gqd>hrPYaPM(M;bHf&kD0>ti
zH9Z}?Mr(G%4(%iIKP{e%fgeMq4=J63A7f!`<v=BfS1^Mr2eRaeVV=G7=EykXdtGv^
z6PB?j!=1CwRuwb0GPa*nt%aRG+^IHtjwqM8a(0ZZioM8IX>zJ{ke>wkuwB(A55w=6
zY?XtMAgRC`ZmKD%a%x`{2$G4)AK}x>CWIoDd$*kZFXBEqX9sk*qb5NbRAn4f)jx4m
zt>;crt${DzC3e=Jx`F5WW#RDBw+8wrmX;q<h0^kqUBdj$KnLC`_fG`2!Lz}+E)<U1
zWoi+<?J9=6cjnbPE9#^MMGyx8iPx&u!I55CTDr{FB3%I}`7$j4^&kvDt)&G>C-Y>i
zufroF6|21*B{%nuzT-P0xQ3isiwokY^aviq7mEc$PC|xsKU1u<C+-B0*0mh_+=>^I
z4%{Sf`8e8;lW`WWW6$BUrQz`m$SEr<80`@_Kh8!_EmAxZ_ewLe2myP(F+!>laSd@!
znt_~GU_c&RJlX81JG?T!9v!;WKF8sE2|o|9Qwd5V{*W4xsUB4gniw}TYY+JtS%*YG
zlNV$7pea4|mWh_S#}n4jTKFTsv_^5lfifcdH=T99zf#ysA1R^_{GyR}AUC1|ga2~}
zVny$zcrz*0JE4U)Qma|0)f=(Z{k)pr0WmN#2Ipq3j1)AvPwCHUT)IGMm5T&Q%Se at A
zH@@1xjGOghZ&!-`JmtN!I#pwVPU$|W(QA7X_uAf{mAZ71>!d;wu at tUTm%!7NUbMd!
zN8^L=Vht>ioojJFFjuAKg!!>EBt_%71D0|oR^#2z5i1pi9=UG$Al=!Xbjd;f;+du7
zx>~MtR~+Gu>c at 7B4-fAb(01ifr+*q9{z<4HmxNdLDVR#oqF%{$Fu08f5K}o6IrE*T
zL)RSAbvQ^Lm(!|n(yHi?E*35Ik0)>paX~1m?{?O-n1-?sZ`s&qNc}XOR5Kz*bKWQ#
zfL`1>s at L<2)@i+TNY3tm8*PN6{1b*y=b=%d&VO}lxbxNUDL7yJ7h~5<qJJn3(_!vS
ze>e71Ii>e=0`_xrnYNBxfvaN3r==g)Q9NHqPu!}nqemerXDAmz=bfB--m-`~KY%{v
z@?XPScvHo>2jS6z|1=0Z27Fp527LH}KnqDI$Mw9T3p(+x1WrJ?+vy*_0>4#cmleBw
z$}S(b%ilshD+})KFZjlyQE^~aY+#<TV4A^mnO#_I%XJ3EugGh$t&OFaXegol>yJE+
zBs#TQi?UfJL+5OtqxKi6)Y{}NNHi3&<BP~k5<I(P#=eVq>SBBF9XPDZQ6wq1vxY@{
ztkH0gXr77~lJRqwW at KtRj}ofj`yC}Z1m6QOIFT?_a$Tj-1U$*M(a3KsJSMxzq-Ktq
ze#L4oK*V_7-i29n;)JC+F{qGg&zgg=yDasKugRa9^|*8;k`D9p2TGa3s at qj1Ni3-~
z?H*Gfn+eZGOY`DiQ1?FnZq at T|*@4b#@|}SIehl at C=*}N+2=bb2YcUfh9x(d^GygWO
z8F)#)t3Lo+0XTmRh7xxMo=0YzqS&HpfIVO}x}gz3v=K>}5zk+L>U^(1*XwodRofc8
z1E&i1#PJG7p%fwTK3b)g!dACc-oM0Fc^~|L8^zhS%D3>R01)fq0vd|E&`=Dgq391o
zVWW6Orro6uzt4d^<b(2EXi!?5fj{7PZEJc+{I`|$X#*lL at T!4SK9^ADcGi6WGhd0!
z+{O+=rYJv;Vt70%?d at u!=Vr*r$BgnIR>*RX5R6EyWlb5&<j)gB&uK^WI#?{F(XJH+
zW@#>Uo-`Za$x2U4%SeRsVLs0kDf1bgxfJU}g8~0iya7g)JQu9x4}U_McPslp^t+wC
zO~2dNtMDs-gV-W~sXAq{D3|lDR7<9I)hq_$>aI5$xO-dw>r~_~x6<u!T)`;hU0&zy
zC>MF=yB4Ft|4|Ch!`2`r at GR|O6!2evxJQi$?U?VBAGSK>c_L~u={G?n(=54*bP>4Z
zDRtVPfoK^Zm!Z;eMchyzu03W}IE94AayflYi%|lna9nU5vUajjCew6YU%y`UrS__C
z`<qnXPm_6lw%7=ri2Zj)zPIb0$oF35?|l+_&m8gIUwXawX0P||=kG1#?}-6h3cV=T
z0!7eAZ7eG=NQqkDFSRkJIWPve^eGY7s1wBiI?(q|gZ~~;^e+O2C2rFTh%+|=d1IER
z73ZmLycO#5Pcc;NUp(9{?{vyFHg%IVKOa~WwHoz^+il)WDq4Qks*RSqK#_4_jNEFM
zXDpCA at j5sGM$j%7$9BGni_0!=cFXmHS^x#&C8YNdMZ(NfYt?kaN7`Kx-EVf_f?U(i
zIToR!dhvbexMNJ4v-5=L?TVQ at VSK<fB26ssM&*|I!=d+rXsMZR!V6Y!TRXcBcoMYZ
z#Ct0|asJtmuw at IZ<$i3zk3At~HmSkz_18G*4QU8d$y>+$9&u-+*}Qq_B`Y}|aT&1>
z9>XOP6v3ZNo4m~-Zv{HmCht+xU;Yre%l44rAE1m_G<xciZj{|~P}+$r$#PV9o(B|i
z%tWlEzYle3tfmX<3AW)ZS1*2ucU<i3ppF*-X1XEGX=$8)eW=q(qCQa6_Zv=VeaF)i
z^{rolni@=@{s*!DT%je@|GG4~djJaW(a6^Lv6px-tLf`LK-mM{MaT~Hq2ebda~o-%
z0e6agG3j>U5ul`UWi-c3>JHsvy$AycgjryGdwBtV(-X(;XV8$qgmA`fkbxAm+brg7
zWfR-5ov=7%Se!B}&cKYc(C&_iN38KGbI at Jh4#e6Mo$_8A9Ix4cBxGOK8d`O4 at XJ9g
zwug=oC|nsjJs?dt<6M at JEpx>b4Arum&XGEx_sW2W*GzDVHNyqerJMs8C=IX=9Kzp{
zufWJXX2m_08 at 0zup9}3VgY21!mc>E0+-{do0>v4R2xm at g=d?L6{IEj?wa^NLje>jR
z-|yodY18%y`es}FkUsfKT$Z-^MX)cj^nG#d`Ibn(<N6ZG#cBUYUmkb=pf3+0VtJ4_
z2>>hMTqjAA+f>a)wDv&bw}$o%y0V<EubABTiK$C>$?jr11`^w8L;1+8e_}L`UJK#Q
z?|Uf53Q#OC??vDIFY{ik&%4!zaap at q?x7wPCmiP!=J7Rc-mP|>Oo7~G92dE8f;Asj
zJv75b+6+$&8Cd}d%H3-NaxLy1_a|Bx*ea}^I|fL`2kjaoM^_e?qMtMGj4o(}&1_qH
zE!64=%xV_>Q`*TB5#E6B;%#uZp|o#8nj_%8mUdDYKyGE%@X`z at y)LRWxs~;WQYgv8
z%;Z)vFfKE)=HmybbfjLI9a*}ImtL=z=0=vT=A}18wicEoeDh6SawWZEjqc47UOHT_
zRg5fsfS2azr4u7d$3iLP76Hsv(KQd_?~Wjd;^@)@UV61&^UTQ7;5Sryjb54`S-P2*
zj at 3)&M3%nKOYM5;g2>Y6d8s2*x|APyVSq<Dz%4pJWfXv$1B}xFmPG+v#Q{Ve;I$}#
z<Zt-6EnQB_%<YUGoFBiY8gA8VSP at l269*Wt1FVb!Siu2YI>4GJfTbM3tpij?0hDrp
z+jM}&C;$fsxKRgajsnQy0QovVYZQQq0~F{0ZBYP+{!IX*bO06wQ1fqXB-{B&TB1jC
zIj^Bmuc0fdh9w+ev<_e}_6E3*1Kgwon4<tjbAX$5fV3!p!5qM<17t)2oZ3$SHXR@{
z3Sb`x7^4GZM**zm09WV$xlsVGae!<cAU_IV{(fyFiQ>mB0KXW)9rBj|#qQ)a4ApBu
zE`XQu8m`}un9tpATWbOyoIoZ<fn1=2B!)nyau6g|5Tbn_eI=JZ9GJC(VCLv;wfp~1
zQK)_FOAVG_77$O;d-DIn0j>@2^{7ri%>k|p at AW8v2ln;G*Grd00a*9-M%hbWivk$B
zPaD^nMp~iQM>n!(Z`91Jh^pah4sd07uSWrVzPC3DU%Dm=;7 at ydWAUZcQ2<YJfUClL
zJqqA%4lp9T*P{UP_x48TOP5ChT(~!C3AaW8bhP$H>Py?A0Cu%%BgrCGs69NK?330g
z)ZQLd!%MAEsJ$x+U~X#^YB$962DrU73bmV~0CG8iGmH$P0EE^k)SeLq at bjK1)Sejy
zuys!qYR`@W_~)J|)Sep!Aaj5lI3kGAMl)h0f43(JwOgZVaPEmh?P3%__MRxzJ~0X)
zVNVolFNy;Aem4PFbb#V0fQ`GOQ2Wd%fOmICq4qgZ0N&kEsC_{cz=OM^P<vSvfQ<v7
z{w2pBk%NgNnYlX(wJ(dRLES|FmxmEZ6u>vTqEP$tD1a|Gz>qKkiRq1X-{1fjhY?5=
zz*D=<!rW`3K#Bk)Ib?Mxp;GTcQtGv81V3pB9X1&Zddy4NX=a!1>!F~5Y#>i$ur!{4
z`R9pjz<-p;WhW?+%#QF3D?7{+BHPas6IlyS6tO0rC}!0>F_W$4iG20}Pt0L|<%tFC
z4NAyJ=&@xyjWiwe at idZeYynRrrN(CSG!kd5n5U8cVv~6qNi8PwG*VTpkf*Cj{>Rfu
zE3qt|MzV-y at HA3DEQP1rNcPXuNY}7mTPcmC3}ZZv)C&7IPa|Q%nt2*&5cVZcBe}uW
z at HA2u?4LZ1L<C#T(?~C{mwB3GkCi-)Q~+DZ(|GW+IXsPLJDbkac!;wip2m}yxp*2+
zTxR8IvZx-((|CrmY at WvBk`3l*JQ-OUPtPRzCr{&f$GZ0LvBmUHJdLLqYvXB(&HWWm
z<JrU-c^Z!)_Bl`EiNjX%G#)POU7p5sguTYocyzF5c^Xd#R>sqK5HN|S5$Cg+JdNm`
zP339C<ZL2OBhqGeo<{u4 at _8CjF}sSV5$m!{o<=0g`tvm6QfB69M4RmNZa&VKZs%!4
zhU_p;Bi>`JJdG%hZQ*IeW~`d05pl6kcp7mOTfx(akJuYLjhKfm<7q at P%*WG+PuK#U
zM%2M(^E6@!R?O3g4%lQ&d$kw}`rRnMgMK2 at fb4W&#1fc(Y5cnC7%iq8du6(+i7G-D
zbuQ9JX=Fw1bp3h(UL$^f3ZXW$_FX-y(%RrH21o)niSpN?yp*Uu%SI`oU9Bvp^Z)H4
zV at QWX)1V98J(I at c4tsv0G@vS`Fn8evg#}CcT$NiHBdWFb{3Oqh$Lk6$o?{rapvr2r
zRT`XXwXfQ9OgTg{WvMib!xBYH>Z0eNM{(@VU4%}|kTZ|x*m+JG<jmqZ*YTVp$a$OR
zWb&M1$oT`$>9Z>|iqZDjkrFAq20JSH#YTW=F1UNJ=fZ-!Gdvg2eb{jrX7-%X5L^W#
zumMq)W~j8sCU0Svm*^MEW}EzFu#JS6uW)DOjV`%?4Q}XBH{gYsyqzt05U;c7@;nJu
zZRUIh<KO7dD7bI1;7g--wzEkD#OS(fHQh-Hs~6uy3NN;`eMk=b6?z|{YqzVn(_v!P
zgdYSWa4{W#$*nAPJDS=qO4G{H^s?kuw0X&7KW$T0UN0U+jv@{ixcyi92A)Qn2to)!
z6o(*=>kuop;dzCw2Dh?oimARu#q_eBbR9w_<4*a)y!}dFS|fd7jilllahkr$zsRE~
zx5E-Se)bZ<Fv_z7MuQ7OJ~;6HvNV8KBMvhpOL#I3lFNCr2$FB|WU<44xx3<s6zlNM
zfcKush`f7|pG(tjm4 at K0Qihhn4z}R(d5+NcE=arlAK-f~|0;s_e~@kDF<qx-S)KB|
zxlZ}ve5X9m+Ut(x|K)xVe7F-M&h-pf%w^BgVz#yL;~a7x=Q-E#oNUOM&2xtEoLtCx
zkmn at xoP5YB;yK+r_?ZtmxAB}Gc#a4;cAm3~=gfqhJ9thV&zS=`BG37V=PZDnn|RLK
zJM_aJGOy*CRXcPl05Y?9re}vv4nXFGJhOPmSvaqkOp*30mn&*p7KvyK2hxQbwm)uN
z0k*6wFzk96wycUivT4t*<rf%xnCJe`?Y$?75M=O5*qosay63*|2F2UE?ahcFaf4<E
zzR at PWLH~7MXoGrE5$GSyw91tov{UnKYt!i-)}EC?WM1I*pY)BgWHW38bmw4O+n;X!
zFd4Z02<HYGV82e&AhK<12Sh+98bop{vuwugc%gxF;K{A{CESib>R%a15E%gB4lK;d
z*IE&z*ZHRJ+=LJlX-nh#Rt at GOy^dW3ak~y7Y7i at Rh>tgg=-7*NsEKw|5!CoUy?mfv
zetSguJiUCfUVcYJ`J_$Afnx3?4mO43u26ZN_CAN1Y+HM0 at A7_n{dej0Pl+f$-V`do
zLodHGqI{=bK1naXE28}4CLE#kUh^;$CsQAx7YL$%FNiq+ at v{!`7(oo^1+k#lig7nZ
z&$cfMw->Gn9VojgG at W6fN)Gfd9mv##<H_d~0hVdty{7$}M#AKoxUL^Pg;;wF+q9Km
zYS+^5*5LQ2&x6eY!x1*W at _g7lkp9+EG{V{9hJ(>mjTqn=epH&B{*)~JNrXrABBMX2
z;d5H}xd at +&!q3I{Tx at HI#~qD$-)w6jg#^U?ng0Rz6Tbg$3*mk`2lj3u+ at Bf7{VxCC
z&cglk0QWx?iTkJV`*2e}k_4%-UNrHhYig-q=ze;`VlpN8_w@)8=YK{s--q<DrAF;O
zz3#@y`|U5Dj=bMqvY76--?&~g?&2B)bT^K63tZpKk=NoPOA;v+HQ~^V#534l*_Ry|
z7FKiGn;zB<wdePh%;fxYljk_9nZ`LP`zp=OoJL!fVN7MLk!Dbe!}23oE<ibZdss-}
zb7fw&@+tRA5%=a?O~D9OQ&kKZ676%r+ at hljGyXvvl&lPwyh9m`!5etwhH%)1{;^KA
z&Q at ij+cQ`;`_28clw_M)Lm5uB)*-BlvB{12hWB8?&O>8-{bQvA&XO8(-h-+T!|Qr9
z2OGa>6M*Wq47K8p(m5a>uV`6tGc1$QPT6e;#NPCJPVJ5^v4pwg at 7$)t<K_O}v$B7Y
zzE<-1fa`l$qHcqd^h8`>k*b&i9;2mmaZN>alt#Poa$QJK(Hqun=krwVOyhUnd)4&&
zU^n{5X8FfvMxK8mR~;vj6oC1hgbEC|!8ZD3n9;!g^%YU{KpLj+AI6O>-li<iCGvzU
zdq9jKv&nVI21BNrUbu|z9!JQKr4%6Wn4PMsTmzs4W+UI+>tfRoSRi21mF$BrpnpC!
zz54~OZLJgk=us|^ek*+37O)yiElHAKH&vroQk%CcY3|R*>&RqkOI2T&Tmx%Cxgs#;
zZCFf&ar5KKYWFHhfiW8(Z7Qqn7S?aFRi!yhO}7MGP0hX*&n_D`Bg<>4La!W!wuP~U
z_biUT>Vd+6^JCRoB`DX{McBfN<v($+U2%iv=qhf at DQ7R6Gm`inQSM^D$GCCNqa3_*
zFZLTwVhm9+s6}p++shBd0hd%>8<U2X1hvnhf*f7rzm05pV60Jbht!SGKNOp at l7@S@
z7>ILZ84abDT<K!94r4QTPbJN5_mU=3tbg@%`0=ksPD&;zeGQ8t8#giJ$^llbRLvE5
zuor+}G62D3YS-u5I)<Lkgf%=TlRNO}cFL^$Ad)!pF;~E9<`XEa3nt-Kfl0hI)P9`C
z8!Zd7a;2*(I;3p8RX*34)+s?OVkhrXRZFY1Nq5<ikBeG&7N*%GrjhSJ9QSHI?tghy
zANQ5oxOZKrk9+r--s7GdG48Fs$Nkm-wEGBJBKtqY(c)xUE(Pn;2S1k`|A1>89>C2@
zENeu)ovrfbK`rHnVyIpt)qCt&+5!K7TC934?^7+F^m0B|Fq;0iaiW4~verSf72{b_
z at F7NGk{qs*r9324L=BsZX|G(VW%5an at xP6e&OW#k#**IgjN%n5*YP#5!syACcZq at 2
z<V$ho43sWXYf*2`dlFYpqmiqe at oM-RYYou4%W5n&+#Sh()#o0WE9v};6rdwo{ptJE
z??;4nG{%Yi69pe)ol=Uhj+uD_6pPmP9PM?se~R9v3tjvMi>o?pW|PklMtV}K)Lif(
z_So|~t>+K&>OJ>pRo>C7MVIjwjYc=_9Cc;#<xR^O*R`5HU*^$1<)v6*=rhnkd!2vj
zx%byfZGZLug72?teSbY1-d`No>Pxxx;j@<Vh+KUsUyY%ao-aDzQjQ${B~{#{{*tBP
zh5UvgY5@)ZCv|*K0ZpjqK5b9~6OAyWMj*D7ao!DBWbjP!ZorW<U?j4L8rXYO!;QZ+
zbcI#iP%VU%Xi6fN*SQAmz-LGmy^%$Zi^{*m;R(n749T50O;S}<$Vz?WB!zo02`|_B
zCk>`Wp{7fI*hgd2Vm-0kAz|1DESLE(d6G(^9;hPpZU%dN5~;#dMvb>5)8Kt7!{8r3
zSSloCV*jN6 at IB8dP4ik!?*m`y9GiwDpCn)cprm;CZbIaDMcD+$36MI~W!<)gab?~2
zEH;+~A1Lb{IRAY(QoifeuH$!5xAZmvDK$Le<BezZkH1iPb{Bu+QycJ8Tf#V03pl|Q
z)qe!GmQ{~H*5fsr;EE1UO8I!A6oTIp$$pDf^yi5fy#eO)qOyF0l<o?^Qy_KXV54Cj
z_XFFBgKDDo0Msmmnl8%x9RZ(`kOY_qXhAV!$_VG8_o+04Q1LQ-B=y$|QSMT=&WhE2
zOo7#;xo6jKE!J=krZ!+|A_?t)R2hk+$~EkR+i at PbnFXIrm_(?3Oc$DA7cJEWZXh;P
z>DMrp`l+T*Sc>FOCQwAwCf}>H)VVHh=QrXYRlKZAx~J at BgER#Qkb{Gb#F)RcnOZpp
zvNma1&rz0Vm^@x5h$d<TQO;718ZEd37G_OUEcn8RO+*xpC!%O#FQVw=GyH`l?S%@}
zlcuI0u0-W6R6xg=KIJMqgK;6hr*x3g`|wy^y<JUjs^k@*M?86arnY*<Yt?>?odedT
zzp#32W~tu+?~KhV^)HHrbPTDwqjmI}{${k+cNjcZy8>~)41x)_8n|nP#ct~UwP)F)
zT%V|_Ti}DXwFv6Q*m{JW4ptCK7!AF3qYz*np1&MOP5+V?!b?fSFuqJ|r at Y+}u*I`M
z_iETIWRO&ZBtkLwQbcZAf6M;M>FqMly%bHr`j;-NGLDK(@>~Vvh*NEZJkQT=r0#<z
z&rmeq5qNnDFZUc3fubB#jfPpv04PSfQIw6M1t9JfQBBxlPcnO4<cSWFX&Ca;mVwC(
z&((0cW}-RUR;RaZsP+-*Poeh*k?j~1{|v<miWeXC|8h!SQD_u}Cec#EzpnY=i+g`Y
zZ+>S#I!^Ep27YHM@;g(J-<gX1PLpzp#_#ZWve8TraLX~s?<g1BgjJ0;9qwR)2yi<O
z0h{9)>QrkSRYnKE?Za``7 at mSv8pGq1Ym^}Qk(?6YX&&_}<HOAqZ8GpQ$6RudZMlLR
zZ4?Y?cnP;lmlp3VkUI1t^y$&pXV4t`2)>tS2c~xn)SUZF^BK64vM0HyTTTD0f*`&m
zh;5=jZthE1$w;48{wH4ktX_T(mG=*oKgG*kdifYC|JAFN-&=vosI5>?@DjIOzZAgZ
zIJla=B?Om;_4wmb07yaMu<0f4WFzk6i-^&NoO;nn_M{KGEC-}%^vX(VL+U{+k=xbu
z<v#A*1gPag{Lqc+Zu}Xp3DMD-2hl@*NIG`{YO$$bp(%6+>?5NmURZY|U9qA+J^I>q
zX(Z6Ke8{9L)n-kbbSMB!)d2db=~rq1h>$sqoI2t~90WltNwbq`DVYxe2;Y5ZpbV=Z
z?-N=D6C?clM9TxvhEz&KdBBiTyT32{cY0VL&=?mM2$b3j`bvouRsvZ5qsa2dmpc4?
zm8~cf5LO#X$M|E7PT+G&D>S)45P{MVh&_`Y5)7;W#^e1hzhTds2>{n%%Pz#0U4Vsg
zrT&2mBbf!HSV2S+hsB-KqIED at v<j<ZO2_(R`#CWr1~lp##1=s%8?9P})v*_q+WaOb
zT1HnyTDBf03tA}Srmc^LOk0yn{Znu6A=}nv*ou$)Yh4O at RNb!c{Nw8DA8z!V*l?r&
zo6!?PpHgaJa2m+wp`vL)rnft0ajLi5<QX8xH7|iBeTf{`2*3W+T72rqg#Nl&;b~LI
z&1-g at KXu=9^Z<Dd`^t%VM<Tqras_Herx1FT-_TF5 at 1G{kt9(#%<|-X at mg5P&WPU5{
z38%b+ZT=JpY`h<|$>Y<YhH+xfDH!>orQR;v+$8vW&-~a@^xnTZxzyWDS>2RXlZhtl
zqPk5=q5&zj)JXNh>Ly`zbxw^kj#TO!=*ZHQ at 7eG>iLkzc4b)p{&7Ykm3ckrIj~QXr
z9QTudV{dg26>~*qYlI?makS%P9B&d^?XVZ5Zuc1to&|h7KD<C5k2hk@^0KdwhbyNA
zBeq;{cDIwx?sjbqF2!)SH-@^cxv%^(J7`DFt+&r}O&a84jaHyRDezN31iHPQeK_&|
zq3+G&nmV??@r1C1&5H|)dsI*>#iFPMqb*kxO===2SkY<~As~uC;YPp(v4K9|HmzNs
zU0&;Z-E3cLt##L0zy(pPXszN>_v%g4x)*TE{hl*(69VFU at Av)u{`md)e3-eJea_6B
zIp at sGIWkG*8aGsDeO^u!b?g at mgfByt5H<FYN63S&a;hljQH|j4d_L9-Ib-qZvursc
zN}{#MY&j%C at L)+-wFaL`kYdH)aYZso4_E<ER-~OmqZWIE2M!RNY~U4;f#?)i=F%zP
zUqlFx(d34D)KOv37;Jn&AEM at nRBC>WwpNE@=Y02z<BCuTKwa!;vZ?bHhsf`>AAeN8
zc#A$`=*zNYa#X6o@(=K>b8oz!z8zW$1LsnxlKd=Ds50Jz*`k1<GGB66sS>Jns#LkR
zlx|Bqq11s;Dk@<>i`WMR6%>$WaZ{>7jddLou4-ILRSH^^s{G?V8LlZ#r7G55<B39t
zjZpBRrBu>4ax==P!*;<>L;0W at uWg?{PN|BUKH`=^C#oNWUZR6&R*8jDgiD)9i6h|K
z*6488@!d+-kr;e&DSbFfrkNovrZ3kCMP6w!A?7CM$F=Yp_|l~a*vpmUT^c1iQhn^$
zOT}8`Ezc4S5r1Eb at Yvn1f*xpx-8W5L&2_{LiP<8 at A-E0!W>3^KMRmbG^7dDHe=yhG
z-p3L06uLp^_nm|2&gm9CySbH-55Rx#$tce?SopC+3;uIB9%m6GlUf8>d7rHQ13KO)
zSPGRgb7v#IXx%Lt2w?zS*&}(@lb;fRZNaxT`1ZoU|HN*UlSj0$00?GFnntGoDnMC}
z7zM%8hq*0pYnG~ve9B-we`z+6#n#&HJQi0>6hbw05CBsMWgA`Zz1-2nSoS}miR%v>
z?V*X*I3gCa#UqaRqrizHs6}MJ6Yi#|9)mNAmP$B-DS3mek;8-v7Iv`|Z^UDpm$(<y
zE)IEmDFNa{^12CdNDaXug~8NT9l*n1xU<0_N{w|P7N!(c*R<hiVkvTo7pgZ9gmSUY
zO+S>RWdV<qoNv- at 0&$tg2*m-;hu^S`&(<;D-lg)?Qc=ffno;?fcs at S_7GGYGTxN||
z!X76E|5!-qJadF)>|1C%kn4^->B*5~Tn^%6qYX6*EYtoK{_v(hS}Blfqpu=LB>Owk
zaI}dNp^3Nk>Pl$hul{aL9II!(ZNQEwg^?P%i=f_ypM+1YIh?Dvt4 at 7CucI#foX#5H
zgVfJ%VeBUuyRUuS*p|BvbD*%;-)Tb1J`O0Eq3XcPAIoKV19d62K*0a;G0u}J%Z0wW
zs%9_U!dd{lXEn1+qf}bX$J#pyPrI at Uq4yo4Q==PM^E&8hJaAaCu;z8D1nlu(H(d+C
z!aYCNBEEXhL2L{xzz2{z;m&q^Y8M|#?@i6z=@3^5=Si3 at QdpRPcCv)}?MTub#$ygX
z5f8~BYR at z@*C}k0vI8pa1SH2Q;nP;-RqQ11x1gnSoVpoW+DTO6Sz|+loe8ezwU14+
zzHOS;eU{P%!6bjhmGW_AE&HF#n(9_oaP9MQqAl-$To)6(r&KOu-mYvqsq0y*8*o5Z
z9=M^D(3gDRKc#fFkS_2;nvUvE0jk(QER0H6S&y^p7QszN0aX%3;;0wBUq?W}6_f<!
z6c%4mBC8e$3z5EV`akZHN7bo1ej46fI4{Kg8wYCkdDgPDi;OAVKqM~O=xz8S9_XsY
zL;*`DMAd;ri4o3g%NmC$oKoWa7=lA~SV at gI@vU(#ia&|m3E#S*EjK7B618DGA^Zb!
zSm*SI2Jy`fF*bU6GgD$B<fkjQ4Q7j2<}EE&oElgKQxZ&)x%Lv97ZRza!7`woJ#|(s
z!DG=9g0pI)oyIsRisKN)B`%iaxyuJbkE4jnt<&5;%N6#2h{4x6qJEYs?McEP>HFV4
zhQ6QmkvI+Lz=0;Z!!aeFI$F2vZDOwAk7&tyL4?zKf!m%-UrzIPnJuSai8NS#67_t2
zd5%ih`lZAY7*j0dDLG2Z5hoXDm at NlgRvAtb@|4kVp#{aTG|6+r76P77>4;%CqAv<<
zo3z>EphB#gzY8JpLbobWY%!b?Z%C<>DidCMNR=eV(#qm#q0&Ak+0Gpo5(_vTT3}fc
zBr|)lqRbv9KBp`Q)bSo=N}cGzY_75^(Iji<WMZ<w?CGnpdx#3>xozpuB13z0lO7N3
zZt$mKD{NH7Pi(~@@t{zO1OKeZd6u4DarJTZ&*SJ<DD)&2ifVoY##<B{Cxpamcoj at w
zOA$2RQ?9nh35{OFl%M^n3Zsnd^vA`+LLg2Iaj!)!n!5?dFh%qbb4MEe-Lt|qv?wC3
zI3#Q%QF!U#TQ6c9qiq|}v?8 at YEa_}(V at Eg=C(Oz}h{<T)t7xjzg7AJhC00+O5G4l<
z2F*vS_b_veFp!$iR1GhHmqg5YqbVUYJ2VUa6NTh29HJH#*`GE)DTH{rFud1a7=Bt4
zJ$p5w)K*C|2x<CCA2gl*lh`;uB-UUJe4cTL)NUBDZ8Z}K{NSn-xr|ls;|8Pgi2*Qi
zAYlZ9mtOX>mh*klwzP3!Ws*@*Qmd*^prT2>8j|=y61FimdWHE3%-sibch{xV>-cKR
zMQK+%nxt2}U4^+C$3+)&PYTJNu4YfZ(|)sAexJ?P2UWfu#qDQ4Zs9eWivBErO9SAF
zMMtNFCX`QGqb<42l+>abPk#1#b_X7R=)`IQ)zU_$Y>hWw2GWF{ACr{dNzd%sGmhE(
zs`7l3hs<(;u`5{qfG(v9HuXw<N~KP*e*&|)#8=krA!DlqPlWwk*3ztGO3*AT%kS4B
zd8UJwiM340OG at f@m;44Pe7EGUl>7{D;+OhjQ=kQw=2YhOJUqoK$wDnG%`8)b7vwF?
z<Czi^Lt2`(OvykJGLb3iO+FGh&eEL3lpH6&W^&yu&B;tjAo-QTlzfd6ns84Phx_P}
zc|t*uj)#>4sBs|kBUHToH~+j&@72IGLX3)6JZy~$RI{BJnrQoj-0<t&aUSKdZbplu
z?H9a5*t9oNiSM!@|DXPPzAvkG at HNCT5u>TXnjl4x68qvy&TLLQD*#%r_km<mob96Q
zk&;?+a)Y$Ymbhk}by_8&Eu9f)tI~U at Q1&PuctUxzh`gO)D<(F>FFXgAm1!aI>`GfS
zv=PDz$R)>OSc{fJu^7Sfw at XE)ypIQo)_5a$W)WbUm{%1F{75f4YH7}3N`5C0&!V=^
zEX~<W$=}IG4wqtS&SOe^5KlFmCA$L61x)E~C}`^-Y*W0rpGXW*R_+00*9~3xLEVwN
zb`@2S+Pi8l0f604+>cj{d?XDV;Rj*m>ugKWNFd%zMvySX-W)D}dRJLn@@L^J&nf$W
z^2a-lx5_l3Q|xgp(fEpSv08t-U9IPzMNkR_O%lMEk`ErhRBha=;=IzzpSF9HKkbB|
zZ5j?J0EixJz``;REjF4`!R&+cBOuS$NT-i|C>bCur3<*RoR5+DabmN*8w?eAK_txE
zm;&FPT7F_P`5g$<OrcWW)PUVlVyS+l)Ley at uPhY{eTW4EyFY9Bt%+6NXPLPBfJakN
zNr&|x7>D|%YX=+v*Z}D%9xkn2;e`ZJUo at cw>$4U(3}Bb6<0381F-0DVePyUf)P9U0
zV{0jvfaZ$BQP(U=`?aNZrUY$;LK(ir>PUKi1zALh+^2}N-N%FFAh!5Hx_La#64JaF
zscBf$N)e6r6b)QiL+pg492V9`AvdO7qa(^c`3AnpuEeaLkdr6~3G6yYE+fEWc0JIg
zG(zI_Vz7P at xp7#m;SUoH8NgK)82EjtBm+J*z;8%T_}7c|EdOxjRiwMPK2eo1P`0@%
z6no-Dl!nVmIn#=X44gE)2hd{}|1F8Xkm(j*td1}%$ylKNU0BsUftx~L!9+p$XkrpW
zXECMELf6Rn4a3n==K0{1i-2Kj+$-mT at JG1Q^n}6Q5kfsUl0;<}U<Wj4h#oGKB5J}@
zq0sQFkR-s|iD(2h5RLxdsf6=Qco`oBRfIC(_W at C&Y9<p^$%TOH**Zt0u&zlmUW_nE
z<X$Wli)QNh+d$ED&{^N9Bs7(AExQ$$ws;urS_^TF(jqVq<fpBLJ)^GysHq&OUMqRr
z^KWUPh~`c9zAWFU0Wu}l-rZR@(O at dPLK7jZN*fFMVe`12EoIRz1va^$30oJL5e&)_
z-uZ;=(YQAeQ{A|MY0zvH-JD?C{MpAL!fu8;S84oRG?Y at w`N2Np!<3!{m}sJ>k?qj9
z%4rc5s5L864S3iw;RAXxL8TxU6Og)aH at FOwLeGr_cRn{1+)OSI+<fyYf_3fX#iKLL
zfv`W~&j4FK?rN4pLAEUVp|T+nU*SRU(U?)@F2zgaBg`GhwKfd0;WjuXii8h$h5yCa
zhGKXsY+C38PVIv}VA8JW1M2L6egRYBjXvyR4hK?HiJ^0Gva-zPI<gB1>s}GXs78QB
zI6^H4Pnu%ciaKHDUr4?p94q=eH+Qn{cOyq|?n$5e%QY&tmG~f|_`wjfilt~0*EDVm
zD=>n0;Mi+yx$wgp at FVWb)@8bSOk;bIB)EsI(%7ari|NTy?1;n}VA}F%^5El;XH$cn
zLH%0RfMQaL21<dSwh)?8#j)THfb~`UAe`$DZUomE+(@Rh at gXTeY5S9Yc-k)054r6$
zexQL?+YuV%VZ)0T*rWn<W}%aWjzP=+?(7ZS%|C{O!d>9{B|7WM at 5rQ?SKHogB(@?5
zjlL{2NK-K*h~-b>RnS_t_<oM0sHnIZzVQ7A0FmlHD!!k``2wv39|>j;^qWaQHGE(|
zbt%v{l`?AS^aS|dWkAwYE=@D~B&UYYY;GjDX6`9)3%Do1P2vVX=2Wh?W^@%`fa2zC
zt`oRsE&$vD&Kuk$^FmUJ3QOrpN(m;VFr*Y(|NSJapA at CSqI!~|f=N*fDasd%;(*jU
zs;=hnMxc at 9^<DU?sNKx=^Fg`Sq7F7zsmxc|m>Bz$>iy=PTahZk&X%%Wn0Km58=xNM
zol5n7QU+gb4 at CRGqhXtSyP8Goa|ljY>OrpiLH`eUshWiUZ}C#QfuC=em--4K{xdIC
zhAG<Tr52GW<fT&J>%Z|*7P%7dCh)P%yW8Wd*4!a{)!YEWR}IuU`Kly;8LEayzRFmM
zdd|zI3Ao+NC7jjG^5yF_{B at Z2@wmjxm#-2!zYpw-<uI)DCSCdRwa8V43)M}oW%KP-
zTsF%>3+Ullj)aa`zQz_8g!gwsVwuu^Nt~3Qk#JJ^&m$*Q(~6V2B>%6RRHfXFldAb2
zIjL?fP4#p(l_{Am9Rub#<roG at 0r_FzkOaO)!=EEO<?^kp<-XjkM8%Pd7$9wbLNi*4
zM!rs45gKYt*^NSq<qcZ?ga*Q6%a<2}rvt$4FE_6-lxtTL41BButQE9lEi)<(8p at 4j
zfC0*0=+MG+_0(AdFUkM_fEnFbFIYl$Z*W=|FkMPDFkm{X*G5PUY?vI`FyO+<wHsOW
zZhAq3EshC;I*|}}h~^m@>jZz;LDr&r-mDKCbn`EYfbDo~adfCT)DSamwYdvy{rCnK
z>e at Qj7vG~=9U at Ub2>r14F!0*d2Hsdkc*|*PXkS?MZSza81D9dx>{cX~g+Q|L8rNb5
z4c|-?8mxM`HvB%>?~P>$96U}QrG;Il>|~%!RvZ(`l-36#-7Tgv)_s9Qmtw3&4NPBL
z4IT&DeF=m4HO3e>;xo8c11R4(Px8Gbzl%ThML+5%Nd7y%l)u~);KpCpp;FJ1G*5}Y
zoaZ6&m&@c5e;Mx~@s|_HM*>qaoJ7Iy%qu95nLv2Vr#vJcGmh*Cm!bU{%knjHb09*3
zSn3!TKr#OfB_$<+sq!IQ&2LJ^vJ*amUQCLx{QzI3jb>jx at 2$7&Q`*A-C at IX}zJuKQ
zlp2@=v81#jA}}dAL at WmK^sY at QS!I-+UFKqEP48*SXRkt57NsM$IU-6H&~Uh>?9)R;
zr$+hLhdjZzX&`Dl#%TFN+KN3$r~TE8MBZg=_Y)8+)ZTWjrp=cCNkylI7TAUaU?J01
zk at 7_%Ang6eRLUgRK{or9)_ko6sE-p{DSKQGE35PaIWh))u)nr_^0sWSlLgL4MZ>^b
zbs%ujP`JG3?+zDpf1S1D5K%P}6J++U!R}F$#N<rXHh?U93N;{-l`wN}Aofe73Q&dN
zcB#9Z&c*9IgVnOkmR7^j- at o;6_$pe4gV_R{_YG8 at mWb;3t+Q_s%0*KiVk;*k-RwFi
z7Y<8^DP7 at 7jN~$<i#^E_fZ})ixr%q|5K|ffDQaZO_6E=HD>3dx#3F8b(Z_^wFPBS<
zdtbT4xXZ~05HC9js}9V2rbA-h=gKAKeKeFon0Fgm5uj~)%C8lqySg{5<SWQvt0V%{
z52ZQT(>ZRaE)S<5^7_BV<Fwdq4`joCPJ^5%6sYtlJ!-zL1D5nivu*1`BB{o-UrK5H
zA+07li1X1ac0<aW;k#{bLaG*_0HRzf7|^bF$px5l@)+AX<ra|-Bt-(z7hk3conga^
zhAkojGZ}e}zhGZTm)$e&<2>58^*H;uO&}c~EreZlu8)%Q4`5oup5YWL_}D$l=_7lT
zP};8DCj at GXTp=EpCzD5R at EYl2w^`**Xjds)LEF at 3UgLvQn;L2)hVE>W9uPYCw}3oD
zX(RU?Euu*IAD4y~W^Beg(&S6|prvO9US{*49IpyctxV#5r$AE!jm(d<truJ~Be-=%
z-CNBJFC@(QpKWXJyOj^f$@WhfL3DvyX=&M3xeDCl<6a>5t=p27*)vI7M&Mu>Q|O|c
zL9<zXS`QZIHduxfnSD6s(YD5`Guq^db;l6AtR0yR8Ma(Esm(5&K&4`#7t7zb`=a1R
z?O@^@8h-hDDz>@aN^BF)A7;bvJLNX2cI7q-R#8)?(1)M4ABAFIzc0N`1vjut^orAn
zXf>tRn36Z|k~_Ura8n9DAd5y0hzBJ%X5^X)Ca1Si0G$uK?V7H`?%^|K?P}XP7=E$_
z5L59tpd)@LKtz#&U2hH~0u&apna)01-OR)_V>2Y7&l4>|pB?>(I6zmfZD=F+(P2_e
z%(ON59-06j5}miJ=m0c$wV*oJ!h#gW^Ey88nn5m;WJF@{6EDcfnay06fXG<aA|KMV
z$cN6ik`Gz%%*fmgO518c at z2GX^j*Z<7Wt40VIrD)L7G=dnq95Z*cA}}h at j|Je^Dem
zUJw=0P&rVNOAZw8CI?EQa-et(Zz|UD=1P<TCAN|R8C+5z93v8feNCcLptReMN`VY6
zDbSr;L<*D`RT-mQ)m8`;?@xq4^{s?J&s`>01_tue>Zt at sTPam(#q>hsE#yheJF%_y
zzUW%^V`mNauFk5d5a`e?iu6h=3rN$rHl3Mqi%d8m3CZOFU*W?y at EVb+0eKnQvWJBC
z*K^MiS&W*<V!EI#hAHi-pxE8t)=@!d+Dr`|vI7EA9HfF7hX;|=2HF0iA1d2b{HTy`
zBLZ80+n7=2&TMg^e1thbZ>jQt5ZhizF(%3!jKRar9Wa;*XTHV=V6?|%aNWlMYdprx
zbsST`1&&GLe1W#vXhRAQ>kYE4#INE9LzvQ6C<+4}n{FDK7D^bYZP}_8Vi;*)iEIwv
zjbt#vHsm4AYWKO%N0V;i8bcvW52Py(BhR6;bs*b)$i5P?@egtB{HXZ3wQNRb`xUV_
zmCfjE^>Sx}56}h&usk86=wqW+Z8p6ApWAGng}loE4~c5;;o+p(sctoBI;*bMFmd&|
z)r9y7Am>nSR19n=Pm2UGvFnGh)Nb|b*MNFR#|@%2ER+-$#$urO%Nepa9`bBD2>~{w
zVt8$_Q<`LeenaRL!u~u^aatlw8cBpngNQIGgf@;yl=hIF=8)31gMN70zCmih-%d>1
zQGwD2G|I#Fri=^$OadZ+)4)eDe#C3}-zl><ZIwL-LE{DF9G7NP?>2h@=f%w;7=#M$
zrmYeha|x~<tmCIuq9o3MlDM1K$Z>j^rb0zcq?8ecaW}!&Zs}?fLdJVk+KZeR>5aCc
zA8%fR>yMwdkw{A4y$LId3|W+jmyRv>e3;UIP-acT&)#aDtOW*MZ{TZ;DF?KrM+=?-
z6hUD9T(elqb%+08`0oh+f$;AO|6Z269_HDG^5J^1k|1wOb%;@2RdmSi5k(0eaW{~6
z$S8X(hm~&<P+VZ!a|76g_piFxg}<F}ptj%w>G(dU<?z)$M96JPH|K=Jq}z|TBUaGy
zR+M0%jCWosmGQ0*ATnN*bhyiSrTd8?_^@1hLZ#BB+_i`fbCBRc>cdMzHUth#uQFtw
z)MGQ2%4K=WQFAljq^ojx8LVLoAjop5uWsS#Bt7qWhuLKaQCcp at x#|32VM&-MuENkp
zO^8y?g+tKoQ1bX*46c|;?jZY#!Nmyx-v-nd4vbl_ at YxlISRNu2U2(R-a!`bf$HRzS
zkIsV06(LML?hsvOz=-4rU(dMN{7gZ`-6-mf24q|iN*>sW=8i%?R~cw6+&&#<OEgZV
zF}QbP>pq-H=+bCR-dPO(bt*jpLK*?G+H1*U#@#w=@WLf%=e^krZ(#AB)p!G|I8~{9
zh=UmYsBOX^C%m2S=vn>-ikT?nLxi=z5eb=4{F`e8-XtSXuvk6TUI at _70_Yp~>-wsP
z0R54F6uO{4iTAwbf<9h+djRa*Dh#F2 at 1((}562+lG~q!T?A?U!u4;s#v}PTJ_Ixr-
zyY0P%u2PGxU3MiseazvqvhHSe4q;Dam!j8$IAl&vZbKWj!jB<%;59x737<gAq!4Az
z_)v_ncZMIHI_r291dvO;n1~-AWJys{)2uCab*DRJqB{N*f0rpG*PQqR!ig9nGSiHg
z%M4bpUkkuXX at IpRa=pQIaNYF$K|R0Q?Sh<M5m1S$x(+n5(JRb at NZOtxZLl8A5$tXy
zEbCH91Z91MYnNPW at bu5FX(x>?u<YEQjpIY)<^XVuxeMTWm{05Q{hgV-5WGZSuF$E+
z2blvbOO-P74+NtrbfP~B0U?eHL at NmXG}!)8Y+1B8UTH}Qxeid0$1<C1>{h4TJ<+Ay
z0Dys4_KrHQ>`iQG<_OwMBv1|r32!^l{LRlWwqHB&4jwk at _}f%Zddn<*{9WL9*srK1
zBzp0TqZiLK(f!R~NV|BZY0CT2%b#v)7tz8FDmss!y<56PEL<p|x6QOMDP7D1$lWex
zvrk&NLKAIjeW94Xk^6?(%)lmmUO1LXE;^B;b=ZT?Yxwd2vgzHG7e%t_o6Ew{Y$<f0
z^fW*NUp at Fz?$W71{`zE^5dhUmmqN{RsBh#B)I_JtITf|nfsJ5FMzy(zhG$a39~T_b
zZMR2Sig#o@$n}mr^s*Uk1F+Y4Gqs!=FE~${!RtK5VAEtO>_Q{-_RpyvI9oA{OB&JK
zLLSEj7K}#)CwCp at 03kV?Jh4inA)?u5D_Y&NhtZo_NK@)?H;rYt(pQmTMuM+SqDh`u
zs?EcBuE1<LxebL_!pPo$9+)i$iTz+^O9KrRd*TnLS*$wJ9+HpL$ZR>4rru9fB51eb
zjd}Q$htI at 5W#uyR`odFZAGnQ5HOYx-VrZm>uM&fI8zgfNUd$GVV7632)dQ-W_1NAL
z$(?x{Pkk$^+D(p^$W?o33g!TsBo`Jxdf6ea6tVui*+2)Jw`|9ulNSN?tvyel(;pUo
zLyH6uiLcjQjwOO8&jQPNIX(n~5WE7yZ1#+{;gQoL*VFkL+cq-IwC62bY2o1-{wmHx
zw(5ZrsTM)AfKV)*HD#ut*s|pv_h~y(V@>uDq4J!AzHg=z2BL%ZX*-hHSA%cd2!M=1
zJnP94IFbvCR;>ymBNYS_!kmF+uHvV3AzhyH4?6j$lA9!QX0uks`7)c0{i at C@{j1u&
z>z<?Mmxj3Aakq>eI|ZsTcd)WQTAI7(D=bwid;)OP_NK#WLoNvKiG?8hsB}Lu0r0Q<
zqnMCQ?51KCUj>}1(F&*}OJhx!moIOC9wzC^m*3Eq9O9Ja%bVFPL3STaxv5!GfqI}C
zgn>iplppMa{4|tRdcz#lv`-f6BQtx&Xd5_%*6$(z6HBHc>ME^d2cY347#!{eptD%`
zj~2?#0&x&+ at fU>`;mdiW^|_xM#G>7Yq1bt#cCe}L)Q*O~VecN>xKGI`p-LH4`6s&z
z#|3y-{s!8UC^zq6f55 at B*n+qSbOxZ)9UaDUH`ZLDRo~!GFmG1_-^w4c1CQ{F-3Pch
zy9a-Q-?t6Ql$B50KcI?^CpUprS8_39$xHenV^N^Y^%;wDDelR!C|lwNkCg$($#qay
zalLJ!gyjFt-kIHr(k8n<yAuUh*t!eburuYrp_bm at x>k671rxWizQ1<<+F~oalF{sk
zDCk)%*Ol3B>jA|1wIIs at PiP8+W3QO)R|4|9S$`>=3b5^VTTi<md<Stp at ZAd{gf+c@
zTUQ{p(@zNf)d}N!XAtAzC=1u&lb6|6)BCNl8eY2}rdxasc{amq>v6FXV;C^41(rXq
zq1I79)Z<(R)CWZ~rMwLFaNyAL`%o2!c|5^au_ZUS7%0P8D0HH+Z at UFzhs5BCZiSjh
z>r(uI5W1Ap_98sco%@g!bBMuxAOnzY3!x9fK<r4g?R`(_!RrP+p+hIpVm|y&#hlP#
zF?@s2{|w;*&x^rlAP6{yFNMRW5mbRVXdklby=HH=_%Jz_QJ6j9Q;C>}_ZL<pDMV at -
z?y89*ZLCl0*NoL+wVJYJN+qOhw=vPhY(-6|u1tH3DSep;YJnI26nV{(zc`Nf*YbNU
z2Sg!!Fv@)E5K7&J$<V-+(P*HYGeARLe*rkA>qrUU=~pFf11#c7p}Q{uNY~=!l|9Ts
zx|C^kkUTaFMc*Q~7YfH0EyCeJG+-mmNm|e!TCg1#^fp;6lyS=($ZH!0RGU5hZ`wWl
zZ@`#W!)zZv7S*M4PmGlT8sz%JFAuUBV!`VNzdU8c3caVy+*?zoO|o~}CPytjbi(*)
z$`&QrJrUc`h0K(EO(d~3FeR7oDPLa4lzvQ@`x-LQ22_p3e$iWwV+k_JQCBggX#Y)%
zS^;wLL;c(0AlCB0b8!T?UQ8(p?U-H0B*6H1IEMOS at NjGb_%+%((E(I>2V at En&;Sem
zHVWk{xjH9smfWVC22=8;6T)@uR<ghD at F8$xN(%5|hk7s96Q at Y>%H?&}cl;<b2WqUb
z4(zrK74tA#v~^=0u<x3Cz!;pF0YpgpNf+AV at 7s=HudqGlzNBtc<2`bqR6|!hO#yHb
z%k$2rb)&KNvCw<20D8{6(urK$dEm+X%|TjAJGJ>1=@e6Pn+B5H|GXu!P|uK)2ml7Q
zn*BjRYS5?bfxNKid(~;f_XwJK<XQq#V#VVf#d)pzj}@ovo;Xg at luz;3RG5;aRZx6+
z#Us;PqXbu%(z(qMHXpE$hXfs?S5yf>mFInwnvT)2(aVpSRRqWPXn)-pluDI^eQJmz
zpwf46n<%+KW%zVK`)dh0bD|;_76P2q)E7|^?9g~^N=D3c{h%S5IYjeFF?gGXAQ|rl
zAa7H80w2L5V+IW{Qr39ixZ>0hr2__Xd@!uNJe22P&km5D-F5ctCtbOxACMnIQ9SK>
zKSm7X2w&|(+s8now|%ywv*;Q`%iJ?EEqTAg0_~}VH5@{8JxlCBSPfwZY`ly<CCJfP
zDwP^Owi&M|55{iA;&#p-Fz{7`ZbJd<2;mA|QEMi=@UqYmGA at P)X;R453&H~<h^@gX
z3xN>@fJ-ls2*$K~B4+`7MvtyQ=8_+!A{QWhavV1pTec{@?bhKdAGdd5vE=0j@)D at H
zy;P*UO^C4Yc*~YQeIIW5w%2jTX2h0POWa8dk)m{Fo2u}N77`Hnk_KXa)DVl1_-I1v
zC4Yjvzb5)#_y&j=0huFVi-y$wbSVr>BB^^N3m)wr&j>7xB-%sEMXHPuZ3{%vC5?!k
zLv4y`%g2Wp%O|N4q at E3y8Lg89J#f7c+MwZ;z;WYiM&SW2G-CRU(H`WHG>)Zk0d|1p
zt2AgPOv~3Bfb at a^BMS&6`WhZ4^ig1Qg&oe<CGFaCb=s6d-Pimri5tTQo$>h&B7}y0
zEaVZ+CaTi5jGQ?b1x-w{(%hL|%t84=P%z-Z$>heiXbu++9dk{!?R)U-yDc|Ip1?mv
z(l>JT?025b=59LipiZpOlo<SabJNMIQf9L^%WOFYLCW4tDX at H^t%k5%gUnt?ZjDD~
zvFf4{vZeXgr>Um|<pzM;Dc?IyF{O(=6u!IW_5n8}KLkR0*gG*>sv9-R-a(v_+5EP$
zH#sQV?5z{`v3#W#tKs6YW+;7+eGW886Ftz3ZP;U<0_oCNps*ovn#R8p0xQ!rYL-;V
zk}BCQ_Hg*_ni~dgNPb at k>0$4V32B{v%;pkOr`?m}R#%(5r2)H6b_V1UOYya^4R~U)
zXQk!52VU}~fOC68<+5 at Q3t}#^)Wr0=^WAC?p~)UQ-h^*&AP>e|dI~zztFMF~pJFEb
zxB&cktKnt0Iz-z^nR9rr9c__+R6lljx67QvyWSJ|b~3yZN;KVjb59|VJK^y1br2HL
z+x#N4rQZG=jx2(^&K^jH_JT6ae?R16Hg^TEL&_e#xiD#fj at f&<4bU;n;f<34W&#e8
z`iMC+t+bZ&!sNx2mG98zt(fEg3epH?Li?iVg>Xj+t=S{XA4$5DDwc1k(VmBNV)^XL
zmSzXgUB{0&#3(>_!Z$};BE7xdL;zjOlzj?cYOhXdz&%@5lLsg_41BY}8hHEkLs4HP
zdReaYoy5O-7jJCo7B(hTXoe}*%9)Z(NK0Q$FYYGf9YK657>+nc$Tr6tG5J+QP0zsR
zS`VgllsDBqaYR#*IV_spiu{Gv<4?B!A<OJK1a!h&V6eJ~wPt^QcT at dZIk(nw$zkAc
z+2=~XS0Ayn>IRk>*8r&I4^Nk>=bpmP^$w?%W`TU-d8+Na7v4tFMIF^x$H|5C-|^Tk
zuora8z&A|dOGuXt<+CDVoj>&aaeC0wsEJhSm=Zi<s4ztOvZMDiB}l_yckoP!E$iUS
zgQh}R#VNh at dAZg)w at Fi`iS*T${K?I8CV&EBpa5=4>1obb=I6?%IHeP-Axkr6iD8Q$
z%FRRF@{Gnjy&+HcmOM1WUC4 at gMmvWEh=Ma3Tr~m1B- at _pW2jE5GUPE*rk#Q1UbYOr
zA(6VC`P)mL$ozYCJoON$qKi;^Gr5lkoi1idM&c!x;)fnUli;!WQM`NojGx)3*bu4o
zw0jk&Mk?<Eou%i`8u at E_#XjJJ1cUkpW9f-h?@nYu*b1#|Ob_}fg&n<z3&b}LyY=E;
zTOAc?2s&}UK`rEU(_6Lj;%Gm!x8AbX!(L?I1sfhlL(iI>Jp<A;3A;uqHYh~uqebl$
z-&fJEw3y{*xMb*8%*ltMd#7|<kK*M?eq3Ko*#KwB722dS%`jiPTCYB9_Qz<m*FJ at x
zJsc*CL>nRlGhDz&rAdBJ>ZOK<BFET$;Ijcfp)BZ=LA^*|UJgJZT3`1nV=25m?7j#K
z(tQnoNgIBpc==p8Q+mWhBL4T2hh7I86~$&ewzhpIle96~8OuG-72Tk9xt;K_M>Vrq
zF)n(itoV6_jJwQi_8-UWDv{$aVgdc=A*W;z_HvsUN);;s(JRSy#0LB9*5FV>Rb?=*
zE%!W at b*xZ0VS=HWVKJ6_3iBhOqp_s$2770KPtz$U^?X$5coj$n?PUYMD4q>Jdqkl2
zW|5wYI=T(@ry0=Bff)Q(4B`V6d<KibL8A$wIvW~|%{>5xsdw|U&+DvS%TGKMpOYFJ
z%e+&gRa`D#7OAs+jxS)2ZpX#R1*WkvUH{{RoJ%I8sbxX}AA>4EU``H2nIWs*!v&?G
ze1EqG&O95D#PZs?K#@9dV)lTE>5&!_WcGz<discS^Vdh`W&@p at D7e9rq{(?DnoP+K
zVsK~-l{%rpT9}}~ixA11*W1V2PEs}LU0Tev1}-*DQ$C<V+u*DVr)q`vq}$IJXWp{J
z$kBEK&f*|fepJHL+5R*Pm9C~5_X0qG3L)(`Q6x#A_O4Jnsi~_yz!~O&VNfpu1?Kf!
z=H<Pp60m=}^E!Znl!A~>Z5TkD21fN?oD%hoPkjl7_r8N=G-vqT_8DSu_h{t&yZ~Ph
zIOsaci6)=qh7(IJ#w{{?^5^WEP at e@v*`9oe8XfFC1m+;wV}t{)tiNn`NNSRjT10Ix
z%-&tsqE{F<N_SHs^t9`D>9ni6upLT^It|qP8?It)D3*&lje8>=J*vkxzKDC{8)OPx
zHSkS(a_l(vHUzVl%YBUtFXPEq6D&xBiX44q0Ov*Dy)*A3-_S)!I6zJmpFH5)AMJQJ
z`Kyv1k2Q^J)Toqlt|K`g`vqdUH6uG<4;Jp#x_ZlyGU(yhBwunA*@MJG!9TYotK8-~
z8X^yx=91$vmw5|ngL_3Pl^6IC-Sxu!bWv>dS{W{vd2n6m+TISNbu4Suc?f;CI7BL$
z*M1MoAMZ62(rNhW0SDNodd+}JjenJ<+)E3A|AQ^=XS%xV1nYRYmIr)s3r%So`Mbb4
z0*%A+zp(sIQKA?;X{3xAo+k9-&oVR&+ at 9U&|7sZc-V%qq?Pxc{z_Hm5 at jn>`rq-Zo
zlf>ZdBgm0P44?_tQj5ApVXa4#1a*W<C#t5GNd~s<zC?5=?Z=}Yu at F2W%iTios5ETJ
z<17TXelH*IvJV_7*@X^TK<xt;%jgBHNCSV67<$$al{6-52UsU-SAAB?vwSi)P+3-Y
z;pG|^t)0C^I%`FB5`<?BERK=OP_^ow!8$Q4DoQ2;9+7C;v`zlbA-2_F5{n8<NPA)R
z-Gv1x%*}9+%l(6a4LZ%1EK~G0_XUzY7{gRLz6?(%#xYwwXAIbfD)R8mhD=K6M6}e2
z$VUlg%JAy=YW|3H1jg@&NkyK8)zvuzQJhh0n at Pf?w2GFr<6LPKuCznl(|WX|?dnSF
z;Y#b_o>poDyVVE2Y*(pHCT9c<g)E09T}^HBpt1?u_ighiXr2#R<xx2E;6pOoik3V?
zN<=~-PkQ@#remH-ZSzd0d7 at l-Xeaq2S}WTD^4Yp$j59E*7K3eG7$%kP*#f6)51nxG
zbiwJ=LpL}nThdm$(kfkP*FAKe4N=()TF%0Ig#0O?TRK%xG1v&pbvV0nspqHAe)C}0
z at X<?fnGeYVfVnfkeeT$UT at y+JflhHlEAdGkzQ7Ynznt%ms1A2``%}21;0)Jo)$$jN
zFu_;AayG(@Rv62N8cY8)4-tm#aV at Am8y#Z172D2HnJ^c?S{`^TI|y#*ioI-wS%$AR
zRZ*heI$O><PXl`pJ<+ku=AX#zQ6c{X!UBTqo%DP$E+LG=^H9rqC2FONt%w3z&c2KP
zn%EABEp|khy)1`3?B4JhZuX|}P``N~1VnO5b|+E>_EE*^0xqyvorED+l|7W*N$z%A
z9t4>~Fs->0rtyL_W~e4ljzocx--?Yerkp`kv0v0T-A3K2{C;ef7TOiZ^7}0pJUQQ3
zX4i4c?~(Q%?8@^v!MUfb6F$bkF?dAyyUJ2s%c_s%b+;T+>eY91!^}Q>wO(DlxR>RS
zCqUV(0s=X<ajz0J&Nk{9?XOXyJx9+U)G^;4=I`kgccq at cqT3}(cI9L7mSz~Za`Oa!
zFYw|VqeICo{uS%gck_J#nLnvhI~ETT7FXlYp`sN`&w1-Chedl(Y{lMCkH!YnW!!X5
zr*M$r)|dB*#Ve%>2x}Kv)wmm<0zXgJ%J`HdYlEE-jgM8wcy$;dmk|q@{1LOS&KkJ5
zj~sRO)(VRrI`z0fdU6J@=c{NgoQgm_O?ucOi^DWytD<mvIg_Bc6>$nFwy(3;uC7d&
zr%jR0EW(WkuA-$geWF>AJYN+)-sm!6*E0qJxL{#Km4lEQ{=yLW!Y!y=spDNo{B;KY
z2wvq-Ynjb{s7Y5k9`<A{RVoMqw%6^zm}p$oqKrkOuI1`9;kB%!M(#h!9EoynNdU!^
z5FwEyefoJGHVy?$)=JkJU9$dh8slumP63mC<pZKLu7;e^7j%)C`x&7DRCa}raYzA)
zZqgn5L|wRp*&OgRxlQuXIiOHBcffDHL0EMTA0h73i*=k5keR2tf%_%A;gMr?p!z(V
zD5_n79MRh~_LpIJ&OVB2zTEVqt6%^6*6~=)mqcA9(CjGmC*R*VTHe_@^^vjsO(Wj`
z3mLF*=}~itko04VNL=$R5s9O^I2_MQ`#i`y3ck9j0kusvoz#g{2B6Rag#MU|Xi41<
zqwOG{>fQO at Mb#SlY9e#k=MiDFj+0v_oohO0<o8D%<!hQ6`DV*K-^E_V)zj^Y;_67Y
zEQsipCKQ5ReK7YVIvpS{(80Tad6<s}OZ!l2u%E4vvlU9VOvaY^>WMOHqk4a?&1DnO
zP4yX-cU$iInLCu$lFRmF+S|RT3Z>s2?rS8flB85ZN>AuY6VZrN<7mD1NWKj%`5psP
zofD{vT`fSp{tW@@y8jN;LqE8HYMtPM=>{iEJGjAACjk{O5>nHTgR9O7*k4)zn+~<u
zQva_2>jp1hT`PdKhAD;D at s_a1>HpNHkT<`-YxoO*5sH8^`B at LBZ+;*W6b-r*4ZQ?5
zx3sbd7EwJeOhX`w at i-OqtQS(~8vY<&mj6W};(=J7`2;|)TX+LGF16|$jIN5%Ii;r=
zP1S$U&Ixo20kgOtb?5s^T2jRQ;q}&WBJf7;@><Iu^N~FQ at OXv$I$5H@{10mgm-*RR
z!h&kbwZItJ%BMk4m7(0EWXaQoQn6?}e*$nb8-4=#kmwN3SFfnSCtCawzAC(0(adkc
zd+Vi*MNg0mmuz at VbPr5)0viNL_A74MJ3&_U-Q~xC>_EGx>YK~{((s$`uu2PCs*j!r
z9xunUROIk!Z7HhNR6ERmM9yKGjtBkfrXnRiB7Tni5R3c>yliUnP-R*WVVPDuL0F~_
zpvg~ImIry{o{pZ~RcF<9<&H)t$+<&pg;6$Ind>!LCtv(2J~WFCv0sLEN07w-bS7Sn
ziQi%?0zx6NFC=DO$wQ$!&2V1BA2#xP$z40B71oyXx?S-gd{;!YQKG^&y^Aj_N~4^l
zzNv|DlG7(y4bQqb$w((Bslew!$U<((BrK%Cni?8ORQd12v-29yP>GRjn(L6P5;_ at 4
zy=4zDk_P?~N&a^{2&b)-(^leZ<iQ4dMdjo`Dy{v448B$gY-c;2`F<dFM!<x--I!s0
z(_fSshAu?>cV_s{g%UFi{3_~{TUz5I^&x7mOR1=f8LlDB at O?};)!8yv2Qnp2R}2mz
z9pL)#(>#Hn)nZ5YxW_98;{9r!HLivI9XZEA>?F2?446Z&P#Fg!LmW08)iIi)TM2er
z<ITA)J~&q5gOx8(J{Xq|FPV?RXk>$<%Tae7gyO41a=DGCHuRjlGQmU7KOme!l0>-0
zyK$L0xZn$j8o}Llb&uG$Y)@Y;o^5h7AFr(TGnrFaZKtiSEn58IvC3)-+A6Enp6BC(
zVC8o)K!q*pYVS0PqJ1<UtJIO;pPcGy+v=*td$?!?Q+R8OOE6XPKPj%I7y2w?7d?S=
zhL?@%nyIy2aoyu1un8d(pyjT)5!oX_ht>;j3d7TJhzlYyjSkPx8=Y5(JzdQaZhlU$
z5XX at 7-udJT@nL}%VXib7(GcNYN^r5_al9IQ0_~#7M4GA)l2g^Dv8|A`%Z{7TMci5g
zzm|(4(KQZZ>mDwwhw$)6!v_N|K^`s>`t<+}OqU}VDsA^2t;0WAPs1;@3BU9M4L>OH
z*_J<aZt8XEg&nk2vlW at yTBKTIHB>|HH~nxRy^ePyCJ1$u?$hysd%MvKx~R3|u7mRy
zgOLu9l!iXeo}P4*`}Zgj&&npsVQ^v&QzNN(!zv!aJb(cENf>aL-oD|+qM_;V20T7~
ztxAR$eqZg6wp#I0y>+ at 0xp~xw`z_W at O|1?ShHi2l(hb;zcljQ(m!h;1hi5z9(S%B0
zRFI<s4ZmBjKBwb<(K5yw2=;lJT<(F6uBI(1>S;eM*fye$4llF6 at Z(13eK;M_j&Tg~
zbXE|%DgAKO!^8msNDNj$8|~8yXA>>4zd)jAesbQOLS6I_P<6{eUyMYm&`OuY^5IK8
zQ8b4M&$}uT781qG4Px+6r~+l<fOg6GFBGmPg<m-2EWF8qf^A!qr1P1`t5q&}wRE at M
z^&qC>M)C;4I?<oJ_(tjUD?~b-q?60?igbLFfxoJ+df;WSM$CX9%ay)(T04o?{3c^|
z86xS67<y6vJt~ym^*w!8@$UB$v1!-CqR~NTSCMQe at 0W8O=#~AdheRSBAk4?q<XU?O
z1Xn|9dv_sEDr!<or11176razc!%IiDMFx$BN1gzgriPxCE}gcm>0<hLhQ9 at C`k9*!
zdhIJnxVpjhEC=wyR1MMZi#PxVD7`PrSuveb4F0MwwQnOmo4F48>~*eZGb1Lp|7^zj
z8le^4`j~e!8n<^dIU6Vlbgt%+t3jo;1$pYR-%o%+TG;B{%*F}rzng*GQ`mFE;r?z0
z&jnc5yBQdTuENFdTmt{5??}b;U|{%7qFJ}6aDj#j+gcKS{!h1)fmUy32;2>*z@&F?
zXqJ&`dC^N+UY_Fv4vwqP$fGAPn`g<D9+m9MOUMLkm|gYkk$Wre;ay8E3493Ubl~Id
z%E9P+$T$`Ho-(s;T(Lo}EPjsnJUFg6RTjMxe#@fe_Q*EjFOzT&dkFlJD_7Wqobdq|
zFOM$9_i;Yt{<fWAn@;tt43kyLWM0?^qQpp&V*??C%^CzgHmfiA*sN~C7wcV{%iC*!
z8zwVf`M1C=Pg3p2#Bc+ZgLflx04Z_0E3qH%RRM|1<t|;;_>S$-^-Gqo1fEC<JcA|h
zNMPYYBrpXDjH at c5K^bRNpm6V7=kVE=!&J&pkDaf9sg&Gmy=Q11S?}ItE{#T;_19X~
zvo}npK(d_u=tSxuyyvQbPN19D9Q2V}u`5DY^s(C`w>C?;j@<dIB<5ezG%<2O$G|qC
z<r#G~8W61$!`|-&NH^?5;(kQjHN^cFaX%&Q=fqu0+^>lHHF4JwcRg`85O*VSHxqX&
zakmq9CvkTXx01Nk#N9*OeZ<{Q+ylg|BkoVcttakL;vOUJapE=*_Y`r at 5ce!`&lC3|
zaW5113ULM6ub$|>P25+ATR_}-#NFA0^ozJ_i2EjSONhIexbulSleh-rK119{;`S$Q
z7vg#ox4ApIzZ3TiaepFiC2=<p_fz7&OWZQzE+K9vDd9!(9Z%fq9(bUGe7KQ6Qd-M}
zs}FEqta`t>KZ>gh at D^YW3YzDm&N`q94^S=8uYZWAgH=wspxYZr{$!LWq+TMr+2z*h
za$)@iWS7Xf+X%qh6^fEdZs3^yW*?onTdcABvSkXrhzc+<#(V{tPzciTRdyfY!BmIn
zlr21>6O73HTIkJ4s-#>S)oAFK6T(_2dk815Iz;J6v6$#F^ji8YYE>e73xxb+@}Q=V
zrMas)*w7Caw3`8ibss<n(XJ7426OSnS*NI@!WjiQ>GvV0Y4(wLZrB0kDhGvX62tW;
zwiP3K!$Mk*N@%RT`}HPN-99VE>lp1x6es#4;r{GJ2f5S)xktd~IIAHtK*QUp4TU#<
z7bOObDOnF8`;dyk(@B)}R+Um~sw?GGk=I`cVry!MxP2D#Ytdao&BSPM7&O74KF`g8
zp1;=%Ik{huRDPLc=k$+Qjo9`+beb(56Z#bQBxFhJg|E;_mN=TF>mylS!7SLN!3N3H
z)nTFk;}Xvxi6MoCG&t0Uh+2dmFiflXk%~aMOcOIwHAqg))iL94l#jd?0D<Kr{UJ~|
z^%bD25I*n}go#z<fk(mTtHSq<B^T2=3){cK^KmSHPdwg&vyTIg!5F^Bps0}y<MGFY
z8GjHqC`x$sE3#?g at EsNu9%T4F5f)=;bRhShkV#6TElm0i$cZN(=}-B4$fC3j&3ljZ
z5~tW0Y)7pyG9D?#n%08c`9zXLnUgj;(xXIcoLo=<f3+&=G%|1M-bdr*vgNaMDYtdJ
z*Sc6F`^VgnaT9fXmm7`aLM+@^<b_@!98>UX7|#dXYGfgzyUtQLSjKe{f|6kyr~&pX
z6p}0?NxC4l^(y2NciUG}TL at W~oo_VT+6{3#<Ar60S9%Shz#7)Wa$i1)vb}?Zr at wS<
zp`q{5r{mFWPrmTx7hZ9XDNvgGA}x1N5N12m&TDmMm&k1P=Z}nQ)W~~#a$$%g{7YaZ
zR$Wvwo2&fm)wfXb(NfVKgfe|tRQK)P>YF-7b03nH)S9cJ4Yc}Jr!tS6B9nd176P+~
zo^QZb4)j28p<*p1tLCk35ztOvO}5PVrs}(p-h5gNo*PV_IGDSN!C}GVs#7kc7b4ao
z*JB^FHptv>t(@!KxZ8sRI(e=00*nx_*7bzvD4{LT5Gmf*$Zta^yZyyVtitcxP{EQ=
zb&<@!Jt|<&+eCfdHVOvqciw0a9$ujlvF>^B?Sb(0jCM+cm`I`bV`6lI;xRF?f-R~2
zm;&K&doh+Dkjj*C{^TrJxO$=8+;ZTgd8H6GS$wo}wBPG4w9~z9sBA at etGzO5RKeq;
zX15zf7{GClj~di&6k&%09-r&x`Ie|Q<4v+2Ise$KoBvDJcORd1>3_-kLc3W>RP5uk
zlA=|Q&+7GGYW?lp<Kpq^9z`bCADtnAz8<JD=!H%)*`pN`rHHSdYp+ct>ZQllxy>Kr
zr?i*VvRoxI57MPf1J3t=j;}^O6E at GPXx9@CJ?~jbU#KYfY4|$6 at Y3Vf)A!TbYaGGM
z$IiBwDaEM5XCr*@d!q?PKOpR!)}|A!`ay~+Z?`C1=~3;~kIQ*qd#DDn#y9QaAW}{L
z_NsyH(C;zfw|;FGymMuo5 at L_F7c2BHckit`s)(vx84ZZGl6kv|uOg&sJ7{u6+d?B<
zQK?c?<fBpgwo#-haZ^!jQ3=WFY5#{OJ_eYD?V$h!=G^wto-tZ at KcPJ^(>H!++UbL3
z`3M;oZCM at -Trtee$X0VRY*hPmGZN<Jujkw8wq<#wjEmCo({f0;{d6gQ(qknk?Zn7O
zOXK~7x7!xv?Bvq+kOG<go2EVnV7m6QU|CPK9}z2bZ5u&Gj}UYG>2?~{vXJ7RmKIXp
z)c?AWVo$duk^TSh at 40XV@s*MvBKbv<pC$RTB!8gfkCyz#A&OSzT at 0hY>%&@yACS^*
zm;7~-|1ZgZQ}SPy{2a+|XjA^=K~j4pKU4CTNq(8+zc2agB!8FWAC>%zl7CC`y$4gj
zhvW~H{Be?>DESK{zew`mmHZ8oKYAGTpOpMqsr(MoZ$~8cf0z7IlD|jtH%R`6l3yYD
zOC&#4@)IS0wB)NK-&^wUJVpJ>l3yqJ+a>=i$zLt`Ws;vG`8vrTE%`&Ge)O08E|Tx*
zgyTT!pOO4}$?qZcdzbWkz2vWv{4&X3BKet;|GeZ+ko-u=?<M&Gl0RH(-*ZwuA<}Qh
zP^n##e_8VHN%OYLlM>t{|4GT$NdAkGzfAI9mHhW5e}m*7ko+@}e@*fqNPbWR_4`Qv
zP|1&#{Hc<kCiyQ*{u;^ODEa#(|DxnKO1`($uU?WrO7h1^zCrSnB!7YA7fAjqlD|sw
zKbQOslD}W_j}L9V4nGQS{riSA|1V4aami1T*6(wYKSJ^cOMZytD<%Kl6V(4h@=r<r
ze#zf1`Cm!?YRRvV{Kb;LK=PlL{Be>$T=IuVejmy2Ao&jlx9(S+)X(#hzr9VrANRNR
zGZ|63(jb=;zu-OUul|twZU44D>p{ZHCQ1J1vUbz8_g5Xh(uh9pZJF at THG9aoiykCy
ze6A^(Gifq%O(|S<ZlP*$Sl$yDZcNU3KEsqBmo_0I)8q<urpG`?k&}^?F2%Kk(}0Yu
zv`h%lW~HifO`JJ5D^C at c3i+k*)`2bI`m79YxO?QVp~Hucg!oxfUly72@{-d{bLM3i
zkl(4!md{BcEs%<nr9n&FgVIghoZReeZqEFLrb6j=UXCdxBRO+UT6SitDR)jzGB;mF
zv&_kxpPkF4m^s<_1ii*EXF}ZM#01)Z@;iQN+{CG at F~RwJe4No3H`y&%3QtI6rzTE|
zm$gaP5-f#lv<Z_E<Ki{?soL?0aZ_isN&jfH6wk)TYuH4#b$v$d<Z12HM}lRBjCr&2
zGB{I2PIAgZ0AwBjRw{!0C8XqL<ZyYjT))Rp)yG5Mby`-VothwvH?WBY{W#a$ik~)B
zi@)_*d0cWP3}HrgmMSe7pr1Mv<Fm3=dFGV)DhlJ4iDb at Nn3bKMr6TQ=g7dIG-9!_Y
zKnG?5$rBI5!phRjSt+FKtZYt|ycjA-o|kEYJo8PNnNh=sre<c6awm|gRa~};j-<*t
zlBz*rd4p6DDpPK5cCIS2AdJqR1QRzsJ9i;WWY-!P{6yPCG at U*rJIj!rLb~`!8i;3e
z)6I)aSzKOw1wg#Ult+L|vr`y7*-m?~!;eic#cawg)Ms&~+_dBr(_nqPDK{@8kHdx6
zs at Cz@bVZVJ$x1OjuD-myyk+p8;q2%5Y+Ql>{EQ{-=E)#=GGqyH6B4JhQ?;|=3%U8(
zS&}zCJJ&QTEOlsD>MUtOU}wlYnUScAo2-j7YR87*IL9Suu~6Cb$(d#o{Um?$%o&+n
zMpm9{eia(Bv(o2G$jx5#e0D}^JeTVXhfH%OWalnQ=ICz#|D5>b+&ojFIR|HkGu at nV
zP-H5phx{HtL7GZ)CY$o%cig-MFz$0;+OZs$o51D5T#&VdC$tUMFUo-#HHUh}>{K(U
zY^pgc3$Y<xKSPHOb-~k|nMx*Yc8)1am6M%^y+g=E(Al60%k!h{oRE=4z%I2LN}Q9B
zu?YI&teb`>6z0vLvu!cW*D_%6d&rO>s+Kx4vy)R*bZAr%<APf}6v#Q at gHtXyJ3Tjf
z(d6VsPQb;%ipfq>Eix^F716Jyf82bNDks+zk>S4HrJm5~66i<zot!nOb^U2Dh1_yF
zGYt+)eS*S|%UEPmIj6#~$VinjIn|_sWjR`c(a at Yi*>DIayUh6PoI)wWZThst$1OJH
z=H~*YqlsEzBH;zjgaeoiTOMu|vZ7Qo$*<2^44_O^rKF{&CUeQEMak(IDXJ{<qIrnD
z@=PYxyi{0kbMiB?up`hUiq8_E at p-DL*^831oa at +}qY87)<Cgk!P3ceolm<nlX6LI?
zGL!T2X!@b10w?^*@98APc#=T+OPrsPr<$IzBsn(~3IH%V!5NmP0w5P=n{!nu*^6?_
zP{UBw_{@xyg{sM>0*+wBY=Ci=*`$Jz7>`IH6GKe}85|i_+GJX`bNb2>veUTyWLVYh
ztn<9Ki=50SGT6@}S^^(hH8?HX3{*fymeliH0<p)JOUuz@<NU!rLYhB=@>H$n99Niw
zNnvdyXQi8DIV4e7Dj*-S6|`*!;v{Ka;vh&x0l%2-oS&^qceNP-<81fPmi{en>0hqt
zB{OUtfF|?jXTVOEpPWa=`LRW``jg6oaxz+qB5Q at ik(PGpp)fd^fQ{OKAX{b5GmR$Y
zOv{p at BxKPaMrSH4-o>V{Je*ejRS{+t28QKDIOlF=b~;`Pm*vls$=W2BMUIG6O*N&f
zG~-kRb&giOKr+t`%NyHzJ>{8ToJ at tP#RS7 at GV*dVlM7v##MwWDKQ4#J;jVHgnF>j%
zv)f&tluUzmMdTE^Mo%V-iHU(#2FXZniWlfSHRq<7T0#&HwxUVkGZ$z<RoIND!WKP+
zjnRz`Ga^<b8Iq-P9)VE%cwB0oOg1CitV+$60AX5$V=_N^F_0bRw6u(r3?K(o$tfv7
zw5oD5(&uw|emHJ8vWt>a=4WJ?FoSamlkhxgVMz7rCTVBP89#06RPE%%InxrfQ{BT2
zapPIT93wkkr=P5qwcqbF=n4=?kL!4R%ZB7a<5G^HeiVJ+{GC9;RP)RnM<-qztdZ};
z8d!fW>|9crw8+uWMZia}y|s`MNwft~fUg1fOZ#Tae3;dS5RK0?CFhx7aY41P&H%yZ
zN;^`{BR#;~H^Q~T>7PDtTAnGF&0<p*Wn?+=tIVBVBojmh%WN_746s#YlVKa2fE1>-
zzyvg;lWjq0!mb4^NOmITj5GvE9*1kJzbZW&3Y==nftf5#&{ROS80DO=qupZj$-Emn
z4=I_Jc?LO5E$iTsaJt-pj&4m(IQ!e04uF=b+S{=yO at Mz9xn%XX;1OxaO{x9px=1Da
zWS%NH3p=lJ@?9AwL|d3O^?yz0=Sl-J!9703)!uogR)xC#{lBr^Trg{A#X;NLr=x2F
z$M9C at P>N{@mk?4*`bWtSw{R!<B5O(SPVSI$-O!w?|7 at -aH+kr<^pT&OMK*T|FF*HP
zLzM<pb8C{|S!X%YdIFLlEhBeP8+s4oQ(EIYK!k)$lDX)tp{nHEblkKsO#~sH(4C$O
z<hqkyf&w8CX?$2-SPEGugltyj0KX1Rf+E#!-$Nx|H8>Z!HKY at OK8*vGX%SGj6Ec(2
zCHfbLbxNMYy1*QW2k4XnNB~Wi?IOSpK!Fl0LG~ecy1c!7DXs)9c)^^tBqOH{b%0qU
zyh6iW1vv|F(#wybm1R>+$*F~I$lp0Gttqy7KsHbUqE#6HSHKr4eOj9tusFk%kBcg0
zKGH<61Pd`KnE-8;zlj5!nV$C~P at h=eIG8X_YOx*4Fu#Q+90vqF5FNR5=qj6oG-Ymf
zrV|~>va at ptWU{ie$bw2XbJ^$vMU#R7?c^o>mYO%miN&%c0%DPgOGZN62_HbxD7um8
zFW2vEX^uF5OB0Fmlc}&9rDYQt6KM;V!DY6hvk}=N{{ujx)ITDoWFY-p at DHK8UCD6P
z1Lv8US?HoG<`qf=eV&XGQqFnSY8 at r#0v1~A;!f$`yOBQmPbPP`wtb54d)@ae at _GHv
zf}D|`rPw}sC-tM at Wv4rQy7f$9AKNeI%T6Bsd$?h<?+?qqEAolhdi9OoKdwp*+S2=$
zYW)J;OWSUrWp|Y<KX$5z{qnDirffQPr>690%k!%<J6En<n7ZWdyoZ0(nkwRE$-A7)
z^iD|`%iQ}bP-)$-%2K}=lwr8c&iy6#rSyLtxvUs7 at tEhNhoyl>7jE+V?B3c<BQsZT
z7<asO$LZL+JHNh^*)ZHZ>zwv*;PEpv#vb|i*O4a&j+ULBQat6v{;tXOKW at 6SvEP6_
z-#`D}JKGO4pZ&1)qpco;m1SOYHk=ID7i#nW?r+PopXz9spY-qE$yH+`(l)PR&n)(S
z>9l;O>gehrLBCutFa7SNzMD3Ce|GuDkE4!l75-K0Q!wJLjCpOX at 4F$ZJ6zZixL`%Z
z*hR{}XQsX(pOtdu&Xk1<=Ot(MYKSzIPL|EP_2)a at nM*&D_pG|3e0lmF=C6Gx*PG&P
zTRQ!|br-*^Y}<o#y?>pjk2n>4d)d#gnheM8Cnaw%{xoINXim2ClZ%l%j<KJed+x+L
z4PzJWIr8cKJI8-sV9OZ);AGZ_31#Wu)Nh at 8DlfuQ^ILC4)EtA~SGCIme;xCZSNvD3
z$Dp7f|6Q-B0&2rH&7b~#X|~GuSlYIaFDL)h;qKWNSJ$3=qW|jp^&8iosOdKL`xz?(
zHx5<L`eDmAncFK>maTto{{F}J11 at e>ynpmNE at t8{zki#5^dG?kPkUOrp2_RvpK_^0
zo%;4O3l;^gow;C%|KSdo{~`LmHE5m8 at 4JtDmTTT(ZXEqY`M3GE<b52~-Wju}&ucoE
zZYq9hRA%?9-V3u9_dMn}AO3Us%q6FS>Yn>GJ^S3YyLYeedTso!E!}>3ZT(!~ljFyW
z-a7KnJ=G1-Q*WJ%yP3Y at Nc!}hpa1H;>Dfu6Ht0@;2R0ab`hDB at oMP16C7$f=>vPXd
zJD0xp`fFLk3U_5pc=h|_gFP&1JJ%e|4jXlG{?vE!1L_nL{da8Z>e2tHfnL*gskiSP
z?Ek}tH)n1P_gwJ(tS`h9)x!?gZ{j~X`DE{PXJ1%fo%+GRTPc5h_{jpZ-&>13zV!C}
z at Yzuv?4{{Gxt*uWyk36p$9K9c*?MtZ_;*XAdu|T+;I*S~2JZUhw=LJtEbDjfwEwZA
zKff~W;<1~}-~T#4%W|skp{~1DzA$iG<Awb7+4&Q<gj|`aEX!HIhzHc(X|w$09mS7K
zuV<{A*Zi}%Fj;#zvunXczcts62A)W?cuubVUJ;W#F#U_Bu5;^+6EpOu^Ru3LZo!dt
zXJ#HhndW~k_J+FQ>4bGVzuf!Lj<YKcZ<uHkH%0ck^}`Pp)!Pq5zV&^==bvm0^%_;b
z{Vnekhn|>zHu1;wlLLk>N&9~5bIE%=dd{Eyc6j!Xr+4{p{O4-{yL+DVnrXf65kdC!
zVLiLQJa5$S-3bxlo_jM>wRiHDzw*V*=WE_&<CGOA_v?35AE^+o-#d8n&ZU63O<x;}
z)@^z%!t&AKUaS8$rBn5DK?6=E{G4;A;7#^=^0X7{hIYBK>FE=jR`+_h#Nst$b<dYW
zzLb4%`d-BP_YaQxa`x=}E4?x^#dFWH$3E&X^U%LSOWwH@{PC-*2^+ph&i``P=NE*V
zSL<&Jp_a3|O&j)}`fA<m+4heoXa?6U%+7e2vv}RYVKLWd4pBYn>*K#Dx>u*$qb8i&
z;4^#QONlvuMw%AhX}UdR$L8u`|1!VPt9-;7pH=sFe%x)lwWLGg*)PMQuWuM57T(U=
zr4tq`@41&9GvsWNS6tx at P0>8t-y-6w_w`CVEw2cEJ9bA0t3?>_dG*QY9|QNUi8kEX
z(eL#y-u3(GU2A7wS=}t%uoGH--o2~?2^aDcXYbe^oVn(c<Ih^l{@&r;H{w^HINR~d
zx|*Z+F8uM<!FzwrIx#P|_m#qhXE!Creq0haBdJq|uH6R&GpBN*LqA{#jQwKaL(`R+
zSC)nO9-N*UT|fDgb#KToeq0gKukMSexes>^`SwESH`nT)-56o%^l|0}-&g(}s;m7y
zIHCTD2}Sqk=bJ7z7C!sb7G3BluBUJBkwZFp-rAWk>3gegYR*|x;jrsPa~-#@T>Y{7
zp<~$_^}mc=bFl8-s0zytpEuqwNZk2djOmNDlU7^QeqXNcUwdzpZ`r|hL2<LM&*_kP
zXTqUpPcC}3Lp3+P)6Cl*1K8iIax$xm+5CoyCr16<`%1)XdpF5GD=F!@vquH{(hECg
z&R;F$k3V at bb7r5oh~POzqXIsR=sD=uUb1I8$yRSm7-m_sYTi2jNW!K!yY9X7>-0O<
z_q_c at _4QxgJ=tj6>9cOFwbzz~XNP?_<ob}(KMNNg_*$3qb9T?!%|nMwxNu?A*|ar2
zdsn0+3W at zqw~fEu-r(_R^_M-<-zbS3xaQ+0wyxWI`R>POAC9iOeSGOd;qcCdUlu={
zx#7KMd_Uf>BD&<?>qC1zc{A8&<nReY`sC#g^VxVINB39#!YQLH6PDy}n4L2D#Jz*e
zl?zAfHr3s=mYg{MT&H)x^&eo}ygz5nKg!u1B@<>3Q21m<_t^7n at UYSj9R`nE9ha{7
zGO?)gUg5Nr2j?03?u}mg;++Aj|M5kK at 6Ws&{8dj`;%_sD#XWd$-n<hH357p(i at TtI
zzUW^3I}s<A9_v*%uXBYpV(N}}N4z2I=v#ksjrSk7i(dSu+B7li4PE9_YZA=Ds7|B5
z^zj|AATd-KZhE%k<-#xC{Y<y>^|YREd^%`I#SiCq9=!EWYrTEm*@u+_u3tI%!-bt)
z9 at Kvk_^hR3V9|y*p4}E&pVJ(CFhf1z%D5%@4`0}{&~)a`%%Z(d`zBml9Ib2mZk_Me
z8y|Q2XH?y at 6}b;X-@^6(^nz-6*0B=Bj9n8v&aCzIu~#4T-muK-S=u$uYwMKG9bQ)-
zVUCynJ at CTyX#uHsd-x{2)u8l?_{eYU#9971pA76ZvG#KBKKcLZ`$X@#eZKa8s^<sE
zfA*+8_wSIuHf40*e`{nXadlJY&j)_j at eM;>P?wowf(Jx8x-5BZYggmtCEd<XFh-wv
z^7WY4-mFz`YxW;K=9=bN|B_dpnL4_1j49q at Y*M!uM`bVn at aa*zPmPp+88V{e$LYhq
z|K**?-xvNo{8xF$s8zo$4E^HuFT)(et`DkxA!4AxIB&qxL2LT=fA5Ma_;zT&!5a&P
z^gX*he8SuZPrT&!%-}n{UW(Yg`1>c{-~ZRpZ~pc4Q<c{DbDFLuy|ivdpS<^?&gAwA
zdn-RYZpz}^o836=2fvsL&XgC$ZJxBEt1 at tTzj=q3eaaOVzSU=3!GSfNOCBEBw{-o`
zotb~r6fHV?FFM<{>RwjnE1MR+ at JIdvkEz4vkBq*Zp<MCpyk~<KrKJ5kJb6l$ZSJuX
zpPMfIkea$((Lc?aaXwvj at Pw}0KZEs?`zA~(G{5oOgOsyz2Yq`@esjxA!)L#)Hm-hu
z&xCJpcu#Dc9ILGgeOaR!eu&LWVq%9}(U0r>DnCAc{>{X6kEm(S>@-jJJ-+4nS1X$n
zc6>E@>cuY0;(saHHARTIIP+YB>V;SSOq=oD-#$$m at rHf2;{Fq}W@@uvT=dj8bBcS8
zys+h^rt{Yhe|PcZXL*-qeLv>P!jq1x!_&9^<`uZ)a`bZpuQRI4*Pj3AUw<UunfvGE
zi%$tZm;Gt081--a53 at 3Uzp=IR!<wT<9(<JfcgL6B)5Ir(dfe-|q~ZR!Js;gQubb7Z
zv&n9+uvFg~IJV?Q$LSMpzWsmLd+V^MzP0gtgLKJ&q^KZBh%_P!0z>zJAfbSCcY}y@
zcZ*1ONw<^&DhLB8Axev20fK^x`dc#t#+-AW at 424$dgG6G7B_o;R@^J^wPV)a11H`t
z;2!++H8pnU$xp;*V?*z0-E(bI6p?$+&o4>LbbNA|jn1i`8)NzO@|DWD7gJ<*lk1jc
z6HQa=PXp_Cp5YamB87%?M{S)~#tj+S#wdRn^>L(RJ at 7yMykAL*eqcyPxA&WP>iztn
z at t)LGSa<1NwZWXec+gXi9&W-&AGs6C4#_x#Jz*U09^q-l9hOSFo9MOYkiaX=oy5So
zmYgL~oK~T2p8oO8$<)4p`IOy(j+~Jy|Lins(X2e5uXos-8Z)Tzy>2PD3*5djx|PX)
zHZqp*no5k(0(qQG^ho@>I{{HMJJQiXN_dftto;!Y=9$4=6na4`HOB)+pT7vy9!d@|
zE7lCXKu;G=?)oe&7r9uL-o{)yjAvQ?#j&u$&--n)3KiE~s!FG-Q~gzy^nsftd~bz{
zJuiC{!X=xF9&qjEZpmE8EC23Sa3{SpAL;g>@ihZMLuZLpQ{<DNX0n+lbz-%L>rD?U
z)n2xUuCYEj*?vutrj6jUR*OJZVrxUxY)9}s)4k_LSGwK{XP{m#2w7Nlv_wB7F8^@1
zkxDn+G+uCH_S}nWcBfiJC-1CtHuOayh;EqA-*dtV$C+&Dnoxc at W&2UcYi?eL(o8Q$
zmqFtzf6?lZ9LldQyOq)pyRXkxl?+(P5V?A=I}JZ^zFCz at Ll5Km)?6r=jie9fSRG-M
zj(5wiWbUtdq~J>=aaS<+>ccqtMk_HABotm}BdWnicI{pI;)Z@`c>1f0qv2r|4f7XX
zUO~i<NOB5!g;6fQS at P1mL_YOi%DPK)H*c4F<3Uck;%WYqR}i|{i=Um8ZYC3b8n{s+
z^^CrI&p{oTbC$cAHMWmFEhqQpX$}U3?`?j}aNMN$ZOWZh#nOv<M{es$ma;Y_91|Gd
z$&0id at jP<4^l{|^;mytv<NE|-&bda-bWAJyEoIx{b2O@<uX{)51N?0$_y(`rDSF+{
zKTJM2U|s9RHGpTLn%gq{DZ@!N;ph0CzlP<9qvxLMsByGXa7y7f^{>|y7DSLSG1klk
z-kh{LpXfcty?w$l`Opr#m}Np{aD*gZ;(NVhE^TAk1_8odvbj>V<833G#qZ{lenhtL
z%3IOh*2Z<_c-OAD;WEpo`iMW^%gp81a%Ne%l6GGPg2(BsMS5HhNBIn%GhD9LSTK<K
zsjYr0WA(FJM07Rn`Z?NNsUW;N91<ODC)QZt-j{FPot%$;Tw@?4)};U5X!O<P8h;Ii
zXAJLt+Ovq663d4_f4wI7Feon0`Dylh&e|`U3D<O%gDwc at T?i(QFIjOEIziMEM($92
zp|Nm5>RwOS9zGwJ5}mU8r at LF8Ck4}B*-_q|+!@EF)0tHsZF}=pD|UC|d><>agxfM1
zrS15fKaEIF%Q5`8$@&VP!A{NCfAUk at sOjssO|#c7#S!<OTWj>4mWQl6SUQkz1m+^*
zb<FrpzD%6N%eKwDSA36fst2b_#Id1Di<pQeQ{DC0Pp;u2SlIPu_l1GOPo^o8N;@l0
z%flE%Sv`vaJS_XCC~<s^e^igS-hSlv_~d4xu0k69dfEA^??Xl~If4&ro at SAo!?EnL
zFIM5Z=9)fyUxbe*!=Kl(YjsUzzFq78QXi8iM`}v3!9ZxEn%Aj$TYN@@BjoI at q%@5m
zyu7zswb}P(SF@>H3}d`#7lem-Man9aqF{0>l82ZBgWEk5=a;BI8(jUazowKSae0%4
zAjn?UMB-`raCHAj**Skv_{|AJ_WO?Yk)D1NA09t&nmDFUQk%E1eEi5(piimFq|G38
z9fengL0)g``F?+#fFEU9uWr$>k&5JsIBGJ*mC#y-^}0NlI5X>XMyYi?Wg`%eICZ6^
z_PP>z!ExT&qlyw^GCcZ at -|l~&Su0Mo(mYXCGvqr==AL5GYPm^(KZ~+|FGBvb4-_V6
zZV_v?5js3zaJJo!Uo$H at n{A$2Krs^`=<}kbaQ at la@$|9j2;wGHZvR at pd#_t~2ot)F
zB_3<0G`Fo`{<ziZq8QQ%pJjah^oGOB<+s~U8#RN+LkFnsGjz=DO1r<Ce({a6c=YZF
zjse1qz<&Dc;frRmMC6~u9GM at g+E5xjGlnncTe-N3A6gz&!F#GOMjraTh@{c(Y-8zH
z>ADR2nf6B$l^tKC1gGqm<mL=KUQCdWRE%EKXP at S;)SQJbJe<@F=t7#%vNUWr>eN4A
z9cfFw)pD=sFeh$H_BH$nqJFaJx?0jTHa;i6a6M~PSdYoEiY7~*i=Hpw2{<z at 1Q}z@
z9qZ31PrKBHMiMkNp2+E1E?RAQD&$T0DE~0 at ML3PPG;V<+gZ`#1c{`EAMV>@+`}7qv
z0~|X$&21)o6Vp%&{;7|qkh(oV>ieTOMc0FfwtGGuexPenGu=<syrvP|+45$uHNwyI
z<*_-M=R8U=<9=J;o~l}?)tTsBXw)1o=z!dxw(~D4&lSNhPNhy3jy`Z0pV<B;W3y%}
z>^QwwWfdX++_(iPi^t<8bm-WtauU^RFUkELY2dqvbKt_uJ4q=iACWPK=~_3QVs;9x
zZ?$|%JZ!R at V>J7Pf@}IwMh!CK<iKQUgMR%*I^G8Iz~*}fV%=@_RqtDZnKQfUn+`QC
zlDXH8Wxjc)aQ at Di*Im3Bb{e-Aw82Yeq*N*9Vj<)Xwi3R!S4x*DMo5#1f8HU1syS{D
zX4h^UPL>TPDi}MC^U>Odp!dg)sjgvwh1DQ~y at -pW9l`4#Pmg*=j&sjbKKH$1^-_80
zOY59dT<7{1y5>$9<C?f<6B7lid85hxqEo#O5OW_djka5D7I)}cNj4I^RIC$GnIh+Y
zewXB^s>mVb&u~0n<2mE?`=wTMmjxW-*5z$FeFjbHV>&E at 8BaNl<zBL06d5A(N^K`q
zICK)1w)HYTJ6~U$SZCcmQcmuMD at g|Rws^ghKT8^sBLZipv!k!hRv)`<-dpp|>?6B}
zZ9x{ULo!JgF+tf|3K2<HLaR`GNS8XbOF3w<#rMedTJCDHrlX=sV{x}%J?kX*d$Hb3
zIy1)-Ha_-r^Xa0r^~?I*6VHPk13PJ-d~RjG>Qv*kK;Nw36+v`m<>z5rXG;RH$1ogH
zgLsSVpSz~jHYRpI$7$_H<Z?+Cy(h`XKB(gbYo9+<-&<nEZh|zX-BGepP?K=<nyDVO
zRi2)>@<M8k)biYv*hX1LwbpEVcArAs$W8vnpKJH-ePnvv)*Gc&pUltJP*B{46v2No
zNsz2PtINeYZPit8>C5}TM7hP#`Y7&cC+?I6(#~`D$>Q>^;Ljc9!Cgna{TX67DgYN0
zF%9D8(YkML--ma at Yg<m|N=X<3o;L7sI*Mqtw_WM$`fAkD_Vi$7BZ924+Fva at m?thg
zgK0#$mr7G>nw8Rf+j-n%9iGHnJ$d<D<H`}HjK)d);E=SSqZ#_Qp)&d!p06`yB7>PL
zUWSH0?d_SK!X at XPT%pKL4PncV$)Q;)sgq~!Eb}tiTwN$$-9o;;KO4;Uysyr}(6G8(
zRzByglid)fr}KB4^8$9&%%`uclM)i2p<)tsn3EUT>Ng~;d+)66vTbL<-+p>)Ctskq
zDUvDCBY`kim at cQ|8%0QL+o`fyAMv_%QMWChpSG)to$$V+H}qyti*NWK at YhWJWqT1k
z;WOGy{V6n2II$G%QpK#R!Cj)$y`s5&<Sgymsw8P8 at zk+t!v;eTvD~D9qT^T7Xs-pW
z$8GI~+6B4xG!e0q6NzaQ>9dm=8UsURuy+ez$3A@>Om;P$G_dnoIVPpuIIE8c$)Tm7
z(mPDU%D^V-{QM#-{I<n+yAr>ZD=WH##HZAeqUJb>)dloXIg3IiLoBB|z8hDuE!=I8
zM1IJj3g!tTuCsrn$X0>WqIvVpU!H5x!poXZ(&w`Xn_}}Z;-l_(RHx6XXmxzNq!?T8
zV=?P&?7uGXdS&a!kMF%*&yb0JLxZ^&gQG;gW+oE0*LG-omzG$Z7Yoi%Ihj#LLfrWE
z5oPsY#I<!pg#5NWg~PPu6Zc+<n5`>RGH>9lxIOmHJyTxrfOL^bg*cuSf+DD~uj6P)
zhzmT5(fUgJRT;0<j4Srj(=<GY<76WK*Eo*yWJy3wFIzJps at z}!t>|jggrr8T(U;qO
zv=7#|b=P}KQa(?oJ*y_{SZijAEzJ^`wF^DHe)_SU&--y_#mZg7qfQI*r+F=BMHX)M
z5$1x+e>1f$i<7u at Td$6m^%nAnBwBIh<gzdKRkwVf&G|C1HFRNQ_51aJGP{mjbyvRE
z<q%6$goxfe!^Bo|mXK!i_-T1TLIE!~MdyVEO*`Z#U- at 7WGs8Ns!LO6*V+$*XzYH}F
z%}<8J-c6%YZi;0ky_4(wFr*zGd5G0eo>A1EP=H4A0Vf4Ts1+hZ+F!fu&}E;m-KwU8
zfq|lZCo)*Kjp`{%?v&G{y?CV!9o<6+T$waAy*2EkHKnwGpwsy3uXBBpC)IR_=_yZa
z6t!?#`=t}@tBC};(?}!#t#=9*y;b&#iH;sxxqKI>L{?de3CoDsv~6LM7TjqiKFjeP
zibaKqM=kF~o#yyB*fIYW8JpMreb#h(Wu5g%$HG1G6679xRIsFEVx7M;lC9NdkY-nU
zMPBUccQ4-;7Iuw&{#Uj?Xc3>=Q55xTA+D~?qsrNck{l9BX8Vr*It{1S_S~V6r;fPt
zo%)2tr7s8ud*9;4`qkkhXhSKeHCqmkip7(shfa`PUSA+){9;Ztm8?aQ%|k+Jpm33i
zy}<d{@*{;4`CiA58z193%12ts_}=6;eZ^cagH7Wns<R&-QtxE^gw?VaQ at W`p(uiL<
za^wd;D{V`hFP-nQny8xm{Q3AZlVXpO;x3Txwo1x=5SIuGAr<ewaOonhhJ}a`o!+^u
z8%}4R-xm?4FT)Yg?F!*feO50xZudqAhVzom?p4C6viK5?b%7u3JXasFnyRUu%;mVw
zymIR#3)>dWY3~M3?zY(zTzkgyoDw81JT6SzXX-ug^L~1m$#*Wl$~iN(+ePp7j at xnl
zYS$M+x4e>1F?nhtxZUYCT|J)Nmh+E%;eAu332}p*h}Lh!l*K2&z0h0wc(N~E?&ItI
zkH6b@@YXo^%Z59New}svny_Zq7-nzl_3o;*z$ILpEzz^4cW)AzInYR&a93Cwuk~nJ
z6i+ at iH@~-JdGb)3)x1rt;^ZK+5=|bRiq;8r<wOr3#H`t#Iur4r>XqgswTzcV@((gI
z6bLr%%1NnzmJMR-fj<$Hl{&2FCatt`S|&Q?nD(Z%#$_Q~D_xHc37zH%Vy)fom`fMR
z+co at -ENXW8KfAJce)TG|)||fO`!K!2 at LI#SiNXe4-wqi$$z8ct#pkea_GrNTj!W}m
z?Z}5Ex1#A4 at p^;TKc?+owO9!*`;z0Yvnz{!SY|%_E<ao6z45lmD&OMr+WUy{w-q8$
zZ)^<ieWz~O*c%-i_>pd({qr(G*;hu&GvB6skABI1AiZl)?Xd-|DSqq?zPS-{k$qEM
z7q(3dPx&;+U9c0I`t>v7=aXq~L*uEobGpy>A}MAhmd?+*eCn91&xwBdiDm4?Ih9wF
zc4Si%WtQtt*Qc7E at zez(O$+fxbBBe-SDbCf*cc7_jDAo)$V%bpe}3A3fL=<eS664~
zeyaGlp7EgkZrEz-pxWKi$MJnR4<B9ce?$t~9Fiq;e-h>(GtxcIIE>rMlXy2xD#2mT
zD~Vg0H+hYdA+1;<E8SeXBK72(k16v3eK{QiyV?F#BUz%<X?MQ*<YhEEvEA~*r at k%F
zuAI3wdLuUSEPss3HNrUZ1*7<pXq%{jJMW^UcV;5-l!7AqSsQ~h%_D;JD7pfU*Q^A-
zcs?4EJft0}S!@<gM}HygnJanOA~Ls>xh=ii5^uPo(D6(4TW`O+TvRGmPL))ZRsE+*
zHUsI4h2HWNdR+D_YL<lO?s7fIyCAbw;P<^ezcc+#;{!Kj0|CS9CaIFn=Ab8$bx&r<
z>JQh7)hZn}t%<g{+&+2Ix{XHhT8q|ag4V<=fsWazhI>r&!ChC3o_A&lzem}BONW(3
zIq}12s>Zt?;!V?a&&_TKp0d04;?AUKYhMHB`VAsPl+(R=^GTd=9Oa3wmXEemFX!jH
zg!IfPbu<`sb5 at J^zka36IU-%T>oRw}`=Ql9NtK5yk<61}C-&5;o6bBidK$^X=5OKj
z$ZW=uRgQeOc<Gvc=1L-8g-5x9cO~fK9$qC8vudmhhmd?4qBiO8u91c6Z!BJXl^$*p
z7C!p&!hFL>JmQL%5U1puWy-KidR|LX at 2ALjHM^`gxOeju({mnNImv%|F<TdLQ_1P`
zz$c>QXHq3M9QL~D&*mW2V_BQIbJFPhI8NWpZTqgkfD32#quh>9Dpg$FxqU?MB5SFn
zp1`q$rpUaV at gtriwv~@d4|m=qTp+k_9AcE~Jhs9_*SuZUqOVFbCqCNyI at HELVE+0b
zAH{tyMY}=r!})Ht)&nMZ16<QBxvH{G8K3sXe<pme)bQ7Ne(oqmD~DPWz7%I+&3Zo*
zSwunLObz3CtI3<(W8R6$jwiOo*mn*EXC_!C@<~Q;CF{L!kTupO+a(k at u2wo%ygAbL
zBWdoPJa0>+_H8<=cO1^RE*pyNk5u_)XTAjRo5{VtY$utUH6A?hrANe?&L`@y>$2gw
zL4yU2YIW_OQlD2dPE|)oxY3?lr^O4B+U=0wxWlq`g6-C2Z+P_l<Xs_yn#b?;o5U`^
z8Z}bT at UQv#j^UZ97>oV$aCzc~f@`mx<Klui-)BEf(EL&xw5)UOf}X&Il6d0a6GDzF
z<Y7HTjTed??nx~a;_rp^&?#~8-TkDlEO^p$D=Hh7mciZW&73}c%zOKhO1EM)@7Q;o
zZd<rz(N3BX6XNuFAH$rq^jEB#AJyy_ at IOuZ8^1Okjk`A6^tP8c?viig+Bs`Tej3 at q
z(h)Hy at P^+^C;sHb7n4leY&^ny#rL{!dZwxx97Sk|h_#Nns%I7rbNy_-9tJx+u;89V
zIsN2xWoM}<157 at k$dh%d-_pa_7l+byr25BWw at 0^i3pY=$)2AtXuR32Q2QwP-RQo_M
zycucsLY6(&d>6j at h0jM5o_t(bi at o;w+suis`Y-)!q;h#N3>y at td8#&q;<q(BLpVfc
z(vohS<>mdM!LHqUD|>ZzFUHV?N_c_Rt4xG<7#5{eaY%ASu01%Ad1*e;^QysTYNa*(
z at 0*t;GVFs0Se{Cl$o5AMm;0Y98=1HX7j?YPZs_M3S^wbihY9^-6HW_xwIo|djxVc}
z`UFxB+Duk>QPj2e<{9Al_n*%y`w_rKbL$nj{3nfL(sT^7q%Dzn?$Ue4Y4%LYc&idI
zUf at P;%SvhixzhEcwY<k=#v~NKIqLJQ&3wMEX_Z(!R8w}soov|GvehJI7N21AV8sDy
zQ89X*zPpnXv!#hEv#E)h9eP~^R=Amyxy2v;_BQSot|s<;_7;v-?$!sPzdy#P$B~IN
zfKWn5h}<A5!V_TM83;cF5qCqBgd||!7zi`qTnPPBSwsOqqyW!@a00Ne2ZSDQ0z^$H
z2KK#yhyq4JFhUsEw+6xqxCU&20-^{YihwsjdOWbN4de(!L$n0abAx?%Ai^O0AxKXS
z_RWE?0WJkwRQwPivVd1XdXzo`AWVQWpd*A at VBZgjIN&KzA3Csa59AEs7O*7)5(7jP
z at GeMy2<%@1LIv_K0Qs|meQ?@EBnZMEfc#0pzA2ECfD6GE7f28gX~4 at MJ!n9pD?k_l
zr-1xrz`ie#3xJ=3{HejdE$RU^zztxFYR at Pj%7C{)dVH|21B4cm0#%_V6axF6Ktw?J
z5Xk>9*tZ124!9C*QSBWDL>}-vke(Rq8v!{EI1A)&2KN1dNC18T@;?gp9f9xy?f_c~
zAaOv{0q^biza|hEsDJQ&|GNMY0O7s+{ci$<1#mvt;s6N*0tdXf-~akR7yu{j_rDL2
z^MJ?p``-o#7vMUuMU^)ah!WtB`~80z2n|F at go}gee-9vMLHOhS{s&{0=oH{`utl|3
zC=fZoZ}$7&5XdpWnfv{J1IR_d&-eS^0SFJ^Hn2tM1DDlAYJk7)_dhBPHK_mn{r-0Y
z0{Vftf4~1xZ3_H}i at _EZKNtw;XX2Ip{=W(Yv@>zqe*a$w0@{IiV!!|GP!C80ZUS4B
zzGxsSfIsi|zb+8EKl<Mb$T<)`yx;#;KsW$bgDon*a3BhRKkWDaH6Wnhh_m<m|0WPg
zz_a`P?*xPoa3|QJ^u+@~0RHh0_TPhL|9Q~g_P>B-|A|=kKZ<4lwb0-8zxfaL{|L+e
z%b>sQe+|q2Z(-U0G?x9hLVw%;mw&MTJ}mn$g8sJuS6KF+ie>-Lu<XAP`rH0L{e%5K
z!Lt7<=x_Ufk7fUNu<ZXOmi^y@{<i<`|6u?3vFyJ9`rH1Ou<Sn>%l^l)?7trR+y1xy
z!Ttxa?7sr~+y38T+5c at U`=7zG|90qa`~UV2_CJ7S|0U4h_Wv5o{?oDSe-g|7o1wq$
zf9D_Ue+0|^??Qju|2mfa=V00Y9G3leL4Vu-Plys92cpBr!J)>7K}Yd%C}H^c5Cc9A
z9t}PmI);xUMTvh1qQ}R<J%Y~yG2!DdQ{fXsjQBW2wD^IL6ev0+J}yWf2+}j4($mB6
z2~p_ at Xz)!?>5VDz at lfdx9l;ksrRS%@Cr70xqs8yRq=#S-6>11Vgb*=A0FglW5GjNQ
zkwJ$batIfqfN-G0=<^vfCs))`aOQ(E8EXsF3D9qU^y66jz7T{uO>#gjaNUoNUP``y
zisa_wVCrP=w!g0Q at AD@&)Y{koc>Z({`a2wTLdDF(!|Y(==7w6&YvE|l<7CNWYVU+8
z$3Z+(n_ne&MX&7NKmYnm4|XQtV{KuAdM?(?)eMz|gNeJD^<U}G867<A^oPHjyA$d`
zAx!YTUg)oOJkWg5j+k`&51qLkJZE_D9PWN>OM4Tmzsh$Ij#^ObYT<@{=*hvt-P&n?
zWi0c-1KsEcwb9Gk(F^aH1<s#8%Ol7yfO?G2)s7i%ZV%K$px5!h2p|BXfe4HT5-=9X
zLI1-bYKRUx3Y~yhA#O+r5`$zQEyxUVgMy&~=svUr;qE`|PKo7U`;KbgQSZAW`;KPc
zQ9?>U59(d3xcg;5jYo2jk^y3Y1RywM0tG at n5Drct4h}BPAsjp$0vsY7G8{@AdK_jP
zejF(r<3IC1$nzjCR35az(E&#f90PESz#RpS3AkgxNdbrI9|va>cN<fC3+7*|m{IGL
zEiFL<SsXmdXyf?D6Aiys<wFqK3M_s<@aX35YGLB=+XuA>{ojQ<?)R6Yg$2qO&=0_x
z0G-Gp%=-ko_qX)cm~G8I=k9M4_P5^qTkHL;23EQN%X#??l~Gvm2PpRu`9r0I)7ssC
za<_Jg_MbeDKr+9%U_+TdMFes4^WO>3VhYcd2K)xYQXALI12v9P<`6;U5MQb~GxIwZ
zbo`h5@$)%OqInj>ZSmxCr>c;!j8Y;<<S6V=!V%1S4!SYbmrumvBrHzG;uI{-#o|0H
zMq=?O7LQ@^I2JEp at gf#4Vev8+zrx}bEMCRpH7tIE#c#3r9TtDU;&m+Ez~W6T{)oj}
zSiFtJpRo8d7Vlv3E*5{m;;&fz4U6}%_y-n4G+5)|#BekV at lnd!pU_JjvLVHQ?4=0r
zvm>x-8!$e3sZ4V}8&^&rM#63lLfwc7+4Ov!&%}4(WQq|xv_s^y at -&5N%SM6 at OY&hI
zgxciqk at Fko*D@%d;;MI^d`|eWv2S5?_$vwL>L at HRyiss-P1mrOPxv(ZbtEjN4A>J-
z*Q&3 at m;%Qi>>Oo%gSsE&cN>-8<poIEo-DEta3+RnQ-vr~ILBZK=K;sPm0(^{U at rSO
z46C-llsE81qnY(X5I+kOU)YFrQ|x7lbomWF^*)uF+m=ZM>M++Fsiu>kFW#f=6N-U7
zRAg%%g=IVg at teY*=g#kS(7i*#Vktp+4s$RB{Gf7x`Ha9)!RdeZ)oN18n;XgkV at O!5
zGzdRLwZLAjc9pON*e?x8Kh5C!kZkp)LN5rvfC<lSrf_*LhlC|5L(F#vD%IwZx{%u+
z?b{6{5dF#XMJFi!WXQ{(`cHm!YSUujI;CRUoiUe(GG~^AAN2A^62+=!7?$nO^VgIm
z)V+;HUP8i}w?TO(3Er_4ug9&8UX at L0cK<fao5#pKfBM*Eks<k}3!M59G-8iU%6X31
zX;LdRWPQsp+U3gdo4v2ZHI9T;xPkQdB?4*}xu?u^k+3pHls^1?DY1{=HkOW3hD3n*
zO+sfk0E=_U+aK#bAA#L+2lbsyH<eO8C%)V^0_(Z~!foU~K-FC%#dN>NlL<#V<YkM5
z6`e)tN1E2~=Xrfk0W6E*TsV$GnC2L)y$Lc-zb>!J;*gr?nKTMZVgx*Q^{E+FdUGt^
z6zM(`mWPA|DTDm4$^^rUb*x-wk+9&S7`DKQpFDHcW|d_~wX-#6X~37u_mZgS?)cs)
zthg1EAD&;wTO)xnSQ8(Hr8?784H7|p$ALcO)pJs>5^I)%M_?t?nEFHEcpfL#!T5*(
z at n<W)KyMSarKm=K at jS1_r2sJAA}c`tz3DuQ+8lN6a=*jj#=Y*N&ZDsAD_}g~oBv2C
zvVR~cJ_hStfuLRd$GYEzopa0WwUsI*)AL<vzeM3_;I2wDf$%dRe(A+LT*M|D({*tC
z0;kx!cqStn;l;^}siUwIC(H!EkLUdD%pV+J<8!<(YZO*03Ci>6j!BDfJY7)!7%Z$0
zWp8h8O*Jl`iT{Ih2Zlz1@<(9_L69*Kesa*IHC^YU_eNon45<1%6p8u#G5P4RaaaKb
zV4sT7Vp}uLi>YI<#tD?Y&?EUbPc^SAFj5w6;u^<fzpczW)^g-5j(}R)w0+kjE5=(C
z(NFwEdTuMHz|`+qx((gG+2m<>@pijxQ}wCm2(u_FAr-pYKc&WC`TnT!O60;49V^J?
zwWpG*n!qi1N2f*@D?C7(<73cHqV&C#O_5~H3-5T(73gFQkHK;%KzsLxeO_j$tgenE
zl51^l5A*F at e18@zeFBq>5}iLIWf>F79-ovgkUCQ|-5G`D4Wq`7A at wzjmY=PG!-$rw
zOuG^X>eo>my7EWiW3b>%Onuw8H251cMquqhV7%NLCauTf1}vtGUBHP}8-s-!qTAOB
zYFtnfkspVp?*M&r$a=%%%!uRZR4T1xY;%tmUajUI-uz*7mOwo@@HX4z&QJuq;(*qz
z3L&-1LIJu@$IeAS1vb+^Ujus<!_04Ni-G6$KaIoUE&zSj=boLyVs<R%z~YMfw at j3N
znM#FpNsnnC3<h&zg)bGd$WLszDMV$-zrIeVD3vF_^h|3<P^saNoO=KTTSnnR$RSpm
zESbWG6^WFWK82B3 at Lp$*Q%2NeTcqC|;HzQ4|D2-1$);3(Wz;AnEZ}_3z3vva_Xcvo
zE6>i8AyyyIt(SFf1{ougLJ+ad24ZX8;Yd;4PYLqbU2WRVwo^M<q!lgidVLXzu>wla
z3MYxwUe#BxW*e3AK(9ka8!M~x_!Hr<z7bd?>iB~X=VIwHptv>)<_8?i{7{26{{{RM
zmQ`K<eZ1+W)^ae78HLq7LCps-Fy`*8_pL<0epxZcgLiQpF%=S{u$UT5|G&NadXyK;
zH!<F5ZrE-!qSbT&<D&`_ZeA?z{+1l1KZQx(;NMeh!83-kUv#*;p7wcD7bE%R;L~~G
zi1om*dn&Z#$L7^C#im`;6na~zNOBCaQ<1QOLQo$fb`i?b<P=dbzjVlh`q(diqO@$4
zI at vf1t8W18`2BTCrmemN&L}Ka7&9K0w<Q)ng5z5TEhc{<+^vAQ<}p}-7-qg(ZKrvz
zmO758**?+ON=m2lRn^qktFTrfD(-?ZLk2yE&B$?y7 at ah=JNojnaQDRtG4 at x8z{DQ;
zn!-EppU&KP^WX#NSg8TNQngsuspGk~wfus+Np{;$%Y~BrAmc3kO5DA3)X3EFl+vzC
z)r^rzsJvA!32LY|0OzsD^bf)gW<2V9o>dbJa>Zun?&%N&hVDjN%Bc6$DWyMh_X?Q|
zX%>aE93HPtP;M{d80_v7&6NB0L*<4rfmBgvC0UJ*OOi}xCZ}wtu2kVsJzvks*I(qN
zw3-Jc=v6z_&7Y_?PgHVGlR2gHj><*+tQie!nSI$DbMiZ^dPzA<m3AwUq~*tjp{|BA
zGpf=xTsPM;!~5aw%r{zXx6 at VID2+sjWQerA3~!cj^WK*&&aWuO!B^J2uXWW_v7$%4
z?L^)>Tz-!suv?>g>}`}x*ySi<Uf~R1`$NHz-5e at X@kEq7lrO%!BQwXp$Fts*uWGOl
zRXk)fV6x*yS7jZg7JiSvS^ulhORcVozMBgy at JJ}k)T#H8TkJjeq|uT4 at Oq<K$;%~r
z+pEcf#p;$bin%^>9 at +TBJ7?b%1wEPWQ%K`lFywPlcT|6R9_NScN2RcYi+3Nd{A^*+
zg%$En98)Uyd#w_^RAWUhT>jwjM>EB{>%0CRSK3Ym8CIC~e7~<&_L7kNn37 at o>vPhr
zmAVQtiJCPr_JX53ci9x*FTW_23tZguu^<u=o(XM|_+s%{HTS~Ox`k`b#ZZp=%{HeI
z#GT96<8IP8Cl=;07X{FLM%<&Pb(874OS>A#cD7SUQ at SnGnDFuYkvoQ-hq>3fUEm3E
zM8&Wc#AmUn7t<llyfP($S77`l`et70J&dn$4b_`vd26dRGn#7zE=_FK=_(cum%i?Q
zbAtnKJ9<mz*hlGPe5+Yf&OO%nP<x}36vhgfKi&w at HMzL)2QCU7SND_;zLIg?K<s||
zy3`fFZ at GIa;mX|~=UczVv8koc_D at TwbtP`gX)?Fcc_jxpY}l&EM(P%LN#Zh7lsvOA
zBR~8~F8nijk)18#Mw6LQLRXKvQeBosv*7p`BmVo%9fzEJh4N~yak~Bu%f;$R1BR1U
z$}NWWSqnPGgS!0pIt(74lyBha(3v55MeDq&?#ffRj>uteq6l$nL)=goMc!{qSBO6{
zn9o(8)adeTN`=8;T(L7<`Xn<Eh0L+r8T5^}uF8clRrt9tzuZ&7bAFh;%?>ZS-+21O
zHf%NQ+uJ5W8*zB%4x+bW!>_OL>k&cS^bF~ClcciQa at n63=pnXHlW$7-Q_AO08*>*J
zS6qH2Mnk8TgIDz9)*j6}0kYAknbT>~r8~>cRDzFOjm|-*RZq?;wT#M=@2p>Zfn03s
znWGez>3r#%PkPe#4rRrqZ1UYXrOFl={!iB4mtDt92}$~DR0 at J@Zai_l9Wc<7G<!4H
z22s!+wj#i+%vx{rnC1h2k5c7=ozZ>QyN%ZU8g(~5EvOfZb5Xn=Ek7ZJpYXtk)k8Hz
z#eC)Ku8UntRd#47Z<S&RBeBh(PVtk3kue*gy=Fuv+1r|py$11o&$+qN56#pPY;|NS
z<=jFmEnVl_ldKeDFN;l>%!c#a8pbc+_{6Og&FJs>HDhAgW!hx6!w45qP-Y(#X=H$x
zu&8!UlFC9Z(fs^_BqRCjw^hwAww&Nc+?M<7%zDkI8}Gug8|||+ at YE4LuIF6L&sjDK
zFL_)qQ_Qhb3D2kOh}Ty1)*0;QmJhUvSkkC$3E~woEZNg&MpT|D?JO6NP1bfif4wgo
zlIbL23wm44$S!iKYqoSjU$rS8E*GX?`^Kaifj^}dt`N{fjLZC>8Tu(`pn9jhMy9cW
z$MuzZc_HJOzQ-3EU#KTd+VMv9qzKbfyvxX7mQ at RrwM<W@$q$rY7X0v7piMatQS^4~
zyMxjEv(~P%epRWux at w1}yGqu6hljUtl!lbU6rVj+!CQ|v)W$9NIjbUHv1;<rJU03E
z=($4-k6T#a#pfLqcxcm4mv at Vtq0$Ri=|ZX^=W1`CzfntmeJm?kIbBG8C=!>~kEDW%
z#rEM3M1eK=nxg^^Z%%bWnOJUsdP>JFs!0tv9$(UNB^jM<<&wARzSOaS?)fuLj6B<V
zG9?+!YVP{%eRffoE-kgbRBj^Ii{WVt;aMwMHh8;yN1-K$nhZC><noImiKF3-VG0fF
z_m15ctga^AeXcmNqK;^RC~T(GmqIF1&u3?yphmQCDAuXiySO`@YV7#z^Fg)w)C=Kd
z-0$)k-T}IZ7*pAHGZ#G8fLh{4PK1HYZisrE@#e8sKZAkejI%cdqKlN0TkIB8zbeUO
zR;XVSvNl0P-Vqp)PrswYKB;trMI0Y-tNeTKBL#UKJqnw8lQl8vrf^Zmt|`IuLoX*%
ztd%|B1 at 8Cslr>4c3C+u{dHIVgRwmmV!w)52z1 at HK$)d|E`Lu+qPjYn2>xtv1AF!Fs
zBW_6tnbQ*SltJIm)jzv)R=)V at bD{5)+`eg7`EI)Zd=9 at m#pGSIRh|FpnnI$TUYmNX
z$Edq-%ahOVoVjOrZYL|>yT~T#9r!e9upxYX_+zL-n4W5Jzl3Oie7^4GV#8oW1aa at 9
zC|L1VLLm|4ASVGlhm}ib>DByeuC%-H0SrIo16c12S7b>neK^IDq2QFQ(4Y{={q+WK
zJhCRTY2342HGD_Ukb-R9*_Z$^OZhckr7(pcnz at dZa4Y2;nOtMJOaa$@cmBTE)BT;-
z&&WO`QBM3i{P|PX_dLA$>51?s+_HCcjY!@ioLrBdcb}NPM5rFDZuKaPa_dfz1WUm8
z4uon1T|0*{l~oxx+t4Y$Oie_<ye}8$k51{Od{WYDQbzFN5058KF?(ytMyUn66Hlmz
zIet7eMJh^pKeWSv(9l)2#OvleZ^APKt-|XnVI>|?Z4qSLcOuK;w*vzwBUzUf%8ZRW
zdfz9LhBOZ9Hmp at E<S{>(6FNJ)xU|*(9v(sruiCO_%59!iAe{=DDu4Z6DXe*AVduz`
zA3?qAx%0*p^2zRRA~ePK0ycIA@!notQ0quY2$p*g at VVUlT07oGqk5bgy9a}%uxh%H
z{Z9SeNx7n&D at tqlGX6dH4xQX&43diB=RBVS7garJCos5kszo}ASeu-+P at K<N&wcvZ
zV^8J8C!g}TzSVd_bgQoKmW<`Q*u=%In at 14v+}~j-|8QHTUGrS4(?pp}^p^^mmPj6j
z*5Q at gL0lUQqh{if59R38IvGxiy*|2Rftc;EAVhE=f=d|p3g>RTjGkF*XcIoK+Big;
zckc|GNmI=r%`;k8J)l`fjkmDT$h}bf=n-}mcndY- at MDRKQ*s7f6+;Hi3hnCQgW>I?
zrem~A at s}t_<g<<|esp{4T<}~gH_3*JMXHNKQ+1D5+2-Kwum|^xMsWUST&(b8SWJ(_
z3|P$kFHG?lvtWfoSou?6h2vs{vtuzOR(vw7aAGVzj1|AzF6SeQ#xSg$27+vGxFm6~
z;`3i{BV#%}46Awt&aY*?E|OuTC&yw^Y{t?@g3Va*4`GGlV=)z0{KHt`8d!V^i#4&^
z8 at oNRm=-HPW4lk!#Pk^{8&UVKc)EO%0tkJEn at Ct~3i^I}S5ppdk+AqEEbA7y-e}Dz
zhjeOB(cmCqUF4wsTIR+WLUZ*VvYl0G2$Gy)6}o^Bw*6b*9u^~Q{^r~VM3mH?Be2X(
zi21Ig>-I&g@=9Q_6c)>3 at m1{lZ#=$b1RsG#90BERwE2{)H(5p}KLShL18hy+kf7#3
zA!;=OOC!Rt at 6#=Q1 at lo@oH(k!q7K!cNjtnFMq$BgsQV at TPlX>c#QOZfC$ZX79xMMO
zEdBgg at ddD05Q~Md_!Jg%Vex4!2IHOS&+)E=Rle0$vbSkIW3X^u2-+ng(&xrX&yK}%
zSm_n8SP_dKU*MYw;{)x9haNwfgIGL-#ZRz!7>h at +m}uZmnx_G{J`O|OKdaH5P4G$K
z92$e=?mz+8LgQ<Du*%bo#r;_P42z#)@h}!&CKq+(jxCT0rRH|rIwHI#jMZNE^DpV~
zTa3b*3PAZhu77!6ovCqN2XG#SHxj6R+#4Bz74U=p>2b8+e<nw+;)#UiJ;pGJPX=wn
zA`(_p3+g-dqWJhoh2JOdF<2TI-~z_5?$QQEQrj_Dl at f-L^P1U*9YHt_hWS72#oRlO
zgtd%9xoj0Y?JY+H7<r44u;M;o-voE%x{W%5PdkD99$@n8p4F&0_6IZj#rWUxL&6dz
zF)TSiR=hWlgr#d>(i^R?M)e9HVUbK2{R@&Gf~R$nuvS3~FJiIthF9aF|8ISe6+~MV
z4p<7qn}hW|wh_PiBNi`V>GQuHqh&M($|H>_kIzP at Ur4!%Y=^V;GF9-K4tka0l=1pe
zSZf2S{hrli-*W9%`Z@~By^Cp|n$X0XNrQbd0cA-XzKHAEP^@<-p*3hPO3eNF`RjxB
zfAX+M-9W$xBrM|^=6>qN^nFGHx=~naCuE$S{su9J*cf*G0qm&=eSd9auhNI|9 at jW5
zjvRBpO`3)QsqI6g)Im at uLfR?U#-4D;){#P0z6O$h*L`EfjpF^f6~`)(Y(uj5RtC4V
z!I=9aaol<lwJHgMX-OMH$%wD#-`(BqgNLw)c8<l%yVaa}{p8UQgIuD&prPy6hGEJ1
z&$i|h+v;iTJM-^FJdCUgw-^yD8PW-H1T_+*=fB*@h^bBwA6M^mJ+1IEIku+giyeb}
z{f2t^al<FgR at SxFBgrdP(^T+qvs-khKNwxfJ5}{CMeqYW(87gI1<9n_&hc(aOj25^
zmPC1En(e99C(YvS=!P*^<9p2g*)Y!Dthg6ruv#SM`=6xXnN0yVBrN6@=KG^^1gEv)
zANSWnyJ>7lHJ`|)+~u4rvj4)(9PK(7wlfS%ssjC8qKZ835F|tY`h#@G<-A3c``3Sr
z*W~!;BofM0 at ZP3379dOZiSFs3ZR3*-co3z3pG;mXbH${nrGXlrSY+Pc at mQE&D8ba(
zY>893$cnr<fM8{E3Yqe(B=)*oeZ#O}TP114p_(da?OwlJ92YVtS!;o4>BJO|cxjnh
z0lxy<9pS5aoyj^s=g%mrHTFpb-e7#vD)_QWu}GW^Ua{0za!R0%(87}R$K&}{r95wL
zgNE3oqUYbOqh6d~R*iWJx0GotK2@?=gO`xfA)B@!omN0~Y0;rXqAB!*lU!bwB2D(!
z&}hj{x0^oS#FdH%{1?>O^x*B!vY6H~5 at m}I3zQGJwusx>G76rU*iuQaKGMk*)-IBB
z!`CA-ksf|)a;A>x;gBV<;^$0rJ$BU+=ANAxBFCjjx!MBv#8Rcm0)o71?%O5NSogQX
zcCOC)DgeHNVe%XE!Pm&l;9ZQyb?*4brK%ei<?h!4`{KiVKNigO%s?px%qOV(#XeOV
zuCSj<FWxxFCWX?NYZc2dA>F?9TzHqS(nu9ElNjDKniDWIV#u{BowFycBJ?19#cg%{
zWx9>EdMEq&CZVcMs!1I77uooG(z$m|e`jfWw&RAS&j6p$O}WlaG3OMmguX9lS!j#T
z$2F2rl}dt_ at 4iSy!rNf!ZOMA!3MC704j0YKq$xp^3~;=k$^q10N!&<D-waXfv%J-<
zKvZ0L_4WFj)ApN_8^^5bIMt#lsTKusiym-x{LBr~wv_4QkO+JoN6_nM!rRVFJPOMs
zMIFy4dOp;Or8Xpw!ZHdl-#7BATwQt&^{C`fi*~?1J*5k6Dy$D}!dJXCZTMy^Tu_90
zng{34?@L`54!^y?2<F3jz$6Kkd#9GYz6=t|wAr at na=7c5EIW+)e5hcNioKI)Q)e=v
zE4}qy$Kg8?mKp`@=TUA#suGX!h1%QF#f}VTmk}~_iQgFCaAt?8B&=oUL>aB$%^0}&
zU^MlON&<iTt;LKBjL$PiRf|puD%Up)U;l7*$xgr6?IQA(wA$TnO_nxkgV?Kw(<1n!
zud4 at 8yTP>r at 83@==ItH|JFaw_)&0COo@}Y5q#ea*Mz>=A$#1jni{JX%GZM?+4e=;+
zzGo=>>?Y}p8((z%%Clqen(S<gN*3d*1NV-Kd|07Y at 6^*<WFrea+}JceN7l2Z68gE=
z#%3G$b6;XX>eFma)exMDYyy{Tm_zlev;|uxl?1YWMT+tWPF{beGZZ}fQo+ZMI|T_D
z at M^Z?kUe_!R;Ivt!|FP9xN2Zm&8Nngqbey%j5OI~ZsOM}d{Ycas8t&#RCa=$9_ at 0v
z6BRFsxvA9?`Qa5wbusRya!nhJcq(`4zZuWYZ}sO^8t{#~A0-n$W9{ZG`_s8(YFJlU
z@{CNf2LDId;2p#^d{o?4d<@o6jvD`5Gc|ahRMTdWuxLrpU$E6zy0kM5lg>tpaS~30
zR}AQJAHQ%px#xaFHom#f4!?47va=dS6i%wGn(e9`^H~YUPL6!u<Y&CGdXl=uxh7hw
zmm(jdW8N`dQ_4t5eC8)^?Zn~QIL3N0RVBGwiTkPceVr$G5jvBDzwh6tp6FHxT{r`8
z>_c$OrCoV*lHpPr6_Ko3_WhgVF|JM*scEihNZ2JQ=DX_eMr5i<xM7Vi?C&c!2J_4r
zFPO+C>YcN(u{p0XLwPp+<_&JSNDuc?J<f4$5zSp`|CubA(9LB+L+Wb}S)X)-%wC&O
ztuDN4-g42qBEr at 0S=Cc(g-+Z%X{mzK#*Aa%S0rgYl(J%qbB-1`3rkzVM5rgzRc;5n
zhm;}lga#1qCXJzq3S~7MCq4OZQ<IQtsl!&hR0}E-1dt~=kJ{vUo=EvpDHZ>aSbsHK
zfij?Ns)`#z!r~8OzR!IqGe_BZY#bIXjQRc|M73kgRq~Q at G2ZpTt&B76aH<)%(Bpw}
zafs66#}3En=0OFjvA0KI(I+wU*E!D<A4C_x`M3{;=kG1=S*rx7#AUg&3tVxF at V{Gf
zLF6`>bm`L04SDy%2-m5_=<+ZHwPsJjp+Yie`Y72a4PjZ9vPn3+rj%F1s{<5XdAe6E
zDHf-SmF3>uB==nrFR6{%l#P`c+v~DpLOzyExbbCzQZZOC#U96NQ6Yutin!*ZX4Q5F
zvE(hX;beCg5;m=gW6H(jPgBId{kZrn^`n;)4}*G3wIH8_D`TU<^Y{1#8FUI!Gkve0
z-#T8KuK&H?E+;}U{7}j&Ma-<EJe>UwqAV4jlOtApX8zG-n_EXj;z=GVMMKK(>Tpkf
zJL(-CRdW7>a(R8h?JVJv=@&O^UNnj^$t85AS<3#Htmzo*c5!eWk}K}!73i5^(f4T3
zXK(q?tQb#N?;UtpaioAM2$!R2O(8<3N7~}DI%2ANz>k8fLn%_OM~dkYzkR6BWb#yf
zh-@&u)tQ&MxWf|`{a-%zGN^V51)rQe%cnU at wdL|&CIKGewWehkbe2 at FM2zQy&@<`w
z>(_(@rtH0gVa{vyD<2V|)`224KL$GcYf~dmG%%?anUd>BYXm#p^?mr`%UNI9s%B<Z
z*+eNu`Bf{2tg9_*SsgDI_ at uQr{R{|O<DN=NRot2w at TCl?d`Ia#PuI~cn_eS7 at aSsx
z3;s1?;qzBOe}BcS_qs7U3$LFrk-l{!qMt(Lm>zODCim3W3fV@#f!h15^R32%&+qAa
zRx5<ajY%+$1XYh*Aq+ji`bH{^a?I|!-e=KW_J~p^`f#Q4**9eC at m;H$bZO>iiMnOt
ze?0RH)1okU<d>4ooJ1-{tQ4{*aoqk^{cUW5b~8pUM);6UhFAVrV8&s`Qt8u(^xdfK
zdG=`@(<#Q$S<7v;F8dQYEVpbolgK}l(_2r-*XvD~IKN!Byhb>3VaDaFa^QyKgYO at U
zTDoQh%^Jg<<rBJ%bQ_2Yv(MfTqUuk{RqS|en`QHK0?I+?i}UB?DWqpUTD^FYgdh!P
zM#Lg*RQ`6!xO4WqFYmt*th#)N-IMQ%@lq_!tEL`ninNHmmaWn}&-!HA<wnc{JM*55
zkBNwUyth1~&UYB6N5y>0&ekIet+rz-&n_Yxc^B at --q@9GdP(6`uja5U<o01>Z{@Z^
z&Z$!4Ug3hcua3nqUQtH5B;n%}RO|eqwQD6B>wm0wfUwuIsfcW~S`5R=+A!-ajL*bk
z*5gfL*3;o&#kZzAAykI{Qmw`D`I6d;TTxoq&!%ua_Erh5Uj5-tOpO at XDy<QcsZfb<
zV;W?tDvVC{GH$G78;}iJ45ni*w{MPzHHdwG$*)$RnA14X#rWe=%jD(J&mX1BJ;FKj
z>c-6bwb{j8-Ruzcl$R%{;+4i=enD}sg?d!#q+_P at dh789oSS`FDSXwU@)^3S6f(F*
zcB!V*4$&w at sDG0-6igALh@^xf!^PoM9g3eCVfA<Mquze|&?zk&pe}XCUXEr>7VG$J
zs9l!do7*6rPLx~M_R{O2(k92lp39tS$ws=%e$~@%-_{+6KFjpUr!i<gb4j~-L!4H~
zuE^6~rHe&wHtX4;yi(ioDn<_<xqQnBr%Sj)?W}{f%+ at xo>djA!9U!FNhfuRGE)y at u
z6?1#4307+^e`6vRa at -x0QfM^dg^eosp2<a$d|r4pDA&lia5X2IOZXg{?25JhB}B^0
zGe5?x)?3qFo`PSPKLn3=p4Yz-AeMFCO(-I=YZTUbmIxB5WjwU-?NMg_{DgADheawJ
zi88u*leey}VTWWAJw7h?NAvo5Tt;5djnGg`@`ANoorxpPy2A7#YIRnvD9?EJG;{UN
z)?!09OPiZQY at 3kLBl)+{`~w?mcjlIq6Sd0ig4TSlkcg&_`j9+S%N2SbafUBXWx-`j
z$si_6EmKUpIOwLPxM_{8{#XNzbdLKP<!zEct$|8HXI#NiSos*Zp6}tHppwR~K6{p5
zHu(KBoN~*6l|wwQDxbV!Rtfs~`Y5ck`dESw$Aqow2rM=nj6dT~T?52a`qw?{q;uM2
zKgiR39B`ntW at y|Rk&UE)ISpnHZ{kwm9J$xN0gqUEU*N)G5faK{N}ywHEE~H}%D~dV
zq+;BulYO{v1eSRQeY_neXFRjP(y1X6QR8N{m$ojxLAL4Dc+pa+k-51KF>W+*NUA5y
zPHjuQqyeA6QjFL7WN&BKNB((WPsXE6lLnl4ZC<lU;xM(mBI&cIwdR+OvD8oBe8nzX
zzRq7tP&J<Zx>16oCPGHBYkl$IF`4pLJ=VfJB_d5Sm2Bf1eAC9hM~76COf5oH!*oZz
zk}jQWs)%7pDt~uYCQvBg=9qBrhe{Kv4SiZk<uYxrD at 8ZEgR3b;({H6lsAQ`-uEnHY
zdX6$W)GZ8 at 6{7AP;{h?@bB0VD_?#Ue)cb{9<-BcBKj4CTr7{FLKyHv3<OI1w7QpdB
z=8!%5H@~Dz4xw+w&F^ouuV4&X%fnI4$x++GLf67v+uB3k)kX`wc3s)TQQpE-&BPV%
z?5bwsqwL|R>|qc0u+p+{Mwq#)J9)}lpx@`M2;2pbs{{D&2K|0dx%s|312IEt`;PgS
zkI%k?(EfaAf4+Sm2-1MiKUW69_qV8zEU1kc5C_N<{CEB3uMX<!`EUFYAhtUQJMh&4
zTW8>d9TN_DKvqE9Ak30?Ww39*UycKIgnmC$2?ttSAsgu6h23h931Bnm;P+4DL0nVd
z<AL`7Q;!MI^V=6wpM&xrl=q;V2jw~_&p|mb`T6`R$3gxF`5xqh(qjVjd4MqU{S*h~
zLzVuwpEf8tDwZ2oJYAsA99{FjL$pEcKmF7|`5pg=p at lBzpKOAz7bf3>{0{UV=sVDJ
zkRFUH#S0D&7k>ZljXC(7VO9elX4H-k_+Z*!jnCYi4+9!txWM;^3>cmksCUr+8YfPk
ze=!)uPACN9+5I2 at 3~vuNP*^b1(fYukM|V6h0UJ~wsG;88eb9~;rmh|)uD==#*b5p_
zW&!d-=||~C=|t&6=>q9}P`XfhQ0Y-=AYg4CuI46aJ;3<(_fYQ}_i%OlMWDSb%pEQM
z at BkKzCSbg1PhiKGEwEmo2bCU`1{Dt#2NjM^rz&$zTS-k0wN<~YCU;3m20HLpL#WGX
z!_mJkhh`aVeGR#T{85ZhRfTKIq4(jcsu=$x6S#l<4xu|4=JyMU_WhUDF!PP~2*!W+
z+28(t6&U|jEPutv82>3O|BQZ&KN8DdyAR`!gL?=MpMa2vn1qy!oZ>JgjEedQ4J{o#
z1LIMqW5-W0vz%mQJH^hy$#t6h3=c0Kzkr~S at L7>_qUSG&iC>hEl!8mk$jZqpC at Lwd
zsH&+WG&C=1X<ydS)ziOn)xgl`nz4zgnYo3fm9>qnoxOvjle3Gfo4bdnm$#4ab-x?_
zH=%31XCTyz@~;6A19A}vs);X(VcN$PJ-*$6EjWO2&y1dlT+#D2Gdi3c!h9p84XJ`T
zK?}G);~a#d>_iF79SZ*;6g9sffK9>CvHnvW)aMc;0`Wsaz at b8TKwLN|9cG?H&6mvR
zS(F()Lt3Eca#YT$U=MYaF-QAZqIEl<<6487xq&?s^w9t_+oI-E)ZFiZo&y}gyv7Xv
zo1k046RqL5E>yS;I>ZE|vw?i~<Dq5;67=kW(QkvEWo$r76Le`Yc`}3CJka`4;i%(`
z6FOH^m@{zpzfxc{@`4a0bS*Jj9e}SJno%+BfL=`5QT0NV&J~>#YDPxIaRN1GM(eXj
z$H&yn1fBCiJMp0FhH6XHF#|Q{F+-f-SZ4;v72-m-9%@9G{VLO+?P>CFTgnk#M$|~c
z9OGQkWpVv0r7XHksH1}ox}FF9e$d~7zxvw|q;N-%5l3`uS%JDTgB-v)2KtDL(u&b;
z54M=v9`qC@#Nm$?{ZHdd;2*}<zxKPuAMGv!^l0z*kL#}xOiMeVTMu=v^MA+qz-~AG
zU-dXB(?R?FyBe7PhxQPJM1Se=K=-ieeyjdZt$}I_R5}0Dd7$sy|FS>5&^7&^?e5>@
zC-T3 at 4>fAd|Cjll{gt0Hy2ns at Jwbd|^jQu{$G@|r1Ka$q at j##O|DycY&{{1ZZ*<A|
z_D2?mP(63hi~m}F0qFey*xybdH;ereW9-{$KO8kHdZI_A#lMWDKlT4To=~N7MxSB2
z at 6S00W9NT57W}F2+&}1Z*q;S$(3a?m)^7f5j<EZW`5fr~-;AHXb23M_zX?_?4)p)F
zQ&d|!p~nd)dcL(rADfubR{<7(jgtRtul#@X-+y0TR1C}*MfIrzn%)0xdHMh7zyDr8
z>dYT??Q)>m?cen1f&Sm~y(PMjFxG}SVmSl0L2F<}AKjeMKCWm!C=Di!`M<Q$uk}iE
znDa5BeKy`_;)96$e2}tc7Q+YYhcxyXw$B?cF?_JzCU>8y_gQM657t8=UtsuPJygIx
zGww6xJ|8{MH-llOgZ%gT_&yWu^NEA}o at 1E#Airr0v+VQy<o@%q``oh62j}Y<`|P{V
z-ut|_&(8b&^PoKYtcJFISM+%a>U;y$7dq%;+`+6T191b#40`(SGfP#lhq at B|eH}{j
zN5Ao+9R!J^uOpqnI6%d8*gxOEXh7++LyrJ2 at LvL*4m%gQUsle7*01rGwhO=7ixYkI
zi?TAz3K&jD4|{vCOMv>rL5+h$1P}o(C7zs|96suEmLEd at KsG0{sD>UP5p<59Usr<=
zbO1AYh!GiE8-ovMNM~>GAws<-f)FA?zkLMtvVntfXlCXHUDlT85#49hzJQ48uj`u2
z>Pj*QSvg3{#|>Nu9{e;gzmUj&I2 at cw>45(l;A{?c*2D`~6;l1xj`(2clS02g#{lE~
z!D(0=3z+B+j>F=3EKb1UL at Z9i;$$pt!2O#Wu^4qv31y?*K**r~|HJ$*j`NrM*RX$e
zivRM4g1=qJzj1%Z2>iq0{Fm;}^g(|(-2c)Yq-FkpyZQe=@cGw1wf=v41mOI3|Ly0m
z5rFgC;r^HI_XzmE>i((zD2IAu#-H?O*neH+{d)h+|80GL*ZcppcP{WvmG}Suo}?|k
z(Wa$WC^SOBDlN99D!S@=g at Oe_8{{HxO`FgL(j-e#D9W^4)K1yP81BcEX+=P$Y<7r_
zIX1h=v`(fKnM203bEwQAc8(z;!vFI<=Q$^rCa9a+ at 3;T&m%hGzf1c-@=YBcQIZ2*`
z`lz2kR$iT}R<Bum+wFIBeeKS>)_wgO-(3H#4d32)_jmql(|7N=ck}n|yTAK^Ee~$p
zw*8?U-+%a#oj>^Dqdh;`_1NympZM{fpFH`~y+8Z;Q at y|V<<t9q^~|$<&;9!O{l9tP
z#r~HLynOJtul(-NtA~Go<PWdCKJdpk{&e)sx86SX=Xd^c{M{4p4gU50zn%R1sSi$n
zc;=(CAD=t_Pe0I#{J`o{J<$5^F8_ab`v39=ijj~1kEs84r`>0>7J7XapZt at j!oAe)
zY_Yo9i1^pW?N*D`IKk+1HCbB3*WStNYWKAkE_HcqmH at Y9vB%zQ(cH$6<HmsF4%afv
zQml*HZ62Si5Z+46Zcicqj;}=>pKjjaNVuIh@%}c;3vaSn92T+RO{>e<f^0a|-|=pB
zIpkw4{43+eCXd}`k$(@`=4_Gun{Tqo&-m3y)iW$@HaTnUUN+PN^5$J`*WRG;F1K0j
z-qym!cBdSV*KOgSPcB?$bJ}idw>i`dtN!ht_Lg?b3eByQ)2NkG^S620gWML6MGhDe
zA7E*-_}xxqRz5T2yf&BB=5YqNZ5=q%sku>K)^?Bfv(}nFV#4~^GNc?fhtX?sEVEd}
zhC`ZvbGz)Py3tweoF^x5kJaAfur~+C)9gYQGpem0=<jf~HQ6np`Dt#If8JGdH at O`4
zWjIZB^UuIKeg2NZzdDQ>w|Q-aO&-flc8A>}zaipud6wH+;3#yifc7pG=llJeTuV`I
z3iTIm^bU at yQ0)tLk6e26CJ^oxo2M<{u<CZSci7sRT<spUU#kB0=2oB0iF*;dGtl4C
z?!`z$s*61~XLGARKH(k`pYTV8TdV=$nrsdw^CE|Z+ at Fa8-4yIz8tm4_`-<T4t{x8^
z$Ev^crr`6IpttfVs at pDpe_M5<_u8A9Y+610{hdK>6iD96Om$mC0W`NVZq;bW+U^Ui
z26>!wr|@}DPx7W%N>i);yy=?-okx8K*ze!wcGwr&)n5o!{dv9#m~K>mCajgT1`#hc
zCvpQ at 9NG~z-vM1I$ln;jpF&<c{;E7jsF^iacCQa{cZ9fGHFt&VZWQiWb(ON)q#ak@
zIII)yg|)S^yH;~gm)+&UT~*s4yYoZNH-sENJs2E*U{F17mfih>!TH%U7 at YqdgTeXV
zq`8%Q_O8L;{JS-`5?`a{R?;_V?iosXG`I2rihRwj<Udw(EBQVBp6Z?^yN|sWoZmws
z?mpq3S})3{xt08OYi^}H>ovEMey4C(*C^$0)!a&Wljc^!muqe%e9?Qs<u?lVbhC2Y
zAl$QN*DK{a5$qn&+_RMO2)D_sl&43yYZ{dJx;3|Qe7)vY^4FocXDjWaxs~#iYwl_#
z|C)QM?9S5M;*pQ$7VYt_>Q?&WA<eCPkf2X<E9L3Y+)Dj)Yi^~!yEM1bKRPtGQa_EF
zTWN2T=2qT!pQ5?1m)-fo-Eh59o}71s+bc^wU-Yju&8 at _52yw>>_gs-*67Ff0qCJnR
z<4q~=pz2<z)Zek=!RH?earYlr$EVUBy~164osz#E_4q=ieRqbqyF=1nAL8y5Zd1Kd
z{~gEG`ngVaw+i>d3Z=g03-@)Rf7XT^pAvH1DBNcA?4bHp%V!8FFC7oA&$N*GO-vk_
zILvTGRAOvlls+oaV2B+ZJG;t66;<-nO`4^xGiC*?Kc?B#&vea`ckFDy0-U#sg#TQx
z%`>}ciLKc;-MN^5hMo`1^M|XmczqSkKKnA8$>nZ$YyOI#5*^J%%$62grK>{;IhPu!
zhU$kr<BaL-yuEW(Ckn2=z4P|fooUh0H8nN2-F90v##&LQsk4h>DgWXrrg at z-j33gP
zOkG@!`JG*z2t|#pjjgS%v9ZHC-0tOW_mJnaRWzGs!RC^g^0?k-ny9{^x}oO5!)srw
zUpMpXHFKZTzp`M9+y|~+#AWv`T7so*vulyt<KkNIE>aehi<;dle7OEDa at d;|@qVxE
zKD)!asM*zq6_j<+ax7f1uqwQ2QV~`K)+<UUxvfp~NiQiameQ;+^Ds&)jiIy>gImgq
zr!1?1vdqbpRhmRu8`Anyd*PoNL#gHwlv<ifsqZJavx^caxAYpyl}gDlm^6^sAKx46
z9 at ZFxrtkX}`ZfwBCC5;*IhK-3hf#7#f?JBwQA~-hNQ#f5c%&C!6603#5RW{>ArH{?
z26uLDGz~8;pyAS`6ysLn<Bpd-A)3-l7gD-3pVV}EbVz5IM8j6&_{3o}(zlRCmM)-?
z()E-e&gtfJ(vU`JB9}2Jx7imbjL#mICXJxsg9!t1{lj{r-BFGDBAp1!?JVUaQjRs1
za?B}|Q)-}`4OxR31EO9=C(>wN0*yu)MxzX)QHIea*#nvV>Al$#6DZr9N7<$0C|k;*
z)B!_(Qg5O=p;3vuA8{*XU5~P^)5>a!g8!valqKCkDQ-g}`x&Fu_8N}5N<djq9uLx&
z>oS(&>to#6d2y5~6;MDqOu8ZwC$~qGL{Yy)++$)WQ5r?+8GO8#^HLf|<E&$J<4Q;C
z#z|Saq<-X8t!D#{XLDM^DSktYTW%8$bCI5srC6PsP7$9sAr5_~6!|Zp(0(E3L(X%Y
zoM*L9WS1t;SYNSjthGotw)8UHSm{z-`oM_(lwQ&PDTsG6pR<6*TIbW)(s?vis;9KU
z)P957Ryxk3j)qAS{e9+=I2z at fOrxwNG^(_iMoE)MJ-<<{lS(yQy3%(^b&n#gZe(}1
zQWvE5MJxJZlhzmc4F{!-&_3pPtv`qLQ^$fz9xK*FjFosw_F#NnGn@*1>AC`Iny#QU
zRaYP->a<>`)+N{V#c`Bwz6yDng1nSaVE^jm@?D^Rs%6*eU;^d=v}I`D;Wm|0qcQgL
z(7(pY<;}hfaaj{}qs<Ar(WUXa(FkX72b|AwVhWO?brkKB6qZWLoZ!<_yj8eI#L@^~
z6w0or1T*TG{nY%(ZFec!?h>?JKH6 at A)^<iNTS^?IGzE=YInQC|HMxE<55X<ZMcMI`
z?Te#qoR^LBvf-ayf;!2Jqs%Qqb3{=z($v~1V%!1qoIHoZ%sdWaDXT=8E3z+5!2B_W
za- at u~b7r?XF9%RdSJcUn<8eBQtB)>{5~3)<6XoWaYe-o``&w=y<@!=67xR2B=J;HX
zzh`Of7K!mXp0`8#Z9rPwo|tDx)F%$8r4#3xQbd at JLP^g1%KNdVg!Rl+-f7p)3yD)b
z|8<=Eci8iR=X-Rf?1BUuX&sGuKL_)E8jToC8A$FQ-kX5_c&u0n`!*`q_aM<*BJ4eZ
z>E%RIPGweahC9764gE(=FK;W+8?f_gdPx!`RR(Z`L?gnvL)vUe{j+~6{L?XqY)Ntt
zZ%i!Wc~**(D6UDgtK83$(9eb|*R2icXKEXSJH~1Av1Eyon?zm$-GSF!t)8Q_KC71b
z=^qgN29`Zblv%#^4ac<)#N3oq5^(KP<H^Pt^1-bBaiqrg%%enyVUyMP)H;#JUoyfZ
zVf+n8xJ1g{fbkc}|3cSbE}KuxC-QtZoaZLYcbNB(=h5}zdVFz0O?GJ}rB at jaBo4+8
z#P-MZV#GA6*Lr^7AdO3;alTAk2Qz3~X*!KNkTW=PAge#4H!bqmQ0pN*meMiTreo}<
z<NB6fpCZ=-mwfIsM9X0_MLn#9ULWAr`UbAC*&9;bDMc8IYQIw3Q0*TH5+yW=bx%{6
z<Czj=Rt8Y*mx2A$Bl_ci8cT+{FGBtG9who5?4({#7xDaYG#=NOIP{6wpkq>I3}vEU
zWR~#SBH7JhJAcccxyJoc6v7t7Q^CnjG9Fw-S-vhZSnr_N(%Xa1k%nW<CatEJfv8^Y
zI}zqvQ%WM%S at l{k)8 at TLUnSann5ac<dkQm0X!C27Ki<GG9CGarJHB%;UaiR(FG-Z+
zpX0T${=pxJqF*Ok71{@bX^P|WV%!YJ+{$ZIE&Kq=@kgR<2+!2=lBmP_GKI1y at fu+t
z#^BR5TKc)NHW6cOAUY(^f#uQWxZwMOPZ)ng>rPxBQc`0nbwko1u1Ni{y)o|S#;787
zT-tbV<YSokL><`PA=>he-z{-i%zdd?pQhF;t1IRDJ_6VGR9xS2ue2*<J``n9>#1&#
zpS=;i7F^Fm&bt-o+y?tF^t_Pk<_?^*3zjxp7x_Lev`>cim!Nq#%sMFaybIOwkacXB
zwMVFbm^|~Ea*XeB8e`o}V at e;RF;b7V-g8F>)Q8+}hROX#t&_25iB`Zan;lShm?N at t
zW66LvGI;!}Esk%BdM#QV5HHuoKHRhUK+GN4=y%!m>HTUNhwy)`B+(2t4Yh7Ayxtnp
zepS&DEr8iV;}Pcvmor8yC!akbMxr8ErwCgE?SOTwaSfdZiiX5HA_{A?VKky7S&Mf+
zt|cGBek$Tgh?S at k_WRip<_1Fy=Ef8<?279h=EfQ?bX^#lW|EGQO5(L~SB7g?BkYuj
z|7Pedunbdh- at i~@6eUV at 6|7i<F+tCR%~9jXjHb+{5$@E+6qBJy?dxb?%q!Vut$lly
z_KQRNVGUo8_LJ8dm@~2d$iey}r`|tzW>3JJB;``*U57lUAx-7E$V|$WD#NbvIpfNG
z`pB^N>AiG;F&R=PLH!nMo^0G>W at EjBGRb$&Tu=NNxT28~-2iildU+c9vQZMP6YhAp
z>)_sL3h9sPeOO at v?!o5JD6<LoVY6``HiMMf5clS~3topVaL*JlW_V8JxwIa4^xAWh
z_ at VbQpEg`*e~nb9pVdFYy`6R)&y1x^Um|6qEi%y-nI5d;jEQ8#STULnWW=@D=+V}c
zZb at D<!ceAkq>)~d(i^tELgt8+q2aW7>WjGN`6}#LQ<ym+Qn at 0vkvYsYBxt^j3tjsU
zsQLH~*5f~hy%}jt%Xz^%-zwJm9)G{(5RYFj(GeKUQNk~R{`d-sjB`S+k3sA1SZ&?S
zdHBhd_&-IWFNiSNW3iriBmA>IdHtV*dvsqG#(pNoeg^K<Q%QXeHKgzH9OxT{>kC+t
z*el<Qmqh#fTo(EV>NE>=n1yrDS0dKGGH+NhZ!8QuZ}jPgUMEsU$NQh{1U%PK%f9J4
ziOh8pt(g-rt}k?NAL+g(WPViRUoumo*I?gM<Nt)~U~s!92F_Q5f$ctFwnR%{dqlkN
z&O#aBJ{H>UVeVfK;=I>j`g-*mfbodDXPQBri*^6dIVALcJ;HTeX`_W`BU9Kma_cTo
z2cJjW;nw&mF_eNjOex{#9r2B7J&ao}(KOiT`k*>1%Dy6j#+QCYKVDj_50{Q{RLHo(
zx)*bcc%I^q|7*8mtpK|sF#bsQYT+1eo>c25Bc3wMNtA&(A)`KZP at UC3sXyLyyF~Zg
zA<=9#Ps8|`?Y+vptXjBdC*k}?#nULC0oT1`T=SA>)P at Mp$2d>*qJ5h#u*Qfq#zOi*
zT*y6v^i7GPzo}@M;&|wJQ*msDIB!P4d7t!5?MFEGW7v}VfH`95x?jw(m|s!vp>ym9
z-;yZq+Y+s=4;i1CXjjy0X6bOstoJ{aQ^S3CqeK_qjW$!mg}W#Cq_t9n>+z5^xZExQ
z>-rM^wTAQZ+IJ<|dJpED(0T|QbCI-qpB&OAnVZojuztkJ98$O8uDPG^-q;17=gV!J
zpuC4ruRi1Ebs^?jvzTi=I8S}gC}54ItzW|4r-kGpoL_`{odw^Q$O?Nuuy52~_+E?8
z+<`DxJ}lAj263MQy)eN2Y2(?3FlCQOlrO@~fc|oTJG*!|<yuEku6ZQp-)zjkxTn`v
z`$|9S3O6o>tdWP;<oZ%EU!<VF8)!sT(uVlK*nyaS<rxvjQ>)c)3hLKD$p<j+7Y at HX
zSIje+m}f>zPV$6?E7HOxBU}<C?@AE!6XqqXokn5K8s!o5leo_svUXGN&*Xa>1J*Y~
zp9^VW<@W~+sF&pM?+=7|E|5J!na|{F2<P`g{Zt*B0r&CQdBZ5xI+9Y+2B}g8CAj_X
zi75RIeGS*}G at P%@3pn4LgY!q>{4DK!`Mo*$J=IY(9R4u@{t>T5VV?1*;~}(M*<*0c
z*)a6pul7Rm2aWeI&-AtRK)^ky+<q9xrEvjcIm~q>+#JGvV_!<}^=ns}DSR6Io at 0Xi
zo at 3D56Y2SpHlOM#yE5G!_<qSx-;wA8Sd}_PL!Uc`K95%2>9~Kvcu3zRzZc at a#>&qa
zhGTu5NQMoG?jiaASFN)H-%t35)A){*E3K2lr7=G-pdKTv-(JR8%uLqN616VFt^F>r
zKAU6E(QU9bf%9ePXD@=*(ZTD2FQp=F*k(1n+^3R*=bR1bQ|fyT5$1n)nvQOUJ)xCD
z%tQQnl6 at m6)t5r42NFzDUOeSt?B~5daxiNkqd%=T)t%C4FlqCAnx4{D8{EkO{mnN9
z`GdU_dQFha7*FvY|MS5cb9K}S%QcI0Q8xxpqJP~cmk;H_nsgMdp`#EUbE+w#e at -0b
zY{7lYKuUjd?{IfwV?t59DWD!W9Im^g&_1I|G6$8_MzWS4_e33Sfwijn`3%>Qa_xFB
z9PO6K^HIn=lAwK#Knkb}W$sVG+;12<_kX&3!J>%u5$Rdzr(E|AIUZrY%g&3xJY#ao
zFGKI(<#846Iw{|07_bgiuCJN93?cWSB8 at O>!UwL^(X+5sfo-%p;My1d8aEhljl&u^
zR@~#nY416y at kdwcXd>+1kUkLc{iF-sk5%dDr?A~>T<Yg7BCY4Oe8msR*KccdbQboT
zz<f1 at nS<&t{OplK-!(%=W3SWETOzJW(A5F%A<qWF+}rV)FC)yyFlnw5ZVPl`osNp<
z20nWT==UMl-T|Zf>m)?y#_P${sH5_W{C<7;dg_7Sp*+8z^-?|6T6J_fAnbAYwc@<>
z0e<TzLc^~wAnXs}Hw9sP#{2yS;b*kzDAH at jMCIIA!7RaB1f7D51SbeyEqLlue_H%{
zL!{k5KG^@);=CsXe<-*`aD(7#LAT(ckoad^Eb=4h5L_wPC3uhE4+VcNctG$C!S at AY
z@)eF0oFrH&Xck-|c#Gh71a}EuU97M`uw2BSB;4nU6uu*PSa6@<F2T)$cM9GlXcaUI
zUM*-8j1fF}nZl!j`vspAd{}UkV3(j<uvYMD!AYVVV+GR$qttqwpzw%bzu-tw?w<>H
zk6^c8m*7o;4#DdMrwSGc<_IPVmWce#5b0zI_qj<TUxG&k&xn4TRv_w4ut2a{@JoU#
z1=k7QBe+ZO*Mdg`PY9kBG!!ZvBREO0T(DknvEXXKZo%DxeS&WYo)U}^<;oGfLa<V>
zPH?{962T6^+XdGPZWr7o*eCePq~LLQ-^5_|py>Zc1z!=IFUIc-!BW9|!7Ra8!84aA
zJT7=xuut$w!G{Gm3EnBVOmK<d^@5dxC4!d<juA`}Jewa}uKl8%1A<S6l>0U^d0cLr
z$G4)P)n at Zm@W-*M_`8s{Ab*pk#fD>5vu9SB>n4{>vN{|@oly<Sr^{<R9{#%DN)fW!
z?y)udT%HvOaa@{a^G&roY!yDA$KKTLvw7jWKtI#szTR$IK6^1AqpyjO?7$JX-PuyX
zdy7;#TwYtP#c9Pk9h at 4UV79l}=onSIywwQ8F0)JXkJU{J%2QWB%oX%z)Lds<P|A(b
z4zJJSusM;QT|cwk;j>q+K&-BLcB`$b)#9N#o%rm1h1Kej_f)-BU+1z|>+DS)i(JNo
zRBdzE)HI&f*V`;sIdbx;c{aK1P9JY7`GHy=4g58=R%xooW)m^dMn1d2XB7perJQnr
zdj`^Nwm3KlHA_?FkNS(x?IRao<Fn1SHaEvU%hJYvcd33Ri?0>#QS6 at SvbH-!t-|jS
z_PgHZ@!DO^nvM#}h^n`F+fk{ls5mQnCg;lJvAaBW--;@Sg=^p$E=5(l$76G9Th0DD
zz)uakox>^)H3r)1?_V4aImq_sDj at HA<fF~CET}hCDs5q3ri5RNv!^@JX={i2H}E$T
zxfhKb8v8t=>uTy})o49ARp<ZyLj_He+sM_-Umd(!zufCaOuoexbhi$rsdltX_u1Oa
zwhp9wGkly at e;e3!_2>x}udTX@#&Im*6*`m6w$y*l52 at Pjbz6MRt$x2Kz1d at NdL78V
z at WhNvHBPI^f~5IV$=nm<Eoo-jP_Sm?EQu;@Eq13k<6iW^#maX%<i3hp)z5WX5ux~k
z2feLpXmu at DU2UQj6gRi~Y~;PgjwC#CBg<o#`#GA_>+)1ku5PAF`??5qbB+{8J(`EE
z(wSV|fZn*6kDDyq8EBbYOQPbmQ!bI~NAV>32G^~U6wPg at saIR(3UyAIUTODX=n}mp
zMw{}L7xXa>$<0yaa`1 at XqhrVHcAm>_<ztmg(fT%84(&h)HC&lGGq|`1nT4yt<*-{T
zJ?;EW%4yZpMK&B<H*?x;o)y<y9PM1bS?p8eY;U{Hwt{P~h~4sZ1rKTqKA%s(Vi$h1
z-8QEP at l|#sd;T+~$Z^Qo5a(ROK1y-nQ$f at GZjq6S>gy{^YBp(%gxT8RsOhjZW2UX3
zhT57swUfK4$~D(%ciMe+i}D at WH25gVO?P^I7H2d3D&aRh;2f93Cc<~>v_8l)ZG~5x
zbAP9+wOQQqv_vn<y29abHPb{%J;m(u2gJ22z+ais)R_LJ&W-`!WU5 at D&VS$3^GM<W
zY&F^4Haer8ZC(M_`xr0Wc`?b at 5od!<-nohB3;H^nWtnY=|8L~}CwBl&3H85Oo-L6N
z&F8ROntGd3o-v`bC+3k{DR8^(=Xt+hbT)OmUnkFr$`_96>`t`E5 at k-eczo?{F62CV
zA<As?wAr2hDG{qAT&G&)B&;<Z%{DjZ7zLHDAdm5!8u74BKiykxYie(4v3X3mT=Dpu
z5LNGUTyIB`7ROxYayxH at kjs8`F4xj_x4Ov{s*m?C&f+6g7Pqh6W2?g0z+j>isN2AD
z8Kc$9quHfhGwFV{{ua8Nfh~yDnp_Vi_9G{2`Ri`i^7*rCX0lF1zR{)a)+$bhC%}c$
zREKw(7$0;4yVTJ_3)m%hxaWslb*7_q^ZB^aX!Luz4G}abM-K;F^>FttpQDTgto>wH
zwG9gc*9t0)o`<d at UpvUgbzkFLX7{+9ZAkHYi^pzh;*MaAaHN8IqZ%AGo14aSy(-mT
z(?NgH`A1nu*Q{__8?XYZa<!xF4v5(<wEIqwrdoU!M}^zX{jY*na{5ZIR01k9G#b#0
zA<Q+rE5I#Ji1Y;xqn?39kP_TO6 at lv;^zlS_O~nJ%rgT|qk=HE&#Z|7h-g)##P9 at v{
z`o3PC5KT6Z+zgm)<cuP{dz60Dyj0};*`ccoH0ovgx+<&<l?dnYyZiD;Vi%8p_&lf+
z--g7c(1KoBS2ewwC_Xs%7}co~Pu$9kq#WR at U6R@Z6)(CGeF&EUZuiRe#f#BZh$bt2
zLDbniI->L~d8V{t=4^B0il^o9n*nRjd1O&~h*c~+X^y(CMDaKl`j2>>7q14_V&8HU
zcF|0Gv&ZFyYmu at V@GcT9CkEG|x$@U6Ee>TL1!JpD{yuD$pdrK^8{#Ix!4`!_TSAV9
zywv#nTY}?P-T!R->hG`a5$CCU{p}ELb$`iC!maK<*`ek~xEs}Ugu7O_hY9x-;m%ac
zCEV(hFoSTXsTmXQ!Nm$YN6KI2qtG%O635H4!yoSx$JdQkjz@(Y-y at Ef&I%vCTO99*
zaD2Tuo<BPL at qe^msjrePuNQuIBChT?8V at W!c>a9nI(<+(s_vj&y$tk;FI||te$x4o
zpIZLk>;GQ=_xdz1bqw(}C(u9Ct-d<l$~YbTt8(1bVE(7lH&y?0;d`bk^Ua_j8(%}z
z`9Uwp^DJKrqKSX=LEZ;f-iMX12fPn!tYDmAyf~H+a4bWFWhRSw4Z at H2QBD=c{uc at V
z%Z0l_xT}QQB*N4S{|3Q%BK`#;ZHMsZ-ySl;cp1ZcD0 at U+W5oHLBG0P?SBv9o#PPKv
z9{#vL?+ARKa6cgOvsI+`knsPp$lnX%`0vE=tYJ!BsCD+QeHkLYl;g&TuYY;g{w=S_
zn?)+eBX4H--<$8x^Yy>I4}`q_!@ERJt(d2EQS$V$6%%yh>J_Um634f{uJD0v3Rm4f
zAaB`TeoLp!9XS6N4<3Hd7$N>u=M<I;KK?1suMm6!=fAk+=$4W6=9XhyZipQJEf2_Q
z4lXEj4u9#ek-mI*$>EZr=f8aDwSf<bif>Uup2KyuD&TmZ_+6!ag1v%!1@{Q<7VHt+
zA=oXrNw7<>L(n1ED7Z*)zMxsqBv>a{D_AX9E?6cwMX*$`NHAY8OE6I|DkT212Nj+a
zJT5pOcu25MaF1Y*;7-AA!7jlL!MLd4`w6%3Zxw75oG)k+tQ9O5ED|&d8U#<jEb=SZ
zFStjrM{tK=x8Nqh^@3f3oq`>LZoyW;M#1 at lwSq;0MnOYJ{3JMdK;&1jPjHW5w_umx
zN<p7squ>-lqu^(`XZvSf>hF&IPd)xgb<sOFeE7kmqW%9Y+UqZ(y+nFFrVxtxQ(d2*
zd at KBOlu*A<YNyZp{LdQxUmgEv4gbIOr9OY`9Qsp5|I};#8)DviBe=g#nIz_w&xxFO
z{!JPF`9EIq at bAO?7wmE3&k+#)4VHovi(s>0ZIZv-9PVmT?!A_i{97Wl8}>5neV75~
zUIv>9bBJ)0gsJ1ZN3i27pAT;puiYZvm0$VCsLnI*zvZ8|C(-|Z<KUBizd#rA(u<cG
zUVR>NPK<+p=QWOBdAO_jfbVQR)X{11cq&mEH1pgDq5^1sD7_#PYcXgCI3f$zXXrF=
z2FwlJ2yTIOKyL-F$tGG0T at Kzl66+A?PH at 6#^jYZq(eg8C>Vw`4_G2*gLo;1Q<OA9b
zCXB;c?u?ES!RPa^-h<u`{$M=Tif8z<r9=;1jQl|F09WNBUFfyo_)Cd4L+69<!Fr$v
z!RCor|3X{A4-1ek^jWa15d9XK`6xQ;X^tPfs0d^6BOT>|Ps56!_kq7HMjoIKfzMCI
zoCCcd99xQd;Pb(6z}7<V2FG57GP6H8?Hc3<x)wYDI|zLcEGfseAG#Fu!J^LU6joqu
z3(e%uE95})CxA}F3ZSzpF?V30Uk1(mMK#vi(EF<Sv&>l6K_3FsrecgjGfl9y(9EW3
z$Q!g3eBnCO8+1QdI1_aRT{KgEkARLsGnesptsm?7eF1tB77M)>EUZUeK^K8r8<00>
z=C<pRH|QPU6Z3Fi0KEsi6O*PDdL1|cw?7W(a&Y>WP=4q- at Kp=)34Id0xCwm;Iv-4K
z#yXPI0&j1 at oCtjyyt5VSd5#mj(T=_ZJ;+>w`v7R><W;DPb0`bA237#Q7PPKLIiUx@
zSJvSA0nHDyR^Nv9g+2;CawqycbkWy|Mt%eNVGUaOyIRo9XJK2R`7`-%!gg{z8;I`y
zHrBw<J>ZM{V@}ZhptKS73>^iI!3AVLv=Owy4nenqzk(g(I6?a+^i|F?_})F}2hf9{
z at m`ecyiR5%EC;$8Tn@{F?f}0BD}e3>{{Sn69sr9rqm7`MZ^5dekAbcCqc1 at -?}53Y
zH-jnNC_8i-_{@VC%g}w`sjd91PEV)7^lkjzJw0WCmv7 at eXY@1$T(ph9!=abCVjF*V
zOiwGp?rr?6QcpX;uI>DN2EEKjVU5r|;E0EKp8`Flfi(~DGfF-6gJX8^^;b_u at TDF6
zj8abr!N2a{{Verl_&$;6VdNQlGuZM7`Z;tfc*`UF{Wv{!f)jS4jiB?v8)5sQ8^Lew
z<ZHT~HiFOX<WF?zX+QWe>?l9^{Q=QUu;W}7 at QokvWUi;9pz~4GKeQV>537YX^x*mq
z^FbGZtv|xp<a5Bsb`foa-V5IU1o{u3^JCm+?m<06CxSIUM}DAd!JenlAE23?zd|3-
z>1EyrD}wF at pZpbnCJAwZhkwO;mgwmS_ at ie~=g__2h-WbmK&OGbVH=^DFFnh9$f7>M
z)36=T%=A9qe-`ZvUfsu^*3wfMcq6PAx)EI4$KNT_%X|RV54{z94t5B7KloSJ0Q5;P
z{W<h=juX5JHpp>;TVSU-EO_y+QMb5^=7V>^VxiZ8 at z3*C#;70A1<QhVgC}4{=s~by
zKdvLtX7B*42>Kv+^>0u=&}HCLuyW{LaNG-MFX%jQ3(N$)6&&^=`X_WOcsr~Sx(hr3
zYvr^+Z$J7cv=2NC>wrE2e)%P|6Lcf^46F;f54`RG`YLoC_$X`>bPqTQi^6W`YVap7
z^K&V*Blt&H5A;!R0T!HlpcjE_VZEIHfPLSXH~$uO4$b^3tPeVHUpMBGL+C%y%;m5s
z=nn9 at S5Xhpb>L%%QC>bDyzTeMKXe!P3)nj7ec)l(M(C5^l}9kvpr?RKVOyac;5Jwf
z^bYVf*k0%XFyjxH523TbXJPxH`@ngxqn|*p2cLi$qV==~JPON#E*e0;`XkyGdO!FY
z%mh6EUiv4r7j!9j>rvz%x)b~<tdq|NKZdP?rZ-Usu#M2nHL%UlYr!qB9_X#$%dkDr
z2f^d8UTCKD7U~u{3e1M}L+60SutS^&aMwHN=g_;~;eF*XKg8%|77wB=pi4m)Y(BIb
z+z9K0X6}RSfM#yQO-3L4gRjDlKpz1UP9jgxiQpGtC!x#0rLeQm4sh(>(Z0hlcEFvm
z9BAf#SROPp^%Slb&}rZ-SShp#ybD&wVZmo$)zE#Q{sW>q=qPaOX<R#?neV`?(8s~z
z4{@!BZUs-mRzfq6euVxH&Ajg{>K3{iEczJbf at W?zkMp2+fT#JXM66!sE0RP8=tKNr
z0G&kT&_#NQz8 at t~E%Z)MA1%>*=qPXntQC4I_#CVYdOx^|_Y#9%3&swU=pZz+05$;4
z{Bo>Br=c6c`(Ooe7^h%yoJ6J2rC?6HL}kz>a1E>udM)@a%nWTzkmwnh1G=A?DA7*n
z4)71K0q6m6eUe0j(98+R$WuJ#Jy61pWfpW4SUv*jLNjM(Abw~Q=*^O-1KI~Zk}c6D
z=-r at ultg{d%x7R(31~a;EtnBHCkOR2MxuP^mEcpbIz9)?%tidrMPPF-?_s7VD|iyt
z3eCLPDA7s|3tpKg(K=`|_&eBE=tJO_$4j&ux)D5^FVQ||=8cy~bcFrEF4$4%O<=~Q
z5*>%m0`G-ICE_{`UNjMP4V?$>gq1?~f~^G-nV{E#?n2}ln!iiej+^RM*5Fy#T4?4P
z+ at x>fIKiL5c0%t3w-igX7kVqmd+8p7X09wjnW34NOqM8dIQj<o3d{(72&^lWs0f<*
zS6Df8&gBy2T`5r~G&6S!+5*}LzH>F=ggy at b{2GbUl28_K^0kNux)hB5BI1FL1-}S0
zL6?KK!{$SGfv>_Ep^t!Ta0l*#-T}s6hqi!D1WRB&94FXOCs8jn^Iq5>^k(oW*lErK
z_$L at 8V{C%MW}+WL$AS}K2IvBCHY^L;1fGHAL!SlPXCa@^9pFA#BQ&#THqwS>{tea(
z%^ReDeGb|kdObLy9(jN+1sB3%4Y<aEt6&D`wcvljvY<DCM_@*1rl}eA2F-j6W`;fn
zCRru2LL0!#VGaX-S5cztU_R(Na6N1#H1nf&)Eo3!aL+R28=86Za+C*pCHNTZIP`8X
z?N+oSH1lsTV+zIvcvUC*1auj=24;rdybA3PTLjHK3A6I~pm8<w2F<L4`JlVNFRek|
zpc}#Mu=UV8z%Sj7KFt2$kMBTUq5Hv;umNc5LOsEbah%{qu;b80U@>eEx)l5~>?HJQ
z at Q$yc&Qnou;0t$RtU>pK(p?y9&{5!3urlZ}@L^ar^iJ at XusY~{VB*(NUTEf}Fb8x2
zcq7aQ-3Z<dTM4}heCHb&o6yI><Znu}5!wJ2!gfO!fpcMdq346`up=B6ELe|nL3e=b
zU}+=tGP_|p(99dZjq{)z!3}q#PeN}7yT60_<a5BAH(|blUJG`77i|sA{3+}R^j`3p
zdyp1%ANV>fCk^v9m~}74IW*G*D}rX;4l9E;Y?f#a%mi%)?}oXdH-SB{4vvTQeV8kt
z_kq{kk9vSE2i at JsD|81qZ42rIx)!|lL5ceL9I$jN+8vttHtaNX^)_4ww`2ZHM;`$D
zVFqaCR~|xLLHocfcOd`JQ^3UUOEd+Vxg2JO?f}~#MtiXaZ+Zmv1HBU5{3znj(93)U
zRsek*9M^+9LFa)tz{;Q(fg54f&^_QykD<>%uLM(fqi;Z`fu6_FAE15U`>>tRC&A`l
zpbtP>!TMj}dJSy`--R84E_eoG0(KmlNzb8gXYf6RL{oo_vS*^+z_Tx)&7hg3FT$Y9
zz~!(q=#}7UST!{Bu72bbdL20KC5&I_Ja8(kgX09>fptPV4xp`HMqh+BgGXUIGW9Zl
z^a}D1y&HVvcM=_9e=zS5#v{iG?uDI(W?uZN#5XpS4 at Mus*n~EMZ^Mkx$G}y8KzQi2
z;JVjQ7HDS50Qwtr8u%yJeCVU#>_4LaLz}?$uvX|zVDB5~XV6UZpHTnM%;#Y1p!b8{
zI*Rs&-UvSTCfbGl!8vcCKS2AyWpATg(4F8Q8#MEcV-kP0iH?G|{26Tl-3i9LgK-2M
z3%0-tpj*Kae?eP7r-7Hl8lk6v3t+9#i at -H7H}qO?8>|C*2lyha6WVYbedJxNEufjl
zV4I+s%_lIXpsnB at SPwMw9IO|b-b1-yeb8p`BiIq>v*1H#(62ZxP<Ix6X(Z-Ga0V<7
zx(@svtY9RsX)$I$#&sOp4BC@)G#}alRwe6bEp#>bDr_h85%4L4j(VYc!4WAsIu4x%
z=BDcCB(xE{6&5=RbqM}_gpLZJDNRQUVAarzz;D3J(CfidFduYcx{elR=x8PMN^sm5
z9j$}T13!fAggy(tkgKCZ(EVVFQAcN?)4-{)s2ucpaMD;EB|=XDH;vO#9&|VO^rbo~
zgWd<eK2b-Dpa(!>0n&ml12-1JpqXc2d!f&Qmrv5se&}lODcC{iO+`8yUaX^|&{^PD
zC+lbsx)bbyC63n1JOE3BJ~)|wTSG at V(6L}4EDyQ}{0gi9dN26Y<vJ>b?gf*t&`}+<
z0hF#py+KET6Jb{90&o`00c`@CVLoUpxNr*U6M7MND{LKfC-`mHM(B;;)mQQJZ(P5a
zWjfjo%^ZIX$_SkgPP`Ungsukv0;4f#qc7^{$FL&kJ>X&3lrg;CL;4juDu-q^S0bO#
zR&WJuJ%<HTtI#&kY2e>rJ<un?Usmhr2=qQMss`yoGbh82LzjY+rlQ at UV{3I(1Iy1v
zyMsT16+!O?yLgWaXl8yL$_vemo{4sZHh}A4N1&NoU`L_(cQE+3En3ej<lm}Te_kQ~
z21W0Ah01R;=w<TnDx8L9@^1;0OM02Sw|uQmFO&CopC6@{$$PaQf at bm_=SJLvGkI_F
z0ca-gKW at T3GL!ch?|^3V-rC*JOx^>#2lt^&-p4u?_k&E{m%0&}c`s}eH1AE#`%15e
zX1)R&gyy}adH?5Q(99GQ+6(u3yazPz>AVP<SqIw+&HFm at 9>$xYnRmc?aUaL~7(WEt
z1I>FF^S-w|&`jQYb~o-*<UMHTpr7FWf;kG-!S@{??+xpQX7XOCz0geF*K`lo|1x1l
ztizcDd_FYqN6Pz*wn8&`ugyKsOx_oBFV?_J-j6a1>q#c>v)BX8<oyzlVx7k1JrMJ-
zj$-m2g!7 at 9FYr0gyoVs~p;wDF43qc4tHXMO$$Q!Ppqae))?R2P?;TZ&Iho1(K&`|)
z$>cqq_CPb`eVH(iF?k=QI%p>Ele86@$$J#_Lo<0V95dz_ChuEw8e@~m`_3%FxM1?W
zF*~4{ywA%?XeRI7Qigucyacumn)h$1gRRZghk1wY-}6$w7owNnkId)CVI=Rd)XAS=
zb}JwE4LDCC{vpz?A^uUszi-zKfBqct9*{pr+z;~k$HK%TDH!6<udIMKLREkMoK=1V
z|0xmtYa{s2kKo at 5|Bhc^ev2nMD!wq&`8>w3<aZl?gLzTscjvvRr-cFT&X+J>1f0L|
z0FDQ^QO9x}7Rae8p51XY<i#JmdHe0R>9yBhqgP&eg?{$4pV8X2YpJrblCHYyDk?56
zrb{llgtD at -_>-%cgJOwZJ5Sr1mGCG2vDg2WaPOTzF8~qn|NIdD^FiU at KBoX8;Qvs6
z*{z0qPjRbB1ieE21H+wv;wg5Ye`5Rgr_RIi1pj9p?7y8681AF`8#t;n^%a#1 at qa=6
zLRsIy{^w4-Cu{a$1cp0hoWS9>8Hmy!-DXHL at PDES&%poO3E6*w{GTQSg*#I|1HnF;
zQ(RF|yl at WsCjYOfSQy~HQ2uY25uBbO9a%qROy9oEkdEZi_`j|F_U(oYgga%FwR|)q
zB)t at rjAK7jKCOHz{-(jFqH?~R-Wg4QSRR at lvYyVdBW3Z&qXm2v)hKkv`Ecn~6wepw
z)z;5f(lg-y!ZUCwdd8VKL&}$Cvl_-5l=5Y4-!7NW%BiN`4Sz*LQn>WwIuxna3Y}7{
z`t$$d(DaO?v>cZbZHD#~I?bT^^Z&Gv at _oczLFpq4xsSkvUQn<4<A3G6kor`*-Z`~T
zp5uNg^b_0FzRLeY(nE(oi=M6xId-YWQ2udC2B)Wu2*LC6fKW^w7i_1D6GFl%Jzu3L
zV?a4Ickp;Wp~j@}lv1Yw^{TkJ?7`ubo_}8938kk7wxi-!r<Z{A<eq>2oJ{%uDYdsM
zeaPQ8!=)!;^`|#Y{qOIaA?c;dZM2O?fSQ&_iBrwkruEH`^it$Lfx*5|O;3yhIlXCf
ziX)^){<%e at r;fT)Vh)Iup4{gb%qhMhOnNEBTHhQ}J}wO>IU!8>l-VF$dehH-C~9v;
znEFgvc;<rX$?e8!`}UyrOMmp#1<EJJdVPf<rE-3l{t_lVdBiLIFr77xHw^8cL&GT}
zUg`4-q2UNK9!?AiCoVS1SZBTcso-&cP8$nB;j~L!;KeRfpBNhMGrs;~!b#-+-eWoS
z)+#H(o26Yc8^}gJYL~x-Cx3lKnh8H6%!1XUS5`LikewC>FB?r?rFi;)(&(6;*O05p
zOszPJL(BtjLU<2Z;aU!t1Aj)4WAx#O;vO%b?Sik7R)FmYVWeiLHsq%rXL8Du;8%t7
z?KrX&dKP>;aLfpEiSwP%9D6&Caf}{0440r8J`U|%4rv!*Tjg{8<qyvHq at cn^#rB}Q
zxhU@%`dBXSD*r*O$&>h29`)Bs50Vt6)W`Yr&XAS14skq)IAZ0S0X|2$BM5Gv*w3|n
z61DbekZZz*8sYN0QR7?(O6>=?j76?Z<%m*m?Ko<aKS}I=b;hnTJ%u~8$0>>43#}8a
zykDa$k#A+-ttL}Ys?|uvBXY!Twjv}2+$~BEAdW1QV+oy;(^&JF>s`s`_vCyIM$G4Z
zR2h)Z=@IMWlk<!wDzB7=Q3=x1$$9Fesd5cl<-V!(Jno^B<iBuz;PV`#r_gWJK5(IN
z)C6~noKOGgh}185Oio8?>bT`mZjnc!8XMQqGMsJ05p_g1BUV1EMUI=*3i+5n9?u2)
zqF1g5Wu&*jRUpUdFHt4VW)(VPe7cz+^5~a)5$f`LsLLe!i=Mx`?3H_f1N<`D#e+Co
z<T88lH<|cbbEXjA;?Q0q#>XkN>2c9Mk^5a}8UelTg5$tc5!$!(pJU(pAVS|#>s0CY
zd{t<bdsw^Net|Q~g=ggDk=o=_j(ug`&X?!y;{jv8%;NRh+L|0Ij2&$br}vt%?H=dV
z-sV<Yo5fq$rtTwB*z9V%+Tv}Sw5)invCZPNFSdEr%|xylTRf>~?6t$=jm9#cr`;>>
z74<1&o2-OGNUyEAowqm?Zuod?Uuj1wHs1YkncZP)v3a$Ffrph1+mMku+cKNO=-~g?
zjJ0^DJD0hZ+B{>8?e>ah-YDprv5PGZuWjtLWtT+=>`(l%$f=iI7L?br%d|qnU3QsT
UpVyiyOcmAD(`QYaKVOLd2Rp@=@&Et;

diff --git a/distutils2/command/wininst-10.0.exe b/distutils2/command/wininst-10.0.exe
new file mode 100644
index 0000000000000000000000000000000000000000..8ac6e19b8eeaf7642387123c749f416251c496ea
GIT binary patch
literal 190464
zc%1CLeSB2K)i{3lCCL&Nc98 at kM2HevaM4B=G+~1-A-hB+;l?GqL`VXPbh|~R;a;GX
zK;q5X+>G1Or_u-eXp2?*<7w$r`cxpk6m|*C2BgZ{gD6xZruD9yVw4ybh}`cvGj}%$
zDo>y9_w#%C<F}vB&b at c$%*>fHXU?2+X71eb2R3sCj^m8*S5=N{<Jiy1{r5iw{-Y<q
zI+}ZV#9wY~(=GVRjf?!>S(CTA{;_Y at FMlNOq2-T0`k0XS;KO<K;-h)rc{Hzdp(pQ=
z$5uRi>!?wg`C1tt{7`Ip>F*OyC;xYBmrnl*u7@`7Jl#aE!P9?)>yBS+J^gEXZ8`lL
z3OnJnk*5FQw2fYyPQOU6PtxoC-+9Q7<t1_DT^zSSXXMguX(~^~b#VsWNL?n!<>)!?
zFpHb at Dg;*e7w8!k at E&f{I2}z-UOC=CiMF%DfXPi15Mfi-)P??fJM`Q(yeTkny<@+g
zrd_w|x!h~eeEVMg at V)^&x9Ptg{#N1P^#WW!8LwvuVOx#EVe&X`*{$^}mJ7>)3@>SI
z|2155hZhX>I&WoQTs~5<3(~KH>;5mng>S82Q~wa$)3!pJxh%Mz_;PvIXzKs}`+wjU
z3=3mgP0a^|k&PF>B^o-orB3ma_A1ijC89alZ)t7>AP=cA`SuOmUcxor#R-oEmU at -G
zZ*v^!t*#FC3w553bWY7)bXy50yUbET6~7VSJZko0hzN$oF-0z`VD|U|X=;^KI$(~Q
zJOLftR#*4C%vN!te-G54W-kS3slmz#nS-Dm=}X<>Q*}%1VP|a(A|>^hJzgJ~QwYHJ
z{?{O5xJWdK8Bv!Bho#q#*s7w*>-DIjS$$h}4FEot;16jP#aKncAWl%lJXN#;gfdoD
zRj3Hi+!MPDKvIGiM(_-q__vD0JWF#oEz%s&t5tc>-mq{ht0tfyUW+)Dg70GBey6>J
zi*3{LX3)H`XW$WlgiMSx(72GRk5|Q9Rm at Sdolq%h7{Dl=_o>;X at LVKji6h{?3=vxD
zRmCiC!03gD1rPz0z<6>>eOtP$iWV%p5G$VsmB+u~^?Cz(=m;KQJhIEAzD-;vM~65b
z;jsDsEPz8|=K&4u;kw!-2N{(Mp(jk(>t3HFxCI+q7;<$6I9{0%fPQzGWmhM0WjSTn
zVdBaO6#AdGW9Oejpik}nyj*j_t^Cyupbfdwfm|S(Ij9OZ^ZpHp*hB;~^2)!Laa?t^
zqeB=AiKBSGAF(k)TL77UfZ>%pmjDFhp1}b4p#bsm_WmN#iv5 at aZSg2Ku7v(OV0J@`
zXF`kXNQc&*rXDA7$McOJ0Mq;#It!>k%5&tPaz$046+Y5nl|y~-5L!_9v2CuIR9bEO
zC1so(?1V_e{-z at SzO;1tPp9BX4xNBdFxtcManeh at aL?I~dAutfm6;oIkWZJ8@&Wr_
z>XbGh)WLQVJb>lqW{@>E8!i}jVS!B!{yv>v-qj8Fmf%}Bd)&p}XE0iV?Qn0|=b0ph
zZw;9<<WLtv?7J_|a0p^^4M!~d+_{E%Hye&x_Ek(UbP|T&TcJ|jBvNjcgJG;Im_RhQ
zV7<0T>zI}-)7<7`Vn2%y at w!%%v-zNS(Y8Mn!kBv+i*-xo&|z%VO`$(w)WX*Jy7|pv
zF|qeqAo$9c2t}39_OoJOgv`Lf^imHJAk>^j>kJ-2FiO_=d}1OIs7>B-1|D0jPQc-!
zEqI1z-h~aTRQ~G=9Y+Tr%VTNA5vT^SOcKH{WbBj*Iq_v^_XI7MR=<+(r=86nl{+pc
zQwAGIXq#;Nv3-J9-Gv=&*)z|~Lok|G*W|Da>Mq2Z4{*w}mr)iVvrf~X_f?nxPF<5#
zsqPBXYTp|`n4DnP(mW1$O8OJRaY8-`A}cs~5<EdOIx~4za3EdXf-uqn>tLEnp^Io}
zdFs>`sv3)Yk!tkJPLdv4pK3;h(H142wvL%gDflMqB64(L>q6+HgJOalip2EiUm}O3
z4-DsDB$R0kopR_5B_n0BefLbTb!zlkt<yS#Y at zh&Xf^7KmkJY#za@<Oy6&f4m<~|J
z;%|w1_dS)YEHAG-2V-t1d&K+@*u$~Ufle^;YGU7kVL)$w$Zje at d|xD=#h24br%qD!
zYaeL5xKv!=Y&A7rEZA^Q<3;<%$&D8u70itn>&4m5j`UIP`0dV>(ezQ7QioppL?53T
zFontoDtTr8Ba{h>GsQFnTiB9LTm$a7v1PQ|xe#RfztzFaR7+*R^DX^28wnY_ at +-(y
z4U>dY_<P{Lsrfnc;Q0kiuW8r7#I&}&StT5u2LmQ0_6VI-NUxFxS*qpq=br+wYT0d+
zXW<>2oGL3pu)y>LB(|T1-kbe~c+q3)AhV{S{DmI%K^Y#8dQx^J3}IgRDdGX*$k8sM
zIGLo12|ft|D2!Ca0i}Nxz{Ed*X4R#HnR%EnljbykRsvHWOxNOU|I>iAnw`gH-U<E<
z_&Dpp#CZ^@WXuC;3w_f9qs+eoQeYJ41LO->fG-q8Hs$v%g9M*f&F%(pIyi;JS`}jU
zSCki at ZSQBJMV<Nivt1>evh6k?5en at Bw{#`5{wz!NFQ8eAbCOxvj6;QGtwJ8A!gA*4
zTcLyi=ha4e0AR6dDC{~d3TD2Y6KKWlTET at WoZ>H0`k?kYNFT4g9|XMFEAQ%pAqG;H
zhNoz#(6E<=JrJtdPlJx*=xCV0`#bL#-c-Oh_B)__D2RFaP_Ua_6n5!imow~gf?bZ_
z#s4~#<VzAXANnN&I$;kjs+R(6UjvQSdHpTVVCQXSD2(L&TOng?y^1mw0f{NJ1+l!}
zU)F-1H-<s2MyUH!P#|_FEU#Ab41h;~+lBy~NCM<CfEfs2g_I&nBjLs*U{?}w1_QJr
z;72IBC>e!ss+5pP at a`mdDFg3?o{6<laAjDzN)j-i0Uk!cA7f`4DOviEECB#Sa(aOo
zaRiZI56u?Pg|;BT{g6Kg^1FEk=ifH#>K?PQJfLBu)9OqNfx%}rdn-^owu;VMJxQ!g
zNqP^0__OH1qO1ozTZc at 0@<C);rj>?V1ANGp;8Az;_aN(lVyJa_7IeA(U^@(F7~!he
z2hF*i4!#+9 at hf+vO0EW|SzH%B&wzna&BpucVf^gzl3DqX(dQ3CmQZjjVtUb^cPEr3
z|7kNkMT1X6h?G=MvUFZ)`!2}o8&EC)Reb7kn81R+!`XIL8J6POq0X8HHTrw+K+H~(
zS$PE%G(^h+R^E<-58!w|l-q~`O1SYW67|VMlxPb>f7(8DJ|RIwFFC6m(Iz+sWZOSD
zp>@K9Mm~cnt&<fE6B~d2MfErmzSjPiCCbv}Daz+kdQe?B;XidJj<l2hFkV3`KIz|%
zce1MwZnv`A0k~~pw+Xlv*ljP|u3)!4aLcn>MLlWnmtEbC_bkm at sO~;sX?}{{^jVrE
zdXo?~7Ky#W_lv|HK`0Ux;W2+H<OXaL)s8E|9DgB2s%pSrfHyBt30O4%v<;-cfXupA
zfev2+LQkzq at ai1^+6O!%by)8cKPIl;7mzcEtLFt|CgM_FQ0TS$1>_JQvH_INLB)O1
zFfDhGHW#VFZuKuMqz&5%ZCp^8BvK)ZY=hew?Dq9RB83M at cVLiSFAvgd2NDS>^pFne
z(Pn9WfZn`rX|AL<pevQpn*)~SyXnmiOY<FY<LIz7-%2B1wlq)Gp<&U9abuCi3OP%2
zCftCM@#bG~MuGa#%DPb%JN-sLQ)zk>X8*90SI(}YYCyoSICd82wq8KY-(x`TZkvG8
zpyhVjAyMTZ$_aFTc=xuzyy8`AYN$CwC%TQ_APqaod$#wHRvEy<BY0(MLRG8ddV5$l
zI;5ykE$<Y&y*Re3+U)(+)kUItV-7LyYWXawT)1E+t9Cm?^9ChgSQ2s_2J7yCa?a1u
zIZ?EchOW^3G$r^L)Y}L1EF^w2rm4XjM|oF9jRl#&AFo$K&s2pm`6qeMdeR=x4CrGM
zRn)GWp at +BCX@-Wv*QUloj>l_1ZSVKEJ+jy*PgY(q=%H2*TC0s<2zvc!FZ0OO*=RtM
zS<7IM2R^4`BG5+%0SGBFYpE99L!SdMcwXsBpT{YWf`Uvs<?@8%4Pi1fpizyIzMwWV
z2sa#P*^41}JlnqtyXd`DS{L2 at T_k`i_69!3LVK`KGHWM*dQdK&d<A_|j>wu!_DFi;
zdh%(=t>cwPR-l%Y;b<48c|4dZ*Cf>JEUHq0!1bzkVvg(_Y)}R%PqgIfXapL at st8TZ
z#S~XxYKWMJN(?$4ki%1hK^p-BgX02)$5Vw#i9vepVaF-KQREsB?kIBg3AYuw62c5e
zuQ1K;WMiAMS}hO at r~`u<1VJ^}uX<>BRUe7Q4N|8%sLgHVt};zQgS4Oac;uh}1t{<9
zhV@}Ur*&d*m5Tbxuzn1pu0XpoxSHOnyQub%?9iZpNbF#@{_916NjNTx1<XYP0(}Dj
z_<IVOiU@;;#qi1>`=})Y4T${>XOCSJ$Cp4xSNj;ktpC+rhyd;DU4E^yqUSK(()=TI
zC@~yh+5=#ckbqKY`;Y}f^KapmA3R8XOOErx18uJZ66B=(9F}K;DWk=#N5#4yc|j|K
z at d9l!?+dwS at W}+q6Exo2vb11^^7(nVQ(jn(Ds`rR2Quhch^kJ)Q<mAq2urz!wkS30
z{QUuKq&4-Jj6*xk9`^1<p;F{B33GTJ>}u%yN*LQF-p`Fe&yydT2_|TQnyCYqk(3e>
zS}an{UV)<~y at e)fAk4<L_4>fR#t4vvKlm<_bXI?E5%vm$o0UhkYE&nvOt6P*m7+mp
zjl+-y`^04hp=4&csWjx3E03TYG(q2%F^^YS8SAMs;n6a|S0+p<vjqQ+tl9}v7Mj0A
z^K&f&MB?i$dt6pBs~It9$cl7XL3Q`*j425WL*DH3>xV>8Us#gg{)Ct)ze2x(KTzOb
zJfv+yb)~ppt6dm_HJ1Vyul$X9eFC?lJFj*Xle5BIq&HyP=L86_c+US?NShdzPN^V(
zry|(Y&rqsQ7*^U0TH3#|(vXZ{Q1YC$O7CKgkd<J%+WR%%pTWyUVj~W5HVP>hquNtJ
z1(T04H}%1JQM3&8*r9xRTWT0!8FCTGNTn7}rPcuHMPHp+&LJI3>X-p5&EZU7s0vPL
z&JjSH6YuoWx;G%rd!NTd!e<7!P}%58!;$*~Mqt3~1(feZ(+fwaR@``d(pGKi$-3Gl
zmUMwxKeo-adTn#fXf{d8O`(~3`!RXEJT~bD>!E(D>vib2dfYqnI$_NH`<?~rDIb8*
zUmerq%)a09R07CDDol<;!YFdm<~7=m%Grpe&21#KDx2FxuE;vuMk7`G6{!{_J#~s8
zgW*b)X7tf?HG6eI31z*fsOafUd4qu0wiZz?Sw$g)N9|#r=hd}Fo`D7Q%DxzN?#R^2
z1&|s at _3_FV(1|3-95iP1jcCrXj!<bKOg`g&iE}S?%P?zG)-g|yY+z1Cn2h6D8XNCh
zk3={FYzsV>G!p*c9aL3e-dT;ByIqZEg<Rc$uYj_Z-mSkKZ9t#TM<3Dt|7)`2d-x&N
zeuLS9edXFIWUFEpn}i|zGBjg4P_n}KE`PleqE^rnQLW4N<85a3GZ!xUj{uH1z0e=<
z at av$l!yUlSEI<;90A7gG1<aytsOM!D!_(d_1F%;I^Gcy#!xklToS4BIummia>ur?t
z9L-;fLcJqYhMuO`#{oD3V3~nT3Z{xLrz(M3%L<SJuS3N<q5A+&khNh=wGpd;cKfa9
zHfk$?$wArloiERoWR6gPiveCanD_*ZpCb_Bie at x;0`7kKO(!SsI`auN`XK~7xlN$|
z$-yof*Nt)63(&-a(agy~g+}mDrX1|~1X&CVd at XA>+E=@JG1GH>3<ff-gpL~T^_w8o
zuN0yEIVg)kwA1_p5c<y)r6yD0G_YykCUdY`co_O))VKM76#|e044n7C;9W#VHi#OS
zQ*Xy{oV}Duc=T-JgjBUC#amfJ4aAVEhb|u=^eo!<AaDaZRBs5VBVY*h+K$*<i2!#$
zsp_*;^%x at eKV#c}G{J+NVRQ8v+SwB>JFptr{O8fCYcpbJeD>{OZm0%x1uht{S)-}J
zY=t3f3BHIdsmy^f at jQwk6wO&UH`GHaogh5G3D`Iv+!SMF3B`uW$FejuZYk-e%}$Yy
z%xYl-)X3yl3|PcTe;x=H{}<ZBGMHiLyao?-wecy$mDRM~REYZ6_<$a2U(G5mW>brE
zaNb;^=sYQ&YwEFHEh9alkO_*Z^k2{EaTv+L0lJ1 at _A8V>s3eh}Gcpx|2_y9OY}C<F
z23iWIYNj%%FIZHR#n8zxhsn3#Tr_)tvFUUQBRpsv1_*l5%)&`R0hH1 at dbUZNF#8SR
zy4DKa>@uA=8eODeJq*!qm{{1X=V-SC+ff)(zX8 at A3NA$&hbduCvKTYQJk4UH135{_
ziw*U#R9^G*X$=$W{pg8Q4lkzKT9nGgG#$n7z7T!ly;M!3ZuV>w%T>TC=Ru`Kp}q?V
z2!nyn2wvX*^Hik#BV5x1fuZF$qOmZXhUzX#X-dcx`I-*`cwKEW7PEnrrX9RQxv4Ta
zn|sk^6EFfdtN{C)IWB0+NN=!T96 at JemN(dMc{;3lk<mHh-HZ0AqeHlR4(1ZaC_7Y~
zQ^aq{)Oi#CCg^7~98jpyfwQ at Zi38<HfVyyMV)()-L&pmUsI6%>nDiSn+Gcj4|5+Rf
z14&OG8q(T~G!93c71T(jam8?UgxR@*Ez*HVI)}RT126(^*^hla2S=4K9kjM54XD~#
z_g=y4n=2g=OK=U`Mif-_(A18tfJZt!fm^Ltjn21UD*%D!q4c3e;7ZClB<xHaJ<18X
zAY%4Vx6oB-Z7eMS^2bkfgIV8(N&Xz2g~x+MFI*Sk%6R{iLjfA+V6B{REA(~(V*H9@
zsQ6@&Z1g9qq=Mm3SS}|zlldnl^Kbi#{PDE5RS*rGwUnl61yMEn29yl}gC;Aqq~n^f
zpbcvIa)Z!40dwkCXCiYD!@cN7^!h&;MEq^9zjw&fHU#(;y7qpB##yXW$6W&tP;anx
z&kV(dwBWdrM<Iv3f(4PIU^wNp<m#Qxr^Wutp_NmrrHW}CS_~NNpTYb>PE`j_3lDfS
z5P2wwAAN^@>NhL}3VM|F52E&Ll at fY!GV?5IZpE268n8XdF0gu(iHoQm#u`l1hU8E_
zq`YIYA8h>zwxQ+eQ78p)4eT6Sw+wwxms%5Re0|gfh*~<hf~C0<lfFi$afrJyaSeAP
z6MBOkLf6z7nc~hN^T<q4IJ}ex{8tRASqvG#7|_M_Flm4FB!iYDPBTAWFl3sUroUPh
zr at m+^Y@*Z^otRU?QDGxwvWRYopvq5H0oCV1A#}c1dICm}kbBFZEHAIV?PwQfNr^Nu
zH(>SnTjro&d5Ad$^U}Mh{rQDCRJ$0Gv=^se=x0QOEe!dW&A}o47lHccF=8o=sD+3Z
z(2mm9MU-Dwq}E83RYUHV!9DMvF{dP%AEydlnX)EXAlR{3j*?XkL=8jz{yfB(MXHOD
zv&vMuSmQSjCj5PI%85;v4H)}){T#(^l7QLrQp+=RvBu+1$4L!m61vd97B1AcK^{%;
z_?6<4<O;+vl4wwLPLTeD<>|!98lWEu?mtqT!iuNpTK%Xtr?9({#LgcsM&z1OO_vU2
zv2|;zTJLxA8bty+&%T$7(L+U-Ow??SsT6?bv&AJTx(!8Bi}Pui?L*9b{tbhu?ecL~
z_3SE&&H4IN&-!UZGeiIe>a0TJJkW*d%FV2op(%=IIkwKpIz#zE-Bkr at 5B_3W%)iwS
zwlbGlShSTLLtD9bMlp4e{102{)mk~MfhWM?vWG#1ai%;sGww+_Lx!8ZA-dUsS3bTE
zk!1IKhTQ-5K4e<+T~{&fqpPoG+B&c18lfwE=m>rYtyyNr{H`ojeV*=WrP)hFlTW5*
zs-^iax<TE!S!55W$XbFYaks%7x>jvzejX>=IZh*ecp6h)!VMIhhunaY%HMkznj<(_
zM}xRF-+dSQzwP1H7Ai}hqT4l48lJm)#s3sQzVuQX7Cmf<=Ido9Yc+~V>@p^sE2*sa
zhwnlLD6L{+K at VmZF6b_xdVB%x%#$)Dw6<p@$ToO%(bXho+3Wcb-D06kjA<LSgz>z8
zGZd^m;njT0A1<Oko8(@MvD&&YT>zkTRge3fNs1(67?AR_a at -5BiyHVRm3M)emc-pu
z7qY94s4x~-E2Rv1nUF7{y&ztv9C;syeh<}&=nUcWv9$#z?a?(bb(ra4)TVLouvuZy
zR}gfwNn!XZzQG=D_vhU`xLm)P?PO5YE^7KM9^@xY?V(PkxqiF{Em>3QQ at W39JJad|
z+~A5`Ql*co;`ZSxv(oEIG4F6!b%?r?`6#_SO%0g!Y~YTA%+jCGkD9j5ESid_3wDj3
zHWW(y#bAle!%C#86RHDD{0nze-6T7g&a8QxW+WZcg?A5a5a at 3Occ)y`fuW92#h$DR
zw|tpEV<udyb at mu>Lh~^&nRwmoN;H|mdMTm at O#)1&ns#b3X+E-LSFODIwDoDKCD>K4
zkDo^O1$$<)$p11Tr at Zt?avj^hlSbTu4%Sot7w<+r>9+e*rR?1bF<y@{X)wyafdZND
zPc4N*`jtv5b3Mw)2VfYL)`IBU21!0%`FRN)U-s}ynQk~p?M&E4rzX^Zd|q at cX|ht@
zwT&(kAI2$+cH3swZ41e)r_l5BsV0X{1}wnW)J=1eZLG|0W=@;Ng5-QiW>Kg4$)xDs
zKvP)ul=%JKyp#*ogBx6weP9g)mhiGGq3oCgbV=AxLq|F;Tbhx at yz>J!KINx#*alO_
zc}sH>#I{Uw2Qt0NhB<W4V7s7i$w3Q_&E>x2vbkn+=P;#)+FnB)$hUi`U1tR-C}JWg
zs=_#r|Mk1S44Sh4R578fhWVqqwr<I=MqFx5HR7Y<uW7`a#b0j39IvvcSZl=U|AR)D
zhd1KHf7giLmJMlyH!vI9aBFdD7w<T?Wp6sKY<i6DxD-G2vNaH9qiW!)<7#kz2>8~5
zlu6rT4J^S7Z#uMfPyNAmuQO1?E3c=Dur&WCI)^&aIrnRgpTdZJleAxZaK at 8fd|{pR
zaDtN_9^iZ-S1%vXdqWKaQCHs#X9*{Zy~??>$;l$y&q18x<+ZtDPX!`G8U})@Fro2o
zPRIt4({nf7pfiMUKBZwkPT$8#hwWq?TXlkYuO9^466OhLn}$BHbBnIB$n6KgoHuv^
znQ{=ljdG}mhJ7 at o9>)+m1(yipDl7eEB_&*CQWtl|Zo{d8tTy at rhJB?axN>pq{I7-4
z=*#vOz;kRmh$H_DxKme~l)w2w&FQa3hxsSCr9~VC21ynqdSiNk3rth&^Qk|*n<b}v
zV}9ye=^b=R;))xud`gvMGrCTGg+BK`4WuB3ZWkrJab1OFZ|R+&7gayPy8RQ)57*H=
z3$L8=w;$>_vIQHhQtxRs31R)R at h3&k;q!*cQhO$8k96tzO683;*cl8%ti6N#O8hEQ
z_yt$u`gcErQFNeKM2-2~g-+ZQyo**!{kh6VtEor~<ok^!IGfC+_RS1BkEC0I2Y^1&
zG#qO<`9*{19mZ*gxp#|bNtPd_Ar<vY2t|*CFp11;ArzXtNjddq;8d%|sgn%thBtsu
z{|<aQKEUnU0hKCmeTXX}UMNZ9TubxM=nO)s7NX8(5YdPqb5bj_JQQ_DyI3B$ZXcR?
zp<M_}m$5_(LmSn<SUEc1ZvO`*T at W`o2duo`IrmFOo-=mx%T><6DwWRrla)>ID%rSt
zgBZ$m7cMeuEXI8)XAaHzqg2juUS-E+np5zoL1gsUAHST7qq!C*a|ws8O7C%EdSR;6
z$zJ8~MOBRl*xCx at SS}?D!cvtw`QwYO%6^JwzgU^fZt^PkUjjJWRd5F=+|d-AS!Gl&
z!xpG?BoWHDMfBeC)D at iL*uueD6O)M0P!nrtd>liE;Yc=`c;&qhpdjf_7^{&$n8MZp
z)LmUP#Ekyc&LJ91R(#^H#Im7@)_~JL(^-<FXHvaUmR`V}Aa%8xU-RDLluotyOHOOA
zy<ZOQ0M&wSTLlH+W!$8DFm5DH<6IC83Fa-d?1|vYt(Pjhuf?fbgRR!S4WdPF9P%vh
z+KGN|y7?6)Ile3e(ht3xQkPi at KP1zQTk0KZa3|I2+o)YxGGs$R8?~Oc>|lGNfvD+f
z at MXHrwmp?Yy~DrkOL+nYW$FiX7dASsu=wG)L8hzs{*bjsz%uv<Tg at l59;TXH=SVG~
zSd;rg{^?C2+e=d0$hMXww~^WVizr#B5`M4aJz-vvI3V0rB=!kKxLa%{?iTx&qeHOS
z4;G0gx at W|)B_f;X#u1#sVXn5b1xeD2J4QO_juCCcNF~)5aFgzk9fs!A4nrW0sS7AF
zxLpxa|EY{R<*oO>GGm(wXF6lWmwnG*y5h?dCYka(Td}3yXTfvJry%nLTbU)e9d3A4
zw9-!UBUkyfB_G;NKFzt7<<)i!3<XgRoTdAnVh`&FJ=DY3k2J`k>qV>QU3<L<yLRb3
zZHpK@#8A?cwWg*kxJg!+)^r8!HB0c^P}i$(n9UYDhP%~fVT=b?QyGXn2U#wfhV~)V
ztWRp-0PtN}21ekj&TG^!+dg!`xv^~@#4QzaLXWFgZ@{PY1 at IxWv9F~C;gxTHq^cfe
zR~*C+72bt9Znz`$=O5ufrK2G6`~dBB(`!fuAm!x7LQa_AFP?|nu$YuOsm?+mW!Y&I
zWJ!ku-BvekD474fDNd;31a)wsSBP#$^(mpl)SZR<*ZoGwpfnyCS`hB~kmk%p&xLTe
zUnv<{;)#;s+xE2WNr at A<Jqh$ROMp}E$4Sb^HW_J!3T4uq0<FNdf2P3yYp_6_q8n1+
zl0j4a(j6)PtkkmsEWf}^`{|IY#ltH%&qDu_T+<tB==<$&0|mbgxxyY^u6bRt&cb;m
zXCG$*aTIkm7eZs at h88s&SE$LXX}G5BdMe~<2Iy^3*R!|X?Bt at Z&EKW00>Xo)G_*j}
zDqO#IJ^R8fPMXp;nDPiqnOw?J5`!s^vXsCqO!2 at Vjk;c+Nv$@4R at _PV4IBW4S$3Vl
zCHJT+oQD|Jbjvk8a?Poz>jZrZID19`F2gSwveuh^?^TAdI_ofuMlxqx0WO?dQ}g|6
zC_g;@!=)wMUbM=+>e~UnnkBx9-cTj+7xW_ at o|qc0RLS%B50p~qR|X4v6&H1t&J=ur
zrz^W2e;WhUVs-xQ^f~)kG<0a(3&>!Qu=Ps`$S8qKKU>pk8CDKYWr)Y$2(f+vf at BIc
z`u*q<^Y~W`%aZBu&A?cm#kS$ozOqufY at g25_q2(}q<7WeF)>eRJIB;qbb2T`^g(=F
z!BvLX%ebc~ZJ(27y(;4k$|km0wov(yYI~wvF|e39 at o?iDtdtXOhbq(d6_#GT;Yqk2
z1Xuw!$y$1qYDm4)$}iu8ZhHZDEhv^RP>QhO%(k~Yf0)VK44&yeNg14>?c5l$qS0x`
znG{&#H6)j3FS>BNDIbZ4&U5haA~Fn04X?8OU99c}tj!S>M<;jD%y7YU(BMZY#N^9s
zh{P-Cg(gQ7=V>PCgbbw?(2ds=&B0{x`sbhRNA+H}(W1`U){ib1FWu$kz_(T1ZfbBx
zg~?7KH<0encOgK*cQnI7H2L-=N^t|8Z{G>HD&tSld8Zmi0TTiqQe&l_S`ZOeoRLqg
zO at GI?<sGNQe3%O at yE#K|+ou at OTd%kE8m>4JmM7i?I at l7i<2Y56n}Y`}&2OQr_Of-&
zH-o2zk(S*$L+ at O}mHWicU!hq)g{+^FsQuW5Ps#q+FVOpYd1p3A%_llhy=s3AA0YKS
z;3B(BO>ul~TY|pg-D3=&S$0?I3|Ht?uU!q=)u>(5v}?L{&CsqU?K(ocW@^`w+SROG
zM`_p5+SP(r!xbrFg^7M0YF8chV|%eN7y3zbAKsA90OJd4xrtn|1n)t!@U*2l%uza6
zp7;rESVS at YzuE#RZcR2qpcoG89MVC%$9{}_imjpz`jlKEQBamgb^wR4m%l)J{LDu#
z+K*9a^3H|xmOUMo-|v^J2C&cYVbA-Kb5`IT!v&i-5PCMJ7XU-ex6{slr`}|H2SUB=
zombDQI?k~lZudJP5Ju{trrMG1OEgcW6}X91nQCG|mA+&*BxVi7w3WHon`@O)gNgHA
zIP!{$Dp_v-euFUHvio*>@03p?ipg%=g at 7f{HDWhF=6QSpBl)b(@&s;gfYKr#ry=g?
zh706K<YOy26^Was93@#>%I+!Qro3a%dakS--#w1RbMPL at ni8>R1<NX7mKX<__D2*e
zc^5N9h%u;TGO|qXVkRTYbkuI#LNg84l&S`E|9`C}sN`L(8kXrAHA#<{@>p$9=Ev6m
z4GN~3#2R?b+SKv^OYlB?->Sh>Uq-u7PrGn>gNe2`RXOX-Yhqx4K|4V9ixK3ax+Tau
z<LpPK9Hp$Y1wq}&I<X5!m_rN)z&qr~D;#he)YBXJyVY<KE at w@VbEYYu8cs%(9K#VT
z%y;3KFuA|oAWX3Awqql6>>U7x^;VT|^RUzpkRb!c(3*j|HsoH*6YW%rnj*0 at xqQHI
zfzsztBnJIHrIVOLWi_lfPT9 at Rl~pI{1NY0JNk{DFEm*XDG-Nu8{r(QZ9zkM2KFE~C
zGQFeG0W)>lvqCh}|6xt3YFMWK+iFVH!y0$ZnkZY++MvuBWy at rR|6OhW<GNBzNjU>K
zvz+#$9{LgZ61$Q%I)wpa^lPGloOx7?&=RE%BQg~-(_M_2qJzvtS at iE0j*<N^jy^+{
z?0^gMW2DPeXXuE0oKtJq4}#Sm4jvRn_eXSs-maJS8;{1s&ycHej|B{#-awx1$`WF}
z69$z628}AGL%sKj|454erXJ(fE4!SRe7Y%vbi{BEr2U{hJU2?0sPd$?e>ml*=)K{f
zQ%dA*_*7fG3eoL^O0!BmplneI6O?)twVvTnwQ%3>GV7`Z3wblNg~rXoIk@*|gITy`
zcmcN!2c~hRB(V5vn78pv1o|$nI$d*#fYA;;h5O|v(&}r5N2U)(k}5MOszzKs!*rKP
zX}Ac9+FF1U7vIW^gtvM~&V8W;*I$;)Pmn{la<iAL&h?S?=7om0<njZdr)I0T3|xMM
zR7{i0&yb24a(S3k6v*WYshBC3pCT27z%`_zScdtoVuAgbT;5G8_!7Cii&QK+ewI`$
zzE`d~Y(G|V{4Csb!n7z|w5*wI+3nFuF0%<B%FVi}0jXVY***67S-gd4Qhs;}6kF9z
z%&l^HFRAK{#7vgm2ai88FumL?mnST{4?);`Oqfr~6a5jZcyE8iX$kJoqgqgcr9uwt
zy;G_N3`Yr|Wd6xDiU}SRUzrbssAYWY9w-3rqz^1|RUae?Hv at i-^2C}^^M>P>U`>Jw
zpfFukAC&h`x2==5tqa-~pG48_(P?N~_LP5?M7rx5NJEQ-KLe70s$A6raYg0kH8w;5
zDqxrbiclfOkA?W5aR{RkDFG#>+5k+`L*BBLA8;JCJc)zHc2urPII6;HM at f6b6H({~
zi>S~)bq^v|t^nb_Ny^XI4(aeQ;VAF3NSgs}dU+poz}(g`^VRZ1%XsngxW{p5O||Wi
zq5KR3wUsLXy=En}4uEZM%T>K`13b*Xr!{Lf<mel$1mW%iAZX?ALJ?PJdvAQo5CqnY
z{c`0e36il-x};ybC{*%Az+gXC4GLp<ud}sMCq2?<Xz}S<EA>(YZuG5Vq^O|`@_h({
zB%TkI9Qryasi9BbGJahbTsh%7fSM0?x|Z>4(_8P+&u_6fYEG=V)6sCm@<bXMMl+($
zoDC0yO>+a>tiYSB{z#r+il&bQn#D&!Y#GEx({JFYeG-WoZ0|%qFhC#K56&gP66WJ)
zL-*-qQ)r=1E(3l$VS7W0q{%Rn%a6e5GLfo*DOD!hTTKxiZa6H1fp4ypy at j&7SaTkL
zq_7`z91 at n2xw(%0LKQlq`CPd&ORhA at l{s$tdj(R<0qAQr)KZWJW$eU`m#s2ylzDU1
znZ-52BcCVp(`0^z%ooUfq0AS{Wvk>eUM^c;57*YT2d*rpf`;vc1?7DiS(6*=7ph5v
zIjDZ$Bt2~6F1#Nf0}6Y>=mf(_QVSXk(npq{Dr8EkX+wdeS~l5;dnO(;P_!{$7{#k=
zrm3EMY2{2 at EOePXvfCtY%vYwP$cr1G#-!W_-P>b7?e#g at g>{XEw~6&qLsp@<3M at s(
zdC?fS5`Rdp%_*wQ6EmakytAT{lWV6H)#i&CQFs35 at aR7b4G!qkwbN8 at 9!byzWXLxV
zw}FV2H|Bcfx|zykDrsdGE}WQs6gD58YjdG?z~)j48}>Q~zK>y2>Qw4j*RV8sp>gww
z6HhlOrq<~9&C;?$PFhxsFA>j>E?PI`6TUb!GgG>fx9)nMU7dgYBXUSyUA%BggZ21F
zy7h(ja2;(s&^+o~unn2Vxo8A>ot*r(NAPJe^dXm22G`|E8)t%UDc(pb^1+xs-WAaC
z>bhyF4^JDCk=$aslOB;|4;Q(oiDM8BC57xRkliz6?=)MTmAI!lPSxj-g=XH?S*fm_
zsmz|lw)?Cs;No*_0E#DQtTo6r17yBg6$fnXJoKf(UBD}rNw_RA0MNEm!VQq at fkZ5$
z{{~uyTw4I;LtXKS#5+yrE-3OAh(;_K<{pqCX@*X at Vg_(o()2P(Ju^J^W6<CGEzN(m
zLg}k>9iLg6kHKvbZ?-fa#2c$PqKG$Ig0I7abk|5RJ(_ODK?Z}UkkrBy)<a;hIE`|{
z0NDt0S+DF$$gUGGnaNdWVvj>EH9uJMh?6636A<ZUmOb_$7T1vAEX}{hnnADHJqCmh
zdF%LD!@DXpGYYJ2tv|C}FU)SbJehEuc)MNo`Fu)s0;W}WPCy at WDf~*gMp4)1D5VLy
zhywBU)6sPFSI5xzM%;PQN-H{fj8a*F+V3{$*c(@t-R+<>kvCv|Fhl~AsZ at hmUm)%I
zmSU5DuO)#@2|rfC#s1TR<>W`=M)C&C8`396DS|DTB_k);Rp13cOLk`|&!593853xH
z7miCG>ZLbE06y~E0_>bg_S3OPp(ID=+C!}H9awmkg}$l?k5A*H+mfWpW|{z0;TToA
z45x-rMXt3rUN%{RRk#bko$CUVSVMU|7Nd@)O^0S@(k4nDfaJU2w&D`8a6?OtLWu`>
z at +~znBwyMEx<o_CXIG=?c(h%7GQ%YO>oLizKf|gq$cv51@^3*Oh^fUD!>hH$^)c3G
zr1j-MeQ~UBOaJit3`6Ss+30JvoYgmuywUUl+EQ|viB#m#8s)j>Yc=tvtExorFv8Of
zi9s$&Fr&203iC+Xkksa0O!b21mjKPiaRi2^s}CnGOY>p+T3{Z!!*HJrnQN6 at r_e3h
zN}saGJvp}hFn=13IF7DeVe5=P#M=0uqyC#V%C$~L)~su_sp9jYZHl?*l3-sYC;%Wj
z44swA>FcRzNS2X>2X=@9n00I_D{=KrcO`VLf$46iw9yIF$O7!7l};c-x<&>QWPY5X
ze!+|e07c%Yn=6CHP?0CQy1?R?NGfyXs#C#Z!Wf{IqcU&J2*9MpI7W42V?cZB19h<e
zKoXnrgV+T7=$Iz1F8aPIsDue!`2g+(Xo2eDBU62ot09)5A!Z#!toB-n<qt({+CPt2
z&-t%I?30nh5X%RoOd3*H#@r-3M at L?(t1WxnIgV4w!Mg$nZwX|;?qo%05w8_gn8>9O
zQ;ve#fUHqgK;H;e`14fU0D9Pwa|>e2GLq8c{;#hT$Lt&AcpG(XbVklv4Ts3_$j7G0
z2c{|e4G}88at!TEa<QS3iKEj1DbhwW6#i-^PI8FB&R7xyCgqqq2IgX#rTMK)dY5i#
z{xjZfG;)HSyd`~*aXfCj7)_smBDy9siSYB8UyHB-5N?;cbh6ucG>&%$Ndfwn33+8g
zcA0n=BOU)(QvCyEF&)na1qHN2$-{X8u!yGP(VgO$NL(*{Vu;-i)kV{R%{eXNI86O1
z?2=t){(7Vs727Wyor<1wu$LT>WG5D2CpL^=orrbIZf7)oG)_9rua3ZaEmYco_ST1@
z;x}nnOQOitM+FOU^(JqbjCpbdzMD=NQtVMij6>+;h;{YqTs=T|7`id*h$JylkNFzB
zM>AqhPNQ0`ds!r&MxUK<BfD4Ny+sNeVX~7x8QrvT*(3_Kblr8*#uby+fu7<A{Kp`5
zD_LvJ*xDbPl2yNBx at JFC_$_<P<#~^ma9C5uO1ZRRwd(dq;s)ph;`Te-OY8qDo(*@F
zJw_M8*$=Li7q}5f+UOr!|F_VSs9T3BR>{B+3qn8Zg~4`z$i0ZKjB+Rx0%Jq&)p!8j
zZ%5JfOWAb>Mv>Mvy-;B6ERGV|4m|BO_M9$>`wd@*JDB5C<pV3dY)U&ZHsOwOv at 1cp
zINn+p!AI*#&^p)LmrRM@#L7IFag}mo*?l;v-#9MTzYCAhSY*1Dr2K5MsWE)%X{FR~
z)++ieyALG!al_SxB})pG>9H`<X-m-3kgZN4!|f?`KMF{)Q4=;$X4l7Xw!_lLSazQf
zA4rxyDdTIJA1X085>Wb~<MF^3wBKk^hWMQ<<KNQ1He*IA<3-H4NeYjQ#51IayEwFH
zFB0zxU<reFnW58hXx$xXvcNM~wp}abMU*qj#B;>G!r)$31T68y8az~CGV99|*q7td
zlj;*bvhgq7b%UkZkd95<i>FO-qSpI6Fh at 6X&)4#hx^`KVLo0wXkxp4T^2<!L8H;(2
z6+oZ>K2^YCEMP^HOQ+*|S*R2X(JME8MIr045VSA`g_EWE*))_6SyY53((Av|)X<CP
z57aNCXMBP|)J;v-UbIfj2V``mfC0<PpoMQNR%-qMqVBGd2wPhSsyG;XWoD+Hqc^~W
zIy&TXB`m?efm$Q|h}x>LcZ{gQERueWkAU_9%{z0P7&pfYSPvW0uIgdT5H-<${_2qd
z!{R`5m~D~R-fIcs+89W*$RmA_TGa=$v1NCs;gIG_IR!ilx*}A1{A0rrzyaz;u=re7
zzhq3-53wI;I9PU<cWD?H4gn50P!9nX#}&(-^0P3LRV)~Skl_%Z^asfE0S-2qHa?lA
zqfZ!5N3vYuYz~Vfqv_Y9e0T+P)9rb}bsD|c6c>8|W1Y#~MBhWK)qrM27zC*7E{mpT
zQ)-q$m*4G3tjT8$dDU>$2u3R6Aa-mlt3LzOAf3n0+7dtro!5Chu6vTA2~)+BofDBa
z#!-E9F>&)>(MvUkFZI&mt9pt5axeYQdxyWOcmAgT-|8LyYkNocHN7)PaC8C1uE%;i
zf#lF<NcD<RO8D_#Mfkb;f1dEANy10dbLd#?g#tL*$Zngfmv~FT1}L>1ad>B}&C%R@
zNyN}e6~lMINd;KrGePSgVQ^2g9kuO`8*QjwRXFAHQxI(-M?rgZ^?-TcbppX1RlRG`
ziwESMUe#+mWOMZZAA&UjikIsYjQ&=mNp<zy+oBUcDe^ik!5g8Hcn(AEA?jM}X>~gR
zL at n=a(J?U+8$kRwngHbAhgeuMb=TBuSS!m-y17|8cdqQ}22xIErzvzx8?77<im{NP
z998hGV`azt!?jR^Ah<23o3x8JvK6!lXqcdA?x%|8l%v%0HtdhYatslNJ4Y}x+ARZ_
z7 at yBn&WRf0f{5h_kHse<>O#ZUqTS`F%@s<u;h35WNV=4mWO?!jpQtMHa~*7lc7Uab
z7|yjqX|fK at y17OsL<9QxI}Cwbx&i47dJ^IpaDy*_Ffsg9GFaL;jbkN<R_U$@4c`PJ
z0#O`utwJu|Hkgz@((Nw7sA&2mR8t0GbOUFDiMUSK<{L>(x2?{qn{U$9W$9{jvFWnw
zgfJs`TA0RMJwqt(`-oEh_V=i5BF`^?GSEC3EnOTbj)=NU_<jp$a1Wa}>Ec*ngmiJV
zV6-2Mr at ewZ`H0te;oXVHq_a-LQA3r<;4PMYGlF5!2z8~ybYv7qNeyQ>Jck^Qe}Pt?
ziS37p_qiobu6wDId$h*My$An-S|?Wl|EfACcMks2GADP_awk^<|0m)9bNJu&pp$zE
z{=bEP-a}4qFZ}=OVJFuPM%+R8Z}vO6zrlY*0Lp>?XYkMdu9JHc{tH(*xyA7R4gA>=
z?xtYB0HX at se$z=sK3H})5hpr2O+5M$k5*T!Yjc at LQCgWx&xcL=_)@E1_OK%XM%B7p
zN3Sr7)R>FhCSjCxSEev3=?H}faS~cL$IjrJZdMPkbe^H!CFE6O;EVX%WWH7TFi!h3
zGufZ<2yss%4OT~D?Ks;h-tfK$eRB_>3BkN_pxVsz+1 at gAdMcH95G`+<fhLn1&z(TG
z<rB;)2W3uVuFb4|r%5l2#>247@}%Vj94<*E9mHg`JzV56uf5yjlP((9+(D{Ltyxy-
zqJE;<^pOs?eC~d?DT*!<$NQpTZrsv*jPgK6!1#deC^_VGxXkr`u3I9HPWhXuEAU-Q
z^MByLc|6x~$<q7++!lF|BQ_PA)?@@O$9{^9oxHdxLzpjTkom==6OE>C7uq87{9-9#
zs-Hv_7Lwl9`GwLsQ%h;K350lwys%JSS1esIt at -HwkjoTW=a;_0 at pOR?`fQC6B2>Hb
z4S1YB&!{u#=9#3sjAWh({lq3p^2uaPmPc8u&_O8AGs$bRs-qbr8Rdriu!mAmDjX$_
zl-3t>Vj9eVHSI$wSXqgO)8Y04OS1*Xg=ltMur%Z94XMp_yl-i~$h<<1sHOQF5 at KUM
zOm?_=bkNUz1ce^VB2h5|hV&u2M>0|L_)HkJ_YL}qsY9GXCIE62^(`>Jcl;pmWMFhP
zE%N+JL>%yVm8ocm%v%upDXOWE?G?N__Sb(a^wZZT;@!Hbwsu=2lF)~msVWOvESbk&
z9mkV-t4KrQ>gZKLRme_a$E#~*`jnS5*g;`H+Kn^i)wzQ-Jvd0yY(~@3(MFu{kZGY`
z^eKL%Ft;-p8!<^2En*rebH=ZeF6M|MNee14Hh9+C50XZlT_asqL!_p?4L{YQEjvHO
zU9~TpRDs&uNL;xd+4ZtLjC-^#drtXCd*nlFz%;VGI at ECH{BP|C<t at 0^si at q#HjB6#
z!FDVQmAcNXRJpm1a%+7L(FZI`?5gbAF4sIgf2Fjc2IQORK6r4vzowTt+_wi$i<8Od
zsH at FMr^hx~C#4(7XnK)n$2_QAMP=6x$NOu+*xTU3BAoc#UJn-g_BCL!Zzpe)x4`Hs
zKVvv%J7Q@;Lu)QjVr at KlY-1)}*%x6re$O5b=vT_Fz11s8>8RIN8cIi<ks=TS57Na+
z^)b@$vi(?5m38ejXidn-uapWoVVq@;$+Blb`Rgm?tc7qN!dq2W;;6FLpF-zMj1%VG
zBjifuo7JeRW%Fv71YONX-~xtPry6 at 2G?-WLZK_wT5QhttEwA$0U31j68M`z-N^WRo
zJ8;L+m9*`|wL>a at N+owuWzc>a8ljVGo(<ir%5*VauGuM9ZI!EDltXy#plE8kv<N7*
zW;C7zBU+m- at la#E3HQB^r8?Nla{0E_&YR13wv at j@%C}kej3I{{<(nHaJ6vttO1bLQ
z_%}9Apa~;LRkPt>Yu1>Ssy0&9vXYp`#M1$48TtguU$q|td$)WiRZ+LfSndu|zVmq8
zQ1f!g{LAT8FOzp8AL?yyNDVJ at q^3>2UX0t1h00W#;n}$U=*PjM;-%m*VMOQwb;>)2
zcY!reORjDDSJ0)iQn$c$1{x~nI$Td%n%}4IgaA^|0_3l`to5X7yJNrQ$r*S{+dt(E
zz{FAhl%+Wbm#i9ga-u=5+DXcHgo=NRhgI#gAG39q7$V2dLb+?E1OFhWqLR1OvM0xO
z92g}_3hU>pt{t*#D^TBX3;^r9 at D=7GxDfs#kh{ob5=S*Pyoi*xNUu>P6{w9-NN$U?
zoud0P8AocKZ81MVMz>aNZ&}o&BcsXNwxf|tdfD|9nAlBvLYoo$E}n^xodV=_LdP%)
z$M3 at i2-hc@2JJ#!ZM*dFPLABw;)4F$Nx&!|<!!S0Uz5b8ls&Fd_TCg_VR-$4xLyR7
zigf99>FhX4*~E6K)7Mu#CfuvhhF{-k4^%DIqzXX4vh{nwrkzBmNgA`|_bw~1<XJc_
zxsSuFJTsa;9ag8}j&F79G#cWkPt>W4X}E}n^iaa73us7R_nlfwLnjT3X;?_ZnKYb1
z!)Y|kr(qrqb7`1E!z>!^q%|&P_0n(|4Oh_6Ps3F-TunoPhU;laUp1aeUqznUOv4r$
zK1IXLG<=$dTWLslflu8=!xw2tUy+?UjfVL&qz460ZKh!(4L8tmJq-mKuBPEC8v1Ft
zf`-dzxRi#AX}E}nJPjAnu#ASKG<4Fin1+QkoJqq122U|8qhToxoir?_VId7?(vYWN
z9u4WczEe#!%%Nc}4LKTS(a=gmGfSm)ZKdI68sY)?hll(^CF+v)(2MkQU7;2EpgXjd
z=9 at g^6j~&U@=X`^<3f~>-I^)w*Uf%Ic&quKP{|_2_(*Nu at s2!2GWV%hu52U`a>4$F
zq1SLx`c##ARr_1^4r#x!=>txuYZ7y(;zc-)7ahn&JaDv8JWz{Y0BR>C1GC$OJDS77
zC$m2kzS-O at d;}&oL}f$N8Hh at Us1xy@)z&ebORhjZxO~Ab19&-n7+Bwj-%23mR<tA{
z<fx&;ab>LuH*9aiE!P*(5~$CW=Je#^zWT8~uk?{c`n$ye#ffRLk#jljLX<WyD-zR7
zXAROtQ~mVwFRA%kmi at p9^Bp=YI4NeVH%naxL#LV_6u$4|)cnwDNZt(BK6u>R48czL
zpMd|Aqpj&J8Le(z%Y0p{TMxyy%-2gD2Hf-kg&$589=c}`mO|<9xVNYtp?;l$+RR|2
z4up}SI!tXDT80lUrSLC?|4jJLfPX&xbK#$bZ7BUww_N(;Zn!FNRp8nK*B-d`!nGH!
zeQ at oAYXYtbxDLQ|AY=?7n?Ua-lGISK(d*&g4F9bd<1*FO9c<r>7!JK>t=D1z$6*u!
zawK6x!fgYg4MhW5ALI}A!Kg3Jzcc{#4m^(aUU&y6z2kA(-ejL#WPM`8KH(mxeS*N7
ztq{zF|5Et3x(!o~w#+w3=P30)q15}&Aw?r at 8q8o{okJ%vrSlNT%|qY-Xt2-Dq3a%?
zEl+qspbsy5CtTX#ABKM?{Exu at 6#Tp3fBtdWH at QITT)5`IH3zO)aLt0N6|Ppen&E1O
zs|l_qxEkSV9NssKM&=FB-|)|ef8mB9ed9I&)<gP&J`eAQp>f0d?(py*$Oto8kZ**k
z?K at 0%G>64;O_%5>8=V|wX|ayGI6S6ax at 4@s?ZP|K2L{1xI|@t;>>JaG{iGA*jRBVp
zPz5H3pwEGrLi+jNLcy5~?qh_53hLS+G-`-I-DV#yHse>u$cB8AmvN3q7}JWmKz0;q
zyz~jlcok)o;Rp(*{ZiEU?^GNqWc~nv-3a0(=ifo~<^(gkhE(w_V2}7H{34ABIusXV
zj6}%Ky3j$38$sRy6vd_Sn_9=gSOjBoPRn at K9$~r$^EU=lh}$F#!dMDOfdD!K8~%?L
z%aU5wsFv|?wJxcxYnK7Sh{beLd`Y^7F at fwF&S#*2H=Fx~n=kCA7+;51K>2{2DrKcS
z9`r$YXz}T_=VX#&6j~Ib{(O5(!DBd7c1$5%4X2+fOP)NdIGh|;)6M{Tve+9kD%f*!
zL!aC*;J6@;RW>XDvu9kbT%f at 4=SW9<ktdMvM>E^2yoaw>*WmH{pyQ8GDpQ~f0W?01
z%rlaaj_9WJ4PzZ|)mx-Q#)gTTOidTn$DBW1J4U%F39g-ztgM|6lawpLD_icR=Mz|h
z at 8hfQu0CGbnT+`hjTzvTH<B^0q49E`kvM_}Hw2(~wXx5 at tzAZ31JBqJc$VtKLteQ7
z_x>q=ov)o|@#7*_!qV)()9{oZ&SwQR&!ARN-!s!)J(%tKs$Q at 1TP=qrSdE8xy7TBp
z8(gCRtMx~z#A39?@Em!sQpk!EZUh7ju0AcxGoT5l%T=alNP at g;yEw7N6t|Fcd;+WQ
zT|ji=y8$jx?D5f~<>d5W`1>QJaIq9UAg4>mRW0q!`|zL=;x<B-0B1-9ipRbS(ci`_
zZX at I|YkAzp!A#F+nM}z{IW*JlG?R&C%8{Z9Jg`hTkh%ghbq0 at Zux&EZ{RHkDLnM&#
zOx*=lS7DaDIl2n4nPa7R8uPJDpKk!5HJ<=bs8pBnOdx}S5jR2x!<&vZMIHevKprVP
zS*^&D-R7v<N;fjT>cgM%pTMVwDIa>3)^d)X;wg;9PXW^R#PH4Z>I+Azr*zb6>{?lg
zoT6s0r=JH%fJz<U(zwRR&Hl!pZRMJd-dOSAl}cWD7Rr(?Wo#I|DPs<0yEHHi$q_If
zp;c+JPOdZ_WCh|TJMw9V+l&V(J*13%fRcxlt&&HRGQV5_hOuMR_J5SXU?iiK#Y?zg
z*=jLE3Hq?Rpi3NS{*NJX91=Q}Zc=`|koFD4_B&VW#Vo08wH_+b#WRTsN_HAC5n8q1
z$Tfc7%n2D}u1Q+9`ipiFNsh~0JOVsa-mNU6zDX;7*)c5W<$_?pn6I2E<G3h?%OuJ>
z#h`CB^kN~d9xMcUsEaQ4W`WRyP`}{_g!!8e;n^#syxY?{$rswFlU~^Y*>xMo<Kfko
z-R%JruT(BY)3qBv_-G|R>0}$ozzi&ry3p;B);f89Jlc5q7BO8a&(X)z8ZX;K14I&6
z77U1%g;;GuXK8lRn|#ZU=jiaAW%1_5%LT%Ss4I(ZvACl(qj<eAs(3Mt!rdhCDR4tK
zGh|8$tC$VoqLo4!ix-ROt#vw7m^xMau?u1r`2!**S0yaF->~d4SCFI9S)*XG?0rLu
z6~wP^`oI8{3I(D2e%W~OdWc<TkYagK<e;NGYt00}+f?KlsGlaLIm#1jZ-i$fe!)_l
zLR>iux0}Q~`aMen9-|KAiZ3Bm1D4-cWf_jjRRh at k*1galo3<OpiQ9GJ9H~n$jA&}W
z^@qlFBk)@xMdHBPZz#ne5Bgm>x+r%OYT7`q0X1%c7=3_SY}pbvbWq2Sm3C<%#tViE
zH3P%?5rjlMCkaz<RTtOckv5c7>|s9jS&yQ$?n^KSWcYUg%f at xQ@_YK3k1TEY1h^C=
z%m&K83`>;eU|xELxCWCf2XIza4lhVIte|^_7lf0}EszA&%~CGm3#=R1sc1P~WeR>@
zvR8X;R5|X#({9phQz#%fC*~;QR{<uIl;r`AqqB~ZU%;_#y-<wK$qyFYcw0$wGDGLC
zbh&HYV_x52Nq_k+EopqJq<6nX<@ag9NZRR#c&;i8-!d1!5ir*lrRD18al$B%GHDe%
zIp^Qw8OnozGR$>L=!Hb<YU5cR<qP~$iZb7YgVIc$wM$YG%VR$+*CdK=NPyBLn$Sb_
zR~@QN4JOBB!0$R&Y9w2VVAMeR>o`Ld)tJSqz#N})r>LDfHIa1M-rB2f;yA~f4NIL~
zURfxxZ*i`v at F{J0Fy(4pK<8Cnt!Eo$T?t3KrFkXR2Ec#*9*vE!qeXaq$`}DpwF9dv
zp`;7!aFr`-jE;`_Y^9+zh13rUVd}dH=U<^vkEJiIrw0JB!bj-Xr(1bt<@f0J9w at KF
zm@#L*?Si2p0hKJo=mrxWPVGtrfFlPU)xP#Hrl<yH<4&sKBx3jl=b{^S_Lp#$=2uYb
zV;|u21XQ2LBiG>PWfG3d!uNnI!dQ&PuZMcnlR%f6(1r|MK<`ube~)1{0Fi5a_${*J
zclm?@%r_T5&i8vf)2(WtIxwetrED&NDa2_ at _(I0He5y?7>2U~pE52H-d~Y7o;${?1
zlRyXY at CuA0)`YP|H3I^kI;|KG6#5RoIz*4_3Yh(k`${-vIrLs2Lz#%3hyn$Nph@}Q
z3${7DJL;lGX)7bBBdc3k2XO5Ge+f at n!a}xTA-N*mZtXII<N)NxDt|3W>RhMqpz7Hj
zkY|se)uDajXTuk%x2}H&@Pzw{`ZBcoT3LO5e;Dg~y^(gB-0(EaI>^4y+QW|XP&pp*
zqpX3#<Q%2CB*i1%I~e0Wi_Y=_(2-s{Pj_8P>J^X<^BDYe3Xc`Xk2@}JFgfO&nmCWE
z&r+ta^bv#U|AE{7(?Z5z7eL6at<p!6BOj#`&a$_(&?t53gf~?2X=Teg6dpAoMz<2K
zA5wHMF}BZ5mLiout0tTKUAn6MtYU+PwW~7=)$FH%yQZp}@cd_(uW+>ok7T<6&np at W
z?6W5+p`MwG2Oj?sPe;yph06ov(&%XiE9HuGQvS3&TCTu-i|}YdXJUmB{$}`R%C*zv
z+8J_fo?M$R*XGEzxmbotuFZ<!fmoi9Yb)>ZcqsA|qs>qsYCKb1TV;1vY%ZRf6<#U3
z)8P*#x-;Q#bV6VXxwi3x`JTFsouGa%E{5%Kr}w+I>4c0?`RL|O8?_sESV190H7|>!
zifY;r14JqQK*;qJA9B46+Ej~H!XZ4v$900o6g9h9i^X$y0#LH6i-+Jn49;NiE(Rwt
zIKe9$zmEbWS9a&g-u$AP9U=^ko#JTR#_7%>?tIyuo2+rikQx_%WsN2OOpWGbjXAP6
zx2WbttZ}<m<1||1jAV^34yp0)Uwl=Kcm(5rTjS5yYc)=jy)%dl^zAI9_b7bd1xYMV
zl54Fo8CTGOJAWo>Be)Ak)C1qceUfrbw>?Y+KO0LqO4i+}K{UqB2GO7C`~mWc!U$*N
z29r;@QO|h2f4<o%KIT*UT5+r=R6I%W3aFn)BA3!|iE?t}gLH#=3yvFL)UI<ST>sqs
zP2zL}y&gfcz#6}pCUs4Ybfp`NAqLn+0iF4cLYDHxT`7z<-HxNETP>>T7N_`>FE-%R
zjUOVyFKGLe0~>KSO}EdhY-*sCxmvjVW;KpBkVJTvSv8vvL$0hrbw#Nqbl$1?s<a2M
zO2b3$wWRsi$ade?XDdi1wY7ky-2QytE%P{UAgdO??%9SLU*hSGj)n}<@dBpc$F5h?
zFQC0(y#Wu<5GMtUUU?R#DZXI|X at LogYuoL|UcqwXY0qP{Y}QMWJam&@G<BD78$J$P
zsRf9wc(gOw58{n5 at kTCb(#@EGon&%!2u3{MNxluTmo!~WK!y!(*1@#Wt~?yVBZR{+
zNpeD(@?I+*l7n?A&HW(iS&Pw}r|om}*5A~0MftFVTQ`BcX`|g9u~sU0=1fzC8M(F^
z)a{NuJOz>+8ksbSq(oN3I4Nx9m4_asXM{DCF?axRf#I0Pvn+!-Xv-au`pI)>g*i>j
z%qU!OMK+BVH2o2po#lOYNZxWh;Z?er)i74eLy^3+>Pz55{8N>o1^Ff$u)D<&nRswy
zq&haiz;UVAjAX3Qko1G}<avDd(}4_oxH{C(E!T9N|7}qX7#hk?9VvF-{4F{F<08SA
zTt$=P4dHsnd4PFr5N4GI!?&RofM1#HTxXEWyO9TBR9HhGL87jn5;RamMHai1Up!8k
z4z!o^!UVOvdp+pzT`f6rGlF`Qe<M_{jZ&pSW7!-&<*Pxq{Vp0)s9g{G=aT-he?f<g
zhrBcB!6F;-t>VZ?OwV5Sh<~#+9o<u!PfoBkhlOtq_hT)WK14583Jf6bl;S$^A4$g&
z^Rx-$l7+B$Ks5i`$E*;6B|^6Tr0Xl?@cQL79bPk<`-MrG!^=GA at QUX&oyGM at xEl?3
z9KAE*H?5#ldl<_#+rx4Ot3&LEMrfy*Xjl*RXX5$+-4i1nR6qL<DWbdZj`WGH)n)zy
zo}#+K4Ep6$J&6v~9ZcHadANJmm*f~=7Z<Y2ilP)NEWd at Z!g`Hef(M1`!8XrUoP#iP
zG#JlS?4pXG4tGH>YrF!T4eY`rzdZVVjbDQOLN4+PjE}AkT(U-n0ZZ&Va*|^-T%njD
zhMiKZYR1^`@nW=fP7Gf-GNqFon9B94s-v?$lSHbY2DQ+b-+l|~G(j#NyQ=lo_AqUI
z9c|gl!c at y1nn7E3upP8g;qP9h=-YtRE7(eC(mg5mhqgK*&}0`hIg2)#efz2kzgl;2
zP-3OzwEs6Z%;l7|>^;b&NMaC4DbGMWUj>pZWc{rGaoaTup&+Bb4C0gCQlYU~%J=U~
zHFm9y<3>i~rTuQRBoulT?nylKHK9EAJ9H)bB|Px-mpf>`1;gJ at gA(G`vHe>kc|`$Y
z(`{PPN;8KBV-0ErTo|;1((kYd7L)?q^u at IMY(>2zRrA2jjDbc$)lOj)Edpj%9x}Fj
zm64aw>Hv-3OM88&L4hxJ#ZM4)r7gacnjGkdG=20PThMjU(J`KgS<u|^O6y>yKfD<=
z5Vnhuy3Ecp*UJSM1M5y&e&1ld;5OrSF5G9`&_~`EOa#<0R|0nsCMYMVmgaqQ53jlD
z1Ki{<e2bu~QkZ6G`8~#8cATo$lY|eq2;HINcB|_2jYi#F_|oV5x`u)HLPt%aej!vn
z(yRPSGqb)MaBC#FER3)pBkdQ&gzXY$_bN&gr1OgPkL(pwm_(P8w;a)RdeW&YAF#dS
zh}QF at p)WoiH&yP%Z_|F`NgP55IbC%P0Pgnz^1<*XL?8XO!PA!DT;_I*eU7I(R+#fh
z)YgvfCv)t>WQ?mX9*3^X!>;`PxAA1(BGD||SR`77T+raeEY#pSK+C&zR*JT^=}24U
z$KR%^QI=AM->Q}IotGgJpJ(T5Ue%q@@$nAK^TR}{L)msD6YiZeNnumX(AeuF#ne>z
z*S~_4&ft~h=jh=M8XM%`ad7BgRP3jf&pyVcDx~cMj$1IO03BgZuJZ6hAem<F!Nam2
zfLlX5ZbNMt$N=e6o(L^9>FfV1G!IPrH1_FFSwxw&f_fQU10CtJ)tNseu7n%E%a^0$
zy~@1h$)md#c at -0u*sB2wKg=BldHr&2Fo`#NmG>S*$YkC1FLb1HjSYQx*d6-AdYP*%
zfyaUH_*0gClj9D9pd6zP;rrZVgS(- at I@;iF*5J^(LckmdjgA|2ZGrMA)TiDdyZV$B
z*f24H?d>zP!?dNA4`90iUSv(rTt1}f4`S1$s2{MwMy)$C*r2jy$)?}$RYpC?nogna
z90Z!OEZOQ?fUB1$TMaW@!+^QEemiY=Z?fS%UuyV6-(U?NF|^_N$%db?tBNT`_qmdt
zw5J;eNDcJ#FLIT~mt#-=LU#2itFhkbke==dZJ<5vE|gupAl&I&(KWs5ngXR1v)PeS
zv$4Zmy+%f=sLFDQZ>zh}C!kD3z}@uK(NoH3`mhZbHlv{pIeO)bWf*}cuAnDJIY%G2
zCm-Kmh6iqCJd7XlKvw+MIxIyxkb{Sk{R2<=TY>&EPv|wg(hodfKZM-5a!oJ3GbZ-Z
z*UbjxVhM(<y1JXR+oA^i+Nbh|+7w6nl(oyy`Q3|^0-Ri|Qws(me^{I1NT0F*F$4;^
z`izuHn5F(XTI)PN5RuZVFK$V8%%Oa`#)R9ci%mmIy)9M$37^tm$6Aa#w+ktzLm)F!
z_5a4F9IhLz-_^s|gW~XWEra|T(wb2mG9Ri*9mf3~;0>Qbl2s+apQYf>GVp(^(dx1U
zXa6taz6Lys>S}m5yGa(Z$t;pUkN{CpsqrTog2V+(fDJ(jE(Ugq7|^Qex?)9`;inRk
zbapY5VTx__wY1fWX#H*Vt%7I;5`sw(s^O;^fnv1L-gU7?L9&pL`OZ0acC(4Jw$Jl@
z&zI-P?9BZ=_uO;OJ@?*o&!ObE4^IA+DcXgQd})936iR;8;N;Jkq8$v$r}ig5)`!Xa
zplRAnNWL$p)lwQ!NUMtzz&tlg>reg%NM2LLXdAo}sej306%DNU4J(LE@?@BnzY#}x
z>qmfOnWh%hjnvJ>x(&jm6H691$<xfLP_}ZEf9R$~-90tCbL)l`>1pbSV8gmZ`K)=>
zKh$JJm|dS)Kdf%l>^eswENH%2WbEUjsW=cn7xWe^u!;F{fmyvizMv7s`Ve(kd_mXG
zt#{O?)s2{2XDf6zFaIWdwe#8)6Xt3jbMT-zlIK*%L0;&|y7aMg<vv{s0hyYUe_#$L
zD8m*<7X<Ulgq^4oYWISme^!o^3mgBJe9b&Wr!2FDjSYBE6WEBGSj;K}R$B07h!D7+
zUNS|$%(6sRiFmQhl7yg$n_+kcDAbiFU<o6Iji^o+dKZsES2{otsVl^Yz7%2O^_X~p
zL!JL8Unm2YQ!6tuKAYl`gpDI(@mZm7 at jbcObg7=Kr-Zt=fnH4g4FcK+)r7Ey=1qQk
zdG{H0H!gGMEmZe~(B-_$N`=7x;09t+c}}Cas-#4`68`NaLJ-Y}h3>_y#hD287BjjX
z7dHNe4wu9WwGtigQc~Q*MqD)}ZzUm2s6)fUQ}HZP*!UxguR?i$y+3)=;_g1 at 0bv8`
z{S}7LV3V4J8HxzAPGKWj<_+D3;V8ZcbrX0*2)D}A;`FJHM$q8+1z7%;aaevbyl(Q}
z4ef)OTHWWj$_>NsYq;C8J1okP`-P2TsOK!y;M;`2rSy_41kl%Gi4edsQX(!W5wD=G
z+KU`>WftNPwbg6;cq5*sB}2MaZ1!8iS?Wr=+-DXxpjNTmH%{0nV=j~w{0X<QX*e=M
zcNqtQsORtXh*R(Y$Ho at W$`bG<-X%Lt;$rN)Z0tOq%%4EJCecJf5P=U*_M5`HiA+|H
z0TQXs<lR3xblPN)$Ld5Njddg0^;gEko{8Bg_Jl~i6q2X^DMaQmsgw_~l!MTi5Sp5l
z*)r~S1JEK?l!%2T;`kD&iQNLTI(q^HakULCk7R>vg-2*jFomh<zYed1-Uv3VR-x4#
z{pvn-cc=#iss3gs`^_OV2je$|E~Y*ulEDL3bTP2;W*xWg((?{Z550r^%ZV4Y_lbB(
z`*nc>p|z>MK-L;gQh$Lw6QFpXRriqj(Iv5QE1$?6VA36JFNI8aP1WTnQ!6r2%Uf}^
ztFA1s;yNY{pAB-{cI0~DG<Ewky!h?NW999y;z=!_g?iDRu=$y5r<<h2%}<e6>cgt5
z{mrGxCR3%QB)w88JyEHaI4X<a?Vs>whu?4D*9O0*;MZEInkr!)#kt7~VW+jy6Z6~<
zaux&w{Z%{@))yRBTy3~7iaxDCtelGWA*zS702=<-1LTs`V%onS037CKKkejx1ctPj
zP}GFfii=_1!$hQQAjG=8s96EU!{RJjK?$(ROtq>L=x(K>ex#aKH%w2OrKdo6by$}e
z%lnr6ClqS;>;aT=&p at O8ro)Oe?_j}j at iS!sQ{OL|KG`gug+->YvsFHGx3F<74X+Xe
z`^@D+;ID|iGevyILOxR>J|v$h73auj?i2z+g0Ev93qNlh=UgZ|%_}o%fOQA~4Y*ev
zSoMOMRA=L_$$<vG)ex#cir8Hujw}&Jmxx14q*q>@9=Zl{zWT!u`Uh-rF<ef(dVlCb
zWQLG3u|ymaR-)l0<n_as{RBEM8ULK2rjjVPB!<&{SrnQU4Na0;EJ{`9tII;~L0-$*
z)4PEn&_@%&6F7*J?!?=X5H#g-aXK^&rVTesJzwiyF%M{+np7{uwNBa=8VRM$3Jo*b
z2aOw7BBlW=-UrpZS`@k&^1pg#Xr{TpjdzD8A<J!)nNJ905L-(W0;gyoCkcT=#Mmyy
zz~Fk^^`EYnh(ZYzxf+Ws525GrSJ#BTriD(syxNum1Oo$YyD`wV<3NMCRS4GL3y{r2
z!p>x9rJG&x+<BUaCQHuKM3|Uf<oSj5n>hs#nm+&idCI6W{W~I%-U|N^v15Z-D)c9G
z&t#yYEPQn={MT{erQs2=@Snzo&jxx|_vz9x^#fgM2Z)$85_Pa(gE2y93cx+T?uG6!
z_Gi$*l`duF9o0%Zl!^$5<}BxLbZf}jBwfPZzAVOi- at MRdnp|-Lg(haGhZOPyXf#`O
z3Hy0}(w}`dDZ1VRM&+o>M0ZI4px()ZDu$@Q$7*O9;UcZE9%23O5M4<(G>L1bTq+g;
zV1QYW;M8Xg)Q-*DTU1cmyK)5Z`pKE%vidZ2fvs++?y{k6$gcV%wbWK;hR<U74qsDH
zkuI(&&TFWdc8QcwSnSrNQ+(4*EA4gHCus|8?AYljo~9QURP?T at g~!U#fP}it99Z47
zU}NU1rA)qa*}3q|XdcehRhL;%5s|Vn`Dpg&rPz4X(Fl*lydN7o0O1eVnoIlle?|?)
zOaFGQE}=_{_|<zkt==<ZIv0bYZx|FEubPMP7e?d#c3&ZGEsXV^@9Zd at wU3Vh_}5Ij
zAhrfKlI+Gwt#~}IVe(l1dWg+`0ecFGBbEJ*GUA=z at Vl_wpFB>=W@@Z3e;>MM^5M+I
z%3kC;16y#{%;RONeow;qc at C0ydW^H4Fa8l*eEJKGCS6C{Z1%Waw%}=LI2$=WdWxNq
zNvGK3xyLfDrFf3J(?G2_+MZ2j|F{HCv17_2=iSq{OBrm}B{9UjJ(BOAmD0cvJ3TNt
z!3-Qd#NylaoyN8$?!>Zx-bGE+OFPxBL3{OIk?SM1IexD`{^)7di-V7);`e1gec9N?
zKmRU&(|4i=(aoekkQ^3EkmVjsIq8E^46HxrkN>uQ`=DsM8$+Lb5t}?HaQDDKhubJZ
z<J9uqh(8R7kWXNpT4@(s(~cagOWw{k(MAyAS-46GdncdM-UVt6$%>^myzh1?M%PsJ
z;v%p#g5x?WZE^J_5V}FqO8wQB=o-gbM9E<&QHkpe<EMk5IQiiKD29`FGLny{8z49a
zfFO66{~Z7q3;+NUN2?E>zk2a})sy7iH1Yh^C;T_nCwx!!-+pkO>aCbz=D(=k`rlM<
z{hsPqesCTV;9LL*E$_S}fYJX231IT?A%U*Ho(Bmw%y47g`H^7zZ;)X79wa<?zUm#A
zq2apoSMT_5s&{-(_1_#mPah;3!*JSl=k0^!|85v2fB!ICdOj$;1{B+`JuejA{|*Z8
z_e1gdq4Pj7(tzT+^FuN6zk_1r_d{{Vq4N+#HfDGw_xuEr{ofEo_IU}Swu%fYjBe;|
zk5dKcsIG_#I>gHh&T_3X$tG9XmWT;>*}O28#Ks;tOY6b&M^g#w&i(5Wgl#UHo1rjG
z32NdK#NAKPJ?b~kR&hk|w0J>IYgv6}eY&|W%UtIuoGX2^Xz_RMv(Ppg2%BCt;yk-@
zDXhdb>riyJ;|W{vu94KM9&vWBy4bigSLV at SoxjHhYt#hwxCJlFuMBf{4eHlwi}Pdg
zqTpU}jJhx9eab(uex5MrjPvZO-!ERYME$JdyztbXFmc)1j%U+BUAhoGbh3oeG2sQx
zTP|6Q`O%XJH(QZzl18%Q7sk>Yg*1zJBDpVN`Kh_WD<>`8EiPPm;SRqdMO0mF&Or|)
zCky3>ZB>Sfx(Z&knSI73ELu<=V8ScymXFK4`hs-c at +)zprpehOUZTFAv+pKlL6@)%
zTDc%a*ygB9Hw)Vqq|`ao1zm-~gVHCS_<W at fwb|JxUXs&i;1e<?M>8h#jD at qMkXqI2
zDU0R1g6BIfUZnQq^!@8BfN$r=E3+(B4rkAbj5vV5bbh>Q6!izjRYg|W<04PwNDk-8
zm3Hh}9#s9Mvv1XP*vMvT;(OoIMCWnoVfEv%Y=MG~ugK!%W%QRPe(5~Ed^FXcgZ9*W
zon0%1xacqN(#L{``mTCB=lz^^=SOREoXsmT)Y4N*fG!>@uWKgwL6eTovnvWz=(!39
zbYW;J^b>?o(zkiiNBfgDuSiqzB*y_=9l8Mbttz1QQzfu26Y5aWRDCpvdDK7<^C%0@
zHkR6 at o(+9=F1ntqvYDh56uMtONT-BIl^~+0;&7dCkTzn%rQR^!Junx02B>yFA61!-
z>Pr`T*gvQ7+c0C~zK)gqJnT4rvk95~>d|-#&PHa&>w>q8Bwg_Er%WFiAN~ptS6jmG
z`qO=P;EKq86COd?)E-<E?R9=6Wfhd##gVLeWNchCrO<}s*HjzC3QsgR8^yoj0?Fb0
z8dpV);`^BEh=OP2jYo`=bUbI59`LYV-u3 at V!S14dAjhJ`w0k;Da#g2Eyg>(OuG8mQ
zR}WRwgl*;`B`uunVNX7d&3AP`1=3JhusbkAIJGW$mQr<W%1~)<bFwMA7_%FTv1 at 0;
zVvNrIZ$A=Sjm^X6aYiD+nkn>aNbHe&tji^BMK7?PA0yv#+3Fo~gH>*{1`dk1-Y~CM
zDy?^)gZC*n%sVGtU+?aN_sedW*C$<6 at 7B?i>J9UBDMfbmS_`D!<-5aI3)>2HVcTpy
zTnuk8jA!?Sr at -4ecsm!q4&Hj<tv5V@*9M$7oLcXIf`;8NOiC>*E^?k+{uMXgOoq0G
z<TQrAA^3fhpFnzhnS1lh at J)Ck=3O1Hrr=eCwzzqy6{j}xt^6va*zdQo$7hj7rj0#O
z%+(P+Y!V*Kb$Qq)PE_Q0BN?AReG#7rp2KJR)A($Bn$}f|qs!N>a0(t1hzWX|+-!A9
zw&h*%8YD1Q1em-WY8p%u3Hkv_V_$wEreKFU6|hXr%!m;}#t>WyFRW<V{5JO!|EXd|
z8!iLVv-DW{&ur23+rOJ$o at yI|_cKTN at R#W?@xeHhkbyzvEDyqb4d_nndGy#=q#HBx
ze2>?6>zzd=_6i_j`%@6N^BH)Q!?fAFLQs>9Mz>g<eapX=r`oZeC5y$;K73=388zAc
zCVv7tJ;hKmMU(BT!Erku5D)lID>f9>VOlRpY$culc4*d?J at 8uZay*S1j_8a-2+~1g
zz6r`v^Is>Y<!ZZ9(FSv&PtWhoLjQnjM5#KgR2);P+U2jT at b2tcelxrMVw0&L8F+Hf
z@@e|@n2pVbASGEp#LL5=Xb}Y!$E3spSG$;o<y5rct~{2iR<wm*YH at Y$0UXp;bsmPF
z6~j<*z+ at o8)oBfX-s0+Lrvx3xj07G12|6eNAD-E`@rZYge$Ya#CfhQAj+uZCB-ui|
zMuSQ at HVBQ(Kq6(8E*#4J8%ZDm_Qh|e_)UDNx+xm!H)BGk at NfKt0QM8(lk%`PCv$l=
z;d-cp-->w!0Niho&tZg3N at Tw<lHnZ#p6bb7E-!8`z0G8*EJ!q&3QeLVuc2x46nLxM
zj^A#8SUT}$3M!r#)MY}5bX?vp!f)Ejzvk^NsAKRdJl3c-Hu)F&o<h2qG83<N{t8BO
z1N1REV?BkJWl}sIzuBW_ybXZ^YD`q5&hVtcb$&6R&STL!4?&%;8g(kAo$Qr-x{_w|
zXE8g{J^SPmBf!D%yr`V*eGb*vdYPjXBi7jbquCye&@;9JBwM-wHrDw^px6C?C^qho
zMk*V7lCmo=8399>h5>#vyBqQ5Y!;K<)Dz__=_R}$w#O_c32&6PhAsY~<@4MgAGU?v
z{|L;|u8sn!Q%Z6(6S|1G91VhAt-4OAHg$+nddxkK-Sz@*!ZfM(Fx7Po=LeWWFg+@`
zv!p&fdiMdSkzE9|)Wli=&=ArE$bNE%fCqYwIAI)+KdEl~B3ZmAoTU8*#kES|fh72!
z>wt$R8y<^uD~0N0_`d~$s=W|Yy##_Ds4RrXkKOQCc?Ue!ErW<3L0I(z5LUek!m6v6
zY1?pBq3y!wP0|+NiL=ok5ne7I8DC#@vbNHpRXc8yzLGx?#PMn)UKUSPM<^q;l~%3V
zI$Ju4Gq6P*f_4UqXU&$n7cO2>(exHx9L#CUd3T<=JDOlb-AGE%$OaTqBAqM`p^}1s
zsGhM0I}@+61N~MCUp34tQyU2{Qnk8U{<ON0C5zV^5-fmchW{dLe-XC72vJgmZ7ssK
z7GYb9mT3yMvP5cw#$At&jjW<T5+UZ-W;;rx)AFZ+n5FK{JKFd~f at o7lX|t_2NfEij
zA|}<1Dk_oAE?U^6ws#+un;bcZ@)~Yc56PcmoVn;G>1<;d^FT?~o2AHYAvB9`tR*H`
zfF3)#r<;`2WBANy$LE;C_{?v^=hXf1tlW?hp`X+f^fRWFeiHZ4&!r9YbJg4M^V^My
zUEO%g*0iOQeyv*={Aw=SmQ(m0>;Rf>NzCm=K4vy;#>g$5Fd$kI+u=FHYHDanJZ5k?
zItNvP?T}@2fL at QmtDbQfyI=z3gfX%Q*1F|5YqX&|XE#mu`>#chv6#RF<I~KZ!ZbcC
zThBoAF~BieAxZyb-YvanVyfjFDRCZ9L1gsGJ-V1&#=KaN{4BP?<e#GjJA3g|*v%4l
zp%*6%=Fn=v+;e;znjlt!9q_4WXeLp4tR0_E9LDEXyg{Pu*bmRj4Ua|WM>|13Pqflc
za1Z at F+dw~mcpH9xyD`{>BDl+{m0A_;6dnmteTYyc$S`hpucCG0OQ~Ja5H46HK?uQe
zB8Edhh~OM?8iDQRs6#0PBy+pb=pW+z%$?;fVt?6%XQ^ChfqC!@qMK$|$%e9bJN68Q
z;x*-__yj08`OP~=LMM3q3GC;5X7LQ at kj1gHkpX+myJM3~e1YfyphE8$ocJ+J9Mzy|
z;Y!U-c?}*Ounh-sKNKkhMfN}_cvTgl`sgrWH>c2+i4s+mWQBtR^7{k)SNa-oc6xFu
zKYwXeDh`w1T at VpK2<h-Avzb%*wH#(m!Q+(<yh_b39}0oaCLxF#u$t?boBer+5%LEL
zX?L>^q9Mot*_78X_kLx}%dn)9-`AC?*_}9I)8Xk`2qVGhx-Lc9<2)&flgEn7HoppK
zolW9JZV!9=MZ+XP02NoFF)-OI-2;=2hyAtyCKflVm`s;ecr-tx^>|n{M3xu2^?VFx
zzq_9HmoOeLHJ9NZ8;Wk|jZ#zHYy_67jKKh(i57t!fNeA%i0b=!dyAA*Y{+JeyRTu2
z;_9p{7*h-%Vw##U9|I3gvFzr-zsBGTH$Tq{9|m(<PU8G%zV>hu$F}BbbC>yVgt<)5
z_4=%5(P6Z-U!8gk3HC!YH(gNCCJiOYt8U?q;bi`n8JBOF)vx;&8aXMC1M{R{%0eq<
zvKg<LEw01JwA89jb{X`OGFh!U#vI05JG0YURH!S5lm!)sh2RmC<}4n6p<30UU(X(V
zKBl+!1`=nprDobylC8znie(pj*fBh{zPG`VMz^TylcwZ|wkf%5Go}<&4=WCTm3LH?
z_5g`>VfQrEOuIpv44Iz8On at 7+t23MkDU*vKV{X;A7 at rC84`ck`$byPiyj|WY>`YQz
z#{kWv2u^*KmF;`ZfKnV9a^T*<ENH(fb+W5$biY|T#zxW```ZmsK~z3LSK~oIH<L2C
zL+w&qgkTRe>r!C2HER+}<kns^`!Ctm7XRI-=oUOEjnf`RC6!HoegqJJAuusFH+Ceb
zuRikCbaXMnKW_gDbz1D&!#Su)r7h8SQF`5f_mX0NGHzWoZR*x>Bc;l*D5p(X5YcAo
zn_!N#>~4%CSeh-6;=_9nqN0=*NVxR3y1uEqM|Gv&*N~jE+v3V}F37A-(!``Q;RH*U
zT$P?^C=^tkG7SKC%y+;|K|_r-k0P)`==F}T at RrrOnesoA!#Bn^eLz8 at zf;f<b>dcP
zb27YOdo5DdeS4=^O0(%X{@%Ul>fKU00gVt!BbEG2<OZo(F`hs=MraR3$sbQq6X3~y
zn#lF!XR5B=dmFF_b0S8pxuBMQO}Af`sHb9v at Tl$hQ9!|K?LVMjJ(0>H))Rgr%6Nsd
zv30=-y9esbJMBi<m{|=&EH6W=VCgRe7NYuxH at z(H=nQ8DLMhpu8CaMQ$iy`H$P<LX
zcH9qqBk}k6 at kZ)X at Z*NhXN=#c at wc)d|5s?nAOiw^3ojmwc at kr8EeqSD?;g*s9(YFv
zQpWB&hnHUyZ{?-VK{mQ)JZ5tn1<y4Ko{0sIgn*K0!8c)yzAC*eoEH7^c;FigN2cXr
zNhAV{56|~o7Wxy)TU at 8mh>21Wk<Z>&HQaAqsP2O>G;Pu1ilC;x2mj{XT2=<BSu0N3
zu2U4IOA)NegW<3CVyPu+D)#PX#J%dxa<*2Da2{BDv9qmutn<LSvCg)t3)LpH_26t<
zd)j%R`Wt84I-BzVDKIj1mJN7;WtJ76$-K{<2c*=zz0O3W7U7A;a0-54{|di_C0F=$
zf`d(tK)3pBz7Cw=c1)tX3;C}jZm#ervHK_Cv?lc$K`HEONUkU1N0jZM?SHm+65bw9
zVsrBO&Wg{}2*v$)5-M at B36qRfK6@<^TaEC1KR5EF=NorqvoYT!p06w4$cNUXQBCGN
z+6Hc;fI|~w7fa<64!Q}$l?z5Nv>wKl3$}NokL!><0Ro~nj4s at Yonj}YXz`HYGywI)
z<%nKAVy&(AOrclTSJ>H#Xfffp(UQ^7>Mv{0ynk8IFIRn5=JlI=HF=+yVKJ5iqd_Yv
z)@q4q86#a=1569eDwW#QcC`hq=)e^8OK3<=yXuO>c$PNP^3^6aq7}WR9@%uOoBa{y
z`ka>0hy12L*UU4U${^@BFX435Wr+-1K;BZSOV?V1`ifalOl=FoATKy&Qd?_3K_;ZQ
zY-<u{%X at o^q*UNS<=tn13*i}5BJrU&2|5fbvh-lS at O8KmXJn4%9Qto_CPRnN6zQ`0
zaY8+%6sLO`vOvGC6lZPn)%>wf2e|(Zf{6iiujwu1mr~+e$U)WOEK~-XZmmjH+lvaa
z>ers7#?DseI&!S_ev?vchyE#pX<N at _`?2ei<pyi{rtUL&N6UOTX+o`D#goZiN#M2G
zHu<`~*0I+6q1H00);|SPu?-1ijt53bPDGiuL|L4j*ATOx;$cGK0MkQ%IIrm;Zh9nx
z{m>lSg<JA1$e_3jr>0Is)@keCvNICr_b0xtKk?-AC${w`PV7&dJ1{XUV(gyaQE?(}
z6s?>HZ!qPV_z4mkElme{zE+Ny#VZ+dT0R1{ox%}-W=<U at aX<TAEaC|sk*7 at zHsBO6
zi9L7?h>GP(a_}g+h7D&rU5=HB{)-oe)AUKGMa$M*MFJw(q27+oZrVPXp1JhIVq(n#
z)?@ukD};!{i{vv`E&p08`BFYJY*m^+729V0%l=MWttHMxVLq+g**d!6l1945#2gOV
zlu6p2`#BT{VNL9uU<6I%LH20>eM<hoZ1WW07r3LNcCokdq$4-lcWgJ$n&`Jfz3g{9
zeH>N|t}&EtI7x^Pr?8)3B9oFt?kagHYX_9F^k0`!94lp3d?~T>Kd+}O4=<{wr!-Wh
z&r;`)cXYRRACcQl3WO=}32|A`xXk{zEQrfYX^=A2IpZ at iKY6up$1AV!dnjB4?-(<G
zd^(052p{<9z!wMFQ;tffAik^n?(tb_E5u}We{MOjJk1H|R at s&(J0a~#^W40HYV$2p
za`%z$cKL{D(c&e$9fnoWGB1Jah=OGA2g#wJt-zTGR at HE<xIZcrqM(J+Xn={}A!uhp
z?yuF|c?WM<GdEmD0H>?G$@W12w*tVKKZi%U0j&d|O#^7{D{VQ@`q6+5__f2Y<CZm1
zz?Uonxcg5$ViUp(XybvX{<*JdLcsM;zg0WiD^aU<K>EYjL$TCrAayuhP2p2Q)i8EP
zGz!XSLnhd`$-Ap&-1=k?OWW=}Sktj4VtH3f4;Dk*c4W at Z*0tkuTBpNLW=`vNQ}9zW
zz9&x4X${91X`|vv-?R%^V7I;t86~jMdmE6AT3V5LN{PFoeDmP+f?3w86t#VNLDu7I
z&jQdlDIVaOk5hJJoEDIf{mD!GQ{>8Q6R=Gbm>YM|O2d;0A4`B+-rIoO($WghQvh+t
zRwXUU89hw1bG9f6?0($qF^=vQH_7W}nk2zLp4qQJIiYwa at SJ06@yzG}2}2WEaSZi=
zzCjGN%)=>$2)Um$+704BLT*)WkE4swfulqhdx0)a5+P?2A*aU(*<lc}-5}(}NCj6t
z12Yodda;TlWtJ-1OK7utyA`JErtKHd(`v)#Fe|NYU^&y}LziKBx>lU!b+d0?M$KE{
z4M3B!&Z?}lDeLTwpEyz)wRMh{UV7=x!p~9Cv_s;Y_jX}MR0MDo-Xe8t>+!b4P7{UM
zNHd^&$>QkFR951Eohh#tWM^^PW~Ein=!8Y>E)(5t;fwIRhPpr^3P6R<mgT4Fga3r2
z&EzROJJ2BJYEOK17X2W!3fnBs)>R{N8VlXpqp2A0&%Tvi^D=GF)Xy->O$mVp=>xw>
zUuoBZy^sm&b9)V^<8)5&tKZ{ZM;U}eDA2h#7VU*hW$fl>k^9dy67<F=@Sz0_N^}_|
z8jpC};KFLqMSEJ!oTM?1`VEO#Y<Sq|#so0~gR(IQ3aZ<XMAu7_>wZfw*w$U)TR9qA
zf}Kzd^6k96KcIK at F??!)G>o>iX}4v|J)>3)t(lWH#-Yv24yVdzN3F8sXSO!4qs|ew
zG at DFC&i9u$M>oLszl<ATsAP-6=bJe&?V@=)HL{uP_{~}-j*+XE;tlClJ{SZxe>U6s
zInFN4>_85RpQ+516BSig`4p+snNP+5CjsGK=+QGy at EtgzwhyU9O!lz9{R#4K#ze|2
zpGv7A8zg1d_Dj}ubNgqo28Rkx{vckyg{4&)Y6ndRR;7&Dj*FbWK51-ABEBLQ(KAj#
zL6a+bMVFxn6rLjYos%wzPtpZR!bj!4UQ`R}0>QUGGQ0=#CWBN-cT1c5Z?a?$*hVFL
z2VQnJ4)aLfX{E~M?32>eCiYe at DXMSAyWvu2UPHn5WnbW;4;?S*u1>ijcPcs=UH*?+
zot?v*8X}~u)kE~|!@C=Aw7B(*h+&q;gNt|<1KOiOfkD^fg-;x4+KeoZo89^XRCsVY
zZqPntRaV#w7BFEwS>vnvr-|P>(4?>G(*k>iKr?Bg6vIf_8$w&KamA#(fO9$8J-lyk
zt|E14|69ZRCY)1Wj21C|AZ(i^Y-<Pu$~x+6mO`X$rgIFGW}fAYto#BkoHZxZOfyMs
z&~dt}LvFUoRUK9+VR=Yx0oYn*pm=9u_x8Z3Oj2gpwHXdg5uu>lD%EQ%m0Y<oL2gL6
zN%~qn$v%WcA+)<xGsiN<TIg(Cc^Zpfr<;UeE+Ac-G4{<qY^2RrI9ocS&akp!kOfLr
z%4}havchKYEY7MPLRJ-it{@-#SqRR@?Wk5gW{<w<h1gCo5SibEB=+&kv}l!A+f3pG
zs9FYeB4)<4%M4{RPA7Q{FpScDci=EuJ^?Knbm(j^daR2mRma$`as9i*2Nc8dXf<bZ
z&aesr)W!4RHA2>W3GQvS<8 at xN8~G;Qx_|+t_MCiHz64hnHn!5Rm)3zh4%oe_bWC6A
zi;b~iYO!xBA7yT~>J{9cT<j}Tr!v|fJYBpjK?r^U*-j_o*ftBnzo7`6$^w^12k;E*
zmR7)yI>WYQKmFRbw83v)1FSyfNMe<vQk^ryQK{HnZ$oflm{H7BB%!L*`Z<|db#-?z
zj0o`v98Xoi?lO at -)z$ez827nOV?GA<aN-3qz<2(U_Ej;gO#d*-56DMxE&%)arN|7z
zm!c*}c5M&ORmSQt7u$CVXJ}kJphKh%6CV3r0j+D2r&{rpUb?`4j(PCxu_X}$%rvhU
zYYLB=kC?($gjfGSeHgEs;jX+JKeoqzydMA2(Ep(=aksc^xVW!<C~`O8SRA5Sj5zrr
zAeNzk;plghLoc;SPyk*ZNyH){lok{O4qwa&0hu)M)O<wN|2MD}FH&2URK)Nk+0^xK
zBkGQ#_xJ~<wu4M2`qcFZ8U!pZ$dqnV*Kg-vBjIbfoBadElD-Xdu}l9*Hwf0hJ|IDJ
ze}ac_pTAt+hUKw8_J?nI83%y6zJX_)2^kC3^?Ts!Dr5w1_EQ|N>+#YP9kk}OxY+{~
z)Mk9$Nrhp4<{^o|p{~b<PW2&LMi=}FVM1sQ{~m8&V-&lJYSq at okXX%;I803jAW{Gb
zpjsOyDp=AhUZBph_6L9c5^7Yn5KdcQjfS5C`WS7HIJu>Yi5 at lA*)2}+DwCO?bU#mv
zqdn}OxbHU2RE|?<+X+5}qH9bveuIV<m(U%V8ss}?frqdKc*(AdPv6DMK<--y({~n|
zhQZ608guyB<9rTJ9glOkoz-3vwVIfZc7EmNr5;wU4>He4V{j*={^7;QtE5iUpQSUh
zCOxAUes)_;QOzSgfBTm>#!cv&p$$#^Zhrksery&WMUwSsJ-_{I^c+m>iY2gKM+a~5
zYO{L&u~<qh*2arPy|WQ+J-;I^QclGfee-rBJ`JW+8<Z%gQt6 at Mi%{>x_Y at +o5gOz>
z at l;f^3JAQV`1Z^{K;lXpNWrs|b?M5wWzFkR*?30tdc62Iqovq~%m-M~%!{MQLuMQ=
z#Y{s6YiE~W0yH$?vZF_)2FbP=u?+9}%B5^w5Aq`;2Rf2us~~+7R10uG at u#x+{vZoy
z8yZ_)t`(@~V++*?dk(X<5iaNY(s3uq%>F!vY;qtG-QeLV2O<wBF4GT7zDOVW<;t)l
zZsuBbB`G|_!zRCg`i`feCUmN at gm;67RU2y>(i7QwjEHD4?s>K%8h>&T&PjM*IDi)!
z)#CAJchk+n&tt;kEBV*o_}4`K^*;Za!oM2%S0Vp;jepJLU(fQd68`l#|GIrDb`u7y
z#{fXrw(RI^w$2BG>q2jw465_#!pq8^kHfi__fi#RjIKo2q2sJeylfdNayzGz@$2+j
zod%#?zR at H>Azz-2D`YIKpUqe<djm?su|VCIKaP40-tFuM<N77psnjLn&7^jExk2=5
zJ`ET&);c`eX+*9c=xrb~EcTnu*Oe$7;04kctq<|z=`W6-iYEP1`*l$lGpg>5Cv#L-
z9b$;9OPXMkK{Z0(gv~V5__FYK%VDhN1D}IF+WCi#J_v#j<f)hf0LR+4-~bdy$Wt+)
zNlcZe9-|ku<u`saHjRqdjN`B+5i at T1sL-TPW47?Yi+p5@(GDTdNSbf$bU5(=O1?BD
z%=!>8o{!np{C3iZ<7o_FIdTkOIa4rzr8-!`7 at 8XIQl}#NeDy~QO}sSyL%nTFl%LFu
zj-&@KjE$t+fg|Zd8bzaV6#c1Z at F>b_C=4gF3c3%!cuBv>A+(QI%9#8#?zGsn8D^4_
zRtac(h;B0oZ&(+GC8fgFSY at -J;1AQor8>=T#rGMPNH3^&+58E0u1H<QnP%5nv|*UH
zx2_`6>^fsAf|C4ZeObD`%!<cjWG!C9Fc7XD$3<y2szgk7or+lw%4-Ox3val3FLBw?
zs1V+u=t3EH(Z9Vy0jc>F3er><9lhSTxif2qscAdIC*D!`8s{;9d8D&h9Paf`Dpw;Q
z(`G|+$YA4=*oMh(09R_sGQa6ISQiVA?$(x@)de>BtS;sT4~lv6S+h7!J_{>m_V+KM
zavtkAPI7#Bg#5|5utnaRpp+~NC(C4pUMIB;;<aiWN)Srwgf4t1)9I;K;(HBVF4;1%
z3*AuJd|k-KUdxKHF^x;eKg^2q6tdry<&KxRtufKn@=P2<ScK=P(@f$N821-v^|Kaz
z5*v-}Cgs`TXp|*wDxcTg>^G<53b+h+&QiPn at RpLK6k9#)*)7Oh7G-g*W`OGGh5el6
z{R?B9r6QDvJIQum=5A=r=c55hMYvZG at tgfNwq+y<UtO>$qfM<}h59$O!i(7dc0<iy
zeuZb?OO+&bmJh0i;_n<Co#H03_fEwDc at 0NU(df7a;`UAaEJ!|jo7v{k1bHVEcaZ@(
zuI at AFJv4_b{Td>s7u~WJR5;dM?9n6#nw*+sP}%REkJ<`KndPi<to{4qB_K_41&w=;
zMNetD_fOv=B%_vj*|ooh21)k%B*+?QAjR)bA#Tdm(zW~+_7#4?tv!q~qu=3Xe||V>
zX2 at 3eJ2WU0evj9X3^n))pPp{=C>3_5K96E(NTY*uyGfgpTR%=3DMtzg8BKT8^js~j
zb)$-bWD7e{Q9v32kt=*PJxj#9a8yH_3|wL?TC<Sm$q~njIf0{MVolGL(hMH&kXenJ
z<;38b2ysGSD&Q&r3q>@_p}XwbDl-R)op}y9TY0$<yiccLL9(WCiXEC7)c`^f-r+GY
z`dT_+H18-BU!)Zm0yajteI-yH-oK<IKO()NbZtE%XmaM_MVMk{d<s{eVQ!J*>Ek2)
z=jFIzGsCyQf at j62*vILl$lry$9Ni*#wbj<<WRsGquUz=&d63BGej52}0Hzqf1-dSb
zrizOf%6l?_oG?^zMSOE04aEK8_c4o3|3l|+5v|0bU#pZx^j!9b^#-qmmEb8d%APB`
zdhJv`Dp0t3^<1h!p at Z$r+N$2yq4>2=lM=)N^n5*r4|37r)(-)8hUhEZY}`{Mcg!CU
zJfGPp7`SQWd=MJvphHe&U5VNfzAdUlu<s+ZtJ1}e{|;@gxYemxc-~R8X%7W`7%hL-
z?|AtGg6Fg6d9Y7eJYQKnM_KGv7MH{>VAr0&Wy=6xnzTxTL6EKlX at km@sqDBNkAs4D
z;E>D(Mx)i?%1P1C@{2|+QTr7#$wU-y6>TA<_Q4Yw;WKE<Vh0vw5(1B#xe7P%N4)em
zZH81<v(9XiN`3RO^CXA96;rZ>7eoi^<seO^R4U1p%B%$Vx2gmNt~p^MPzl7)4BT-x
zko9D{4$|fq!(kzZ3*d4k=!PWDmK9fe^sNXJdoK{x_CmL{5Q^}!?XgfAmCqtqF=;_O
zrSiC0M-SKV7lLRKg$KLK-0ZI}M(HT<Hch}YtSX^JF&l}zMy)$YUBO;64HwM at Ov_W$
zAl9=pJ%75X&<)Z#heeWOx=)uPb30wI6?;G&=1(fq>6QK97gSuqR>X!l!f(~LVzoMb
zvlJf7tM12Y!mub-Z7TIo*iPyUq2 at khp|KsUum2~uqg8tX^{ocj;VDx;@+wa(>!VIa
z?dmaj3K}WXg16&7wia~LcD782!kbfWfCJOj^WlOb1g{M13Q}E-f45qW>nlo(4D1Ry
zYRpBQQ4&5B4IYkU(U9m?f+7~U-$4!wf=l~|B3^{Fs at 4yfhHk-vXd_)UDZvs9DN4Gp
zA<5G0Pl6)u#(>~t1eP6ngiKUvbUSRGs)<y{9L!3k%Z<HqgLNJZG9hNoX$Y>1DOT06
z5=fxTC=uSE_A6$8l7R_8J_5Vy5pL6e2LhNN1V^F151~wc7ONwNHxHk}(2ZR<jyQl1
zw_%9K>U4;cREJItS8S?=k;-F*Si9S*lmOWk5)3wZ2PR2W9>WxC-w9u(QXat1C0d|`
zN^>ZW(=TLv998vV<&{{C+|qLNTb!<ubs0S6 at K^g9kg?(+$7`A)3W`(m8s60L(!fO<
zT$r*@j#MxI($`0__uF5hI_+x0 at AjQUd3cIds62d_4l3m5xSC+GRpE`KaAiH3aN<}p
zOG7w@(5FxkL+)#^d-O+Y021WS*Q(Ud`c~`&z&(2#XKU)|a^5G3S*2bC+=l9a{Azb#
z2HJ$VTBSPVmPCuLPwb1yWh3B&t$>2`74zAB at GIXn2jw0jtSB`kTK2Zk9-v}<FH}zE
zWjOm8cQ`tK)t>m3Y4FJL<Q-MN at hUVdo%_Vmz^d(T_Dj6o5W>b=_Np`AD+%qTBO5eM
zL{&V2E>KaU6m`XFCeIOqXj80awRwgRyo}xwmI%QtL}A3Y at M$!A^x{D@@`!F)gn&*v
zSPGRV1n@>*jngRvm<cOcYE-flC&+M;N~wTpO=8J=iC%HE!WX$sEudI&m`>T`W_xWP
zPJHY3&fxPZd+m1!L*D7~a%Z131gi<wQg34V#Oj&eGCq=tIk=g&E-IbOS2fZHAk5SS
zredP#pci|%g{&l<e8F0JYHkD`It!jcFqLG#0*c_rB)o_aR?Ahjw1b!=yc(nn&Xf2U
zce9=d_txGd-ma}H2_6;m_3NW`zPQd10Y)1k>OOWNdNcbXLPOYqXNKJ3kb4r>?L;^*
zLb7pq#6*^ds#8&}`kSBkpA%Hs5<T8k<5Bn%$L8s?ydYoCut!&m(lF*c-Y;C^WlygU
zZ+NkBcH($RIJtY+UmgdD!iT*49KgckaGgP2_R8Zp6TU!cK2nq3#H9jz_8WxIj%MRW
zu=&P4 at llYv5u|zeDf<F5iH}p?hp&U7uO`82kWKu|aE5XT1hM&#Q(}$h8Ao{vDUT!X
zba*HZ>B;=j9GVuD9V<e3f7YMwE5y}B9Wv5;I?y9=;>wZi=D)?(WhDpb5Uc>mlI{Ky
zca31h!EOzs)!=2Mq=pFN_z+TPP13GZ979q+&G%!zOfz=P<}Ov6*q^@U7ERe}$d-89
z53z;4WbheJL<0Y7$l>BBAF>9#$os-tg8-5gTERl8kEijK3T|d<>z0E2_aHR9j6KW<
zS$Ik!-DsSUNR#yIeYkpLrC%W8{?nBt!HKJ$@YQO?F<9n6Q=#gd)aa_m&c at MuIIWDO
zF|2+w!wLpoVeMc)PUA}^xpLW8u+sLSRcYLFbzBGRXfHJSvBoWU0a<Nqirz#{#|?-}
z$?sFsX8hr_ic_`CX at 95VMPQmkfy0kj{!dcbvA)y!V%%ks-T`s~mm)pvO at 79Cv|QO?
z#cS9-LSU&G6}{QQ19U^!ECiByz^nsY|29>g>gdE%rocaO&%6)MtAqerNAqR9Jl*6=
z2U&kt*I8t9|JxQ5<MGVEXGN8Pi+f-}Q&zd<igVa1^m_c~o&?f78s|Il(scAexF(tp
zjo@>}UHKjo at c}EUZ^@A<E3fddJ*bsvb)nv^+M4s8-*VuLuM-N$`3NMxd*3H}KTzrp
z-!TZ>iC}rW?AUuz8S7bnD>fOzT4P~9_c^l7Cf_43A at r`la9b3MY3UPMgQMe=o`K$=
z)h_2ru~_cY#iG21di$)p4T&}=tFj<#mS`)Sic-RI2F%pTB`%C}*H!eeZ)|AG+X(RJ
z86s}hMH$eA8lBi%*x%mgs24NfqwRf;_HhQ<r4hbfI%|O<_^rj!w9ngrM<~-gBiijA
zzm-+v7}WDI^gCBz{8mcM{CKB>_EbQAz-uh!P4PL5jrzC{ypupzl5q?slKqM}J+XXr
zbQK=&C38wu(aw|{YLf7Vb+sIsDvqs$e4}9fkDDdZ2WoRpBIap~By_i<LR$ZKxQi=0
zd4WtD)c0YzF{dC|93nSb3*`cn)a at xmPbR-l;>=l*dT9 at P2>bdSlrr#2Z$?g^n at CYf
z!V0a|BRap7FGDlli~BtyIB;E_cUqB7kx}#V_87<W?58Dn2p7p^YNl7I=ye_!@9|G9
zV}6{-BRHRm3!EQ|^YR)h3sxlvk2b(UbThWVnfN+9rJ-yRV08<HG)t>DKMyZxO+C-q
zEMCL*!_eB&hPN$0Z==hVUgn_}aV|UXVH6Hr^`r7qffNyf4M1X0(rD=5dr&cc%kxyU
zctpu9AoNSaA?hZI;<bvqlw6fukOA<o<y7_RQtts>sp?|IC^<!-4Z?<VP)|Xg5cn3E
z=)^z6>|y&kvioxMEgD2V2;{1yp<<w!+k;=_PgqojKEhBLwA|9<W)~*LaB>?Oz=ya|
zMx2*Dxj)7dJ|kY(mA6;3&Z-0%w=`mD>i%f--tGgJw9fPD8Ow~#eYy0M&ffh1(SUBs
zyi9rokL09?n1*Hl=n=9DSPrdooeCYnw#>E8QqlnYf+Xo=EiSQc6y&)$e63!cki#5_
zv4vrNv|~a!@CL<70C`nH_s80-1ZRt|AqXW-<z at VY6p5@x8T+mLE>W`(xD#t#(5u<E
zE5nort%#m$d6^#}iy=YhnY}o*_p&!OM&X57G^fv)eIcY$waE<$mBGEzw>d7`fk;XJ
zc*4#NU&O~#Z)`lJs!8?M<SA3dF at d<j^bro8=LZd@=l`3*RBjnOn4Z}^U at -midj}J0
zRg-7HQ_dzSgW3NY8%#s?@@?H8d(>Pn)BudM%s<t`t}&=asXF285euE&xO$nq#B7Wu
zBJaeP`B=IhyNp7bq&v{{-FWU>Pt0OB at o{q#+jKa7+~AE8bjTOP9S#^6*CxcqOp|zA
z$>jNI%#28gjhQB~Ny+5cS0rTluk&HU?lwkD&)QoH#)@;bi92DW9M_O6D<aZpw8owU
z7*H}pKQ)$6 at Yyv>o4CDTtklIxa)qDB5esLv3C6WFaY1-4!TAZEg!jaeA at UD{43SQW
z3 at NWGnpX&1pvRG6Ig#NgY9!+5epTv42Zuk#_5q=}*@I?dp!SGvAkYQT2rpZ0irMpr
zVX3y{G+SIJ>M{%Chv(Ax;hA%&e|Ubdf#ZP>&(;kFxo6_=#Pe;wllWLO9}dsQ;$b+<
zj}3<fHjg)WP)w$SbZIzy_h3|EwY8Zmb(8B*$y%2X{#5-Yr>ovpXHO~&>=iAyNGBo7
zu;v64dlYkBTn^0T>u at ud)MKj5F#Y<V3AN31tt3Ai=C+j<`8|HL5VcIP8H1J7?6 at h_
zn|B(A;k_G(1fZtN3lb^wIHN>uJ(ZYfl-LRqGU~Lv%blad3W=SsT>Tz)1ETvsE~{x!
zSw=}|ZxT!h)RpMC=07a0 at jInOFR5n4fd(Zy{?ih7 at Dls|Ia#|K$;;CCdb8Y!8fSBP
zfb<pW1DcF$=*Ev~9^mu5G3N8U2z{Pk7w8nnbD!tK$>+J45dMo+q+=~PjZIonU+nE%
z?5)>$Gel-U&PoBgx2C`(CKnbtUH0Xt`mL1lle{kW=6WK{yraNovRO%6jEA}Q=z6>=
zA*^4Q0ZE-ISZtGSKnFnV-YS&I!c&E92TsqtZj4pDu&~~7!y)ko;f=k*wu6nI*izmt
zD78x|CE-!R8wUy&*u^1*!Zur_bDy*i(qLQ~RxvWF>6kXK!k8KE7#U5A9 at C*7LEh<I
z6QE1vDc|n-5{1Swn)TZT)J&M{dkR)|dam4(38E~Q<m at 2Hj$eC-ta~@f<0&B?8hx$m
zW$G9QB=YRUFt0idW{lzTI;&Ys_3DSbEL;b5UItvF*;d<!Mu65Hqm)bN3~UIdlq_!j
zP=EaAix<TtRej<+4WtjkJ)r60cq^Gz!LwVEY>}Sn0 at Z3<lA=t4DjQXuc}EMz;0g%+
z-=2aIK9-F<8I^{QY$sJB$)-)Dh5&!Isx9IB{h4eEt|`&aswA+Vycc&m=JpR|3r+m4
zGcf2J!jU(MA8DkQCDh>&ohz0}spt%+DAU5pUiOcN<4(<@H at JRgLiL-W@lIM$ywrG$
z<!Pjfbct^4Sonvr>3>EG9Ft<c>f-IG`JOsgXu4a^r?G)|5|)m4Xz-uYQReZ|n(kUx
zZ{9m@<sPO>c>ZBP1M{Zl>be`Z(LsbS?Ydh@#n<vBXmR|rig~f|nT(d5Qq^0mmiO}P
zx$NgPK=-%U)mb+B4%Nc<ckH|2i8raruCV3h*O1%4q-K|HYH{J}$D}>v;Dye$THepI
zPi9lFP;{Se<KI`-(BL{<Xst9hTT30<0;ZJps*CJs#<ui?+|ws6(PqOUZ;?7 at eY!St
zy+d(zp`+JGaIcuIe#io4CKFU at 4mPYVtj6nNYCF^_y;oBO++n<$G!TS0t2qcB0^tVs
zLN#fDa%?miM&-hLfiKh++_mEV4(SK-)Q&8)HI(hcU$UAd at 4<s&0VXKF`Bpsx!{t3T
zZWND=FC%|~1q<!s1wM|HLuxC#j0eL|R!Z!Hp}J%cRKp3XF15r44SefXlj$w&%Yrhy
z5V!^R=6JFD*a3(Njnez&N0=}hR+l>y(eLbvd1R*xxbf%Xv?*k!OBEVFPm-s=q?~Uo
zC9(gcu`)A>4m8^>4KMSb3fs#3--az^{?mHK?YPoSR<e at v8Wx2|uvz<%`jNufE$}L5
zbIGcIvIK$8<8|&^yU3YJFqEn2A5s09VBp>$lL3a)GjoA~Op>SM>SAtmRXZkUpXit^
z+GhU?C}evQZWCs(#&=^-?1fh)GkO>rGwBEfkjeh=E<wnC5lar~Dw+U8Hjbg?7c#|2
z^W**^@7t;C;lFa+*$Hjj)Y<8oHh}prs?|nHdA~w_bPsC&R at umzui6cbgtl2GQaf8*
zouq+!6la8d-~(rIlN7lNe2*gbq7~cSF%Dw^exeuKqFf{wS78;M;utDB>Ohl9)@Dw&
z8}2EQ&s+vDH02!yFrboW&c-tvwG}~+wF4TV%{1F%{wDpIzU|nG-vMOgqz#sW$NVYm
zXE6hpu;RTFrR5Yhg`ltMMH^Ilp`bZ&xK7-G#o`enI<!`#4%JE_i6$FJ1YLrRC%31I
zVFp+vud|t?$pGO=EIjY%48R11js7kw5|xT$VGAIG_Sz=U*55IcG*Vk^FXQdTGR4tl
zydiRPVOUnH+Eu9o%76L{`N_cypEpA?IZe;ua5nZO0Wv!hEx21LS9R*rnpq)qdEqbg
zXR{xjjwxV^cWQRZp_pYNlx%fa?QUw*?iC}O3#}&ErZ*QR4wdh)ndF8-$Q1rt<3AE%
zTH=)OKDVN0^j9+yj+eC>Te((5_29|kPQXeNpt&<*Om`jFF-Z`Lozi*T(bF!wO|;ZB
zWp}&mrb6IHmYRJv5LkmR4l;c9D4D2`MnugDHniyEv71 at 3aP-ZbShKG%45Py$K_*y6
zp5t4$c67{>&ipMn2GD3aTMn<v<nV3<*E%*2`z;-=*6cH@&FHq;&PQ at H9~kPey4w at 1
z>M(77!uXntYt$h?WaDeHtBny5ul-n1)hPtfL6h1Xx<@CyjXWI!VZDR}tM3g!oCb)i
z$?o&8E6>Hb`zrkIMUA|M+Gg}q<3$rDuKkK at kK$^O`-+7Pjs)DFoMT0wGS&pV457=B
z6yf0ropPs-6dwL3y=6Luhdb#l3*L_KGjOx80Z+X&>l1V_t)y2q<usD92B*tW<x-mm
zSwv`#SwtAFt*86M>c{vg1Qi at D6|Hhlf)K3Yg^v+7uE6soGzm)OD5Zx77R4_o3(=z9
zr%xM{vCg8q4zuh=^z*#m&2HR7ge3$=(q)o8ZgyKVB#}ZI+)RpwoWzNFJze5PixZ3d
zCg1(E2Goktj6 at Fo_TSr1{%Vujt}JL%n>Wd<SUzI9)y=x at 2e8{!SKIL2imOd^9o}So
z<>_93rn`*2e?NI9a;2+BHhC?r{wc$cl+9yr8ljm3LbqUOX_g8yO9Mn$BEyfUjqn4-
zl25bN;YSqdM2TFLZ3c+|$|UbHnTqR}Qra$F2;sVvC0Fe?i6hAD%+DM6QIt(}?ct at h
zsE5K?(LLmgK{RBwd*b#5Qrcp>ufJUtcTHrDhr13(du_-d7JJytZ*Uak?R7SC)+iuB
zJ%W5;3vr8l<Q2doCdmzna!HR~*;1&RhAJf%co}CJ+FW=MeB{-YT%+dGw9-(hxqE7x
ziLrdJ{4ZtR%7W6*g+Lnihl at EIxfdlh6OXN3y_Tk(m=Yu(=kc`(>hXdxSY6e9yE@^A
zSlBaP8z(^X47R1!QI2G1<BDqynB+SwrkXp()yQ|`nK*p#%pMBBT?66)fMj_OfN~yK
zeo$_)@+#Io9Zpi|El&>~UAG)%EYhO99i<pAps#)i-hV4S-Ds0i+?){s6jNzNB#ziW
zEmCIO3hAvk)fHR6!~D6H2J(Ja#5zlN^*Vdj{tRZflUM|y4TUFNOu at R7YUWyN!VdzQ
zuMHlR68)zahKIrk$1`lX;aj({Q6G_YU7QZfmTBG?Y<Z+~sZ2wzh@}*Uhag+D!mI5&
zFo{<JJmI03tOf1P=`-XWi?D$-H>l6`3>)hEG|=8G?rfdL1=ubw?m+%Yyd$6+F4C6S
z<#l+q2v*OytOBtHW)h6m=ALr4F8@@&p8e_}n2#@qB~^>HR!8Y_?VC#mS$zHv*8}L9
z=zEp~oq2y at TSQ$C9Y1Nvk+d_y#8f>04em4!+19cbWK>?rc6QOVd4X-B??U*#1D1_?
z;-jahn+&h1(%dpHh<mFOfXHt&{TWZTr0g=b3mtSS$#^YDD^B+(D8)7<X(9TzFSdqL
z)f at 5h=`>|=rn1;tJJ*h?Fo4p+MR*Y+<`SdIW)d$)<GoN7mMf0(urlbc+Dg0rPjo|@
zX at b=#s`i&dp3_D4LNQ5R1DVF_v#qOBV6IBg&3Mnv&Rw^ha~O_NCCiYcCOxU&0_t#B
zjnt##7CYOD>*{GbTBo*~s{auM20i9=sUNXxf#;yxdR&(DibLgU5NoXFvEiH8$OaNG
z!xPZ#tVw<UX67fgJ{y!K4QG*E5Iy+{DOG*{)f*X#_Q9johuJ3-jJup4?xNFa#THxZ
zqjYPsswTCtH%{I)*;Jca6jA352!v#n{po5?tWFqYEH0f-o?cyYs8x1Ndxi|XHIUIa
zGW{0ZPR}lS?x!cCCmDkazD-XQY}g|b>XJ!0Z2Yd+9?3<o>3YqMS0S1-s$NFrqC1wI
zL)O_j)Wi)D16v)6r&Dj?)<B#!Vl+gob~W}Tk~QM3WMnu&eV0rT|0doPF_&=TOc5uj
zyU`A at 5PXmnvz&cG;79OQKQeExvX#u*Z6y<TTVID~-D6+DGjLD{6aYlClpScpu7HvW
z8!m$&ZF<L)g{uT%=WbzVP07muV~2nQDeMe1`ry-_+!(QzE5WBA2XO3%_kl#Gj}IOh
zQDl-e#1&NZC{;(u+Pc5rmfluE>sCZx=uyZ9b;vARyh&yjSQ;^#cxg$Am;qlxP=aV#
z!}PY$y|H&|s3P`m4$Xu2T6)Jrs?hXUR6=MHyw{w)Lb_6Jvhto)7xgMnAS}+~f;O_h
z#BK@<B at RK&Cw#rHVW_b4U691uI01ydz!_yLwk-6qjTd{_z+#zEY=g}d#X;zG8y3h0
z6-cFQqEcpy3u?~##F;f`mx`G+XIBZqT3fU|$Q?t=G1-kvr4h4?Kp42ZyFy-D6n;nO
z=Gc37=tg+2Po6SK8e3Cso+xF?EeS^6npob^kY-9ArMQl#cotLW)oARHQk((lYs%e1
z;A{N7R|tgg_W>dB3I0k#;3NE9Ed&nW??NH4HyIIx^dLWreQVN!IIi5nw%u-ar<0Cy
z>w`#k<>JMvhNDd*f at 5=g(ASwkgy{0 at 6<l7%r??WY&H9o#twin?HllkiA+Uy=T$TwN
zzoCP8?0=aR!IHj2Tv&qZ`Vt|yArbJmJG&+L=sB)W#7Upys!-d3wi&nM1$SjfCk^Z0
z(n!EA60{O@^+w%=m*hfQ;Io^!LN1_B2%;KQTvsWk_-JWpf0Rn(AfM5tMDnB#gu at d$
zkgEDg2q6#xFM-(ZJT9ed9*@={f at CC;o-M?4uo0m(cml@&?S}v+XzQ9B5SuQCva!v-
z!pQGdA?6Bi%;@FU`L!T6+^vz>OE-J^t*EuQaV^B&=+gM``Wd}-L2kXW!wBRVIaHx*
zpd!^08wSp1(W#bLo$rYgdEMA{VV8*;x)jql<?KEXiEG3cs6fpb!N+LOGdtE^H`|TM
zBcWFGa~fGDY&=CBehk+Tk$Z#<+bH};!p|n6(p%(S at j*HAV{w8Uc|ZvM8sbh<^)(!9
z8*abfY4irPJ-k58i^0sMZ0GhEZa{Melr3AdSE at lIyh<f0Y~!y=Qa at rsjn?RBE96z6
zi7=G{x*Ktn34y0kgte$;R=LSWei)=-HD`Y$-Nng?(CaOr_jO5}Bh>D|C(0-5rm94M
zwapb~yn(gua~L2*w!m?beWRi_N0hP^=~az{hw_3kJ5)<1wV`Guh`NR9j9w+U0f!SG
zk~*<w7)v@;!w~_YzojIY!|T at bOTOZFwA3fCUGko++P~un6UWE|s7XpUiu3CE^NqBy
zSl)qf{z%FM$uus3yFbB=S4qQ+&M?#5gYEE!&=e(a1NN7q;S?MmFS8Xw05z0>t^<!?
zXXCu^2B{%pipx!dI@>|405u}(DS>rfa}W)==GF!ON`#Da5GqQAQ2P>k%Yh^LRxG*6
zdb%-N%;e}e-3Sttuxmzd*ovcO6Uap~dNI(l3Fg6r{fR~g4_1#{YYjH67}nh`A2f9z
z>2I7d3Lb$zIig0SguJ5$?yBr at ba0_eytkX!&_J3{TT+^&q>Bh>O*+Y^5}mk7u%S8$
zqs6OKYWSvG-DO_(&>KLRjk(GboGjEM@|l8lNfyFpUE1964$XBkr at 7bw4A~wxe7luK
z=nwH8qWWG<L9Xc4xpi84Hk)$`!#B>Zgz#j!A;I~cbTWs???5Cxfml`cAXXJlo?SE9
zBqkO at +HZMUUX#27N0Eu|2dD(b^M&GdFCe<aA-u1y3THlVj(+5XQ=U%{hw+bW-hZT>
zOH+2v6CLy<$$}dFyj{9n?STc`Me$R}tJ$}(&rf0X_@#^1pkDp4x}$-bNZ6(Ffg#d_
zlm2a#8Q99j3H;3}jSG+BrGea<do%l=0p%U7{=2+`CO(y566XU9Zj}%#ypiPgCuy!z
zQ2rxW&CPOi0=sH!RLL}3)i5}?7gzJAqCjlKOGz+{8pC5`GEA(8({(D3Cs6mXrI>`n
zU|M^%0jGGWN|=a)Hv4AAPJRbkWF;nXxK1J=8}(zHx+$z&rMb91xsE36K!cc}YB;s4
zMD}^7>)S#t-=0^><=is1w@~;a+*{CQo8Cz)GVQS!nNGp~t*J$(C*l7E`2Pz2vxOql
z0E1~Cz7`Xv4?jmr6ClhNI%osh)HL11IzK}w$Px*9yX-@^AE>(YWPirr at Qk57bkJ2G
zIVg>IOsTSa*-I;tmrA|3e32gTa&JxlPn?3-`ym?g9N%w7`|lTf*~4h8sw-w3*{d(G
z!MfE83xjWn-45O>&4)HOp$6!kD`VS&@phGcb5n{;>`3(1(s$!8{tLvv^{?agBM|@V
zLGh00DJw7jp??`orQiN9qp9$z(P+QJmyP|H)d~HG9x(l8cHr;)pulfgqEvM_r)FO@
z!&Eh2?cVGdGs9Gn^{VJnt*@Rgw9BI4X21N2(P0*}KBK3voj}PT1+3NGe2;-b^og&s
z8}CEwJou$#y*ewrc`#;={vCIr&1~_fF~o9<5XE-2wdNB`-qD&f2~yz;Xy`>OP>XF|
zby`j&sIR-y>-MneI4`@I|7xy`(jp0vFVL_?kUP#P#rANrJlCEr at 6(q+M!C{zQj*ZQ
z{XoldLp%)kG|I!*c-UHgHYz6Kj><S(@8iCAx`%y)_P^PlLy)e!y?k_ou|a@(%ZSUi
zPmNu3Jk2n(pMJ_uE^wH1IP(#kf`)45gb2<0YQbc2v1%fOdY~B}t)N}LgVU7-y-{4M
zCHB*evUp9w0-H3<&2Ea0lCgmF at Th_+o0N{`o}fRjF`^{P5El%wUWSu+aX65tt5Qd_
zBRuR=s-!8}0T<92hs_L~AX-${F*fg0-V=vAjPn2=w(RsLbk#_*A)ZjNzC3(6p8J2c
zoR4CB|6?C%O at GIrsQw9!8XL=5K}DL`{=R<CIk=<ZVKr!C$7P3ZD}Vl{(nz-OmDtG{
zb{#O0YoO<0no4C;DT>_6{=F>kC`?l$l*QKP9YWw?tZ=SP?&wp}&{SKoUGDfwZcTu9
zNC9;H0&Njp$re6nY$%C#Hs`_UW>PAS?6jA;#%r>NU3wV1x!8_0=jCe~pdWRbR$^At
z)VY~Ck=e>ztFvd_7}Slkvv3ZPpW^Bbm&&c#P%Dzp8gxy52HDyfWNVGUsMG_%+lX5|
zHh^1Mv&D3OLRr~7)^N~dx at 0%ZOxV?zLap_swmMsQ1S+>{E*n+1Hdb9hRj)Y2z!O^9
z=J&dn#H?_tNd+#ORICdA1h=!)F6FRON9o83H^i)@I_q-qD=Yj)?Dg#PMO?{Wn8$v2
z%(&|z{#8jna9O`UIXvbwsWrH3I>5tRiH4$)u+wELmy+a;k7brz(WKvGT|Gn{O1_td
zHr>iDDd at LZP-WA_q>51A!B|0M>H?ddM3(8*cC at gcCTweRvy&P>$oiyz^wyp*4cJ>!
zOHuoeJioz6)H3m!l(v>`Y|&*Q%}qk?cz7<x8$~;EF?2^dJ at NjIvl}L{k6w;V86P~w
zC$WG4OTreh6Gm%cnY7%^{(=W)=iNMNZ|vclM^6mcJi6>Z**p?6+3o$6mm8HQ3FNw0
z(@>|IJ^4cr43e6Ii|A$;UCL5+;4%zX?z_`~O#6gw4Zx>1a5i?Mz7^Z9)6F$)D_xI!
ztj}O1X*|-+s!_$he3q~++0E*CPU-7VIF)uXE)|0J!Q13nW+AXJ6{mA0m<8GDg4Ymk
z=hL%<o_ACDLVA|d^DcU>pyzG$ysr<<?BP^qR)Q1ykhwt3<EWQyDhGS4N=4~Z?0pSG
zwV9c1y!6mJ$QXL&3L9lwYiy&>E5v1TPq7f7J7PVvMTguoM-uy}me4ODu>KZv&6!Ek
zSZ!89&E$#F2%TzHBg$61=%;L*0?+XMHD~f9J0-bJvcj_N_L?(yi{6?u_lmdHoOwVj
ztvRz=EU7uOP#P63>Z7KG3*;UjEPZ>Him7tXDse;~wKaUPHp^Tyd8zm~hoO&J9k%yr
z^rhif%??ZnP3WVKyLgYzgdb^?8bs<<bCsZ}kKZ7q0GM-5*X+2DQf at 6IJW?4_CiGL8
zn9 at wu=u>7$$^6v}h?VzcD^K8ZOYZrxc$3_77Z764Lt;Abe1j%xQAH^{Onr=~g<qqb
zN!~E%VjEh{$ijO_RI5r*sv33<NX at D8j=OnF-a*G}Jr4*Q1Buu-OK at AP=Pn_T0V{U@
z-3y)VLIAhQgrEHk)u4NRv~HH%bFX+2044+<!mT2X1(oPp2)qCb`%v3ZTqe<#%O?ny
zo~wl5`XrO7nJCO0s!l?M2bo6jRgzt$+m+3xt7e3U at W32tj869$2ehuTC)T<hC4|Ie
znNsP(HVO{kXAmKU0#R1h8 at D>$R0`64oi#-ZA{&8K19BSoj52M9o6w4EhOItpFi;`z
zH#CA655Q+<qX5u|O|9eOu3n|v8=SeSrg{?}PdD(CTzP!}TteW0X;9JiXQN>93hMcg
z{2pNUorwXPI}~mD{R=d34A*N*`_W*x{M(Wr>@OKTbPRy?_?ZD{NP5^ngS=xtC#ofM
zQ5+N8gQSKpE+IJE{9QccQy6u>)pF0jh3Ml*45FEXA)0fhpYE at cu8fs<)!-7 at rv6g;
zfUf%q?wWz1;oU%d3y&O7(cdHeP5-}!>%@MT{I)?YTOJvJ>t6>AT=VIH1Fz$fl=9H^
zS$5YxI;)EI#t2(%m<qv($x(#sL7as6BsI^ibLM2Vn1r3g|4Q6?{+zGkOlj?Lgt}qe
z2KU1Hhnn0%tRCeMgTbu>7KRgsc>r)~-yrbzyvxC(Aq=zYAd;xJ3_`&J-2<2!8i+ua
zI7jYTAq|%!L)H$FBdNmA3TTTnTbc;0QV8Z)aP#{K97K5saiUM2e&|zNds>8 at b5f7x
z?Nf=t1`h}UlO+y6UnbzkZ5(q4;iE2?9Ywx%JooB1kR;kp;}m%j;*wwD;@qGmh*HXp
zD5bsnGVKFra|*y<H%oy&0#d;bz-%R6kw^&LgZI#zWVTEQypLP;%pwGy>f?KP??eNh
zKTDf>Vjj%>PJ_khz=``blrUlqxfw4>&|JhPqR<VA-|eWHYV%@km0239tx9Mnj-&gt
zHP<n80A*fam^rAB?0>2#Zg%}6JM<*Z%a5TDK~r?`l&ho%0|&+X>(gq^REU+uHyl`D
ztD^}sY^!?=f9nt2K))-ltS7R%Vz;pEtgx*Ob)S;eme<S&|K7+f+RVgbw#->KNsM=k
zu#E}<`TLXPKba4H8cpK3?qGO~u&uSejiLtR<~i$=rc?;QyL2vIjZVZ3bDH{yzC%C6
zU6-C5>1s}IQYuV_L->?z4v!kpuMxC`(5 at IWSgsJPHxDAr58_hP^sE+_)buPAZ<BW*
z9IIPs=;P=RFN at N8no(;K7dcvJj(h<3R<DMH#;ECuNqNAnT5zcNa~Fk at 2UlKxDmD#9
zVGu{if57q+`0Q at 38c+{j)Ci&F*Se%)E{cR!gSap&w7fSeZ4Qty$57pPEc(~@0df*;
z8x!jwIia(Eyoe5Nxgf8hQZa}2kxd0+Dp2o^1f2azf{e)o(hx%)2q*rBU2 at OKxGvc-
zfe+FtcyHbi%z|ig&gcNKg1;;i=aZniq=u_D1^ygM^MEu~?wKrTcsD>Lu~OZEVx{m*
zx25rOOZaBL$v;#lNmHk2mNgsiJE>>Gxm4yxUZ!~8pxoB7#?P(n|Iqd}a8Xt1<M_M*
z1B~7=!B8=&uq>*j(1I>1pde+~&?rcX+Ga#kTWa^JZK>EWEyHcLwryYfZne4PcWt+A
zZOfNlRFDF#c2}*;(k&{oGmRw*0t#_|&-0vnhZ(%EyPxm>??>j|`#k47=Q-z|=bYy}
z_nhbX^(kbxg7eF#g22rEPg&bXpVi)+rpr<`$<7-<x-P2DD^3m6Ie4KOepQj(WaT^f
zs%mytP;c0Ys=iQroYW3tD}eO74|@-ox<By~$WNf)UQ at X3v~0dVMYQ0L^cK8?YCB;)
zuk8xG^%9`<*52OujWsmsZZ#p4MyVduQq6HUeq&=#oIIxuw7Lf#nz(<6I<Gw$QRn-N
z>%8vmh&oG_?$0?>MW7RIqKR){{xgTWubj=hE*}lFE+mcS&E$_&eAp_u%fwl;^FhMd
z{L at 3$D*tq at K=;!sZZ_BXL(6g}$+57=lFhTD*x%KT?8ERpn^%D+7n|hO)Id8Y at M&aH
zR3?8r4ts(|Tih#O7d4B at qibeNlIJQu#MZ1a-zyg=J at s)g$oY1mTmC6py3m*?Azsi#
zb8YWn6(NtqX+3S09qLxp2-xg$vX1itFIT9k(92dfRa?eGJ2g9<)Gm{hfCIV1^Cn|;
zV1nW!Aepr>^Fr<$W-+_V72Yy;u6-lvy?<lhh at BI*Z#?|o5cUmj&G_j<|A94Q{^WD4
z8JT<joi$_9S_5l_Z8&QN%9C!*Sc<>Y$bRx>(n5>bNSlVUU$<%acVZ)wWkda4*tpR5
z7&k8b#yQBc(ecgyPnL~`2MS-W6+YIfSvGJ>Fnha{TQ(N=M_4vcl5pz%NWk?7Xidi-
z)W!pKE4D(ET|?;~uxl)(4*SOjasxq4RU~YSb+MNo)T|r4{esKUWJ0$v*H03BpQ5Fs
z$Sl;hpru0_Oq<El0ZfspV`m=yl4Be)y)^qO5#3ByCb)R`<$iuy!Y_C83pac0Oy>+;
zK%HUqPX`%2uz>$hj2?3}qeqc$^uThJc0Y}oej4I*qeqcu^l1M#Mh~pq&_<7<|I+B;
z;qw- at KN>x-lPf8HuE?U%0|6mR))C$4 at yx9LNTWyWP)3jG-hZa~ccP~1RqvHtUKNcV
zDj7YN4s at 7Xb)(19fsu1Uw9%s%jULZ%oe#NGh$vT0qD&vxI&fZ~`pEfMUV>M>Uy6Z3
z<!iWR&bQP?*G<Xm<8|_LC8)<NJbW6BDC&K}W4|FI%6v4UsGuA<aodBsOsJhnCY0yV
zgreTLZkAU?6N-AT;CnnK%ma7sB-HLk6Uwm!G`}o~u%G;r>?hb?&A~?p+fQ&v8Nz-N
z(NLXd2ka+#{r{Eygq8+F+fNqI8^R%tr3~0lR$v6tNCxhYwx9fcRoH$)M+_qDCn%o(
z-hL86I_0d+rfxr}{12GDd%()^_VD%-%=B;9^bwRt4N&$Z4C3{kX!}XC{C~Bd(Cas}
zpG0u9e!$9bIDQaEpY{y0pJ)`%wV&h+P<bN$KeV4j6tv<$v7h|6ZM~^G%BB-lpefpZ
za;r3y{e<#x<>=x!qU<MsvWI0<@Rh{r4X%>X$frWIpUmq2xAqffT(tcJg>aDl1m~VX
z_LGi5?BO9QU=IeYST!(ZK$_1rrL3|JYK>D}QA6c4D-BfAO)&69no=f(O)2Vwg717X
zrR+p`GBl<96OAaRaJfe#ZMdmEL5l at Vx6p{=;;@=a_`@sFc9oaT3)@v9crA*st6&?u
z_$blhLruxRu7Y3}d-Zj)tKb9KRjl47OK=u4*Ftuczv*U_x_)j~*{lN|AF!*$>UNdf
zShTAwj;z&9WLKdtP&6L|#h7znU{THV#j?GKs~H0(m6vCpYf^b)z at +js&7^X7?Lp~I
zZc<q~oJr-9|IDN^oH6D6NMi~h4t~Y+aWl$&ni*wtOn6GASFCAb^Z$)8CE{g1FxZ%~
z#yqHNmJc$fxX_p~XTXZ$h4tuoy{8W{q<HQNEsR<sB3(CIxJ|d7*anvt><Ej^zq6ir
z)+`CWi)It+Og`}k9|47<h<>BYCR~9%ww25#9YXC&Tg14LY(=w4Hc2ZsA!r8{Ygi=E
zbb_S0=_E*|6B<uI6*w}4>4a2);Y}wI74A6|X*x;ga#$2;I>8U;*vNNzVZpzQ)Z8oX
z(@Z8G&<nUvzMRY@@6d#d3aJtxYuyWt*2Y$=_C=9Ep(WgOauW?(2xnhhXVQFk`hP`9
za;c1)cp1|DgDBb>TW#)0k}TQSoMNUlqll*7KO)Quk@`=hSz***vjXokm%hf$3X6CP
z^$)6Q*oZQ~SMXC)L at Vyny6Sb<iq}vLkNrezMZeaHPBfdW=f(zTx6o963+so_<&-1)
zUfw1P8#f`s=4NAwb)IgJF@<W(7aoJnNRjWqJ)-Uljq9HAYDC?o*zRYLp4Z5v@*DGj
z>O7ryYi)cW9<&aIh81cjtLD0MW|U#YiiQ;{SJL}w5n03sme4(9SSd2g=Ax)dWNC(#
z*dooaLdh|BJo=ZTrd#UZbID~<8PA@;sb-``U);;hT+s%W at io(7HM!HIe;+|33y)K;
zwAaU?q2)?=3BjjLx^AY1)`iD?6kMoJv(?1rJgR$s2;Fb2Uo^df?c74P657x at hmCm@
zrx+RwcwUJ}#)dMRV@;uqs1c_2l?a<o1lfCD8D!HzErs1*7;V$Rcg1Zw*y5T^$4;+g
zx<Vg=$fk3Rm#h$G*~xyYgzW&U*Q?>w3H&<;H})Xau(9W%>0x7!<~vYn(AN2`jdY^1
z-z>*>)zkt>vz+Mdx5%kod9%#Y7>|W5g`^&{(!<IzPlebbrIxWdc<)k^i}}It1b%c`
z>4Al&TE_mk8xIQOxnO=Cn4hn&Xr7g6Ghr;)a`vx!e#Dt#<t(P{bal8<>hEt+00wVw
zzdW98s^@3?>+2)@00faq-;p(Taf0RwV4ari_HZ(`bRBetgGv*gdDgrabZ{n=IAIsC
z;cYcb&2IMHZe#;P2*h#E1`ap-sGsk{3JGk~K*145za84ftSaEO37B=W!iMmMEN$1_
zX0LR?X1g>_U1etvgOahK%F#_hCn_**fZbnY)6b&qXd4F%pLVHgl&f%hPg%I8{hEw8
z^qUF4IcuHdD*xqJG&{)?*?K=7`DS<c!=2*CYq at bqLC1RZ8v5OVRNf3$?vbGWrtEaf
zzWrzX&!hv*y1)$htvO?nVrtHq<x7<kXZL}wMzcJDUFheH8#_SW3gm+mfOglcY+ITn
zcq}lrn9A5YFVSw`G8X>`55Qjg6waI0u32%m8le^=9kY_BaKV00*1nwLBv2HgRN=`+
zmts|?#3?7$WD}mo+=mBfmqKdsq~ypZ)`VIYe&tBEVJCZrnuIdrKAJ1AQ3{>HBNyYh
zC12jxxYoo>I6~YB;#{AoY)z-cHD?mgC7CZ3o|IT?i+4 at 7NlLDFO{tsTSyyt}yENP6
zvEcsY&>Ue`ql>)|$H%Vgl>nBR at t(EBnbnX7!__03(7S>q;F|?-w~KxCSK3WRO8gU`
zX3SKHk<fUL+07HV*zSll_u9Bybx%y7q_fmx4g at d_u3E5KT5dJ@=+!(L6RZqy`v>UO
zSX);a@~HT*Qi=(hjyt2o7EiFzM;=53u(wzn{XXnFIa at KK@Msq<Bz9u`Vbb`!DS{^O
z5J9xLuyaHd;a4alrZfwWZXw+X>8nINh$gjMvt-M)$QKIRv$FPi`=O;9LDlyXGZz0=
zte-!CC2I+9Q3=NOVBoPTks1ls)L0G?wP<`wmw84kaQO+(eifBCmFsZC2XdnnI&-Kz
z;o&oQCdZD;t&#Pkd78R;!W3nsf0mFPJORBaYae#aZ0wpF>6;n$8IwQ2NpGeN`uNs!
zN}zB3fIa!WtgZ~|2mxO){E8(`(D+Bs%=bToFRkWmf}F_tXf}<=E2?wm6>aEON1IG5
zKT_IxmW+06 at 5%ff62$m5&Lo8s)0v5Nu~~Rjq;Js~G$4r3-ycS=&cR7x)-8hX at 6k--
z;V<tdY!Bd_bB<jWi(QP&&lI+gwti;)2j|}l{EstlMgBb_7FPLF6arhD&0CQK|E=;n
zt<4*D;%z_HY{!3Fv%Ada)(kzBMAZyKQwQfzN-pbH=v9&t7{EuZViDCkqg6{(enp*q
z`*~*S_11r2)kgA44OyMdFrck$$m%L9tDZjsU0Jt>Kp+DFB%9Gl2ftiOqpPeMBZ6v@
zd06}mR4+}6sP0gWW>=tgDrPon0Lfw9iV4Nf*1s2W at L7|0ZGbFNsG=EKrL*<sNJQ0U
z2JnJ8ODX06rckG3hsh at MHuY|rMV*-PM$`Aw9Ac91@$RHF(v_Z=#W^H`auU1L8=gCV
z<i+^pp&6QZW_i)Td$nE{9L)11fxnoV>~VCaJe_^>Fy>Fi{3jmP{R at R03NeyFLJ`(R
zzMo3Ia-G$iamGLLTEvY1D$mP)`*7rp|K#8q|4}X3if}S^@2dZ7#_!!eXvWu$54hRA
z+>nMd|M~3v0m>7#`WR>c%U;tpfSEf6-1p4qlQ(F^f#Tgo-ZNuYTfGfw!nTCK1G@{n
zDn2%%hku*cD1EvlqjB-M-s^g=qek%^kuPSCZV1o3emUBA9Tcjr53R=!T)r+jo!vH&
zbTK523Wdtk-h$Ck)+Cwa#4<N4QOVKW)ft}wv$iZKxYWh=zKBjmka>E1{Y9G8i$HC&
zi9OQC89hqN5{)qTZWlh`p4E^OgZ{kbOYwW+`Rs$5FgN=pRV4;0o6xsmTV?P7YR;PX
zJN%BJdB5Y$0B~%D)7Ro6q0p|Bv at 3os2v`0jJe?TDK!h_rJ2}O!sL)7NqZ<x{PWcmR
z;)9_VL_$4g1SOse>Ul8|WT9>Q=YkGKfourceJ<#wNKiFyiAU(K4WTtr+3g7W$+ at 6g
zqd at 3Sh|NA1G$pEpIKBRcP<s@#T!Wt4Yy>?T1wCJbHXA~>L_$4j2zusRP<<q52ew5O
z^rrC*-G>MBxpcD@$0WYWyVkx$vY;F5yuqoiA2yZV6O#U;RNwz`m{ibhH}190G@&Ck
zkVnws{gs{m${v4ZdkD>sY0}yH*xcKsD{@y#7v-*&&S$T#4a>)t737z{2qx?|&7ib|
z61^pT=1}~b at n{1o>9b_*%P*>rEvFso=o;vO^>5&z8s9#lHU$qB?`oi9<oLx>TJL3P
z++V+!B}%sxOEbMJMY__<5~K^g%*OXkkxyuh$=XubL)N_aoWUlzCVn2ok3L;Zd3>qu
zeJ?NgXG+#i2Zx^`B<LEC(-_$2{i@&F+UI at GlGp-OCO6yOwLK<T3e!ys-RouZq+7jg
zq*PceWg=6_UKT6(EO@}Q$<$yn$%ToZhGtWYCvn3aeJU)=G+%_Fs|h+Oetw at 0nW*I%
zwc$M+3qryUJOb-GEZMU5g)E7Oi at 0&16-Sk|?V#r(tNJ_eSj(#4ehKEC?!W17a at TpA
ztvb49`ZW1oEoDsg-j^_b*PX9~qnmk~W1vuz%@ms9u~+XWba5owj%bT1l;(-6K1%4z
z!ss#H<~T%~O#`Kwr1RLH9}M?{&G*49mRen7=3n&-9T;NT#TsAM9O6qj+g`+7wxFA)
zl0JX2BWs_(*ygwUi{XE&QsVFzJIQA<%&>cLhJD~Kctnj3R(Z_qioftfYEh at E-i<s}
zl_D)fHzlx!m5X1*7RCr<KX`kbtIrD*IbCe>OPcxYNj(>xjJkC?MPtwoUeT#6hpUXe
zLg1p%FEHeGTj+LAHd{_nLPyYXHrp#=lQfq+_e7t6;~snnCXdE5aN?ARGjO5t;e@}{
z56UUITk!1I5{K9;joEOggPcVyvH6$S{S*C5QoT1j!xMNScHS2D!b>=RQ?sOF5a6cw
z-^U<z^U|Hltn?)1ru3Bgx4246t6in}y3ZcINPpWuE8V`#KM5g_G*ek%htyYOja8OA
zy#4)>2sEa6&-TNdd1}>Ffhns3i(YjqiSV+#586!f`xM_ at nQ3Q#fZp*s9R`Mn*We+v
zW!YooR at eJ>$`bUqivD-=ZRovW2kl+Vg7WN2u{~=q+jNBrBTsdTy4;~IcdE;i!hU2a
z4OGK#*@2#Vo8|)<yK<SjB2`_HqOO?ClC^|;Y^M2T<!<HMtOn>J2eZ1eRQ13 at at!&c
za#Cr8mQlChQWdcK0I#aYcqBiM>6Yp)XqHk|x8GzUCyw)#d(!Q<l&VsC65Fax>3i%Z
zZFEe-h$Q8nxQeWXz}mKsKQ94AKu%^auL*blKi at -U3z*I+@^V`lD5m~G27=%(Y(<rz
zu+`O4*oJCEOX2YX%yM)GeN|FR+LZP+cBQbj8Cq}zCiH-2xtF}jHtXlOA`Wx1JE{K8
z at YtOOoMj#4y!q$1B75_ZJz;=7ht3|b6+niZ8Z$sr<HzNn&mgxt-ww3Hr8-D6;ipdn
zQRNOg`MP`%Qp-pOPv;Ghk&xF7t_{X&gbFoOV09{?Iysd|atg~|9j?vvySa^shVivd
zoU%Y{#)=(K>2U+e&gaQOca!!8y0O@^+UiFajH%QHDO%0(Rmsc+Lkh3Fzc5Mckfw at F
z4_yQ=XALN!t#v7(QTS%utN{HRlJzkL2C<L;ieqJ4-J<EL at Jxf3Sr&&w=mAxaX;34L
zrMjm08XmCnQ`J0S%DD%LPxKq4#0Ys%#p_mut2o7 at ozLbcS-G#M)gxF^I7#U0Ncv^`
zpztpSs5i%&&~fQ#_JypUtz at 6wMN}788r6iYP$N$nd)Fo*PtVI?o}Rl)<H at a5I%zcl
zO93b7_|6>JA)g1T0-DQ#S<T0~lU0Q?(^pGYCZl;dl`}5(uDV>wp6S+DKSbUzQjq7_
z>`F-J&Av#kU>D2eJJO}3lxqq9!V@;nnyyj}$3p9=pxK|1c%Dh at goJKfL#faV<F#gR
zvQMROGkjdFh9h3;Zweb?;9apZyde!6ig)ize)tbwIi>9DGf1hOP)=qAQn-O<0AIng
z*!R&^E7YDa;qh3(_YGbxQX)Tq8azxZP&3@}v#;@GZ0=Kd$~i_s7ij9L6m^xojG4(A
z&R*tIJ%x!9Y8A|jwmu9a&{gDkSS~FizhKn!Gt1a}{JvjGFE3-e-~raIW!mDEy$sLZ
z)`=Ck5iqV|iFU}fKF^l^aPGr$+=jUhQ<}-NVO<KI-;|TF8g^;qi=djA<hYii6jX_n
zk?LF<ZL%C0N+REStxj))Grq`yr?`3V1T}`Ck}z)*OpAZ at qIGr+48CJ1tnB^}j3IeV
z${kFfI2)mg>?e at 6i9Jk-AA-adb${?R{H$)Tw!aOu?0{FN)>t9}&J0BlRABY}>MiB@
zTFm<AG1ovioLYE#z7BsL#h=G5=*l1Tn#owq#uxCocsaZE1%6H(PjgS$g;~_8wc*0)
zM0G3rO9_qNRY%CWK}d9PDz-=t4BHdOcGi%uoCr+;{hW>j#cLdv*82zUrnU{zsyN)W
z!K{(TDu4-$F0P;K9r4;<K>N#a*(5<Zd3Tq?D%-sc4zYRV at w@jqtR?ovV&nZWh_>Jc
zTXpKAVV;`UWVARmENkMXhlZtC<6^AwU!R9(m_}i+Mw3au4LU9^5c;h`?J+V)FA+Z%
zYFlVCW3o_7-#&ZC2(?@3CRV6 at iEacQpVQk5UB=sMlj6O-iGmNKiL$H>s>&_Szk<iz
z7{@(l-6Hu?b?O$lWm})cwk}$P9b7^!mB8gL)_DO<`0A=f$_K2gU(*mvQOyCp6y|3<
z#%&R5 at mz(s!4{f;0TCM<N;0<7-3J}kvPFS4ed1Zci-HC^=;uLPcEL3M;2$)Ljrh3~
z1J#nJd#61Kt!0v at dd$_gFTk$TlZoFA^3lJie*_{!W7(a{$vXu%fx-K1B{d6Lt|_BM
zdH at ze(kL&Bm*&D!M4DVI_)ttE-vdr|-O_z!TE|30=(a(f2NKNH6Cfg#r%ru*P=YJ<
z1c(St9Mm@<fkRJ#2)*Bpq=i%~ta6mXwoo>_ZfJ`2&r#p&HB0H<UaK_G+dD!U2d{G-
zw9BY<PIlXU1NNWWY2amz8_^#@T6Hz{pZhk39V{Fsn_J at gIa0FH<grv_G)fo2pB?_-
zUY^M=Ur%Ry*B*xHB?&c!c^Ci;S=@*}q50}+eAS8QJ0h^$uEd5)@uZX>U4spJVKFRh
z4&EDP=bt}@h0W``un{*>OO7E~+(?a<unU`J<KvdF^KF;jKCM{|&<bXu_7KWpVXGYH
zv9f0u;uoJbjLp3mTO_LimY&#|6QD-eM{M$V<>saSk`Mi}mjYa#uH3xLU$Vbt_OeHL
zx0kO_Mr?+T=Px;`jCcudtn`>ua&+xjrQ|4xs8YVjKWmg-Ej$|f7yHvgkwZeVMq{G7
zdYN0D-&zqG3yrC)Jq&|_Ny7K^F-l1j;iB0Zh2C*FPq9kje*f%cSq<!h=WrFG4|%_}
zgu|7TmZ?*J8}2MhJ9jQKhW+#>dOVdiw~<F?IzW?VDhILkVTj%8o|kZ+TYDxpVISow
z3g?-{^E`s-+8`aYb-MPMNuJCufON`K{xMfQ<YMpNL#{vBNXUiHZ{c(3Cb!mSl^zy=
z<!gC0@~IqFlRGmp_Rc~n9-~JkFUZ6=W$5Jk5O9&i8t&0Q#j~yVpgEa;{|yJkecTke
zeIKK9Q)tg}Wo?R|f<+2GoT<H=FbJfHOl5&*(A|jgUMbfGT<oToaDxCYm%HR2bMg1~
z{8V03R;K9-Tagf(L|-eDc<fG?p%xUCx!Cysp&cz1z9Ukxx)m}0tyq*hD(km at 4woQ}
z!>~MUPeH=CEc!YwUzoF#LR7}NS=}W5l{@TAsN({1CNx(qbwg>v1l at ho=z%=*CQ*+L
zy;qL^*yT&mis7)x#mdy<ILbA+T&QfvAv=eZ!@%hU9*djZ_LP2PLmKIJY3yR7zouTt
zOh6O;(gtaT0qU*btHQfYSRML|$pdi~CSi~XSX4vaWGoHG at LUx?sA7V;xFDY63wPD0
zBf6@)jJs?rhh;Ka1P-ZF`%<w>_$&K>&vvmxcz6R2!2KD$f!Q{1k8P#wW}p5EW2wS(
zg?RY>;vM8`_wfA|rODbPHmwv~7-Z;!-*V^3@|eI(CqSnYbf*TDe$=R1T!AS&v at w(l
zm+i_ld{RHANLOX*$IlZpg?8TGXnj%Uv3b(iWkg45FN?@eiJ|wzcv2QDZt`9NjlLLN
zxzynpn(;LBfE(ti6!(&1di5LoZJGTvYDtsl>B5a^x2q~SIqp7OK at q~rb}ZjrVA_R0
z<w}p(e7`_$he4t8w%Ek}l_!@hrc`S%)tY~ksvK75+cRm0D^|t-GrWfsUUl-yW4a$=
zJ>J8Pl3UTVYL at r`pZr{F<A!WkAx8DdI{12`8Ii>)!jpTHJy8Ko!U|4tu63`lJwM4^
zCTw33n{}`}Fw33-0x=#|BucSEJhL)hSzxz*BKEFKDDXB+77wjt)b`QwQMl>1zj`yY
zE%%MQ5v?CC?{WKpL*$;UcU)oD$9~ct at vhor$}_RcX)T3;J>~&lS0c>CF)qT`=>vXV
zdLx_Iz2Dnv*4|TOOcfqPL at 7M3*s~69$cZtT8mQ(Gnkt6sJSRigjmw5?iaBz*ca763
zrz?3$)6!yoIaW%Zb|GFyPP+&%annqBPnqQyR at aI2(DqV&{Ku%i>?bGGO-f@~j#C-|
zYEj at 790@^NJh7n>?@(K`0nUP{XidZFSL=$}HK=6GU8KynD~;tkf!B#Zi%wu5KRL*A
z*?k9T7YWAY at t%#59T*V-^w?k<0bs(#r4J}=Ze{UO|J<btXlrNef)7v4c+xABb&I^q
za_lB=qhrGvr2o)&ib+|wY}(JE>E!s9qGh-vAhd4U<N7noe(2t5ztEp?7f)#Mv|sAa
zK75`#EucT6HWr#b?J at lsr>oFa)1J_u>+$)5X<IGYGfsP<k<(toXITF%!yrEDs%0L#
zda%O3ZdoV}D_DvTi<d5pjD4~L-yD9h8bQIIp6%z~k9b5(l&PF)Hnc&;u#y!xHMVPG
z0|l0^kIhY6ZLe*RuJWl;dhSJXeAYf6ZfQ}@&_?Ql&3prixu_`g0VIth+q8UoiP8)M
z3lT=j9WXA#`1yVp2zVBHW5&QJK at 7Q^J54grE at P{nL at i5-HB<I)J+|59?7m;}fMNb=
z7$pwMe~(jehqB68A!MkTKCL3Kz${(lF}qp*FT?Z0j54>2-SULCMPHr`Lxi=XX1Yn<
zs}ccr0Co9Ecx!@Lqa~&Xs!SnK8Y&jTIHJX_`!l85OsV#&p*G!9|C$AQm~NaSc65c~
z*p at 0KPIp#AnLkcBrObD<pbZpsI=d8)x at e)@<{u4{e$DiYFou0jVc)A42~Rc at p+Bfx
zHO_ZXve(DfSql8~9rbkPtBAGzFK^w^Y8-_aPJsSeAFCER>dZw at 02Ehy1k};BeJ1(E
zn%*=irlvPu{yk8#H5aH94;X4K)CPeKe36nZH(rh{3U$m4eMN}<q=VZ{z+fR~(5afU
zj?Bc*3`O}h(-%o+klRaer_ at 9&Msm>Ut&8Xk*a$S5LNzD|V&8$FZ=ZDD4OF;gQ2|tF
zpL`PQ0xwjWPdwUzg*mzL(rlR1rwO|nT#)!;rf%Yu98n(&)#H^@;|6LP8o`cgNvwbX
z)O<%#und)&gHl|x#e^+}8Jxk9Dur!Yyb#lZzNnhRK@%Jl|0idH*42Lunmdp9`y9;i
zW$wBOFIZ6zeG+tnjVJ;BsXKU(#W<*2c8YBE%Y7KVu&~GFb_dpWGIWNDU!LD~Oj9vn
z;*Cm4r}vC{e}U4|_>CoC6U4 at q*Sgpp7yz`rLhO(()3RIp%gd0!z8`b~#m_&DK^&((
z*M*~G$g0PL46_b%cN at kFIGoibenuM-R#)X at NluK&nUg8^XxH{~Wg2eVC at U+=Xev9<
zljm}mmH7)hK}#rvSyjq*u|tp1T;2yym!rPe1G+{hq*6ZyL2;nov2{H6*ydJ~8I}pX
zGhVw7YZltqK%Be0cl1z4bC>U)do1rwP}EgbY>`Iznxy#d{m<7zlF%JxSZ_6X4eHZ!
zUzXeh6FAa?#jE2QemzRhFb6`qRw}nMtFS$*UF??QcHIn=Lrz}@h94O*YPQML*y>Dd
z_furA?nC~mSPB_cy^}s1KhvbyS3C*)i}@|%BEP<))mcL<8y9I+t_(Kox?g4RfPSl|
z{p0_V>FZn~uM@&KFj?}BR+%J&E+;#4D#@HlY612p^o7~GdNljZQhmAdt9cYM@?V(6
zR+kbwsLXeYAJFY5-nB`pvR59hv}c^EPpET at DY*VU>7twEj?e at OE15L1ZdCsDA4v9_
zr5Lp&N&X&*Sn9@#y|RLNWdY>K<EmR^R~H0H!%blmmJWoSd>HhTdFeL%c+$OxLLTNS
zmg~*(RS6B!CD-3(mM0~&NfR2G^ZI;?JQjeXuD{JHJH3rI?;cx1n|x4#dA{lK at eK!B
zy$$B3?mhGcC#Bxv-D3ta-*_9%km+dm9yCu$FH|XjK>GgxXfx7(2k7tgHd;b;yjM-b
zq1dDam8G73J1qL- at jPs><YsXTDH`{UJO!v at c{G4skFVnDpW|n at p3tQ(b_w2!Evv>V
zu{F8b)8tX*^qsNtR_$A*B<K-oKhPp?#s+3(n?s$tYsvPkBVva<nw?mzzo+kRq4zWs
zL#JMVuM%GE>NtFF4*4)VTk29UTmj2cRUUp-`cXN1HlV6`75-R^ahfI;I#$^|7cHR9
zP_9{r|D@>i6xOzl#~)3;MW15IFdA-`h{pG2VOD<3S-BZmxei3x;?Klr$NzGp- at jM;
zX^AI(Ce3lP3!wbKax<>sR=t1$SF%*@0rPy8yg0m6O<0UeRr)H1%eM2wERBE?K$<NW
z4mk8VU$8w;3cTT`S6nRgKZcqf%yGi68fXE$yE1GNd8-t0p&7f>_3%ouuHyr=!MYjO
z2hxY0I9BtRX8J<x*&mG88J|Cf7M?iv<DY9DCmEJp3s3+~e~f~5;rHGKWvyN8MIW+F
zz{s_gdTUJqKKyc9$m5>~(jdMk#phHySI3L*$q8^HV}Nj6HNh)<7my%5xL at oi$P7hN
z&n#ny(c at g6AKtiAx=<{1u8MCdY$FeoW6HRJo_*z~HiM#5{FUJ<jMl1<idEpR_Ielb
z{i}dh8T&g>ne9I(;0mlEez(>tqdCn at Vs|alD|4rdS4Q$*rxZH)yN~dl`w{lQS16R^
zVdw!Lxml_b?g7#txcMGB0rh~<i^4s?z6g5&ZVelcJs?@D4J`Jt6zT!~wdYi;5cqB4
zz2f$psRp|qjfjVJQ$$D)+B9M>_g8)h&Ev0Z^;g2e`KVuh&0o1AXK|8HJJyP!ZEQIy
za+*unUX)pFbHXYqQ1}|?au}Y4wg(8lcP$hmd88lrMDE6Y1m*t157|t7Hp6Wj%hqpO
z*_Cul-=h76huH;Gt8{<i(VWT;h1&HN3S=kLuEMR at O+xLx1ZWj%@1UE*LhT~ll3Ms7
zhW}Mcj at H@idDBo)eCSl55EfsnY%nit+di+st*C2RjPSF`7Tm{G$oz%-y*=i2S7ldz
zFI^TWJW+3lu{%xJwni1U?F%IWy=I#L&-U5knRO>&3eR4k%AZ2By4^rI2FrnE9&MC3
z>Mv|S9d}hMzQ8n}wn;`QX;li3V(Q)K(6f9)BQ8Qp%k#8+tCHMo48;;p$1IOg^rpi^
z9TS$jAU7qpXo-WiaN-|R;x=V%YoHE~BxN1+uHA30@{dyHANH5L9e4~)Q*}$$*WJ3l
zZmhrLtw22j{3Uzq#;%)>RdxkRPWVgO>PNx!G)vfa1JwGUzob=scm3qRqs7Qy$f}gQ
zuNJnxf%_PMGE4*hlJ}L8Hep+QPD$&!FW{D7ZP^v7ysaA92-G37rJz^E!K+;#_!%`*
zIV+1pW$G|M>JS16W+CK}2HI$l4zfK56pQ2hFh`#92)dlU5(&E4+vj0I+$pphtB}#+
zVy_TrzxMPzMy}q5l-O_2BaH#N1a}wWrvjA12LB^_>E>-ekyZ-d@)Jp=aIc>Tp)vH4
zm*_+CQC4~$3TRX4K2`pZ%|h~W8_T6 at E1N>s!|aOla90bIHZ$}duLw>$S}pv~BEbJF
z7S=*#?|8WY?y$_fSMd%DZFmjuuvEUf37hF|N<=dqd#)PlxmU@*M~vD0T>+j6OkNs-
zCMsPHD)NLXJRpym!>SFmU2J9W9h~4y997xG77j<`@E!OK&DO-G4wve0l*-=3gyB*>
zdIx?iazuxldY^WJ+?4K2+zY=BIUb&E-Cyi+n%v!AD2EZ-y(~S+%J06gey|Wwx{XM4
zfghWPVeM~#3T8AGS4Qi%{J6^e6dpeGgLM;eSJJyTWT!~>JeWbcy!%vT4`dVftTpHP
z3t{l@$*D}26S?!-Je$8TU70$W%pW<GlR>oQ6d0*-hlTAO{vkk4R}bNLh;~rg&|+~5
zfY;6Rv}PS#(B19rv8=o<Fl&Z=8h-c$y>Tt*tjgQa6H;4D at 59>;jdHOEov7 at 9!rfx}
z*T3-VlO2@)kkwz=nN!&*y#$&@C;ym?`#saqXe!jUVxOG)A!yoeH%~y{nGfMg0fxdx
zK);Sf`bwOzaw$wh;7caHt?sesIpe938G~q$P}cT{z3U1hn7<a87aLcPVNGL!^FC0E
zs$1fab8m}1G|t6#34 at vEhpZss3|OB)5$zGBfaZ3Q+nrOHCfnTZP^>Da(H?-FayQnD
zHmySq%GruEpbGg|bsDX*Dgt-3p;4=(FYE7Yu00}}9&R7Sy|{PbCUki+tU!>GMt9%~
zyo-HJAL-MLcIK9rM$dHR)C*E0dqL_zFG#~)kVL(}InWDIu@^uWKrL|ny4($FyQ;2V
zJtgwpZ%8@!-LD^cPV=G3{EdKZhRb7ev%kd9OkS=T_^}71QZtT%+PP3+RFn79XOtc{
z6DR78bv)cy7seShR&D||RysA-pk at luZiiH(X2PYcuzf*6nQ~ItUOdZR(g&(N#Vb4<
zXQGuP%-Wrx1h_m`y90%t9y5&2$55LN6xm&#YjoHafF<@|96V2!4trXIIbEJ9I;`#(
zvf at H;lFKty2d~hQrno%U>aa?Hm3gjn1DpeH(h{Y)JeTW<vIpp;yF3|gexit7s3o23
z@?4=O9ihRlaCxrOVQ0SPuuPZdDjoLq*I|-VT%I54;D2aIQ(c}+9rlI>%XWEY>9GIR
zV7V?&p$>aufMlM_Q>23*)RN9}d1mXdJ2hCb%Tuhw+#2jAm*)l at He-OK%jLOI2T#_L
z&U1O@=&&RWw!r1NNr#y<*dmvwM28)33zJ;x^32u2AGPtM%UqtDby%YYt9E&^b=Y4t
z*gY;!jt+ZjfaG$Q=XxFdu$EMEc|;v{uLfJ=^5p8UTQ%52F3&U_Rum at r2<iqd&vYGg
z1yB7*ErDm~;A9P4N8mgi-2W8^KT6<y9sJE#Ve*d?ra;I1UCZz!foJOAZ5sHu1fHye
zf3Ja`Ch%q9{y#weIl at d-<vtB}kCx>HLZl5K+!_MUhq<b)<bF%XU8mu;5H5ZI2Wr%K
z!r_MLw_RM*2|V%^5$CdOaj~8+dG-bZU!#M+(7<mKI7<h=tAY0tc!~~w^~-Rj-Y3jd
z9rKiyVLySd)xi&I;2i|MP6ywufe#b-aveNxfc#OyWayadwG6EUzCs6ItbyAIe5DR{
zXkbR*t90<`FT&)H6Xr)c=ExU3!-oXU)WL6S;C2Gf(!qbxz?}px)WN?VAm2lnA|3OP
zmZ6Wpvvu$t8kp2*Z4dzPTn%g^@C`cnx&iWb!rZ81CTJNP1fHXVV>Pgoz&GjO6RjMa
zMBow~e7H4CK7}xIb<8d;Ln?u9cJo85?2j5ajlkJ@>c=&3I)QU^@B;(nClls+9kWEs
za0P)y9bBw|GYOolgRj=WQwTgw2ag*dpKTr(D*$8CGUS>E#tHxjKj+{)^T1dE;DZ`?
zmU&>T0PvfihshV42gVA(Y}7K`WF8nR09>zuUFLzY0>Jlc;CW^~R(LE60RxNdTL%hQ
zV2&6q05wC)v&bAVSOEOe&oKhpQh=7}BLz*MfN~JE+{BgvlQG|>DAeALhgw(J*c?zz
z at c8&lx;mIhS0~G&YZA+#YYMv<uh+6vdQ4+UbWLXtx=v;`x?aKhY<SINophbTj?*=j
zwb3=39i?k7`_P7*<*BRk*ggWyVvTeyX0OxrCiV(lUF=1=&SM+tx_~`J*G23XbY05)
zbX~?a(6yR9K-YWN3c4<5chOa1x6ySCtDx&ctW;xR8%`>0CPKI2M8dM^7AFsOCEenL
z!7id(oFUlxbc?eBbI>i$1k6mgIQp|LE8gO8&q8#I13YV`TO88aM|6uLGkez>UfAxY
zTO5JeYjlgFE_;z~ahPS#S~Zq&bY+hd0!LKlqgxzB*-z;fhfQ`L-QrNmmeDN^kF0`j
zaY$ry>Gn9)pKd>-`qOPY)t_!Vss41^L-nWIKB_<6;=sksbc=%(>$2c2j!rCO(OAYo
ziT#TZI1sUebc=%zYoS{lZP;6Miz5tsg>G?FVgF0FIFhj6(Jc-g>~Xrq;ez?-7KaG-
zQ at X`rf!#;9I1aF7bc?djD(DvFoz10NlyNqbZc(UNw$3ujGP|4*D8FnX-J;C0B)UZr
zW$|>2Qpx(wc#ERQI?S|3SpWV at agx02(oE%VR>N9aDyUwD`wq=aSjE4u`c!TMy+LVK
z{>g6X<iQKe+3Y at 5EoYm|bcXV?mY=<**%3k(rB|VO3f3KtnAd3i!mSd!J|sH>8Mv=G
zm_155-(6O3&dq$tnVYlTaYbgGIX`=p{N3V(O}V1nsWjFvFxREzWjp1r&^HldgjAu#
ziZO>z0Jm{4!y*Stkxh&_szF9^h+T|n)gY>gAemx}y`4k8Bgm>L;#3Dg%GeQvM4m;}
zT~NsF{<WPs>r&<9oOLO()4#UQzqaR1EIxP#HkAt+>r07moDx6BxWsuu3LZ&f<VmVf
zdOfy|P3!>)cOO>Z61qi>cQX$b7&-u at __Kk$Q2U9AjF+X7_7Sc{9+%Zs<YxIMTu$|{
z91}m#5yXDs&la|?n(p?DEQ4hhYG1E^LQA2UusjlU*rrctWS$AjBc~4gg$9Fl2G0wl
z{@PFU!F}cwJv)G_2%MDIA_;B^99H4|hZ?*a4LmdLq4VnPHD at v(I<MwT&iWBG!OS{)
zUQM=14u+=JoDt;?rM-Txxo%_;epx>q`g$WanBTe)Uy^^OZDT84?Hloh_-AHr#MgjL
zJ97%G3MsmCp5F{>Oy54q0ymFfKT at V~6P;vrsUIt(XJWup7k!>gQMVz0jaK{h5zG#^
z&|rmqexALs$4{C3&%UL1#C`vljwsZAYoaamVoaOX(|*<8k3G#P#xM=y<&Y#X=C}q~
z!67MPOuGhI!Xc?*Os57Z;gB>jrbmN_9Fi`^^l6aGI3!z)acaH#d=AMKW0EvT42R^2
zF)13P;|GGw5 at S*|$QK+^EXJg1kbe?n)lK5mbWJ*TBE(2K;FW0tl7a$~8Z99EEt-Hd
zu80;8!AgDojumQzL at dBULaitcDMIZOZp$xN5f+D0ZZ`FYev*YNDd|Cyg6zOdSnDV2
z9em3nE(oyJPtsveAL5-J7W!j!Se*ugh5q?EOga=61Xx_B=-^5%DJ=J0I_w4w25bFN
z9X3^i!CHSD!C>Wc;Q-0YT%NII9ut1XW(iu-%Uzy%deW|sI4r~EnXkjX)L>V*JY_oU
zA0LHDUg`3b>)`EL(yLsaTXfj-8tg|dk6VX5ropoHMgviQaDZg4-fjS1rX|hO+YMkY
z4K_<}H-P18uwuP|05*Am<V|`T0eI9$`k<2TW*8{i&6f7^m%4ziear<{bWiyKg}zN4
zdZbA+8fjp}-G!Y`>FwBr7PdO_;u`Ny)b4Ju;gCDWra!{Hp6G^5|C~(C?Q0$mL@*pd
zZ-s7d#wckqc;FPqBGhcemaw&*k3;~tc;F*KiZ%rK&f;i91Jh5>hDW0vLykr)qZH~~
ze2+Lnvv7|f3wJSExc at X)RX0usfo$5CiN8#B4Py^~Xf7J)QBiBscg-m!_OfyqP_<K2
zqgiI2{^9VdDfG=aUzzXp9h765&8E;7%1Jz+Yb&~0?)ItD at r+aTmO9&*f?61VZ<afN
zz$i$@j?Wm_FtCGvEswCj<5;QfjDmm4d`I90Q at tgjL5lb8F$;gTrH4+U{;^L5xVO<<
zA4{lav+%qv{r>NjM(^8ci^KjE+*wERaw;7U6+tZ;r0Ghb!`k#z#va-kexN^)+pb=x
z6gt&RcF!(t&uoHH;`FxI{5he-2V&>?Wk<*wSZo$69qV>2S)8 at c*C0IExKL at VY}zo_
zjtZih{61Pci&FhXDVp&X^bHrb!<_2pcH)8NZ$Hy^-d<n*Gyy)*0P+M}clNoe6HTTx
zb#<z~g%}OwefyfqnD1YFBnzF;655<m3DLN_uZi|T{asCNhmI7kc@@06DzUB(3dH at 5
z7H?0|$`BpC*EZ^9b%oLb`M)p7jaeO2)A&g!A&|ct?yWVAXM|mi)~<lmzP~=9cAxwy
zZoSKDs81-UIg|fTOwE~F*V$`=x7DDVj=T`l2l$5uOxC#l+|GW9-RWbcojpby7(8nN
zg|#l;^KnP-q?mS9jj`ZI*>W{`FRxGhg=9d=3*}>CZQZf|Ev>mOC}$U_+^^th at 6R!z
z&A$sJ!@rAWYs%ZmEVOG?cb at V}d%wC*KWJ21rnK{>RGKHLFr!q%D at +@K_Hb8ZqlPXM
z<v!^`+6pordX17<t{h_B{kZF}hmFN8{bJ7&=@2yN;(Z1!;O0&6cnLN^U<<9J&|RxC
z0V^7AjH0A-TH*uTH|4G3drNOe&jr{Ju)2NsP2LKE54Sn$1e`>`f&|QA0 at 7~EbS?X}
zmPdAAZ^h8->rz6ubQRex^39-xK*}pzTGDsD`l<V|>b2gvc8h;qD$B$us$!8tz7R8}
zDrNSrJ!Uznoc-np{u<)F at 0$H(c1WwtweP?%L>KQB_X at SY|9313wJGo>wX#pVL3=Kc
z(M(_zzsQ0zp0n5~C5*rog1^WfD$ZH#ka8jM(;khdAC-B`p$U*mo`Sd^X~h2UhTdDs
z+)4|mGn()TNEuH`Eym4v>r$YE$gV=4F at 4`h^c90H-7qKOeeURs=+*|c)cbVog+=zf
z&?1dJtIl3RPLWf-8tX98yUuBnGrj8^Ch2l5+0oavWMj)*P>UHqXthXyS7QV2vBje+
zfPMX)?>PV1U!=Rp88C&7^_cUj8z0SwhD=hE-v+jz3)o~EcC#iZ?1sD0j9{bG<3Woq
za<Xgn+~XU(RVk)CWU-<G@=Gxu%YsmxGKK|vX){Dq0O>k7UEqm_^-g>d-=bvO95+zR
zm(^jKc|giJK<Zge>dD6<NJX%yEj!Cu-i^>_c05gUp`E(*%`^y;BMyI at w;?aE#OAk3
z$@mr4>Gq7N-v3sCiJjn{%#}Fropu~v7b3b$vEtDWyp317*=h9J83#9R+ylSfZW4TG
zK`Hm-RquTRH}C4+(4{J6yzdBP6nVzCFyk$7GY&Gsnir9oh|F-a_B##U!EF5<%!Ue%
zMV3ONTV_X2CbLugvr~zO+7Q-NwkyqER at 432_^ylF*>BEJI0e3(cn|O!`q>ZYw0}3<
z2kHJ*6K)!5hC!`I+fah11UzPSb&8tYpdD19FRX=Fr_oi)%_&uWJ6uw${Kb|k|8kpB
z>4d6OD^sx_zn)~r0}ggMt@;I+QapLE$h<>ukqP=v=9e?zJ?6QqQ39|e{4RKfCl9s3
zY74>mx55-pg2(9%)u6KuR#`(E9<^OlwA6<ri8R;#zA-Q>UTqS#nS^ce8I94<DkZi`
zG)q?t+eQi78Z!1sLe at dV?g`8~4;_S#6SftZg>5&OA>+u%jHunjLe4vf%IV at cOU>t^
zrUJ?WD2s7<Rw&PERGx|(?}BsD+n`yrNUMZx?ZP%oM!PgRs{vl*?;8TMOz834*BOU?
zU{^r#1!e>EM;r|-6d2{+#?in=ffdk+I$AQJvPP#FU;;r8 at cA~ez$UFGJ_>9Gr0(XV
zVgMaufWC>NV*wp&fWDHW;{Y8uBqMfW#4Z-tbw=z4q`r(LQt^O}H$XRWbON9g4A3ud
zbRwV=hhzl9c`Er1vB04-;xHgp!AXq(^aum=^&CAC&?61d<2hOYv at j$iUFfGg-zgS2
zbw->9r1r-UDd at ZfqYTimaP)bAKF<LC1V at hs^yncO`3W(SBo-v;j3gP5n$1Zi13K9N
zJ(;7&0D6o8I-aA?2lV+vGSY at 3lb<3Mr09&K7?9d!BT{1lJ=OsIdyWPPEf{BjuHoqM
zfF3_2BMXU<RIwmcXC&2t)KpGt0-z at tpvQ3Z1%ST50NrgR^h7{U9FmcP=<%~L&ATVf
zdm_!NrU~8`Vg9uK)B3SjohbYa9gnK%J74}|KCs&+7PLugfLt4pZ8Ip*$4mSgl6-A|
zuHxupfIepQ{yF*^Kz}nNzZaoX(R?NrFumcJ0jWL<kqQDjXn_6~M}G_GZ;jqRM~46%
z8j=z6uA6^cEI6(+a@>H_{hZWyfd0+^J&&Wm2lV$w at 1LVj0Q$s`j5vvrcCnycXQbVL
z)VF3LbrR4g4bbm#^eI4}GC;q?(H(&97?P2nq18FRQ!MDz8R;}2bsHyj8qlW=&;=ab
z1?VmV^u-+A4e0J686hwA`8{GmkIqPs0V(_{1f<RY`iuekb&fs@=(7gs|KaFfK=%&8
zi2Q((-zOIINq2E3`XHSUMMf?M5<dXyhjUT6fa(WS|GB6Og>Ct&u<b?_bORwmH_(bD
z#ivQh|4J<QO1hhu{nfctj-U{uFrSNRh!EmIs7(<9Y*^`9CI1_-;2RnzzCm#|XuL`l
zT}!TDLrCv*jvh*SCvvndy~ce24PQ!rNGu5HeIR5&>cf5_b$kfv-OABJN$;;YT9;m9
zMph6bC&YpiIwL0xNX_A-hLYYZIC?1Q9l_DM^cpk5(43jyAr^G#jC2@~+VcaE>KsCP
zpXcbIq_>u%b?G%`WC=0SEf#d^jC322%HgDXhLGNI96glu_Vy84mtJE=4&kCZzgH~i
z)fwqEAhm^)>eD26F!~o9txNG>^h%D_CE1vf5=x}Yv`{+ at o+IQscqK9VrC9K#&ghp0
zMaA)=zS6{fFq-ueS{M1j=zScmi at h<UFZ3cK$HanTB;UtyFfgFCmebN_^1<lEy~B$C
zjNT~mH|As<aq_KL at U7k?Ml(5vc#D|H&sqPR9aj9eosAOzA=W<_X)XVIvEX~15u=&>
zZcggN5aNH+*<r>1%Ck}8Kg9Zn7&#>toYEOFn#sRBL!>%}5dWq#!;1e4XQIS^i1iQJ
z6!N>of-apAqnW&dlj<Hq{I5SVtoV;V6D9sbtbcltk+WjKS)CE1nS6f_k?I{n{9oxA
zR{Wpni4y-I)<2M_O3Cl95)1l;+Ddo|eolJq7h=H|BuQTYjV}zyXL0gh4k7YMJ;RFp
z>Fy|zH)d{sH at 4E(V!_vXD;eqTf8wNu(%qHrVI}^a?kI^jW at I)o5)=!9IwMB2|ApOA
zv;SbUrF&S3|EeoW;*A;E)rE|FCl-9CGh#IR|F$b?_8*LXpleu(zqKn$;*A-ZN{pNo
z3r^~c7|s4dSJdo37~Ot)ScyMyI!fY=8F}e6GIClhIIS~cH2XhtI%@VGjK1^quo7Q<
zI!fY=8M&AkIU^RF(HSwC{eS3;n*9f(KkFP;;&*pONxU&5|I<0_wEtjd<g_o;UeJTb
zO~XlovGDwO?9=(CH-DdR`rY&Srh8$9{n+pFP1nHldGP%8|K^(#;Q7U8^G*Ig<eL`3
z^M?P)H>JU|37$XLlyB;JD&O>*XYx&}H|Cpegy;L<c_ci4|6IPQ at r8WTvESyKYN3o}
z at SF|Li{LpCo{vBoFGCsn8Q!%{j0R{DYPTnuOvOU&@A>5(ez}leo*GRaN$?E0C+*hY
zHE6)d6t9*fVOyrKZEQvdw80UqU{gREufmNlgUl1l&BC?{vjN({(H1~k4A9?^HDI|F
z&{jYT5omY^U6|xu97C7e#1%F)fiAZ}Qkwy#=Q*VqK*t!MYdJa=(6I*SJ2*NH&~ZcX
zQ9yjy#T9nq!wyO929(BgO7Vb>H$Zcn#qtC|Cm5i)&0={Xpc9AS;|;U~EO&@29K?qM
zk~$11{gP7}0q7A1Xo;go0(ztYdOk-BfEI?};~L__DXwrL9~DkW?lhp3$SIA2B%=(_
z$7!j*{5(LPXMp}YM~?>d=pp#{6B;CzCy6VPs6CP(X_5gYFQ=3Y=wt)*GL9Yt=rIQ9
zLXJKk(B}`q$3?_Pint<$_(*}IDF&2!$)vD+ETE0V=_p5!1N1lp^iGZ*59skj at PRf9
zWqGQ&B31H0(o{&AIw+~HONMcE`2;{sI2W}DP!|B|f^$*%fSL%XiH2p%7g_gM-_1CS
z9wwX7zSv^@C)pQY;Pyq!P8f}vgTF;Va<z%2ZD=!r39Ym(;}m at v>B27~(s@|Y$50Zi
zQcAx*7xfmPKtm}#b}niopg=n*{bmqK<L_a`#l%u3$-GzwsYXW;5PXZs?64e`C8a?F
z^fZnJy`=P81M~$P4SGpw$dCni|7a<29T!WF>uel1AoLNf^_Rn<r1U!j^frzLt)%pO
z1N3h=8nlwq6GN~e5gYAdX}iuwy8)rOoDeKYN>3V~f5g$4`;-CN$<d&bly(fk#&>8X
zaCM5MohUS=pr0)7G@#T>c7WxuA}Kv>fc_&#gGy4`Wq_{ZXi!N?yNBT84&tLnEbY;o
zqsM^I3{D8vBc*2y&}keE3Q6f%1GJf=K_Mya9fFO|&;;P>6HEJ~pKvz%B2&%-LI>q*
zfe2_Mr9YgDf^lIvXd|Wlk;M~AvMGB)fX-9#ApDRf#f8D&VI+CiS7PZ`dP{s|P?xD>
zg;?IE3GZO^7>?Ejcrdz~*8YY<thdC6xbk;>BbI)nv+<1qp_e%!HiR%g);p{)FYk>K
zW+OIAh>eg~8q(Pa84$XZ6FNSGFxz{F73SdCC}B2YqZwEIt`lPE37w4-285pDgxZG?
z<_%|u73LqGjS^-fHf9hT9b##R&PIm;p|PA$C(5${`s|ruW%<aNC|NdQV+WcZT-{=6
zHw_xyDBA{<e!(gA3?a-b&kQTf?lV!sY{W+<@zE=m_Ug^iYd|Q06YA51c`!Oei~F!Z
z4 at SSw(YjC at vGF1$LGKoMWLL}{WLKO_gUXj;>6bd2Um6s2J1^*~Aq0L_&#(eNsV7R{
zjo7rHoyK)cEIp>Pam;|wQCiuDC-K4Po!!F<{IlIr0&m2|gJ^$neJhrJtFvJ=k<agr
zn#c#Euk9XI;Lq=l5_lsvI&snO`d%#kUT4E-BL7EM)I>fQ{nxHx1^)4_D1kR(<0r(%
zDY5jF&W6!MKD#SwA|H&N+%>Gg$9F{uyb&A6aKZ2D5=*;CqPuVaFrc)Xj0Zys{Bs=L
zgF<gK{&Dmf6#60fSWJAJ6-&?R&0#c=XP=Im$j=%7P7f>aXFH<=-iVElJJFKp>aP+@
z`-R#nk}bG{VIbp_Q0qkbj-1<J&;*q_*h6#G7h>rb)QDd|`Y#O1|0ys3%OS>rg`LBW
z1G$}1<A4$OV=<_`>ua&}YrPSTbbyYIC>>xh`ooT4$APUKQR9FS8&7l~8$q!&sIy_D
z1KiUwtU_PXG3+>SRY%k~V8liuvGJW)`kl^(kq+?nsbLlR=2OFt1AjafH4YfDQHycp
zT_?rTlhip*;<#Wysq)mY8vXQB!wv)!Pely`Mtt;POn295vGlaw97amOp_9WZ^xIEH
zDfENUzdac>@f)!r6B}p5(la_6MmoUFCx=z&GfxgX4vab(H4YfD at qK&bSRm9UpCj6}
z7M#8J1aC*7DzPiFL)c~(wvEZyL*)(ZB@}#LkZm9zuw`Zg^d6430NP at Jex9SP!nV0q
z)FLp<U8EL)&V~muFlaO5vWUV5p>E&>&$WbJ7 at kHNr!m(|t|IYdO^JhV<qY0Q9-_HV
z;fgK!rqQqEo4($jZ~DiZ`KHhQns0ja)qK-pc%BW<<?w8S=R at 1_O$~44oBsK7zG>~>
z@=Z6xb2dB|!*kaw`KG;F^G&b3o^NXWOTOtoc+Q9C%i;NYcxF(>8&JmcJMv9UFXfwV
zgXb)G9uLpa{-6G{^CyZ*2TvUO!IP7LA~oXViT8MVWN>o3+h5p<G0MrITwpx<LO*Wu
zPTOLgiJs(`ytX=ZmmYus{q4ZKfCi{(7-yi`2`D$7SQKh00MAr(R_;;{Rhtm;JQ7hq
z#^>WZ@%gpz^L~2Xp+7(00C#v4w|d7;qKL4!0Jaf-loG}r(u<AK?3 at vpR+`~LxAb_6
zu?>JRRl34=M4F7uNMi#fM at u~zceK=FcG0<m*sj7JvmC#n#A!06nL=3`kr%O$Ntd~}
zt0i^6zwkKb-MB-;2pnTq<>Q1 at r&4_o<|G$DZ5T5Kk+`l6 at 6<oCt6z<915iE?*(sSl
zny8;qs#p6}rI~tJiPN_a-C<SvV?+N^4pn)tH%YPHJ(ip0PN>y483%;z<|@A}!P{u5
z^3i#v?_n9O%6lLKyFPK?#L}O%n6-AdQkhi7!%Q8Nof!R0_sfcYg!KbcYJ$$p5B>e*
zmlbjlAC}Pm2(B!l50yRMUR6#}PGy|r%#75SxfGZQU8KMeFKl<jR{8DdpVzlfioIE~
z&XGevFeYmsfPJdqdw>KB2|3ACYn6Z2jr|LP%T$crY+=h2!Zitcbf2{6wBU_lri=T-
zEm(+g7h<seK35K9>=(A%tNad&#!9%oKE(D)7$_z5mVS`Tw{Oh_-ZPeU4(}POoV4*n
z@^eiW{|NrT4#p3}Kl!0w9f(MUj<KC>VaM2boRqewlY_HZGxs_zk7K_WAn<b_Krz^Y
z$4uw}mLmR+WA_cDxF?(fj}MgZ##{08Rmp75K$>Dm6MW1>@!HT))h~BLdla at Sc}{W~
zJKvshNZ4+>#@my>a)h_%*41_lT)cg6$fE2Ow#Onk5y7zpZtyl+=12+HUNKeTUg;fS
zJ2Ycs#-7l1?lRm9PaEf5tm_tvW(XyuvfXwbmYDMs4o<AJgq+ at n{wS|l5zdsSVOU{P
z89ytewT)~K*J@>-Uh`U-2pquC=erP)BRf|mu^R`H&hDdLy9Bxjc1!4!(NzVpRG<>(
zbE?{CQA?ay*@IZw{ZbP&i?Dr9Mq_AnWMH=|$i0^{>|7x0NKU1lj$q}Np^shm-qqaS
zsHKeA=3=B}^ds6UO$1z7Im`Va?Clns#KB#nUfSEQYNDvQQ*&z`fXT6|5?d}dNiJm?
zI#6 at FJ*vy?<}R(9q<lPZS~QhhzZdoaAQOFBgQQ&RrXd&)==8Z=Z0lA0K+c+E^7|_`
z$=kH^nCRNQE2pwg>cWGm+Z*uIaY#XL_4~u#>c^pvvS*-qtGqqjU;Wpa<gdQS0=))A
zUOwg~m-TN=;fJ986VP2T3ObU%gVPF4En|Oyd{tibTfYYV)?3P%e+n^=hg+ZpU2I&R
z-a<Ecn{0t8>ma#Fen%w|?0n$QJ83`pq`%n6b3K;JPVUB3Th8%Zzw1mtx)s*liMMC%
z6Hns1TijHYGg5OWZr9w2+vRq5Msr|+rQFS)%OXz^FkFl%)7*l$ELpg?3D5W(eHD-L
zIzq>E!LNcjIApI9-<8e-LOATenAu*%gEK@$0w)PQ`6hJO5@*&C at jZETWbAJkDKIWC
zcCSlVbxgR2-vO04Cq_8M<wYmUAC-|@i2qo19yyZHGG#)h&<v%pGiQMvebptEYW?|c
zl3i!$T6MV%VWqlQtIHj9jN48L($JT8WT_xy0thq*4T=*UDK8_`NFU=7;xe6!%i1gw
zmol~uW=QnLF(50CUu_^Oz1`=^N>*37G7aB0`lyc+o25IH37Q}HGLV+CGW7%Q0Dc}G
zVNbb6mzHK}Ciejk@^T%ff<AaH$V-`v9n|FIfl)bfpG1z~pWfL|j^Vv6HY`Nm7%4!Q
z;6Y3fY9E+Cr19Jl<Vms6algP`u<GuHu^$M3Epe17CqcT*MKA&Ox2_&t=CP|&*;!Pq
zYWN}RYrNA^&7gBhsbwtrayr8(AEcR0iq%PlM$ohj05bygkqJY4V5eF&z=RW+m>9uC
zC?A+`mX#|f;iX84_&P3!!YY)*<gL2$+kVYIij*XNcE2;Dqs(Kne(q)!uThs*S_WPN
z#!u+!mj$-lg7o9HO2U9+7h?{OpJ`fo9(y5-VjH`lJAS6@^|nrIJQJfh0vHmpu{BoM
zHOp-Cw&qo$XXwf38Ty$is2^l?<;ZsK3_6)DvV^;JMHkJwp(Sk7+x at C1ZK1UZeKBl#
z2M?mdyJ9~FFu>T(JaUa5D0H}}ZmTr-Lj|geI&KwzE at zqf<UD4u5AKCiFxF2Vq{5iQ
zP{f;51h0Ic(5Xkdf6W!?CO-nPV-UjO;_maG8!E#KLu7a~3a*SmwH at Yl?>=)@L(Yh|
zK^YaEZ0P=U{K1P4_>=Jwlokq9u-BZmFDHg4XjB at b99=lFj^G{*u|pmu_O2qY=S`9Y
z-9CpN<PTP_UEQGK(6VYY`_!cCTZcPox`j4P!NDwjJE<Ev3zB&J at G7M+>2>tYTqQQH
zx>oFv1R$SS?uOp{H|j&|F%!Pb%kgE_n$WLq<cLD at 1-UK}dsnBiWmxJ(<o~^s4}m^y
zFbbj;Sr^MWjpeS10nbBQ>1i_Y0B?z-)LmZYF$YSLO5OfS2aI~$Ybs<+g7H)Ek(OCv
z7vGb1`pu!&!|}xgA4VGzJA~T%@!Bgqf^K54DmT7DRS7QC_0vJ|Pk-4T6$|z_qP!)2
zz|3*hv9(_f6duQyuE1hq3;7K!>?3FW3;Q*%-*OB_p}?>hHEVlFf9i~MzKXOc?lH^w
zqpG^lMQLH5i={?or*Udu)BEq2VdU$?26-|%4TgJ1*1_(Rnxo*8O1sk3y$s-cm1ewJ
z4}@mJ{UU(OOLt(J1Hl7F4t#syaN<6>v%5oSN9V$jVph7d`<V5>%8??ZTWwz{h>&(w
z{M-ycjGvdDlywB|>~QDk{<QnB_fwPduJVEM36`^XkJHqZ at 5qDY%>#Cml&;=jTa&1a
zs<WuD7H<eSgk24#E_PzB7E)NY#>Y0PvHDu7Jv17IzxqPETH;_^ak81N@(91{ztM+6
zRHxdNPuRt<IOv+~$U{f(eNqCRSe0$&3qm)tc))mJ1vnYHP`yd{=NyLJK!rhVIxdR7
zMdd#Ga(RUE&n+|9&4BS8S^pvXfWRg>f$jSNC-FYNJv;|)IKIXb8S0K!U*z=%gCxMh
zB at o#114f_QjK9wu#$Vra^xICq at 6#`%-~IIa0{!lVU-lL#9WZlbFc%c8jfEvA`_-DA
zZvh$u<@k1>rV0HXRk!#=3Q(ITFD_%(-T)2x2F9AjD7E7;Jna}5h`*Sqv^Vz0G=33l
zb!^4a81&?KyYkc5%fG{PV=-NlK44?aT3bL%wWljDy+-h1#A6K9Ak?C*r2c&W)}vIv
zR{CwA-<ROG?pG(^xAve=duxKpG~2)PZFtO~#KNOj11M0?n!8}NAne*BY^zDx1$?#&
zw3ZGak-Hn>g>AL=Y=C?0jm);#??bTIEYxB+Xir>)*eLvLZ#?e#@Etsy57Ur0fUO8k
z_hdvfz8}%B?cs(EJ_Z?~WG_pTioMJ#P047M(u<3wBzS;20ycgtCJ$D at tv7(Tw&1Pd
zdvkCu+;5<J)X0J}!YE5H3+`*qUM^n&dM$tB%ECVXGsubWh~Q5epx8~opamcbae8+{
zg0Sn|z%2WI&Ux?~I|fO_dYOVp?40f;(R7(c_bp7yn;^KwjuhD-iufG-6;ZiCnqPCa
zTAEdJ_8uvv=Im<02a7SSLKwU+crPZKagRJ^wgv=Mk-x*B+pbr at 8Jr!yPYa50Umury
zt$abvdh--H#oJ=h^45g&o)2kqvna(iJjEtVp{|EF!&97&gA~hz+VAjsmr(mPUhflX
z|AkjssKtZExod>lzvFd*Q2P$LRNjPr11|V(O#vTDoJ-iY$Hne{fRw3vA9ltH=^`b7
zau}d)$nNch4jiX-&DsW`_E%^IjZAS9qzK*~$NNgpGT|{g`&PRegCO*{g~vM3o>_}Q
z2D~g^azY2HfIoY2vEchz3^d at Lv=-`(I8kwd;Ek2%-AV5>u(J~`JJ2VAe`gzY&!<s9
z0)+r+p%)g5-Rn7c?56&k2 at NRt&>LdJ%krBd(Q?87N}&$nJ)_DouVXVq*Wn4BfQ;xw
zShi5R2_2yykrOwjqfav*0u}mgA*sPy1n<Y#2 at D$kec%Zy`vBT;F9F(E=_e3*ZV^(Z
zuzhA955N%cVU=A09}XBU_G%BU8_ at e%3yCaw-Wy#%v#*X4wop0*@{F9T;AOaH)EX89
z$eybd+dw;>%FD*O3%gDJ0H&6bp32yBpeLddZFp3b_m<#cxZju-t}v_#pf(l3MoiMP
zTzHH&57r(=V@}T<!lQpB_?^Pfufch@=Pv0d-ky7<bZ^gng724r>!R{&_<$(gro9TN
zcxb+o6=s;Zx{zz3_M)?@%Cn?Njw;26-41P8c7E%R9cYyx9wyfM4{w$P)LIG9-H1Xa
z)IJLdt)D3~dlYwH at LhP2N7bCYQ@)+|C2HI99J*f@%LjxF)DzJ#ziyhcl_1TP=;@R_
zkq+WPowma7dx^KQlQ)uoD|S<*CzQYmV|K;L?X=z|yPtMLDKq>0zDH4*xIn5~Q4~Ou
zR4Rry1VEprB$%VzrA{rzxR;02{mL`#WJ5{a at D+}*(gaOpU^UIjm8(<dX=y7eYIY)@
zcgiWyeIpXM1`^D;QXZwf3^UC~Sda4H$bPI1 at e{ylIh5{Y_Y1Xe_fdzgeX^fCWeC0<
zW)w(Faj7Z%lKES3(jb6)-RdcUt+d(qA<AN%?<4BLIQw8=sGzH#-jowXxC|*b*}59j
zq!iv1U5%g{3A<<Zg={D;Pl3iWvk!sRr(j+ at IFM+(@8J6LAF}xx?jHp@)<IMErvnw!
zx<4r8%BM<?Y{@#JwcA=pL?0OErD3<5MxN;YptK}5$*~s_(*e1I&mt-fBff_9v51zg
zRH))l-QseWvtOQuiZo{WpW$6X`P6$RXI-q7TC;BC+|bTIVMj)Dk;WK~bUv;tkZ9gc
zVJ!bqlanbeP)S-9`fWC6=7sWZr~<u9it{#DeEZ~68PxX<^n at nUAeA;~kkX=D#88wA
zo|YHo-H9RuZN3&iUJxIf^Q3FIN0 at m2vaSrJY&Po;8KK0 at 7HJfJNaHUM+fAYQI^&sE
z`X!};YQ5PZU#j%_j;y;da`xDgb~8)F#@UHas$}!2>pxd^Zl*d?^Add+3Tdv%IjxTb
z_6lh-zq82`Lt{BLP)RfAus1s+=^a`Bm3JqNVhZsj%>y1>3bm}TJ=W!kMfcGG-{V-!
z9B;FQ&FavGTRBY$AQ*<sF+345 at meR&mq+Br&;)OQiU}pDM2{y>-ebSOB%BA+Lsw~h
zN|O|7i3o1f=CBdxa*N}LNgAz^j>s}!0?m38!73D*hU??%Xs)hpkOYN#d{$Te{@`2G
zjx at _lBlu06;QJcr8>qs3=nQ|8nZ1dBlX-Q2zG=%p@=agD-}Haxo9f~3*YI}~{ze|q
z&M>&^E1jsn*B9AymkYIM!>{Run;+oy=iV>bbKR0H>tM*5b+DkO_kJn9rWXPG0I)9~
zH%YvRt~~b%zI|~0+p_ETNF}*<Nmu6HCta2+OOtZfNEhZVkS1LJsWi5x_bw@^ruRNc
zsOgoZgqq$p7}s)vycja2K&B+f<b+HP$Yh61dMS{10_IJ at yrVJiNX!eIV44O<(~u|b
zmFFl$4rOmvNfHb=(9vZvt1xMEnmk1rnbjbcID{t~LQ};9a=J3tq0CR(fs^&cd%Hgw
zzwhGx-JhUcq5D(uUG6Y<1W=97+IM{u85GbK^zYYkm*)T9*KrHmhg!$ww-39HyQtl8
z9hcayuj9<^gV%9r*fQA_yOQ6dl=fBXi at W{y$i<yfn8e+M@}*tYkwBoX8;7!D{Pn$r
zzt7x6zgy_Hnto@|?;`kRA?S<Vz;oTWPI+coHyRq`%h}2k;nm%JU-hfm&pZhj?9q>R
z4=nC3Id5QbS0VnFi at T@)v&G#?Zt|%05P!AE_s%KA{nN<FPW&~?BXgd?8VI#N9c40Q
z9Z`Ik#2dhqd2p%W7p;y9DECtOb<yu~`rQn_^(l4#kF__CYw~y=$3qAZ&Y*&#pp6RN
zSP(@JQ4oTlARw1o5jnzr2~R|<AR4fQ6kF?styNoVJ?gFCg~*{GY8CMSJP<9lPZ||e
zs(6v#%sxo~Yu}&mA3tBt=3&p-+1c5dd3N{t at fY~bz01oT*attwF4ry~F&^Tx*f>j4
zyaF5gIv1AALN}$oSoM*;xDjh->_ua=7wr^aFV3{9mE>W9Qw=ZgE>JdSniX>umG~?{
zP2?Z1SD1%G;1^5}*xkjrptC1PNnDPDrD!D6UzVtUtcy14pU2;T;mPu&UAL3m5>k=8
zk9ZgUvg~RdNrWi0N5y5o0;3h;ANc)KWrc<Q8j?IDlVl at yPc#Y?!F;rPavUvrc~8)w
z0b8T+4|s4Q=N)lv#$UTy=?m#bEGZ>@1Jn2NQ&-5%<>eNWVmiF9X7FHvZ*nZerbGq|
zPQ2W45bH>q=jCZ=s;R)s>wpzmX2e6yat|4 at LKnoWkT7i_8LUFW#)X8X3z3aw%|>Vv
zRiXLph34}lG0ml{z#1?m%F;O_tPb;YWyjRd9NBJo);){QvaLpF!QdmYp9|Hw*s at hd
zD09c~^6=wKj)aj!reZR?I8yQ>b$L(T5cVK+(60X=G(%!<B=L9JN)k)(`*LTIjzH!W
z+8>iyv%K)@3pm(fM^NnU;9J&WL at j<yh|C#sYnDrMFOdDgEaWnzky8b)u+~uB)7ON{
zWhV?#-$;F}G{Y}5W;jXO3P}<|aoIXUWq-f!?r-0Z`zxQ?qqDKU{IAaPa(N2uL at y-w
zD3P=@RFAaG3~_L*&b?3y*~Unj0si_zIs7Gik2~~9sh+44d><MhzUV=ChaQA?=s|dw
z0ZX2NYDkGudK$A$OBD6 at P)4E$fy`&wb^{fqjAiT9&wSZe2IK?I=t0OP9)xPPvTTmJ
zY@~tGgOE);2#{kJ<{%yfCWos5<3T{if^~EStfR|d9i?C$O@(zdQBsI=N4#qxp;{hF
za6kMGHSH0nyv@*7T8I+8uo#6<K^1L?WX!|L3s?&Bo1aGn7Z`BPV3ZX!epKKdp36R^
zucikqz_NY%%$z|t!hSrjmb*n$=|*TG7DPc0H$t4sjc^Q|NmYEwS)7%OxMZm1qMX at z
zXumSEotJk7YkV8Y`)W5Hf2!=o$axgR)o5_h6|t2Jxl9-Bkvy!8_zg0;M^B#6BVMmN
zUP at S}+n-?f%CVS1!yP?AA5T(rDiLSFNe(&-c)26cjHY#Xxs#P1gBx75SqLkj$xiP6
zQ_g{Yy{hA#fJrc$bPm6bsg!Tydv{W#U!y at Rtri{Cq*W9n&Enaz(KvJKrMb6oQt(c&
zRPKo5uZa$h3zZ*jNvqru$&#}u0_D;Fjm>m|&D3sehJA#Mj>J(Gtj0vPL#sCi`J`>h
zS!|lYLy}j78OwD3haljSR;xACqfYK_SJx}WjZzMbGsKpmtlAlZl~cyV86r7L1o<4|
zJtH$gHbY142q_}Os5M1L$XFevBLuHe<>j`rm5z{sI!Z at K;s at dg;pL*vi_VY<#2NAi
z at 2(?yP)#0%O97<9N-sH$zN^c`5G1Lxz5glpQs2wnNN*ciY8sFHh^=$mv_2YYP1!U~
zPlDLcf3z~rmUYB*C$fe(TX422oh?My(PqY6HceY4lUx|wiKB&JYxnAP72|3t#Bo<K
zKOR#pBsgnG{W;<au7vz0GvR!M9M_-Tpt8Yud7FTSH1(%i`&-h4zWohkINlSJXAcol
zKO%E5Zo(gn2%jKUXwqXkx(MZcL=$t)ViPeU=mwdkL_n}aQA%oZ at q>vfHj)%NAa|!Y
zk{WR3!{2l0$Z;g4v&*H$_^a{;<@P0hAk^VtcKpcjh@=?tkT%NKGs`Iql{LN{Bic>(
z3dChvle%yi7m*}yDq(pddKJYe+IVF|qAEz65J!;=SSp~WQ-~-J=oI=(<rE?WLZ1(*
zETFejh at ppBo}y=l9!?=A;uIot1Ul))5H~=^CT^QON~W#JLzP|!iOe{MUcY6WL&Ud$
z`i>Dtm20G$!*UqTVVpx~uNIID=p0)1nmC6t$<#pIiij-YJ|Tn?cLE*pGmM($j>UxO
zMM{Quv_%QW!R(q)C_u?n92tH`1sD at rqZTNcGB=@jg&TTSXxXbmEv)dUMt6l;GCV;n
z{hAp*WNFzfNPbnt=jDEfU957UW-;xE`u7%$=RoY=$HLt$59-k}v&<<O1B8<72un4M
zKCTHwtZaSnn(E$n*F?FiYB|A at CnH9Q%6k4SGkts1 at pX?nMAm9L at Ny5a)C36er=PW{
zW at +vMC?OleR--LtZIMs=&$fp3Xp7$y-woXgSv{g_?sOa7cuv(l+;}QhR0{51Zal*2
zStA+7ByPM(632o<#5ezIvY<=0GRj*vuChceQhE4#PI8rp4>_-}_!!Y`h(bu#_MT2Y
zWMIb0ckrRo$%kuI4<{e8G~?vU!-|NXk6=#xe8l)GE>bU2cz-VO_3?7iBvX(DNlHTW
z_sPmxO7(?)i|-1_2u<iA?_rek`an_*S}-g|VO8ofD!o<QdoAo%Ef|4CO?0YOzuzFO
zGjfcqhH_o6Xh$33ZgjdTdO4OPUGJ2Sb0mFGyBxdAsa=kc{~snl(`!cdYKRP#$~iCh
zHyjhPynrgUnm8g6wO)=$6bx!dB>tgxMB*QOMp!V8NHikGlF=ATybO%cF_8R-W7h4B
z?AA)r8!5o=ixbOQ?Tz##BS2VzGy<9srqk)qRCO}mNMhD7-bhSmypjJr?O7l3?-5l(
zN)S_dBgy#p at J8CaR(T^!ntOO7F}m9uNsxH<w1+p6G=tvAB7!Wz;xc{tDe*=UZvlEE
z37+VUL^|}qmOq=G`EP7-H6z|ge4u~cjNV8RH(yF at i2`l4qMsMhhO570F&Yo(gT!AQ
zUY>Jj_h^SGdDhpl812XvGo>5%6J!Z9dcOCfhImotUX+;^W#mQadr>?uFJ9g|<Qijf
zC0DM6KO7Hnxv_*4bRyMcUr^+AyI<H at I(BzuOP(oIib7965u?YbJ)b_`qUZD5bN5|Y
z&jE%uyRxzYA|JT2k^zRaxUzyCy0YSpV?A*X)<CcurJ-E$yDm{by7^w0au*jmpYWFM
zyt~V|PJ at -3yw0hU>C(C`h5U}Rs-jb?aB3bpl%y}kk3Xw;tS!~ymAPwk#4Q at s)d1QH
zH3+frJxi+d3Su=V+W`7XABgi)n3ghk((5zZa3q3OIY%sPWr?#|S=2yUcn{rmtr9Nn
z*^QM?t3tJV%Rsq%>qjGnq6&HtK(D~IR67UpgadvlMbIe~7>5K)1n=oUXjgnqF(1Fh
zL%+PMs#IDdZII6*J2v0@<BmavsR{F`Wysi%K at LAJ;79*?7dHW3lU75or5w_0rq6}$
zHk=&(2Prfb1jA5U6>kz7i_|@12u)sANJ8&PLR*yk=q%`hyTp at 8r*z{`CB4DWyL(gJ
zAUZ;sZ>BmQ?S2jDeqDO8;tkN9IP#?JO1krSW!|<N at k0%&+K;|&+=K2mKzEB?+m;%b
zoz4=$JxNfDd?5Yj9d!#&?~q0rlzS8Vo;0Y1wuUZMk=>tt)Z|uZZFn_<6^p6}?Z?I4
zcs^W-D;KC{DE2bo*0`EslDrT6!6|e#fh34R1xV`36^C<J5{(|;b2A8mK_X7ilwdk)
zgv#mZRMX4n*;w(8 at p=B(%janY+5Rt=CpCcnr%A1TzxW;aOu5 at Jl<V|>dLi{N*rOrh
z=9XvvwTCVGL!8Y8t1g(vPuJtO+dQ};U2!&tC8~9iXe*2mQ>iwk>tIIdK!p5Qz8O at Y
z at PHZ6Ccda4@i359S>ZN0`4901vH8dqyYzA2qocrpw-IkgFr}lr(TV(>+4VRM_k4-B
zb}^%<9Ja<TW_xSv$6FYS^vR$Y(C4wl$J_s$UQ(uxp~jT4!w^wlW+NSQNW-oiec{T@
z%I at E&_V<Z~D0kcON{k>^?~ThY^2 at k)m{+3hN9)d0I#06j96~;Ba1+1L$hLCyF<!y9
zt9K0L at -|k^1<d_}<8{e)yisH_L2f(}V(dhUr12dVX;9|Uoc*e;ebHo}9@!=+TY&u(
zG<GR>681y}g_ILHV?tUg$L(=a{FYVk)_8eSa1}ZIul>unx_a(k_7^(`KA*wr5^2#Q
zQ0afdKk!|K&PjCUz?^2GfWYmATz`-eCz;RVJc<Td)#8 at cJvg+NbkP{4b9EJ}f#cHK
zka2h?%6p8P9<zV4m3{yjtDryxS3Q|fOqBE0I||JLf&tw^ADZ4-*oC6H3^fO*=JY_R
z4=p~0G}Fj7DHPCt at fra%jr)2b{=9+QP`V1gpTs-CnFqoczd9mag`d0Rm2k{tX!GS{
zb&x|G?PL|Hk}==1pv5PdI&CGP+=?tN7pn%G2oL+w{ezW5-nT%&jc4Zz-sgvYs1|gw
z=F3g#ZMRj!WLKLX`o3E5f#nJ{#%sW66b5z1YtHpY8U`y;3Bq*HD)QNR)VEXc1wy9=
zuSC#E>GDc^-c+=3^(&Mp`n31jgRVSM0xTrp7J8l&zZsa(hmeffMO#H?iE32I=tVmU
zxXcHNP9pX2&vawn2?1AXh##W at 8o9~`<IYO_>^T=g=ELzYIW>n0-a|$JAA{i)N(4e>
zJ5>8Ym8|rwqDPzJCpH0gcV7vNbAqMqAV~8=yCk8VHG(=6qms~@l&&PSv09)omh06B
z8p%GPP(`8Oo*!)sv?JfuxexaJN%T4735-R3Uki*`E;=lcmZRVv#mVte<iMgPZqXcD
zh1X1%qjx2Ta`U7{c~X3O9I&;-Ol+i}dGN$+<u$Cut-P{kY~QCtZVtF{DP63V1=T*O
z5#YB-UF4dQpbn|PB;I)b*a~Vgz!-qR0LEh4MYPK?sDrlw*!YQjNR8kLs^fA~ctAle
zH-HCXpJ+-#frX7vL&dD&m8bA?Mj_`2%T}zkqeg*Pu>=`Cz?poLCD0iiOd{(V0ZnQN
zga@>A<+I^|G%&-N1(qr`(U}DlfufbvXnNL7RS&Iis(Lu~B6Bzlcm#@~aVyt>2ckF&
zjB5n#=sU=(f#t)n2-d3C*N6L<<<>5PJnfhVRPw&7eDLF}oK-#@=(`pBJmKsm#<iwy
zR{I`4rOTvqK|MF7beKPk>G68Z3XjKbhSG;`FpK99di at QY3T7zM^Wjm-k}X!AJ9Tyd
zsf3)`&TM8 at i0DE=ogbbn6m=jwHAn<4P+O|hrvpDIRgRxA5{-o+)1a(Y2|LgQONv7&
zDEse%lco0mrJ{5(>Wm*f2ymKDKdVQ~K+};4(Pt%UQJ8LE4kx((pLIyvgXx=4em*JY
ziN#)t4$wc;W4{fN{yW5-Y>_r_tBYJ$+^H!-#^qved%98^XrV2w!zKEymegb;HZmWU
z>iGr0+_f6{-fYgl(jmFfoo$)J&&l`skR$xyneU?zWxuM(;pOfI?g0X^sg!tj0Mx(J
zQhhp0G>{$yIIVI(E0OYr#3LSHGvaqPak9VnQ`lDv-m~b}y6TTk<z&4dB<d%v0W%n~
zZyQPyfN?>-0O9Rvfu$<u$SZ*YnjZztpTik?isb at amSrsBfwnZfMx>?dV=ULy6 at a8N
z9$w>ALAg64Z&9P2SuXH14E|y>f3carI3VueFVHTAfKb(to53o1QbYMIE0G?=vE>{T
zx5~N8%f-9(?G%8AizKU+m-i!DnSu@?7lv0j<Fw}G;&;xRiI7<0RKd&3Bzb738s3^@
zvc-+P)JWXPSvpqSsm06t0{6a^3A*eQp1j;48hCqS-b~yHC1_Q2u~_kJSzr8cEIT9w
zNU^qqETQ_9)Rhntkp&UMD<R6MikJHh5?QlQ&`Lf4##K^{mzzPh(Xpv`U<+OzE;p;P
zTHDT?V~OTruVsxaHG;nr|1z7mCXOr+9Nt$U3Uuw0#*9_1^Kxh5hJ6zGj&ZdPuY{KJ
z at YRBhi&v5fTCH#;aPERYGzjwTa|e-4%daUVh(y#W3xke1V<$!CA at OX;^TjYV&Q-WU
z3CTZ=wBpL7$iD}&Am;I!Ur4LQ*7y~=JCokt at g^_oK!M=p;x;$UPNxSe?}MZOw!YYX
zE(RGK*;>Q0fIGpbE9c=#8}5JQz&#Yk6*A-UhdMcrR_a~K$@;*`=Zc0BVQ!A4K0a~-
zr8qfUelJ0iO$5m@^{iEil6Xy&B)~dWC}7A*6;lUtctSK$B{f>Fb}9a|9S{umbi5|M
zKKL0g46&D9?a>P&KqMR^P~Bv~ZxssW#FuVmm*NT-OBYdhTFU~GfrW?PWo-eSE{mKB
zssa7j<EmOecB^Fc<1|Uud(h<KU6=*9P|)HBd`7PzTT4yAuu-X6eUX<$_ncl+Q|T^H
zt|d0|b9M^HtQKmlW7a)tFfaznIiFUrD!`yn3cNyhF6oh|mA2v#)eI`1z(QrsB$h^O
zTR?&!rmc46QjwX{YX;YT^!LB3mZYKza(-q$EL7dSp_(1DEI<_#m{_gEH*xp at GU^iP
zOt*c8Ge%lXA9}6qjM@$}B@?3ss?o&d?<c^a%1#08J7%>~qp(xvO73CUMoInd at tLRD
zZ6h;hONa>UX&ai8Q+;Nj;%Sj%CFFn!qdRLrx<X{8&Q7-x4ce at SvPz5zntr_+sB;&L
z4m|hRq1H0(%4O$>@Ogq~6-V?iNc)lL1bKP6M1(OapKP6m>XhV+D{3B?UXDkEx@%=l
zNk&7~5NQ^Q{1LQCckVu)%sm-jaDbW0E74W>7%K$uaQ&4#doV}h(@6;t at qxS&-Ya(v
zhzafF<#iH`{iW<SjzAE%pzo9$A}vUf2Ua|Uvh~sWm#~r(wb9b&P(`#;s&SeDgncu#
zqcu}a<hJnKKW!|8W*JrxGf4g!??g~%p#l4q>tJJ4c_o82$oI!DVR5Ob6PgE{W-5_-
zPt8Q6pvm|0c?AOi=P5U+7wWd5;hB&r+R2kN>q(mRB+Yt~W<5!>o=mgvKi%y6Xem~4
zRWvjjOh#R3kCz9QsiOA?E(!x3%e-9N!uG_3cI8khzb)O<zWVD!Px}h&QE`?ri&4Wv
zlt$;Z9GD|n9b}u=$nKImnS-}@q at 0(#8A-0R2Bqk)2O}u&u4p*@`6cxrk1PAc!C85j
z@~FKf&FRjn0a=x2)qCchz$?)Qs`5%I++Mjm4CYYI1Y15Bx^}fp%=})((0C53tt!yW
z#m{us$_>e2lt=4dgu!rwV!V<%k5}$fpdeq>ae4oO{?JHnJ at pdo8!~j2U>SNuJ5`AK
z;92}1F()rC6%E#P43SBr8<Yf)OaDdX?J6sl+!qoM91?_TN^GOX3}Oxa%4e}cK`)m6
z3FF{x$$9O{!FPwax0yvvzyo7a&-Zp0Y10*5P;;!Y)Nj;Wsh{Os+5}hL^W0E8872+1
zG?uVWqDGDfqwz>skY9XPYzs6JrI-ZK${lm(VF9${M4@@ff?T7d^SIJf196vvnj&6d
zIZi=oJ5iS7mRc81Tq-p<Ch at j3ka!x1R~!R{XF#k5PJH)cT-uAJd<a85@&uwCB|)7M
zuOfiHRN0kBNy*=<GC7C?n1cQW(zynX{#=n3=~`8Q{}~rB?d1B(2Pw8Sn=5hSND>T!
z{Qc(551s!7exI^V#hw<@AhHcKzH1)XIFg`Nr*@G!eZPir!d2Gv^o?eLg at H$KU#{Rf
zO(O2P&*)<{XqM1h)EVhbXUJkDn}Z)PLv=a?)<KoOR0kHs0&g46s|pQ~3B8mlyii?O
z9*fs1DpqF9k%V?Q-QeYFLnW^&*u1<C9OgLqqyl#vOLlo(XTG3|S61#B&zVmR^b{FM
zD__^`(V+T^A9YEBjHRIllF)YfawZnEVFRY67uD(+(RR8^A-9)SUZ*8K#!eM!ni2ut
zcS7gk0no<zP#mV)5W1X{KpU36NFKRLVkC3$bB;tg28R3cZ&>_!5_h9f=wS>r8Yksx
zUVY`t at sw6k%L}};a^C1sX~^bOB20yxtS(l*zLZz!rU6zcj$F07k(M6iC*>7*8(Cpy
z0m(c)pDX3LFeNZ6o5^aWC$b`0M_|@LB&&%kD`eATvW?I~_YLh2-SU&_C?;wvOwE)Z
zqg;mDW_;+pT3&t<JUW34_$mtHbToWA3f;9iuUMesx8kPYtS&Y$*MyC`eJQ;hCq0o?
z4woa>%;CS2hPH$1q*)~iHI at iEoEmxJ>qKf97idf!#JpylYDgL6Yr44tjb-b|I^N;w
zbTQqm&{ZSHK~JPFu3$q_wWgk21oW3DDFnv2I at 81_q3DgZNKfUL76El=pbq0-_oPg6
zvJ`sMscKC_sSemu`QF-Eax16Mb}+;+VF!$pB3^;xbc5OULZUg4-wkgN5E%1Td!X<!
zmFh5kcfn4w<yr+AfIO(UU`fLOKqq;5b;M!K%SAWS>;io-r?uT=YtZ{s?!qgnmGeMf
zU)c{DPcxbg($yvBlpjn)7DX5I<CR?El}Yf=yjqbVEj+Js9Lblvf|AP7Y?Ql5VfnR%
zE?K$d2ij_Z5wJqHpr4eNA82IdhW4cDuMuuz8|8gpH*Sg7n2Rnz`psXYXP~l;4Q;=U
z7P0~8CG8*- at q?s{TM#+`#8Ydwdgsy8>hlCWpiTk<O2?1R&w?n>RK%TKygVsmR>{Q7
zuXKQ2Ef at tGD>TcYB8hi at 8+kSGgnqR6e=`M<a6sb|T=5%VizebQHKh7POZ3H6cz+Fy
zysVgTPKUUn%c&xLNTDY?TI{GNG8C6{AT?f3ww8EtjLB|DF&Mo{E at 2l6I{%YPz93u@
zq~elM(c-L5J?dz@;k;;xCUQzAatc1Yfd?z8PPM!C2tD~2<QyF)a~@%xE+lZ#V?tmi
zUYDr^@K-Sq`TFcYnA4OI=an`xkP%z18!crq*<esWq{+TRjh5gSueHdjS~P$Tt5i$Q
zm`c>tW%0yNOycD&=wj}vk}d2amvw!CFIkLZxz(VF2X}O-USXNj<0~0)-LJBzxGw4a
zDr*p}qs;cCLfxdS7^GnRf4<VPA+NNmiL&4%Fb1_943qK-rkY(9Dq$)=5kLpN?&|&W
zMX4?a(BCm%?!|Xq1L)N5wwE#WeCqwy*_U0)_gh=B+A6xxm+Z#j#`mWm`hzzAO3Uy0
z3>KT}6R!~q_JK>hCLP46Pw- at Oklc#G+!^r`W(4JRc#y&C>%%YVUy>MNj9Nc4=ygw+
zlqGfH;9~EL-y*krh1Y2SA9oT at lk}p*rvo%ayz>ZWe6~P&69r7uG{-`BU9K~F{xbU0
zdvalP?m0D6w`w%6T*0G)T=^hBdgpTJj8iqGTf at 44A7ab7(ndKOR at 7!{ze3azpww^h
znj3y_u%pOK#0d_TYctkIyaJu#oy_TeX?p;@NQ!f{x==`?rsMP8?sr;{uD+EEG*2|D
zK;2xt6lg0lD-^V=Z5Thgi#bf+>;58DNxZJel&(IfUMs7PDer=U8ezRtUg>e%2FV!D
zraSiceE*_s^>0y^f>$CifJt2tWRe at GF<3N&EL43#ca}<&UV7KzAoa<8*%Th<_!K(0
z$PyFGfZo#U?iFQ1Jd0k{J6=;=Aof!UP{6NuJX>90@~0A at z-J$|9IJz0i)PAb{zn=V
zqq{yz((EpF<v-G(*pc2zny~}Ai*5XmG$^+G<D`)8VvGJG4T`ySebi at TWtlObN`L}=
zd&hIs)O+*c6A4h at VefdZy1>OxB|w409~)8RZBRSCF&$smb>KBntCn%_LkOawJ%&5;
zgK~8TE;3Vd(MJYMk2`(1^w!?5Fs23&?mbVBODC5YNd@>O at iN7hXgF1gtmvbq>N#=f
zFdAQVSn(C6V!L{oFYZAKHK7c|Stjfibr4#vyi~vi1^ax6B=iX=mH9r3!etubGUjy?
zOv>|qFiRBiTt9kf9keMrj}Epz7$;q;nk3>Dd^5={(8(7lX38r;d$E>eb&9mbK4xq=
z>NsOTFVL9 at agZV2cs`&YvVv$EkTe1leD5ZoT%a>`8D%(i9*IIFR&FWrF>&-U5m{hP
zQGb$wGj*QGpkStkxP}c?L4{I*$(Fg&&Ja|fBtebz+ERrAU+ZtXNEYOKAk`&7G$>tQ
zLjtO9(mL at Y4#nk_Jr_T-kq at v!rl$M~UAIpi$SbSJ&=k}B!qi$Pkx9A}+R#2`Tqhqo
z%|C++N{5r6HEkw{e at DR+Q9p<l;R!B5OPXDVmXqk-(s7WRLySr%slZI()47K$a>ZB5
z#*{V)dWlabS}=lka$TH1c|*_`LLZ34<0P8Z{jL-H29%S|0+vF6l#&Tx^vUf~XV6c)
zha(yZ#zG+2pOm3vXnV%MG;ML#6IMna&?TlhirUj0GcZE2)Rx{0*261-nMMt37 at CxN
zs6~l=+7<Ht5X+`?aoI)-L<bWjB52h(Y!P~wfGZ_jlBXZ^7)bEC5wzylO^BQiXqj~^
zHAi83P`m2=pB=vGRhqw!TKFzdq!YXUDHwYiezfz4o;R}eFqA>!)BIkE%J+!q)>7tC
z)dRwg?s(s;B8^@ZHS|jK$HhLF at uU%({AMxOK<)TC;g!hDQShGXN3TBBh0C at coq0-m
z4ozC!eMKj6jlg2d%dKXqFZer}LKGV?zDCk+r_|HU<-n&L7-Xo4yMwAH*9SUg88015
z>vxyqeq2skBM(Kl&N360$O5_-96(<(B7QiM%~j=da2VUnE8*DT?V5hH at F8vn<dtYM
ze+EA!a>fHM*yH9YD2`JnRX?^9sy?H<LrXbi%7L=MH?ad~>SXu+=l#kH07I)BLGx~1
zm1Ruq-n*f)sen!QP}6n2gl#~u^f_AKqW<)VlPaI9<$m&_6KN6R7|PA)N9(E6xFk(3
zva59rHO?Ob+PkZx4>v=X{_6x}8799AGY6Dz at ta{8wbHA&VM5>Tu3NRXb>SS1*NxIQ
zcJ;4C2I9B6OSN(;LqNf=w8`(|>V}-`6v|C|h=EqMHcNh}T7%_PFaQjuK3 at 4cGo9X~
zy<l!Nt2AIKwF(nGP_RULdF7efuypImwN%#%0SYvJ>m^i0Hq0y12BKwYxIiMhYN04v
z=b#O$ucRa&)>73$-|tQVrOvK4*y#-rN#lfy5Q=?-NO*6rqmS{@KVfiu%<_4>jpZ&N
zL4Y8)O9(-3fW#A*cZoZPWmJIpN57lI=e_hb<fi()zT2;&TF1ID1B~(aXt%#^l!(kE
zJ_cm4O#A}q<P#_#JEe`%O7R<{S_M+A!Y1Dc*xv)};P-g_Ti#s?<eqWbMTq<YA55xs
zSacx4fVmsg8|VRaodc4tz_`FDzyF+ku8Txq=2V>q1X@&EX~I$y%S=zi11?&rw?Yfw
z at uMPzmtXm*yw-$|9szyAlmx<5BwUZ=svgO(T$$v($~HvZmo@^OeExhXNHB}M@@##q
zlzyv^aWLry0VDLssNilvVQK^Vsl31R>e!1St$6l4IerYTmbV2z;-=AknHyKRrBs`o
zV)uff99!Y39>8%uwQ^%r$g at nOP0ZG<JVo|&=`~WAOA3o?Q9B)5q1h>cTp}~TeT?*~
zwDMC~+F`upeZ&ey)@{(@@s<;DmtsYSSlGxCXIZdBT&X5l021h|0;cvWULI}`aq0rC
zggsAo246egb#r=|ts||H9l-bd at 3J}Zn(|gOMin4WOk0_ab2|Y^YcN0AX%BO<o`J3U
zjA|_K)5xu-u1kIH73fakrg5fRTDlLcUqDjSDnAWKPb39)sS0dm3T%}6w2%VaDVKO_
zM5t^sg<518_;!8LDDpxpo)w}H-HX>nEN)91C80O*7GpEWaSn}=Y!}8>d5N~l#6ov-
zu2T&N3PlFYit~W5MGXrL6?q>&`a at C|UM17RO6j#tk(Z0S5<o5nHo+z}NR#LjK?%fo
z#DKF})VKh*FUy|ZqkTJd`zt at ue%8O*mv(}Upnwsv4G2-zt!k7SfKfLIHr?ze5x~kn
zEt|ZkZ>1+?qt at 6szfgElZ7G>Yf5K9-k3&nzQW^vc10N!Kz4nP9%c$8WEvD|<FJHz&
zTS0jH<&A&cetCkWdao0IRJ{_!{Q9ZeFUbkX9=BiKUfP$Q)Lm!-6q3)z{b!!WPM1Zd
zxH)aE$~DehG$Ws`NaaF2M$c8<feCAuF0oed`b%^Ak0YuY|6gyyRP9324wVfghlINq
zsu!w-D&9DD6uKym{n4eMn?aE-Oyx^Nt-Ut?#i?7SqjxbcH>U>E`IOR!5KP~1!@C;Q
z)DKn!0T;FehtP}hZ2NhO-X4P%TKWfi3`DKLbP1)lM%Bp0&e3;e(UH9jweG}WzeA$O
ze<UjIK;r0+5)Fv;z|?B^=`>R5^R7>(kvi2I@$fR&cun{piyIeWH;{|S_k(f06L;fP
zsrKZ7QPJ5&>G&~kuzT#33$eQ!uMvQji-b6t=w~<Z4oI5pNRR|Q!Lsw?*(3*D4{6ei
z!!JRS_>BV89gzVn24maJ-}3TN3tP!`ddXW;3QuQnA5cI%%%~`G-8s3!6XY?B-vD~=
zA+=k4+acUfL$sb)+`NUYglpmgArW-uDOTvM<T$-d<4CLIY-u$JNxTb?K4zubDL~n8
z!Pb{)ue<?z<29+M9i1GIWiY7&@&nCOD6Fu>D};&jg?2oImmFw`^l6Jjs_i%8tDtOm
z$j>^xTe)NfG at G6f(BpM$y>gcZ*`MH!_b5QDU27NFn7s!GjxVNPH+pUXBVu@^B&Y*-
zLXM`-9#mCNUw}txyK?WgHsCs3)B)=UFZUHA5 at CpGtTeG#Q%%HuC&M4yfsS)ybBo%2
zXH2IYB#y9n-FdR3y?u5==l~{-c7We_T~uW3e<q4dJ~ICQ_S;pWe!X(Y3Ucd2dVg9+
zS=W;Ts1z}`gC7o{zyIUEJa<JO)Sf#-y5xWo1DMMn`|Au_RQ|dre{^;4=``5F_@=eV
z6fr>ZcB>fNVEV)(Y`GKFjCluoxGv>Bc<}_e0#dm#UD_#D5b2JTS2~Og{AkYuSOF9;
z|L^_jHF$YC&^;FE{@H^bZqwKM8G78?-_vcn;bXUHoibaoIvag-$N!7lG=QG|=)Xw&
zs;h at n^o#DABlrKOQ*=+WipXC*QWF{UQ?lou^uN%A!9|b3Me}Nbt0lWUgjbTu;gyv$
zM+vWYu6l<XwkSXNg$QZ}{Nnbk5%|kyQA6;T!xA~?cyV&1dWGJ$mIZxs9<9o`1iHO^
zWbfG3IhWXSIwzd7Qf}Ctk~1_XPXQ7^q(ioIhF0OoTd~SnEG=xh`-)v>g2`}af`|pP
zP0W<`{!P6&cx^{koflf=7kdRiOJLf8ZrSqg8pvv5-d(^<RX}Qp1#r7J8U1`5{h{|!
z5inp{oyfFm&9pj#*@yNSvpLO-zPL|a<%Mm`3r)QLRmnSs^qzg{cb0$j;1)yro9=`Q
z{|AHI2%v|we8eCOcMMhSd6)C(Y3d3;#T4uBshA=St76`VhXe;meT?;MDIesFEQ1rS
z#}<_E at jDtL^jZsasF8Z*)NpaNhPXy!%3o>PQ~t<cvmZ~Xfu|~Xs-Ds!(ih7#MAz+V
zr?gPIc>Z0H#(v!-t at vV5ShsB}Fj(3b^!RA8Ba6~?5m&IC1O{o(L#wVRtd_wnRamVc
zVGap%NtjQ<uSxhV2{(|ih=f~7xPyehkZ=zP_ml8236GQTBneANSWd$8B)mw%D<rHV
z;Y|`YlJFi0TS(YSLYjn6NZ3xo4ia{f at I47x6ENhEkV`@X5*m}xgoG5ynM%S$62_8n
zF$s-HXh1?P2{}y7Oe;2|MG|(9u$_cYNJx{gm4q!Myhp-D65b?X9SN_H at FEG%ldznG
zr6fE_!s8^gVW>);_mB{m*X|ib4Z*j8T22CERKF}i7e-~9=)$!34_(B}28#^O9M~!7
zuWtk2x%koRZgnx6<i`~lN$YZJslhpKhKV>HGvypGT|Ekna%)9TL5J7Q=SnN(x^roZ
za43LIf~t6a>>iw-t6+(=dnFOwlZfghpyadZ<VIo(FuP4-e*x7f7<awV8)gyl=~PV(
z5O?+$>EX at j!Stc;yYS-FJfdieT6nn!;Q>t37CMC}6S&Fz2b?%rEoZjc&44 at xcon(<
zeGzd7>oSBcyM>Obdxh>?l+Ad(I`1iIN(z=^BN&9a1z`4pWezJ8D4QYCB+;%Gx0qr|
z2)zR9Ds*=M73^jKYwAK#O#H$Sx`B-dI0|l3nE|BaU}ed>PE}Ma*%&Azmpf;1ow}$#
zpy-yel_=-p+lpX<Z~*<QGFGG`eMbtMbN_h;OEv+XrhZNhX6mjGDwtb-C<En}0%R?y
zgWN at vq90L*AlDB}F0cf0g6Zm;pr=pO29lp`Aa{+2g5iD7QL7GwXu4jFRum|q=<wKy
z5kKKy&^`KRyo|<7K~hQNSiwzaG+36J(rb3%mWyC|QWy?1jB2CYBVG^RPuFNx<DG?=
zpSFEQv|uEl$cLW0i_wI at 0643`bd>suo{F&!hSX+y0;y9a14k0CVGDf&Uv7x%=*x=`
zb7?i54PB`6$A2<%)1Rmj-%{bY;8JiAS*?GAawlWF27MVhSd#S~k!*3A#J6^1WkvjC
zJlKs@$T{rjKsifUuUrho)Fi)PdPFR?q4fTcJW|eKlFD;jW<lxs(TYSgI|Z#!A&q`^
z5BXl)t=K{`D<&Qeru|4ARUe;cx(H}g0Xnm9^*YXkst{O~c6VW}ZCudB+~qCe(<6RS
zuTaB_dfSk8<??7y3OXDYaYY018wm8wcx)2$c&i+oZUudKC6%!14CYXST?_i;)>D0X
zC3o-$3i>3W(M*M0KTjdo#J||mH?Fz$qRS3JJq*PfvMo;Sb0%80JEQBV^gMFWy=V|E
z-HGqE=_NZ={<xIQ#4#E4T43-7HSzmIy$iHu&L8zx?NNx{iF@{^>_i(wK5CD#XxJVN
zYRD^r0UH)+`<+MvEwb+KRByq*Zdbmj+u}l;@G9r|uN&v1`pNqE- at a>XRxH3$fM$TJ
z0Hpv00N(=yUt^PeGcK`NHkX*^F%UNhzzBd1 at Zu_)^$4I2z(JY+;AN->;0nM!02<&e
zfYB8;YY4yufEfUD0ipqx1FQx38Q?g;C4gptR{(u~XS4R*Vzaga?0`D5;Wrsz0l+MP
zi2%a at 3;^EVWV8MTxCw9`U?0G at 080U)00IG~0gM0`1i%I8yuoHY2DlAS2Cx>|SPeh{
zL;=hNm<nJ6PylTVfnQ?)7WNBhatGiFKncLF0N(<v07wMb1ofSUGHc<N0$2br2Y>6}
zZ-5+tuK|7mC<C|$@C4v3K;P?Z));`P0CNBq0;B?D1FQwu0Z;;P5ug>I6TqY%Xba#0
z5DbtAum<2efPDa!09ODS0saPf1;7D(`T|%0OaO4X!Hi*-axDFT{_X(w04HIL_W^7G
z$Ol*k5DTyXz#Cu+z$k!00D1tOQ08xd+W?mUP6PZ3 at Et$_KsG=!z{1~|w#GrfT>u8v
zvH!)Z9)&Kf8E0Hr<8HgK_AGW~jgNFyr|lRtgZ0b`j9AF?@jor=RX+HOHAVxpm$6yx
z``9eEG+{U;bdO6DMp7whnfy^!>7y~9Pk3r at oG`;T+9NJesElRuVg#h7#wEx6E1rpn
zOO8&22tjfbKTSx9(vs8pR#8x=GQL-&GR`YGj+)Rj!FK$F at wmZqAwZHaJv}@|7#5MT
zj1{HAG%S*|iuFcA%RT;N#R#dew3HMoEH*)ysr)-VRTvo;o){LLk{BgS3rh{BVp&X)
zu=LoJG%8X=vD^Z at +`Yp*eCGrPD4+f2`p%r|>Jz|H{_W=L<KsK0N3=3NAkcMgV2~f{
zqkP at b%6NA{!0bR at KX<RW0=Gclx${5D|L<gFx~rd`yKA6p at AiBIbAmq69*Jgo$3-km
zkE4Xvso{|cK)iGyq_PTDKxA56Duprd>^9fS5BlpVaCH~V4Pg0sy9RoD&0u&wXs!UC
zy^_<Z at I)A<xRhjmbU08uYCNVVr|{E7k+FP+rp(xjk`t0sGLrcOFC~AZV>_NRg;W4D
z2p%NA9}IyjD_WEsN$O2bq4?oTq4MyEL?M)k6(%OyO&A}Qn8 at -VP4cM}J~Lu`)rj#&
zTBVQVTl0l!X(?%Zn`KtZSOG#RBqc2Ym{ZNuqel14fVc=LugH{S at 03W=rB9{1rp1Vo
zgvnI;$BB^UE=(r`WQsEM821ql*tt(9!askJFfG$7nG&W&herxWdHD&`(&N%8oIt%=
zb4y`n7NLJ~r0`SKrKhK_0Ei<zmV(m=2pqTklX)={W)1N52n=zZD_H24NyVllD}UTl
z(u51GqQ+ZAEmRITc7pK1{6J6NIi9{g0%t26OWyziR>}$vPZSB6M^=O=E|H2$PAB;@
zy;G87!aUMal7ds>qWq{djDw6}9w})_;S`exNF3%Do|Y~Q6s6*bV_et_s3wZkOa6BA
zP_jqZ9AO6h?HdsfV;Tmm<VsO#0aO}{Gpjp3;Ny6&q*UOPFy_Z6B}zmZnk!09M(M at O
zgYo0X6WWOqqX-kGqzaSysVV8$8>E6YGunKsbUg+~kGNz)Vt9s{!UE!w0RQeb12WUY
z7!F#><nJC7)EmZ(;di%{m=YevXUNZo6qbrkeoz^OWs#NAQex7=ljejcVR>KR`jlvX
zk}wH4duVrGs8}ICHBD$8*K at 8aF=tr8P>=9Pc=E{J?M4H8^eCw+G|DP!G(#&YE=kB&
z4l~Qv#)j_`9wp?%^qQii$@tVv)&z(rD}Y-{YNj$lRYslWyHuE#kp at zU$<R#)<-DeZ
zI8foR453J8Wk-oX_ACX`Me!q}W1_;TaDGyFOk5;CS(Frk5+_|K<VQro+zHEwOU4cW
zDx+C}fN(m0Zc0*kGR{R&Dxa`zkM`1pF;EC<fMQW88T`n^@bq*h?|9)dAv4cKsUalQ
zjU>Xqz}UESen{N%@U$o>2&7Vx%qpD^gv?A4rST(El2S!b^LV~nVq9bbe~xe&MI=`W
z&@ovg<O8+ca55)itZ-QzMTU++lc}4?)_{~~DkB_b<R|86`bQH~Igr69M&K8)85Nx(
zg4H)JS=rOH%+dd|PFNdKkS|bnEBRt%I=>e$QJJZPE>v20a*U9bN-|kRfxti!{VyC)
zjwtyJ39hV)#hR0%;=5k8tMNr*sBj*y)LoPXmUj0qO}In^;vK|WMr<6cKpElbWL*De
zHL7}`d|Y=Gg#RfVS>;MO9tI;3q{&C!PvMKwg;Q8T$;xQugdYZ at F&8G|QlV8kvd%ER
zwTO?AR_WF%o=i-M!97B(j0hI%qwFl3i8lPX!Wh2$3_cNiQ}|zy!l72_&b{YIx)8=k
zn8{yCq@#OWdTL^LCXouNej&|~)omuI>zpmjBsGS9a=tO at 5#VH<nyDNyM at L7PIgo{v
zWaf7OFm|MJ&Z7M4r4PVk8t7eotNEW>C4G(vcuukML1{+hO*pClBWnCx-V#jar-=Ab
zDay_YlaS`I;Y(q;7ez<MMaIE$%ny%@1Z9w)78essrR(9i;K(I~N5;k_3$cJ|N|N|=
zCBs8|p0fq>!`y=A&K1lF3=0Yn%vHsC`?|S$hxxd=d3wzeu>Re at Xh0j3k^k%<@>Ta@
zp%%HyQsea)F at XH&LE`ukB8p-d>Z948p_=`OLmI3s$~u`Ur$E<0uE5gMy`;}$u&@L9
z3UXSxo^_A+!XApAwR#2uzKO!{bRkR{XddPd2<9~9Dw6uIp5W@}l^&EXOmj_kjY^73
zR>?;A?bgc(6Yyc$EJYn0mXj1ROrru&ixVspg07}g`;n5cvOtUBC{*L3kqqe+&Y5BS
zm=vgNt}qq26NnoHLK35q&QsK}FpmgNjgLT0qMMH(x3HV1{}s<n8_-XCFYBy-t;v8H
z|5K|dvp!p=k=ffV2b9Aqj2g-U-oSM$ogbczo#d-*l{g`a8!Sdd;{QuNJ#`)!>7MD4
zYCI!^Ju8L$Jz0P(co^49v_NckoN|IQ`To5;_lQ?5>B at F`F8`!I|9{L|^|*XAa}nPj
z;;6U&qnv-PX+m6lp*`h8MtCw=bQ${S^_1 at XXwXV~FV)j~%n4YgqvO(&Sggq2@(F|%
zQAt=tpBc{&Pm94t1k<fWSRTWWG*Ek0x(Of&nHZX-TSek;98p90sbIT8(@>pLkLU5q
zU;d~xv}#5(x}z_c8A+fydL)L&D0NFvei?NacpOXMyk%GgdJOtc3Q=giLBk_6nyd{f
z9rsiDBS~WSToxrSk4yb%b;j&0l^v+A01K-0&;QUpS#yQqQJFmiGnt#cG@}SurkMr1
zS9zcx%yGU~^hYDQG)|a-lOi$}^$?gsnV6Lf=tt95NP#SiNgoH=9kw?E7(k_!eL at 4v
z=+?bvfN~6reOeg9wPC2crKKdQL<lP-B{iL~f0K#b5-y at r@E0_RNR0SIS(XyDFqPCv
z5>nx)(2{x>)t&`H6kQccJxBd_ijoIZe=8Y<vALpPm55FueiVWO6-Oni^;Z<cXgdI5
z7%duwNjRv`jLabVtU3#FGgxPdiJ9s(Jt9-7Ag8k!rR87ic6b`h;H5&A`rjrSos4ne
zM$b<B>uwo%K1t`Bj`-9`yCP*fH%4unkbl?cmy`E0hsbWV<TurC9_wAAdv?W<Y#r;9
zf2|vQ?%OEi6N6vy5662hIrXyHwJbO5=AA+E*855R#Wy=@@@|NOixT^l?@5SS{wCtX
zlUiYc??MferbO+?NN3*Le+;>W7x~Ffi;UyETU{65N?Q{1^YvEEsWWeKW`D>ttWPM`
z+x2!&@ub9}qcd*Tp1JGtrgZ-wiH#FP3m*us8Q#7(-}(A)`)!&=Okp+q=lC~V>>plt
zuDIj)(BbEg1#j7S`WkQ7*^}E&vPW_AwZe`z8B|-!^pAYCBIWZw-m&w38ysHYY#m+l
zt?Rv|+Dq<gl=AC~#u(pfljI#)@>y|-_O8}*+wE?iq<^l}SvK(vi??=<?k2M$qlae<
z<5yZcCvi6?My=CW82PxvKOrF^Jkg at jMwmB;74hQfM(xBecWIbcbZ}RPoag;h-E>&!
zCp*#i?~`TH75S&$KNx&J!pr)O>B|*2z7~4ld^a!rsLxgZ;wcoXbmt?RGdEp#J($(7
zvC%o{{Pkbnb=<xYFN<?~-;_MjBR}Rq-O0sw(yck}e-75P3-i|7SG&US{?sK}e*0Y6
zBaMyq%hvD>YORW6LyqO8 at O5uSpW5CUe%0tr^P-~KrqRQS>JA^@(@-<O`PlqbhR4S{
zEj)YTK;r3gzWC(Rl4Iw-H+WQ{`F;Hn%5mnczYk^9|7bemE=Sz|UV2~s$UkzOqBcy6
zPcq!IAbz?2HKW!a6}sz39%Shq*`|}_zMj`!zmxk_><f({U6tB#p&=2kFUJUTme?l_
z{7X9_d8zr$u9mSkBny_`F}^bEeoV at PQ*Yk1m92FvJ2Bwa+QW<KowsjiufP7&`N~Fz
zxi230K94zbJtm~|*ZbPV(`VZs^}I9Iu+iIG at 6fARn)ZvAb6l(17C#7j5VNOkZL)1<
zS)9k$$HFfU5=WQrs86x7e-t}+V}`*M&6)aVPW5LG`+S5}P?^)|3#0VU9{p~?@v)rv
zV+(gH8Y*qC)fG#(HH{m5u=$I_l~F&8coF$z>&|$Q-ufi=UTxj2)9sDqc`-U^{X$q;
zt7o0tXtMm|ql06QeCc3bV(`P-`tJ<OZvA<p?cR!^5AN#UtiQ2l#-p3hJCEIuO%~tz
z?Dzg<tG*a<>ea)H!zmdvPnbPkz|BvM=P52ZX-6;A*XX0zCj2JuU_|E)MMAjXT4MiY
zkMwptt2b;26m#ZO9 at BIT9}%<rb^pb6J~QLI?q(!Un-zck;JpR6o1*m}xU at SpP7XL&
zy7$7iGtDcn9i1st6x&$5ID59B^7JK}^~VB!-Fe(n%f9aPdhLeaM~5^Ao{MQ3K7M)h
zv6HjH&$G>ALpO{~88f*||M*X94XVr^Xe}sgV_TE8*w%dD>InM at RRPvxITsS61RWV!
zYj!UPuG!@3%PnZS=yj&@dI8<`_Ht9l9|pd~`+c$x7O%AyZ at Xqu^p$_#%2~$4n*wg6
zb}akOwJkiT;ox|a$HkKyii<2Z<%+fD7n!d%+spdl?py1_-(R-h8ycGN*dj4e at nE{^
z&22^te*f7rcjF(X+rQ>}9NitBvA68khxGHm>R!^8;^r#h(F=F>9SjYXZ})H?btNGs
z?nCO*g9)~dZ41Wm$LZ?mCplR3eQEFU;HXY$^^(BUr#8Zbj at K{8oGGca{aLimLNalO
z&bRMMw+}d7m}`{Tyw}R1?dViR=F9XlPdYx!{DSM$G0pR|d^6kKvm<0*S^HL2TLj+K
zC@|gNa>l4oOb`FHvdQ6`;e{Oz-W_L#ezSX%-qlTo{d8GZ7JAw?2&55jlP?84%m at rU
zbJ{d<$Ijc+3-dP{ZCdA7)X>~#@0FVRw-2AJzx?)}g$)sDgCA!mG#3ZDY|r(bKd-M*
z|AE6zd3RDBEPrqv?z}tUgYfZ!$1ALKFNZ`q)Xmv>aGl1Z?FD8-uk5y4{GoKrp@)_S
zp4Cl1ZY}P+J at KgSn%^uvYyUP4s2lB({Vp~`_~=#U^v_RtTG~_Qx`QW;>C1Uh8Zi4<
zq37JxW?`mnTlV6vmyiECSNWl9#k#s%&O0t&d23%FKBKel`(=TpM;wK__slL5JL&B$
z8dm$ZSU3OjL1W+0wlJf_4v*ibHzj>-R7tt at UGS1U-1YB@)WnJ$*Nn!Q4fdP0A6u`z
zP|Vtun`>S=sK9l}7iSj47SS1QO-+dlhWJ{WhGp9uY_&EYdEbIHtuL$SRDi8`$F~s&
zrPl+Bzw3XY<9<j-+xZQ<E8A{uYI-Fr)j7DQ(Becwv+dR~ZDaDDW+q(P at 0ofd#XPig
z{1}gi5AB<ycj#PL85u|i4imog`Sayb_Aixt&12T(+Kkw-ee}tL7h2zJZ~idl%FEkd
zexR?FChX1mu;A#HX}a5wu5`%#?XacAxaX!ilO}kK8Iqo1t8 at Hes^>p-3I6tCkL4Lh
zLnG%jyuHkO{ILE?@s&4)xeYC|`ffU;KfJKyV(N|`C9Y?3Jwk_T>LfZ0IzQdiHqXdt
z)QlqE7|p$b+2wCDgH~OR at c!(A!>UCc!;600ZFKD3CewZ9tiV4P*!sTT5)skR7?62&
zfbT=E;Ow`48?76 at ylHVIqF+Iw_1rU?Ca$B;d{)=AL;K0g>_rDEg)@`ac_x0oBS1vk
z_nop=M|XI9pe1*#aC)EC%-x%Id6s??ZNBc8kz)$Zwv=9e at l#=)JfitS`S7;KO=lmL
zn!K;uZ8%+AFe3Zty6LAZ>ry*SFUL7~Jf5*U<HHxl3Br3F3$iav)(v>J)WP%hk%PJ?
z+qd`q$?nSZg0v5oc!4=<a(tym^37b$`DGsLdwX<s<dv7TkFF@><n{N}I_clf=o_c&
zyxV!34Ij1z8AQDqq#Lllk*jCDP0xAeLjBa8BP?dtwhkVW@$+Y+2QMD7U;lISAHtsw
zs(kR9*+0c`124Xq)K^jTy5FxOj`UgQoo;NhV5;eGn=X^(Yftv~X<a^`#ly#;Vca*4
zYrm^?I at PH^W$H8c>H4{Arp=vFK2_+)cAht2k$p<m*2(r&cP44<HJg}wF2wfOt&KK+
zC)}8DU!#xRw|^#B?*3-4RhMns$l5QgM|k^04F7WEj$y;LJm#Cev>ZC>__8sdHJ={q
zvH1PyC3 at 3FbyzI1u3CC*-1is%8GqpC$)A at OexLgKuX#%j&L5J##qM63h1L3uvA+IG
z)1D8Y1V7voExRYl_AQycvOm`_YiPu^6~9n9nd^tlSaxX#XZeRq)n6VSUz+%pdv;Ru
zTZa_cw{MdZ*Aypw at gyUjJ=ZpNlEce5?#e?E(@m2iqkkJ4?q4BWe6!(K;U8zCqE2fL
zi!O|7iQ!*v at Eq`ysn?v(0%m85*3Eh!+3b5+*J93hCl+|`x?ki|^!<5{L+#o#U(In5
zR9LQdcc1XPYx+E%%b3SrGX{Sxb at PjT9vH*63z}9c3emlNBKYg_&VV!frp$e0vcm6H
zc9}oz_-Mg{0R9(io<`3<^3^Z%Ca#l*YQ7u2aDgCYQPSrJ!g9<fJv_1Gb<4ABM;<lp
zO8;Zwv8j&}n!5g)5OeZREyLxl4zotI@%XLJg17zrB)nts)7D3y(>L;;$_ng%lb>A}
z_jmirejjS;ufN}xxVdYuc93G6 at u0Wn%NyU#IKS<U=-|RmPZ{gwN^#|j5ze{oeL_5*
zZ*bM^*g5v{KW{e{zW!a|`)YCMvZj#9ryGqV at 9#MNG3{>YKTDdW+mAh{ANtSzKW9(A
z*JzN?&=$S-c1dH~%~eOOZ*hK!sI$Ae<9dAR;~SxaEbEQC7F`&*Ve9$qF?XuHT+C{&
z1cy|<o4T>;=hZhVHqw2{_Z;%My#2yAFjBP_pZC$PrF7h{xFxOmeb9~aYu2aOS6wz8
zT9@%Y>D%!$#(p<&lx+RhX}@mzEih{Hi@%3$ypYqfp|hrR`?Ukv+cx!c*!olE+aE1c
zii`SbWo(&s#`cG->o32znItLH37h@(qKAfS<D}QVdGcdU!QIz0q^rF+lH%dj`T0 at b
zi_aO&TmAU(keusx?yU;EvOYKRSO2^zW&^%5OuMyaN8O{nn@{uiT;)XXdz1XjZ!4C`
z56<p8`cU8fDF>{o59~Kv^?di|a=Txb28n)goql51`O%#_Ub;>BY2SwxJAd3<_H$kO
z`QpC at Y9BA#?NTE7ef0^0yT2cGI>I{^!Sgy2B$OVG9oBFr%+vI=_N#!Cwp-VoI$qFR
zDsHhjduP#tb5AE0mErss#+06vUew+9>`-yvkj)X_OlWVOd at n4)>Xbvnk8PulUnub9
zW<|A}P0{#Dqv7Pa+q3RB#=o%3c+e7T964`$$UwihHoIt(?Z$7Hbnc&VvHZ&i2X@!Q
zxN*}&BT}yZp1ShDMpLss*6&XIGJRW}*;ga!YlCKdlm7F5e)ZwnnajD;4o%qc#l^K|
z#W7BLb*!(B#yR+XZV>il^P~3pd0%b*)A{;WYlO#JrrclP`^~lKqwF%)7`OiYN5;Il
zhK)~MV$b<^{?s|P{rvXLo?~o=E$|K5_UKiL*UI(We`>OJyWBD>f1fm`Zu_LMCx#bZ
zFx#|!$I3Ay2hRL(dIjH={oOYm#;<A5J<jt?ewa6X&+w9O2ix9w{gWjA+ENqVo=f{5
z>Z~lwU0kJIpSokwi2)Xm=bzl$G4+9|civx>*IRP3<BUGP{AGe?M%B+e!^<_XN76^v
zaKgQJoNW51D8>C-#f|sbe$mg&C*KM78F|WRl#5nLb=%=zcIF#c3_5&w)yjq#$92o<
z$98;{yk7J52&d?8zZd6E|9sukdFw|9E_Od|tJ7)lV2{s`)7PH=`sBfPU6PaIJz at s@
z5Xep)`Q(ge`;z9*y=!f9-rNoPOAxtr$Ml3ZHR2lsV(lx^cm<i4Cx^Bk^n19_MG-j1
zs)&A-o-aMvzio2=PM6i3A4g6rwfsyr)OA_VmO~9K(o2UI+BucXf4b=UpFxMS{bt at 8
z_(YL7)G0#O<EuM=$tGM}y>@Nt&23Ld9eLybZCG&Y>M6GKrigXF+5I@#?lW%18pEVt
zrxgG4(B*8!n)h0tkM<fcYtBE1UM?LrVN;)N1<T6D77cFN%%5G`v26T7&+>AO5B0mE
zUE?hlZF-&QIL3GLrtP6Go)7<1YhZ%U;_QZh)?SbJOJ33(Hg~OV<z!j$@+J>fZ0XR_
zy1#87<bM+!X%qhD_At)2`0vmDdRC{gLgSo$^6>)!x?I!m=cEl*jJ~?7&zdh!P!DT(
zzc(3wx2J6X7>_;!9fmL6l_QF-ZZy_dzPRh)wX`2<(=QEs{!7TrO=fL-9S?lCvZ#+h
z`<c(uNXhf7g(KYW(XO3Q{+XY at nK)#<>zyMb0?yt3UM{PCbL{J%1o{z1?E`h<ynib5
z|6%G~`;obmwtTnAuWS7HEvEuUyl<wr^<5JBbw>Y(6R(c9-}{?aK_9{6>6-ji;xkLv
zwfxcV)xs}6%$Io;O$&NHRC{%zd-$}Q`>sl>ukD?j?clodcIb$z<YSVhD{eo#bUx+w
z;Q4w-etOtC#N_3w%ssOk;x6|)YLxNY!k;QnIaX(D<aF)b`sWr?OMUwt_R0PhYj^jL
zUQ at Z`&a`pODdW6O-Pq8+ile*n at yR1!dKvB<GX99U=d^k^>-ov=s$Sid{kks3|FgY^
zuPnc6Ky3&=75!XWtC{#_b!;|0GYTKKMc4ndPSW{-sqr1($Rc-5+7`8Or_tT~37Pj!
z{?c+wHe_>sQ@(DA_t at +sE6)CP(pu-+bA#6nK4Bacf0+N`<*6l}xn-`+gYMkSO1j at F
z at 3>j)FTRmi(=V|oI4WUJ`G<%%%L|0HPfRox`bKIeHofIJM;3Ao|8Yv at U-XVMUUX}*
zYwOQ3OVXxlwqBpjxjD1mFz>@Iz2byP#e3e)I9il=_e|~W{iSbQCNw7gA$YJ*bnmv|
zwcoBg&mYlbv){j&HRWQ1f6lqO at cu)OcN7O7J3su|>5W at Xp54V8#Xh+$Oe>#TZP0Y|
zh`!A7^OO}|&5QN!QxQJ+w~}aU=cV^tzty<AM7yY-U)pxdc+8R`c at piC;?K69Yu!bk
zylJ;gr}k$a>&?VXx_j0>G%7M%8E<%oo8)Z0E-G>J<H&^?2?_ok7Kz~zc|x1U7ZI#E
ziP{^VnrrO(ay7T3;ve4m5aHpb>b at sre$uj&f4 at JKzarxP;0LC6ti8UzvEt>so8Cg7
zqv7+W6#HM at S<0erp0Rl}>%lHp=f;f<zg$0`bmMl%JGVGl{KVv at _XlF~J?<<%S?A8N
zPPfw>{O3MB at 38xZD{B3;mP{SVcHLK|Z){v^z+V#*TWrNo$vbu``ljyH at Yd~%n%@|W
zZmKOhTvs%#reV+V`Ny0Gj6ZI;>crWFPUWW)4?I07=6`?gSczuIBLnJ4{r7+0n(6ps
zea0cq-4Ukg_xg+fh}7 at 9A<8M&Fe!f8^7sXNT8*yhuh&)lsK+`uGD~OMk#^pC_pi7+
z>xXE(h>g>(?0Ov$5}G57xjZn at eo1nI_Fr8$&6i4UjBPQ#vwT6!{aIJuoO+P5wyf>V
zfD>hIix028b^G>C`lsvbvmF{M&-*@jG57kJnCHKihQv%S*1qp~)P8oO;n+Ke^vu2O
zHD|qY<t$(PU~ya3o|p$gw#jSTJmShSFNYudx-?om$SS3N$K2RQ_E!utHlER+sX2_@
z|5T9Hh|e#ab}Bo1R)5sk;|so9cr2b%*`U~6TzAcOT+=q`7tIF;{}5Gq_(|l85u*5=
zTiHqL^|tD2 at 0A<bPfyc{$<tzm^xJrD*6K$mmz#WfWb8qM5_5;|>ev48=dCis75Cas
z=-+)Xbj^+Wo6m1Pnh|^dSm$SV#L275`v3mw)QB%q4re?xJ25jOpS$3(f)}3}t?hKF
zkB0ujZ-m<voe>A)!V?rX`X^o!?9h9(tiiDU*&L2I&{6YP<?fgf;dP7qzxIlo=`$@k
z<L<%h at w1w4FSzIOKtFnNqf`6d(t`obXSQ9KdGy*!n_`9R?AaF<mrhp}1RPs$V|je%
zuczzmwSI5VULV*TGJ1GZ%(-LH%g3J&pLH at c)|@>iW$cFI`el==4A%a%K<k0IHM^}4
z=l?)+Th|Hp5v#{q2UJBRUf^V9bO?eM>|W#Rx~b-3QvvsS<r%NHZFIpO9Zi?_7yB9%
zAI$dICbnMtRguNDS(SbL1Db{#FY8FX5#Hwd-S~qIL6eIgn^+VTH)x4-H?1}=n*Rf9
zui0Viw|Dp2U;h4aMrf!aG120t>+}b|FEHA+G1v0vueY22vHPe8zie+t_;dQ<uk_2h
zzp9$WmUk{36^4c$+~@AGU7nI~Wz^Et4{?sR2?zOO7PRT>=#J}a;gIye!~SJ;sLs)+
zsewy65`;Eq#=Ly}vu$OG#A2Q3Tb&&f2W&5WXOvrb+G=le=G3EY4(Ts574fuZnd=4f
zta;62rnYDLYRMw9-79^qzq%V}QN6*mK%>y;jLWaXY4JISrb>q$7Yv7<>G0OuwELTW
zg`2J}yu#8IG}wA3zm1S)JPf#W`b=ox&K-%S`GwPOuiIp_xw)apujb0$K2ILLt^eok
z<@IS14GR-89}jj3EN-6fo4dWgQQvty)8PXx9a8T&4|n}R_#t8UipL8chg{aRnp5Wx
zrLpedPP2mTkL-3|89Ju)!{P&$4-XxmUiWN!U$ONX-J^-Mo|eDW1(^Q*F56 at DBVk7D
z=hHJ^*;{&^7_4hfar%y#G&`X5#az$AW0}I{)Wz9twtqc-+12%-^4zVubt|r1-r+1R
zuz&mgI-N5|N&}be*)4Q*5*N)LR<u`7xA<+X at xjaaVQrzl9vz8BNlnv#rz(xUez~Bp
z+uyFk*%gVY6^$9LIh*YpX0Ek<ti6j>e4*4lH+P9^!Jya$XTEUDpo<nHHZ_ at A`wlU%
z&kh at DZoPFH%i{j2B39oWV%q at e!H93aD-O7RzvDvx^KBg=ZI!z>ylUEX>!41l?1V+(
zo~^dc33+4M##~Cse0n3*bAM;3dCEhNG2^3~?H{hZptA$a|45&g!eQ*AFaI>(TltGk
z?z))K+jop;y>Rg4hvw~XZoj-T<r at 9r%bdLlrCW|J_;7T)?zG=>9afIBusl3T$MpG-
zF&-0iY%|h5Qy(7pPpJE6xrf+3GW2N1<+lxU>K{Jly}43+rKKUa at X)5dvq}nw>;JeT
z^<wTBSBd8EP>(?li8{8X)6b7GGRlkbEt-)XxK}eM^KH3z#N}1194>rTG`wTcF{9l-
z?lawV at 6SM%`Fmg61q~4qTdrmXG<rSs9pLviJNU~6>x~gtEN)sC7WA9A>CD{E&d}?$
zcQn;4%6|F(vG*QeO*CKM at X!PlkYGWu1x3LEA|N(Y5^88cKtM$#K!8Xfn1pJhND)!0
zfQ=$eiik>81nixrVn+}QcI*xHJG)6Jx$oQie?8ClzR&wz at 3-sm%x})jnRd?X>@wMX
zWlBGNh-sGO7hsn?XOV(l&s1s2A}Lk-b%bT~S!snKj}z+5^7E3$d!CG&srLR#UFXY|
z9XX9ZJKXL(eLHn at UDrw3!j``2agocGB^}-D-=bQ}xH9G|`Kig`)jxwm at 6&xM&uw1y
z at r|AI{4Zt;0&Aqpj^rq0^~No`s*|Gn6Z_K`5;?tYzS!0qexfGzD<!25-XC`5^h at y_
z8;?p*$41GGT3$1<b#a1hdQ`j2inm?T%HKSsZl~A{&(s(`!r64mgz35d6CYnPoqQl@
z(j?c3Gse#uQL6l!ws%~~!_!KP%1;XO-d`N`CF3{Y$n*kvj%Csq{Z*q!|I(T|wq|Fj
zV(4S51y&}V3lnDFT6`&a=b{l`YnK_l(O(+9b%cK7BHJaRHgr7~MW^}uHnHa2J)@^P
zu1I9AW5ZUhT{n+uU-x;TLlAkO>hmOVT2Vrw+S|FmrfaOeG}X<@a!R(^2IAA*Q&dzx
zjF}l+p+4()$K)A(t|sbBhp*RIKjGu-V}WNhKV952Xa0dh{(E*bu6KI&g)?bo8T-D@
z?x2)u69Si0XK at wZu>)@IHQv1Ke()BHD(WU#sj(ZI-Bd!txOu_kNhzV?*_St*zx<O~
zr&;d1*(ip!;CqMP_r&KumC?*~L9bSO&$Sh0d{{8g?eG>Uci%C~XtPQ at U7w#?PA|B1
z!^7ibx91eG<6fPN9cH(#5GiAF6fNu~Tbm~Zgit%&`>ZEOU$I<OootoyAm8Ndo($7r
zy@!nrtUnt?sGcHS(>5{~>BJ#Zp3XGf9zW6kowv;j9Z at gGfV!m))$P)DUmN3X7Zsnd
z**N;q^7_p;S3O$Tvzlo4aHZ$#Xs5iGBWqqJ={nE&A?Cs|UbXhn9N(^a<HI_?tgn7_
zr1?!ZC;!e<{bSD0e%<kTQsbrlI8;{R?R4`6ZypmzzB-WU at Y?kwt!K`o$IoBKUVmAl
z7x#kUeDde0n%=(FwhO<~nVG*=3 at iGsEI<3l?a=YxGS8B~I+q1}K+nzIpN`zpyLHL*
zcP5U6kJ6;nPgiE;e%Z0>`)BI!Yj=XzxZXZK-|=qWHo1FCyBBt>|5W$zSl0FjpH$lJ
z&$oDT%SYyRdy(hc8*gt{-PAZ5-r|-g-kRN{bN#9Rx;9niHRoJ@$)DYqs&@YF%*_|Z
z8Bk6;I$S-oOaI5I>k$VU2|c^6SRFoiIpJK^#Y-E`Um8JpchyLedo9}6u(|QNa+7GS
zM$+LlgGArHpyXL(&6MZrN@)d4_oaK-m+YGI;(cmo*tx8_3tuxgA8OvWU{u=v?;$xE
zl`PfWK at y|(&OKqi=R at nJ9oyz<#apbE+$r1TlF+=J5f`@q)pqiid)ve*5wYi|Rz~je
zh>dWPYY3ZE{xtml-PWxsSM8&g7r4hLj$0IclPz2Hs3rR#@pyW%r+8CIp5M2!m%$qk
z&rq;9#41%NJ#>Cr;k)p01v)S1<OQq<%&%TX%KkdzY|bLX54jtE79Xfj-(Pu_(^4@^
z=~<OQVSRPPwQWbQ-IF;s^2p*Nl#y=b+vzJ#+?wKje2m%J8oSTKYLoWOt?P)ZI60v+
zvSF3W-TDmO*O>p;)Opd1r7vz*s66~8!7bf!en+qNG at rHi_upDjd#*zL?ItN|9P4DK
z$1RZ<5%czjn)mB&Kj?fIq~qi+?_i_knAMZ7_5Hhi)@X9+*YyuKG+y+&P<SYSEoFGE
zi8X!Kp)LLzgmGh*<yHTP8P}4j+}xvffRjKjKTj-`3N^iyt$lduxSbbQ4`1w6c{B#K
z%&}R(NPo3fCTeBxqa{z$W9ZQ_tq&G;Rx~G2R|V;)FMIJ=KHAnPsN3N6ZP~BO8 at zjG
zea$gT&pNwmiq_0WnU2&g6xQbppQKW58We8w?Q0x2FRR6R$JFXsS!v_Wsm<Jyef+1X
zk|>F|QT}5>@<FqnFMCHjEtz_7nbX{fiB;QjzFZ$2*u1Xv^1+eyTO_-NopIgjlI`F2
zbb?~_$D*2*mSZ03x1N3$#n>Fyx#7wjxidj#K38N%9^f4DzCaVdFyl^5wxtm(<5S=D
z--&NLZ8kgHoj+c#R?VtP!azN*{O$P(GO at Yg_sW$QdfnPGt1UPw#c%S*#nZotMea%T
zOq#PSc1DWR>k1=Rdzr72b0=9Hd|2?V`S`Eohp$XDYqr_%RrGqL<}bRw*X+b4%Q+qQ
zzJ+PI8$Vm&vn+ex^~ej~PU(3ohQy6zKVCEcigTAunYI0IgU>w~)5^BTa>mYoJ61fx
z;A`Ddwf!p3C#&vW5lq_NdF!x_bNS`hE2|c-c+%=(YO}fg_ba8FZi`izcVkSXFKR!3
z=D%}ig!=2u8;Q%m9f^4Cuy&Et+(m^6(vg#O{GQ52pOUIvRN#Bkpes+JFZz at sWyYMt
zpRCQbrv!e8%Os>_%&HG2rr((u{PB`Sqgk0|+fR|kbtKRHFKI3ls52LatjS7Ce=_ym
zdn+F$iBGpSyFPPk-MO}->g8$aowlKs&*yt1lRGVSDs{0B=f2Q#cSxAh{*AV0U8cC?
z$%2y&BByR2s_ at erBPDG&k!`&vziGzr>J8C^kr%qS$?|uu%`B}yxIl?u5|$r0_4aws
z0M}3vd3JNzugjcEdmZ!MO?f*m&GhG?g+;~$m#cwRZ-zC!+va}XX!;J1ucV%Db3&Gr
zQYPuzO+Rw?<(~G2W8cm{A7Pvmuhc8&mSf3~)Ze?jeyf_^y|m=r^E5So*-W>u-JRLf
z(HFmFy at GDn*q|ak%_c$|r9^DmQ{xkn;l%FFq`=kApGQ%iul)IL#nKFB#4wc`OKC>u
zw>K4Uo?q15zJ;{F at 67Zy8w0l;JA3&}`^t&!tgf6R!#|9k^w{EHNcgBLj9Vo^az|@V
z=QxXOKEH5Z(XTMoF}t6@?f;0K6PEi$dyXw!dUyTl*{qJ)sn=^M(&FL0M{1t#%9W*T
z7=J`_l409YvmbsdHJ;!5d}g^<Qo+^oqRCvDrclpXT55;Huy=x8222K*&ZaTvFzJ3?
zTyLT~%Y%*tGHkOhzLy=3MSqRwvRFh<8k^`wbNAuzDR9L;NGoLnB#%Z*agc)KWC;5M
zX(1%th~y=QL)aCF2sj(zZ)lbZ12O>I3G!hOb^;m)I1!DKTnyo0pap<i5J8dvVQ(OH
zz~#U}fmAGz8Q@;XFAiaQpwVcIR5#?G1z|3bF32xJepv{60I32#2pmhl703v159G)6
zIRi}qoPkD5+Cg|DkUrqs&>lqyGl6CUt^qCs6c1zx_$%ZWgRm`-0+gQ%<xhn$EVrbz
zL4Fp>9|2)Epeca!fQte}0FePdhWyZhQmcTJ0jEOwh7b+~S_Jq8ls^i>>wsngt^khp
zXB?0@;E#}B0>TbJW03(gWt5~2gad)}Kz<d<9|>Vkpy_~1fn)t04P*lN737zOunW*6
z!26(lcL;9=S_=3+ls_KAen6Ul>wwDv?F6z0+{YjP<v;{ze<XkW*8|N3`Dy<6(|}X}
z9{?@_6b?iJ{D?pPD}j^%C-cWY1ZW}PHvafCfMx(b3LLBNHXsV%_x$l+0W=25ONol`
z#y<dP9>_2A#~-X&Y8v2T;8=e}0T}~+!5{xMKobG);gA0&pe2Cs^2gs7NCWV3;FvzR
zotCl!{GC7kSe{YP{xkgX=K#StNT27AKh~!ZFI at l}OCJdY<1GD at KmMzMpr57F_~X9;
z2>L;~oj?BIZBo!K=_=ruzU at F3fIsuc-w{Z0(D(-d%?EiCfBe0G)Bu+O$I6QVG6npG
zKmKchVBDlL`QyI at Xc^!R{`j+i<^Zk-j_FGPq5}T)FaCc@$p3TDQ2*}|^8X|u|8EuY
z|08Iq|G)be|Gy;U|3zr1|34S<|J_3Ve at DpwYtc~u|MoBbe@@8%^U+ZMe<I}nyM+Az
zrjY+vqM`o(>0kW+nvnk=LPP!kwUGbs7xMoHLjHde4fX$@|Kk5=g#14j4fX$SA^%Si
z^8f2X{(lS&_5Tn5;{R8K{J#VZ_5YVb{=Zkq|L+O;{|PkI|9||8|6dUD|3Wm>|DOr@
zf4Y$W-xBiwYBbdUzx<2;Hw*dyVKmhL-wOGEmXQBH6!QNDG}QlpBY6oCq$nXGGD?Dg
z#!HCE6C@;%l7xu(7zq-ZC?R4XFCm7;Nr;G!mQX<xBt(b`64FRnLPTn;L^v{ls>@4=
zLjG{buY~0vN05-j@(&v$LBsO9%1el2`Nc*{%*FC+DM-j-`DMmRoZ{t21cZH0NeW3K
zX*3KCM-pfR5=Syf49Ox<B!@)MNPI2h&SGO*wTXh2k2f7##0<paKVjm>{&nrc!S<{U
ziXXa;;z<QEZ23gg&>;FUI2>&ME#1#UgXO8=#$*lE?Z)V at 4IAG<KS+k_7_1!IKMKn)
zUu;t?KNG(FoWC^Vg!sC#n4JFn94-s{0S#WJK+lk}aRKj#L*8^6_8U=mwmVj3UmDll
zyZ^VMe3>Vc=G9-1fj$%D6!axeH~)uQ9Bf~#Ko7PtmQCm2KN9k#bG=!-?e>T$_~9}B
zqhfqJJH8Q}ICtT~c^cYUbFrVav3-an4<;0ZU{u9ngonXsOTl;#hq0E0 at g<;9ND+-k
zlhIT(3+bT6$Pn2fcf>)FC>NbU-AGh~SDw7kN#LIf{Bsol9L+z+ at K1R}LD<^~M8Kwa
zb-?UR7IG>f6*L!-5DkT+Q%FQ4Ttq}vL`+0nWSEGQh>VE5$T$(Ah?a<fi0k0;1!W4#
z!pay6ry`u=;8cQB8P4%=PJnYFoCa`Weedf}<1*ZsbYlN*WNg2&rzdm}o&U2wY)|vR
z4uAfReB8qMDL7m<o#s1`9@{i7Ec1gg@}tu+C%}K`Ndp~W>&b`<G<qHxc>{L>vIJTH
zqz)v{mxXzDn)F(<5+dKA>`Ss2DTy=f8wcY?`$GG{xRcsKe6ffjf8e$hnx&<+Kg at 1I
zOg0(t3m!J;UF#8UGhRMR3KgR{yR3=CS1Ne=2mJI0)TZFLgNKhN$Qn;O)I!KOD24RK
z6T}in^M0 at 2$TI_zBq5wEgj0lYsu0c=!Z|{?MF_VF;Wi<BT?lsx;YUKaTL?cE!cT<o
zQz6_Vgr5uH7ee@@5Pl_u-w5HiLbz85zZ1gmh42R<{80#h62hN_ at E0NcRS16*!rz7P
z4<XzqgntQPG)BlCCO2(YksdF9<TrXCB6HO&EVDZ{cwRH1i~;tkso<9V*|qe}$QHux
zE2xp$Zj^q&;j{E%l8jmGm$6r6$C`}EvwG~J?b~e<T_<TT`?{sG*JEvl{0&j-`YCrM
z-&daNYHj*HT)n52kQ7s?{qDKrn$vT1XHMVHLWnN{f6{2^38lB0stv;aao(Foorm)F
zV&$#qLS&}QwsU~@@bK7OI`XONZG^;yfOo!JO6)cz7QJsGl+k(h4WC@;?tKx`@8hM{
zbs6z)@q<Eg at uoS}=Pb(idM4*u6WCb>RZ~7MIXU*6PCVhFnQCn-A>$^buZp>w-Pu>C
z_^O4lLmuigQcWrBmx3=Lq?xb_mg$YF%SOm=={29*)<UQygIr9ZYkHa0YRMY#-!YK?
zjuQK#OwSh6(;#2OlV?}Ut$%IYLP$18#KRX#tvXvA5ob{UHljqS!MHPjvfN;df;NvD
zjGs(<NAKDoZ+3it+xm-!v%7WAp4QqXwZk%FP0^QeTIEHFM_+Dlv27t#e}wwn8um)H
z;O)-mt*ebvtGPd#G;@?^b<Ui)Lhq_c)gtwkv11lrrWI?9_E|p4v|`_n441DnGB$Rc
zq0G46LMY)t{xeI%jy#%m+rzPiP~?Z{lQ>|o`2COG?(y<lV_|+<>hLv82xrTlxa at eh
znXsD+?Y))mW?(*F|MBr=Lc=DIGfdu~vWDgY#Q}SwshvPU>sko;^DzA_Zsl4zK|fOg
z8}V>9NyIdIc^lzG6>?4AU}CA_yDKR$xs{Nt4EW*d8}35+J%sQ(y+*O<oEAcaIh4QJ
zFp^Z@;I+P^g%CNOhv`D;Q|`@U^r&35tgp at Lz7RSi)ONvwuh;up2?e#h^29gRy>yw|
zMyQ&@!v^(fmd;7g-s_;xyl1|_lce(Q$Yw&}C|>(fjCjE0w_qQ!kiMhz8+w`e(LkZK
zA9rfn*oJ|<Z7YHDPp4}<vR6CGH6D<YTu*ab{aXputH7QlJboqSGtVy5ZzI${Md+)<
zWyhbo{@KM$drIlD^aJd%-{Qn=M0>~+Kt3DNA6$|nN`0p~VFO&hU at 81noYrg;Q;?#(
ztCf(-;!OZr;{HEo55i%LkV)tEwGs|4gZf<BPpi>QP>eXxMu<L#`P+-#w<{mdP8fu<
zFI;VoIM7N+j6kkZ5-Ab3&mH~8pKK*;Q^MMFQ7``U`;_q$uM=|R0Ed*c7OZnuU$U!>
zP}z?8%eWS;chjohnkviZzY}$h&wN>$GqGm$JdwFpX?K_nm%NmB%Wc25S?|<d^HhTM
zNl(tzGh3<x*DTq4!l<fj+Fh!9oR^M;;@;l|ZG;1xF?*F-ud;oI_KcuDi(Qt9v$Xd+
zl<Nw~!|c`GM|?>lpG<wHm$H1(E6w at 24*Qzg2w8H_-{+%0KUOL&E88YzTw8r2I<)@L
z>v=-?6DKezikp??izZ<Hc&(^wguh(X{#HUx6J|eaMy;jS{H_geqSox&<5TE6>RFtc
zqse$u8zFKJue~Qe*l1N|G!srlfW4e-8gWbrR|sMG9bF>Zt=b4tYw-T{LX}+<J(KH%
z^e>>#xaHWIls&PN(iJRfWmF$t>Uz?1VC1`BF7t+2r-biSz4hg4>~ymWcDqY-tV;9d
zDt?+cKNjUO?)-iR{=Aqszo|Y7U%2wqb;8a?pwD~$&1ph-x)4?q!X?LEPLSWYhmxn5
ze0l8IE0O9#^6oqpllESYY1}@OXB!mF4024mZ`ys)rc{U-bHn6RGxEB&icKA}&oJ*|
zNs_$nr|98y%?-qz=G5{`divoDbIO$@KBwBKt5S+rwYqGL4qKRYvav?>wX<>L)0+!r
zs6A&D-xk%si*ThTZ>8?2c3%8EIHqNR<EKQE%!cFk{_Adk**Bu3=GEy?YSNCm6!cVm
zxIy2c??KfJi{kJgF_+$_J)IKmF@$r?gl*XMM}jm%rNLQlZ7a+VBE0#bTxk9a`>kta
z`F7xX(>Th`*Db!4aP%5BAH)-gjr(5LN`e1Q<y{Y6?No~|S=vg7FXxT_%fruFHDSJq
z55{rD$Kx(zm-~W!9OB763iP=zWg-7GUjB;BrwZsAZJ7V!a;}s8LbvrUCe at KMb7H7(
z!=q1HjFp|(X|-qZ9d?@O=^BOMS<aceS_rv$&>pGjdh!QTQWwDdQfC6~VLtjK?^$aw
zrLvW9tOBs#&u6K7)~#GB(n{E&%d>~aAD4E$hwEF$SYG)$q94K at R<{vy7xU)3o)cs4
zTJ5?{E&n*VvUY@`#dk|L*Py&3rg1wLnJZ<CQ)4twS{m<=X0?B%i4lqWsD1JDC)Dty
zQzqqk`(NL<x9P>%HzV2(I!jQ>7B at _rl)cw(V`Ss-uP0_2N6Ch??DX7N$PLc2lCc)2
zq-`)LZ<ASS at zT67qN2<h*0C+=Z*+a#HLUvr549^97r4(q=`buj>g#q-L+evVDe0HE
zL0fNatzJM<8F~FkqWOs;wJVM17VI(p at ylY9?l6P=`cj#4hxN&Zd-kXs)jJyGjduzS
zy!GsxiGf}9m8Ih>>#aSmSys1~&blMRO4n>Pj{RNU8eP-zpgMlaPeNI at d9>x&4 at tvo
zel5D%P%-<SCAoaYmgjq7&XZ0MH`T8Dm~MGo-bGK!P|7}N&6dJhnrDm(4wMv#NSH4_
zW4GGPtmKsS at yR)FNhW<t;f*$BZ7<^tqgTX9YwBi%GQ}ddHL6({BuL3?$lw3TZP|1E
zXTsFOCWk7RQD$O{3$!mmiif=8tYS`TRj>T6^T4j5<lL4n71A~o?Z!HNiL>J*H at UU>
z4C$E5k!33ioj&%YXct&}-ZRS%c^Hr>A^m0Ei~NXdcg~rn&FES)XT7zb^^Juhzt+8{
zL?<peeEI3`8YM?Uo at V<*O7X^L7Tdeay<~NZ&yIZWZg%+X*Uj&r9-kJmro`>k&ofp<
z4<uzLQr4tDn at _GSbu=|hT3#N{)NcK9Sk>(H<NF7V!yom9(53Wr??qKD{YL+6nZ2m{
zEuEdUBueeryW^~8>i!iQc5WHtpOlwF%nwuiOg%YnEXVNN;jum8s`Kh~mXnW1xk_Gs
z-MoKI;K*6e8`qN(cS;oyYN($V$KAiPwOZ4#Fnkq3t8k at zko76$XQFGYtBo{|lr7Jw
zE}v`L{_d!wSzgn at XXjsRQWO8U{e$7e_v91_uZ|JweNz*nm at ZS~Tut}<dZD9OwVtCD
z{zzw%b)ZS)s*Husi_e^RYp`nLkL*5+81u&WoweV0s#>LYoWHZwsv+s4 at p59VVo*w$
zZ|^z_qiv44LCZvma)meP?y at 7F7{`2;&G%VH-Bji7lGt#{nsRg>y;}Qvo3g~~cVB$7
z4wx2~&A6_3zSr|n*)3<KDPHC^Ync1G99*wBYMrcezC6XGLZi;%-tZ at 5{oh%$HS*q4
zv!++cZDk#&Zn9p`a^`rtX~O6$2WA{gu3UffwuO at Kb+h_(@)V+!oZ-a18RIH<uQuK~
zp=2ZX at q<1KasP{%AE%Rw&Q#8v{E^TT{o`eoBtxIH=L_|8N$<vUmET8eJEmulPtcN!
zI*N^cFB*qbqi8=U2X31$oas6%*R^EDlf`2ct+K at Pf9>uY^J=b4Yuvq=Y2<@n9{Ve3
zUt+t=M>8#_bWmzqjby*PU2?zW(eYCc<#i3~AA}wlF(q`re2HzQ?AM2s(i%gpPu{^R
z*lli-!_Spl<VG+yU1RSJyKpMGV at o81ntLMp>0F}u)MJdxW8P?;qLg;|xSU}huJk@{
zb9B?EF6#pQ8FJ5BizgdMB%TeK8eqBA!sF at puj_qM4`oJ0X&y2wRF-C3aVWT!*xbg@
z>8qyhk$G9(+gG7~An at VCnHSxy64yBxl^S!RN<G;RxyfE;J60 at ir**_=>~50CRr at rH
zvR!#|;P;I7$LsIVI_g|RskueWh;1&;;)#!}<}XvA8z*@zJe%xdvSF{K$C8 at KTGYM9
zpZ%w<4QUi#G;!03jxnTN&2whlok6^-(wk=+u%XB-%f}+-fP7toy;-osmGiSq!WpsM
zHl;NYntE#r`)sPIrLzy#7tb|HvG-fJ;any%tRJo#@v=;Ly56*gj)Pq*EvpWYjH6B0
zy`Ys*C2m{An1)qJixSUPqu<)jmiteX8&+0mu%B2L=PA!VcX>(Wee2{~KALf-Qgz44
zy~@ZS8d*gfd8Vg~IS_90So_W8xyQ}JsrfJ4e)_s}-mGmXI&W!k^k|uH)nSVF#*s}Q
zL?~Cyqs?yKun>Qnu*P0A_jiYdNl6dwqQ{Puy{+ at blrGn(kO~(1nre(qpIO|fH(S9e
z#-gFcvgP5Cy$d%Tk=@X?FU34v$K>iZQO%9ROB7VrUHnDO^_G3^XDXtZRhC$^I6KTb
zwQje<EgO>foa9LqLx+#%g)gl`N9_pb9=OL+*7)dTSeQ|5#a%i5oKKvsZFlVh^D0 at V
zc#Y#*HJ;}`c7FMIziCa at C>haM+KT)6OUK7lMw?c=Jvs4=c3IhouXoLwpITFEkR0Q-
zb@$eiT?;e!O&&$9Q8PPg!CcQ}O{=W?9P-ApdfI*6$D%(?GJ?Y#sqt<`C*0SIPYpXF
zU8zoWW_;ahz0>vG#M+I{7bYopY?-?~pORAJ(`EUcVz{TodaaH(jk<0B+-8&X{gmmq
zD4SIDC8)cLf1bW%YU1D|$2dlNzL;DUv%s(6w)VoS586|`%>zie+>=h`%SQxDdK9e<
z+N^I at n!=bU5hdHR_x#9fkJdjiNlRRPEz7a^m~_IOv#PXC>TYs`$5<(iBJ^|qv77tn
znH1c(tMgNSR%qJlIa|2D?~)GRo)Db>q3pnuwWdivPRFfx1hjH>Yp#8M<v*+A%ia|8
zlS@>W1&7~AzETnMw&{J8X|$7N!TF^N&L<pjTv4zlk{T<0`cfRB;Jc)bUdt8rVWh07
zGaR~~bUvGrb~qtS>9<MP)cs8*`<8aUnWmOu%E~mYFb$veeUoNFOZm2{>wzaMW4<`8
zk(254cO6FUkpG at wk(WAbJMrjL$q%XXWsEC}4RdFl;cA`RG4p)=hS^3JhnpvTZ~FXc
z-_IQJ&O7Zf*Jc^*cXS#4lFDL_U&w90V=HMLY3+3>TK>cSh@~oFKkKNLv5F_uTot^E
zW~pACwsFsLYFKCJ4E102<n99_Myxe(Ar-v2+&+yMY-bc_70H!uw~qFEFLryx0{Jsh
zb-t2o*p`JsTV4fA&K_2)`_>}5Fu>q=tjw(a+lmrChKJwUHubS-k*jOn>DMVEwpLzo
ztax5#nnOJMP-kApqwWvqUz4^<lMa1gPROqAFdcC_;&$<~*Och$r(Iu0U;7nt`fYZn
ztDH#+_eJb-{l2i?FIU80uI{p`OH7P3J{$JA*kkPp@!m at 7omSHWlst7U({-3%jvc;b
zoS(Id@?64j^Qn_!Q{E{@7{qI-FU%q>u$<yE_sW-PHRL#Hd)cXZ`g6RUxOdiG4m3}?
z_9<t^kMcmI*u#F+?P}7Xs=s)HN9-_-GhbAS-|RI!v3!0ltG&o@`?nIqnr#}UwM|d=
zM$G6{YIWD&cF|bTs$OZz;%DQ#>C}!ox+GPN8d<2^m-lefgYEa8R~*+}Xjyr6Y|hEq
zqzTKdoYMlgJ6eZTJ6LJvRl0EV^v92$Zb7OUrQCFR>5|*V&J873ovTexSjSw6InnCY
zHnuy#R&Kb-zDZ{9IXC=s at 7iT2GiIn5G^j1N?9()7i1BU<`=1vziwwo0Lh^}1c$^Sc
z62ip4V7Vb!MM#c>%9j(8iweo73t at So^fE$nX(2pPD1D<()_WD3CPMKTgcu?-mWc?Z
z*ILApnJ}}7aOerFuZ at D1$Oz?^6~ZHgv5>yu!dNK1n2=mT2rCGsA1Nfa5yI1i at N(h)
z7Vb|WJXWYYSD#Ne7q3*7uf(2TX*7hk%%!eW+R{Qel8Qf1Z>Y)=&DYg$CG6V`_Zzht
z#mM2vv at s$rga%pYznX__N>SNP7ggs`Dk7HMo~pBms=IEezCIz`!WfFP&q~RU3T!6q
zd54IH{n#Iu2-SC~5H=9PMnZVCaQk~N?{*<I6Jke0eJdHCvYl=fDVj7BcJ%@FmaRy%
z@|9cQ)l5i};^EL6AGAz8S_wP#vGy+TE&Dv8E;zQ85cwQ?UUL41?nR{?A%pM~q5d=x
zDnD6Bzm`z?xk6Z52<r&pX+n605S}T7!QLkfws(q9{d#I;UZ#b#5n?nE`YI*0a+Xm3
z=|b38D8H!?HWR{^7tOgBJqP+z9Jil6SA_6YA$&~;HwocpAuM%ae_Eh3+#g3{&(F#o
zI}$@u)vvY at vcI6PwNVL2P6^egQ3#(G!Z(HR4I$hlgjdKeV9(l-YZx_Zmfwfby3cim
z`s>UATPH1gE1 at bE>KCx#+ugD~HVYj9=kRcEqQb9}&CP^dEf}9uesryy#<CWHErguQ
zJUl!kV{FBv7DD+EXz%U&1(TXfHhv0jBc#ay&Q*?XJXoPTVqF{I5QT?ZI+tgT^aHsF
z4{N>Yi$A%rg;3LqvQ<koPSlK^tE^ejLMS)~{yprlaidFJ<V+Tnca~RPV~0)2#6g(2
zF at E#@jV*+vWjws>f=oeQXA2?ShL_*v>D0K>b6W`8Ch+ukEqfDr$FYS_tIflYgs^{a
zQ01e|1Nx8`8ha=PumKOhyK?N*y4V5yUI=#!>D#;^-p-{B>O<z$C#1K2<JMveqdI@@
z#|n`z>c&|#rCvYQN~o>C`tN3W=5BT)<$Eh3`!KJ6%A=CDBwsma7*>?57E0Y<k9Gt{
zNqR$n$@8AicW$`C9E_Xvj$R0R(?ZBt%X^;Md*_U at vtlb@S3Pn~Pk%vuNbPN6{{nx?
z$Dgk?_mzgopPX@>uv3=zyp23Ysl`4-ic&Z1sNRTr<Kxp4_pkGlvotA3<X4TGo^s?~
zzx7h<kus`C3I32V%iFn)dr4pPn4Z<4rNizlW2{*g`+ec7!(Y#lwyG?sZ%Z)Yluvth
z?b1~x<D|{nYuMi_nwE8bUgy#N(K>DVm(JIE0WMy7AC##o8RW#B!^)SASom#IMtoU%
z%ysK}_Ds_UDLcyZzxgPc9P70%p0ws#wU_r1 at 8*=JUUw8oG48t+XTEV+l{4+o#Z>J#
zq;UFrMT?dRjwjS!-Cn$mY;a__dGj6B8+M<T7c_3KXd_g<<~^T{RzJOO=lwRqkrv+H
zf085by_?HvA;j<I{r#wzs_tzz==oYy;~2(><=2c-534`SXMUSS+|Ird{iTVJd<e$7
z(6VKcZ-n8vXK%=LD{>yu&TRN~y*z7kR+6M)iRRvMu5)ElLbji(8+&|?QP|nIm1ks3
z7VmjV%de>zMM}!|IA3>JS4$_+&ELIS-8|n*wmNLs(_6P&Qg0US*kF9DqG`?X(h;#@
z<%iJoljn_h&S=R>vFnoRo_t$F|6q|-u9m6le%;kM^(hX&J7=3&Rh}~l-=uu4R{Oyr
zvwVG3Qb~7Z;k3C&CF!0ceqHXYrQ`(9a<15soPYPHcijESM9cV>Bu~T2f at y{C%Ec2?
z>x|O6$Z5F>wvT)ZmsUkhW*O(~GaHlnJ!<>1dd`-RANrJn3!A&FRh>vDZtk1#JR`}d
zVC3B5tLz&6b?cP1C%1pFNG}^*KO_2tUe>12fIUg$NV{*{J1TYYs;9Ks=RF=y(=7{$
zr at q8X`E_qIK9b8#I!M`;J1nPc*51PHLeICOzpU;EH3ht%hh;Z)My{1{Cp9R$9_30*
zGAOHfWPIib_}3iX-^U_n+;pa-!hC`~FAh1>%O?D$+<)O~lpHn2!>+(^LJQ}|sYS02
zSX3&+-%E<AYRw9}+Pr2)4>_xkY at u^D<|(J=?SpiNw{`vWg;hF->J at H@OuwHg(MQhS
zKl7(b)y*#)A$`sgk{t7+K4w|d#x7kMs$LXT6Y~D~@F<JYVGmw?GuTEtPDnqV;uK?A
z*hNxXzkJ1r)ChSclK5})uu<QKb4HAKadp&6m6whs)RI+CzHfLjllkse??kVo>Q>w3
z6&`7e=ATus`<)$O?`c at Cwlw_N&S9rF(lk#HrCSMmMqt<T_ET?;EZ$X-(n`q4<^8=;
z(_(e^U3AJKYt(`|!lxUGQB`@zqN*g!cHdd^qAf;Sk9bEzWbofh*<B-FE>ed1 at EG9X
ziKTth9tVB9B58Ph-3doEt^@6{Z)?b#5*33T`;!<)Y3+{W4?i7zf3^^I#ex4^%1+!x
z(QsXKWG}hEPifv`s-a at i59Jr?nb8)B&oi^)T;3kexUl4G>#i3TiCQOiKgw97e0NW)
zW&Sj6^JCSz8{Vw$_E}lLS<>=^Y<0MCxyo^}^N!Uc(_-h4H&};_;*jjZ&zwms&}_UK
zJ&Ce+DtDo|xY0q+Wj=DP8I5KKru^vOKKgO)L`G8atE(EO^{<ukK693ZiYDYwT6J?G
zsXQ~2UaI1{`ohWadT*YNvaWY>dZa27KC-gv`a_vh&n==p7cdwfML(ZQ%H4G%Q{8f_
zNJ-|f^_c|UvL|U>>n2zv%A7ZoD~?sy+&p2noW at Fn$Vrn}5jxJA%WJY^E<Jf^nCsu`
zwP93 at Wq3pRr^@*87O52FF_|(P{k0{bsm{YkSyr at Le2HXT`l`;AD(GI!u{tKTQ9R$E
zLHX;h8FyTo1I-&&zPO%wpmy^t%7xIKXW|TF?s;>9jeh$V-fnU<UpCt?#YXGBQRElu
zM^dZ)I{h|6T`^|=Gwzj(f3i&LXd!H02IEEOdEz+sUd1hc7qgv9Syxs$j}yIofBlp`
z?r5Wg>T^C4rH^jaml335M%Y_svhCwPQ$&1>WjkrV6I`v6t?Bct#wt9}d%r#YmGW9j
zMrzW{jr!g!HFjm&)FrztQW`0<ZrGo3xF#O!aO=vz^ZQ+s8%?9SW|Jz<QPm!%t$Hy<
z$+k#A%E&77%$Do%Y}S%dW7gU%^+`56z+U+^c8}Fkj*xu`|GlF3N{)x}BAQW>(|iVl
zvC!t8{Jiunn`Rkr3*a7fQonAmxBM%4^SynBQSTm0t{JuV;?!$(TRYa?vn<Oy>`}8M
zxFnXn@#di$-lp}U`_p!5-*HuL`}uU)*Z|7D_=2qQx&FFjPlDd4Tj>^iBe`3PTEulO
zP`R|qs3g;(a<wUeT6;$gA7N)rcp7AxTbekxWs3TEMo!@5)NiE*2^XbT_QaUVhaJCt
zXclTAB#h+!J@=yFL;3oN*9qHod4IpyYFXFDUS?}vAim+shm6 at LNDB8jQIo=rcTx{d
znm97vF$d*Z?%3N(*gl0ff6Wh^{ANKHtj9xmxbx)WK5vUKi=F$p)90?@#BM%ZxJYlW
z4EbR9mrW+zyjb?_N85{|O|7Z}wXf#M_>YS-x>gar&(kPbMAJ=vbxc{9>61Y2p>DH+
zU5ksd55JQQeX3u0B<`Kj4#T#-2A>HnmzO1O`qnOQ7O9=e6bX7{nmS>X{_;!JmM44{
zr+koUO5v^_u4>ml(Y)aLja2;~zn0wG^*)HDp=4cCrafmVTe;Ht?rVwM3`Ntpd*`0r
z-970@`pTc at eX?TBV#HE=<l;M)nUJRMrxxuZWo0csGQ0EA3dZiydI`fXQnn-WS4Tys
z{1_h`6IZx!vU%~b+`apB3-8?DQhvX3 at dV?<`ZQ0YU$@HZ+8WpUvacE!G-}R0bx&nw
zK*h at GHE*iT5+siWhmSOC&Q*vIRjYb#8tZV1OkZJ5y<K);quh)-$~NOu1`{r6F{46m
zrQANY)hKeD*X#$`qD}4e^WWZ|R<dl+iJWq4-kjwf3Ln<LHcTXK4SH_p6ESauQ{iHb
zH##@TCpN6roqL-Z97*the(dRcYLs`l-k4t(>dzn96+5|Nf at Qv&tOMC5l65%r;;(P>
zLX8ep6Q>#_87Q0dc=_&IU1PPc?m^cavi-Y_&XTn|Z!9w?+1-92RDNseD|!D;#kxkL
z^m3C6msV%q*Lp6kyKohZ_jlfYuT8BTq+{(g^6pKs=jALWI<-8G&z|<Z#He!Pg(GLC
zcGkLHxqH$vu*`I;aobYm=7_T9RgzJYr at k;qlW+6c;PiRH*XgkbS>s|T#T_qX-X=8k
zELTkPm?za}nDFanV6>f_ho6>#(Vkl^X0cE6rYEcI{ZaO#t$pmfc;k3oF^7zx18w0M
zBmEANXHwI@#(nIZen-RYwsLES=SQms=41zz-Rs^Z%YK#}=iP2{%&DE`|KPFbTFK@`
z_tt+m5AR)e_UHTjnuZQ-_sST5lf*_B#|o*u%z2x16waq+o7FvAw~uk79c58h>T6}?
zn5OT!)U#yC at L_2p_w?v#ttNX5ulr}d`u6(Au%7jAcJ!Gv#0Qxj%qbhyR<(`3V{N8I
zb?4M;ch+x;KkMUhD&u_;^?>+GjaL8t%6D29hZfB{M$Pm37+*T?QA?#}*O?ugz8Y0M
zkPA9y<@;EN^QO1&>0Z;UX$M_T>*ntK?pHw2T%c^6tUF0g;jLEGk>`arZwKvnK*Iak
zEc8Cq(whiH$9elLTxaj#?Z>;t+fOGVl-^r$vQCl21FIUpyWLjzcgNXnn3p=^a<D~Y
zS<f%7^eAfchlAxhh9wrUoC#MZ9Ln3C669K0s(QgF;!&jH^kQap0-<8@&j(soxn^0F
z?G4JmY-?_<X#M=2TpSRio^!O#<GlTJeKyC3dQ5&ryFvn`jj%Cd=QEvC7DvhPcXCc2
z6TjeJ9XeGm)GF?PQo|wBj2X>e74D>ojiJO^{~)i?PF*awO&)EF(I*|MGy7CYICfYf
z?&Xg+^<<+kYlHnv<1x>Tgs$If?28VZ&aNP*OJyHD{vhZg<(=BaQ!CW1Qd}G#Z!EjR
z`SI58>Sx1qCTU8`Z>~?<vPplej!%9d)1pDexMSZ<v7CeJt{+kk2r)k3+0L>Ry?SEm
zl_NxNMy+-AjRIfPvhhun`?vM&53ZYKpDG*HQ at -NI1Zf?=uWbgVl`fiuR at 2bg*)79A
zcRjgcT&di(I&1q3-TA6UPraG8)YJ#Ff3<nNtxbC{jkKs!jFjNtxpGt3;(cd0I<eat
zS_$>@q>$bbWwEXwm-Za!Y&WlX^GHEtX^~<l?IoKXEoPV$@c!}n?V1|{R<tZ~jI}XK
z4kFa7zPD3)->M1s<9a%*@^f6j&Loz7`S7TsQRO(tbjNWWmrEuuw`*PKwc7u%+dRpx
z*eBw7$g1HB(py7 at U$n~Bc^x}@PL4&_`VSQ6_<dG;7TXs at Y+0`FR=#d!Tg4c17WcXQ
z-r?bP7fL1lMYUTA#cgmue at abGfh<urZ=RM><m;Ov#hzhL#WbFjUVB2ci1__%JfXg9
zVq%C|`#Q^J!j2fQKi5wU7o-(dZU{U|&N^=N#$?R<3%>H+N|hg)jkd`VSXVNe-igYI
zj6QjymlWImI(NMaeQT74+b{=jSEC(W2bEMRCRn)EJ7kVL*G$+m8^7K*$tut8RjIcz
zj4kJQ^`*Vl at 0EEMRJp{HQc0{nN4 at USE@p5l+Q;gHbzz0XFwe!B-cwH3N59wV1b=dE
zop8%pUHo`ZN3uS_Dkq;jZ>C*m_e7OrceXs4ZdCkM>)^0M*VCU>E>$a!H8g8@`{?3C
z!{R5Wymd7S^{Na at Rj>EXx#Jo-{;FlN8$HT0+Oah#*>*}*NxVvO at vC`;;W}Yk+H_C9
zDWw_ot{l6}yvROiRsNR7$TIl_>AQEuT4Y-JJ&)gIdlz#$>=6d$h1j!WaUj|pe?;@0
z<L`^GcmJ}DgBjQdNw8NNBjk%X$Q`i|8`0s^L>`EVuopbv{!B(%U&zRQ6+(6aepW0$
z`vAHl-NW8Hz=X}P!}p+@)BH^6ZdNol$)9aS3o#GyGY?>r0=(?#{#19aH7n4Fj=wY8
z49-PRk}v$vLA>{kd+^WMh={ECC$T>!gnuGDehwZ#haZEG4Z=Tzg-CpE1T}XD@<nd&
zKfX;FSwouwh2p4?iVN|A7(3wp5HmOx2?Zc8AP&N}up at H_Gx_CWVLwFYm+cSf8F*h=
zAsU3;5%wJq6Og+B3c%w8`Dl=DAcj|8K^+D464Xaf2SM3_a(U&1^p`CtS5T&)983=l
z^aX&-gP#LSgVlBb+e2-!6r4eEj-bZ_Z{a|!J)|0pt)R|+gYwwnbsLOncw2bo3Ca=Z
z7U&b`5#$FOF<a#8yNLIV77sXP6RqGNVu27ikd at UO505z^cp)}0>?9yCFFN*~c6_{9
zf&F1HJ{A at PJLC!?!7KwfP%*GjTn88{K9FF#415f+w_s!Srn|8NXuQ|bgMH&c%#1-l
zrWezR>B972Iv`&NrUT25<;C(K at Qwhs2MyN&&Vq-)Z`d4u9JrAmoe#jP at DMl{9s$0_
z<KSVC7t4pG$I at YGFgdb7#>f_|#~4{69i$CjYXl)n-Z(QkCTuz#<92i|?<-6YHXRfu
zF_|pfFD(t%+Ec8IF>bxW%Gj1-h{st`t&QzTwkxr)q5Vo5V}4pgs--2#-WU&)EP2Ht
zW)AN=V{Kgq$Q8mKFUe?E90}R;!>TM2GUJCkqe)1gA3hyPLc08LX#@$0h>D3z43m_S
z9zH at wR&Jy`L1EPBF=G|SDJhSiFmcjkqRN!1s?(;csn3`>Yqo~w9Id(9I=b`p<}X;d
zXtDm1rOOORWJ4ok6H_yaxrL>bHPvRht)2Y}2S=xst5!R&aarq1b948gdwO{@*7-1f
z{aF6%*&J>_U{G*K=!T7(Hg7>|HD at F2#qn!_76UE8=1Pn&Mz}ZofmJhLo}k0*gv}Ie
ze9k1|ZI?yE`95U{zHA5Q;50m`9iqU9nf{Fgn-8eq4<tOr-^sDV7U>}^7;POqRs+(J
zphmp83!B4;_{>JcXD&KEr(z{qLI|7PJ at 7bBT(2*l)*D*Gfe;O!k$E#9HqT*mZ~*SR
zelQ0S;eQ(5@<3d}fG$kVz$G-u$3UU{beLBU$Gx7XpMlQ~49H2tYs)K>2&Dz!`Y|~+
z-?Q*iF_}M{%>EoajhY~#;H~6o^@Ugtj<J+JpqE#7tX){`*mz0U?1`meLCcA_J|>=?
z*D at MjvY?+d at U~%niOuQQTt!6caLsWC#6~ml-ovcGy}wR_`;+#!J>`ej5wjlNb%%}D
zg*_yv5nd;3CTHO76pXoGytVts+YfSZar^MYd&>*jN`w;N+KylSFs(f8OyIoM3Py at 2
z at g3Bo!sX2U7rXlFc+&^<w;|}U=Z_1!U&8BQ7T#;v+TwqOT?n^BtG^wBItlvi?^@s?
zlwTVy=+7U3kEk2J7yr}?SRY{Z`>Vfdqxt{E at e9IR_-8-+yYlq@3*})}>hWJJZ(e_S
z{`g2?dIKRn8()E7I{wac1im<+QJ_!vzfk|RxK=s}#%n%@Z&5sijhtW<|62dKXyG5n
zn+2uO`8DSGGm9_BX2C$*8vkDJP&>kE<BzZUxcs?AU^9Q7 at 8=G(pTF1R%b(>KxW}<^
ztsebzfX~0`|F77~(2_jx{-*J at 3-k{7A=alX+#b~NdDI)f<`D4}H+_gT{M8?|293{u
zUOy}a&jztE^TjcDNd2 at 1jn99s7h7#(_XYxuoWIzw)}Z;?6CVSf_wcSb{(u>HP9lDF
z^2cM?I2P1`m&apBUtkAzKNnU^#$St|4jlX3cuNr(PZ0|o+gGCnoPfn2B;&tzjs-pn
ziwBPF18OWJBW(YVKX7GCDe&>Y5(~&^0&oN1ldyQ;lYuSBC*v!emOL_20p59ljIfWN
z)&a-XuGzptfo}!g2Ye&&- at u)LTj9RJ#;?=Z^%rZK13vc)W(7ku3r-^R+|bpPC4{g$
zsDb;Z;rxEk#7~6u@%tYZ^eL9gmw)Z$<;HaR;Jq0H|1af_&Cn8z`#nMh*KIRI+akQp
z>i8WSW|h1h80vljOeO?}VgE$1-{*=AL&HSn#f^=PC9q?j7Q#OgtWI2D<0L7C=4)v=
z+DIZqBw|B`q+GpS;Xo+ZKNt=v>~#W?ND6;T1@@zKERnlA2d%I-(O3XsjL+4x#Mj;{
ztSN?6BV%M2!h!o4!RJ)9bo3B*FK7h)N`kT}f4LXH4ha}`!5*iH5Ekc$(*}iiKv;?&
z-Z?0oFescjD4aAXoIEI;GALXjIuNcL6vm!ONb~d|!QQO at n-Ah&PWArtud=_MW`Fsm
z`p|Rh-<(5JgbzAJ{=;){{)j=R=zn+$@)G~2yZ`@y<F8}t{lD7+L<Y|P^cZ3RA_J%B
ze|Qd9z<>ArxAtQv_U44aXt3-*ZZf~!L-Bvw-hp-t$MANrhJ{B&ZjFkLiQN{rJ$}c|
zgv6xel+;~m=^4BC?A^CNGb=miKyF at sLE*uo;*!!sWrxd;96eT1Syf$Ad;CP*$@+%I
zQ>V|IJ$L at X#Y>m3T)o!R+|t^1{l?AqTet7ry?6gX$HUI9N8OK~Jbl*l{Kd;xuiw1w
zefR#u$4{TXeEs(QN8it1zyI40VE*|5%YXC$=6|~V|I_9FFMj~`&+`8x`ai&r!KHg>
zaJV!s_L)k=$36@{FAtV4LfDs at 86Gqb;&dXH<wo<uelE>n1#rDJd{}HcjUT3YvKj6)
zB#;pY$%unwOjaPx2iCy>bT*fz0nu=u$JW5UV~WfDu{YK5!+xRIyG?>LLg_Rn&CdgQ
zv;4fENbEE55N6_WUf7qIiEeBLmxg~^m+t3<$GeBp at n^~cWr7qmUpiiu01igbR;UXn
zh=D)+<^<6_7#wd6Plg|^gX2%bK256;NcW?M2GE(99zk3HJHRV|7SfM-?IGgr>5rqc
z at o$^<V;Y-=XBracNAsl(U_YoFo@!7UUzP`*?KcS1gCUh)KZbts2w>x%1MZIl4X|FN
z4XHDoN#xL&fiw>mdmzp|U<if*XJB=}zI^T<;KpRQ59D`efr$`#BRLSqWcj)=XoJgk
z_r^c3*^k{=OhzCi5n${yoY>dgu}s((U7;uG9J+=Zn-<DoGHCd_a{O5AAi5U-4Zjcw
zb9{!xxv_kpZ9~#Q7)BV&t|1st1{-f7SOjpuUUataAh7^51A^(kZma;d*Pu9tyEm8a
z2Wx(?8GalsfCHWYC3&*xe(v4_diZ#V9)28jB+X+`9ydCZS6P0oG%!0H2oH+)83_0L
zU&w&}31q_lC5Xp-M-W~I7KVuhn8Bv^hr!wyZf<m%-=MshZ3)6qVeA!A{TMo7Ak6b6
ze7rmYxPzOA`yDo at TsE{He=!lWKMs5077YfTJ}4cB;p at +2crvgrs|w;Uf9JcqAP(a|
z-l)H0G2iBwiVqNtXF-Cx4I2LbIO5;LVd1*tWMpi;0v+V at R6dWzISGXUx3(lBE{+Y6
zKk!v%W{8P%3i1GMXl93O`0 at IDtj?E@#<4vT=j-XM<>`N1%hP`Y=a%SVEl+<1&dKN?
z&M7DZ=jJFL=c^DG=XS^m=k~}F=cdSjuUD7PRe_t>lF?|K8zO{rOVnF~aXZur+{l=O
zuHxJf)!^I`9mKgE%BbO$9}ApfZ-+Jlx2D=6Z$7ujxi!)UZeveI>R5U^J2VdGWF&=i
zOZ2fC<5bj%b3=3%=SHXk=O!o{=f)@==cXta=Vr(o=T^uG=hnyw=M<!ia}pxrd=-+%
zxh?9e!uSgG3g;`)ZJaxxi@@z1Oi*JLuU&Ojy#A`G;`LJnpO*q(0ebU*o04tOzA9|o
ztWhe)S3&*w at f-P^TZN6M5%LCZW?_n)F}%tWIq|tAKfeK=>jJm2rJ&h(eJs!f;HyX`
zNFKNa^e5uukCi;!3EbYEiq{t_ucea58!LJBtl`&JMn+jiQeID7MoL3mOk74 at UTUHg
z)zAi!fUNuHPV4^p)RZpRi{OaA4bF~>fB6}U$39rk`${&&&lCHsF(z>2<*}r3xFmNj
zBam*x^2a`84{<!qQ-iEPBh8CWW(7N<703?%i|OAz9~l-F79AEIhLF5ibXasm*k}m}
zV`Jl}s3-}9w6wx(!eWsWQde7G;~0h{v4=V~HnG at En|fGm8012(-mc!>-cnMM!T$b1
z{{BOr=NKX?vIbg#?2#&9dn+5bTludaVO?b8+Caviq1f6P+0mCrO3R)knru6ML3i&#
zqs`cjgyAA*tlv1!>);aZ&T{r=v#=iIIP)%~&hEUAi90hHZqC?C0Ry-UCdb*G<qKCA
z59c5T{t0)Dg_>HL{vK|F%Fh0Ykj-)t)F~qRw{`pV`#1LJ58dE%0e%=`@JBf{mM4GA
z$H7AK?CpQfUmE{s`Ha**%YW?pvpl0<sQjvsd|Jg&d83g0UiDCU>>&A<S_3rV_)vL;
zQ2xiKhRSaX$#<Opv;3TpT)2J1F8o>E-TLSDf4KSQ{6TmAT)&5R4f?}x28D&w<=h{T
z9lHO|>3(z$$RxWA{;^(_gJh=$g at xPK^M+S{?8qM!u7309c3<rKvs_DrG(>(;Nd8Hb
z)L+(I2>+9wXJVv3mZLImsJul;zCmfIT)6!b;|J=|s{GIOa2)^Va(WH^EPrqIXZcZ^
zq4L4w-r at -B7RP^<6P-v%)#=arAN-5larIESu%CZ+{_{ADr2TXKPPma!liQ!m5Ayx9
zej~p>%g3|+EdO!w&*j&?`LmtO=>4<)`gebp2YvXn{+_-+%PW8Wx&FJQ$$ykzQTnr-
zGV#yyvPplIpCJyF3)|15X at AbYb2?9s9qtgWxiD}YcDXEy)U`xW&!9N<Yof>r;wml;
z#GP#tMXK7O=<J|$md&C_7t$pTitB|q1aYN<^2)V{q6v_fczGbs2I3O6|9(AC3_84l
z0)aY!nt<wn;(+!5IW+U~kwKppU*}ASQwEX)5(Rn>^?d}?0<?gizU&I<0lErwAE*cD
zJCNK}Q8Woi8;A^K3*-qD3bYgG08j<cWuOkA&p-;$N5wZq(LSJT(6Je~AJ8fwb0A%y
zDL^tn-*1Sb7eF_F&H at z!B?1Kkc>vi1Ed`nmqzohl^!2(ZdJ5DIR1dTh%7_5s0(k)0
z0_g*30mVTXlYq+uA*@}{CwGCa0#yMO03`x#1Y!cEfxhFAXD4tj&?+EnjKi34fx>{|
zf%1Urf$jsn0{RZ5&?<^%0_g);133fv18oM{36u>~1#}VUG0<0_(QVMTKqf#AKun-$
zpk$z8pwmECfo=o60Qw9h4)sw0ngFB?WN at 7~hQIh at X#@Rl1f&O417lnalnN9J6b$4I
zv<k=)Xc3S)kTTG4ps$eU1yDQCWuW6g1whF_aX_1a{D7RV at yePF?KS{XZsFmr2yQ$4
z>1=L at oi`os9k6A)A@&@PJ|x}-?jb;CNVPJwx75+r^k6a(;)vVv?m&!r??K+k&uPTK
zUUto5hd_=eB5+4y!eG)#TrQj87Qm%*Al6aListXYpa)SsF&Wy%&xrw$_%r;xNZ8-2
z4Vf$s-3(UbkY*iL8kWGG;Y&x4kr9hy1X(a-&+3mC6*V1Fr`SQ7NvK_Xg`e+`lC30|
z94?zl$1WRQVpai6E`uBbT3L<^54xc at jg5>&`S)fd4-YoJS6pArl11~dWVo?ucpI-H
zBRZ2VD5Fu#mQM4)l_O6<oo!eQKQ6Wbw^z^~cG#mFHxX=;Dqj<d!BX3CJ@^eq9$3kP
zuo;x=PGjQxTGxn};J1SO`#Y#b1eV&K?(2_fx2E}Gaa#p(HZ-m`;4v7sVtE8G`MnBp
zxmcV7 at 6Fi8!6Y<F+?LJ>fKK&>j`Kih6ILr5HiN}xa6=54G^__2u~rxcu-SCK{w?%p
z2gM2U?#A-+fItJv8?Y}dAJkyfK&=MV9YH;OS%E{WX)$js7=gU}*h{!6eqgkuzsB2P
zPwFr$8vR%8aPw?oY-?@YZ^?3^1NX@!G#eiymOJ(gP+KgB;}4p+o+OkY3av3>dQrG^
zUwe8mlp6#wejWp3Ko_+IOQ3P+MutcQ(*nE<88JBi*h}IE()EejvuS=D>_zVUNGM6v
zhEDexNOK<<`+3;VplF0PW4d_T9<1olFn6e#93s=b7=HW|d%*@hdH4Ldts+DaTjB2k
z1 at IpPpogM%-mD-2^5u^L4`btQ0d at C+J7GE-A7s$bBZApE3 at L}jCZX}7R;>QV3TPiz
zBOb7Y;X(66ZCIQ^*64xBZD^P=AU57h{Eq8SqK5YmFB1F|>sww?Xoa1ztzcyI1ak_7
z%;19SBJ`B+ZM;V!2o+#CvEeXeF)@#?5yb#C0GrFUU^23g75055Y^<1&Ly%8jFf*_)
z3$o`UI~J4SL1qVFPb5u^DEw+LvA$UzKxc>GyT?h`vH)W6>B)i~f_#U{vF=3HSe&t6
zfG;G#B3v;{^{4ys at g@ul_NL=G7T`MZYVgx6!D4vLg%}bt9l-pGkc=EiHiBxRNg^;?
zGnvN0ba$9(NyyI3*ivBROk~Jf;m7b}a2Yh-b7Xml;T1>m<9!wZp`{Q<8I*>_r1SHK
zi1yncHq(+g{c~=!s2QE+k55Z*$;QIGt at YDH1WD{!0~rxg9Td--(FB at a31-JZPrAq<
zg86TU80I0E1AEvo{ORa}AhkXAS%x>@FPQPdB*Ormc65Aq5JC&ZEa|jB`d{LA;d3LD
z%9}``|IgzViJJsJrVVe6EuGh%al*4F%p*!7gMUVJ8T*X{7 at J_akH+Uj-a{ozh98W`
zbzCD1`=A!WN|ra%)7abqe at x(r#KkBazE&37gpU1^0%kTk2lJ;!aeF%3m*F=siNZAq
zu58|Tr96y--Rb_AUg#S9nv;*s!v8FWl8oqX0bX8owxEO;(3gYTL68t!4((a}*GQBh
z=)qMiKl+gNu<>@=U~%ZJC<W7jeLsWlLGg2E(|xfb(96N`&~1WvZ!9nNm=w*&^iz$k
zu<&rGr33c03`4A1*o1jb+`j)e92c-~|8F}kVyORDALlWI8|S&dT%jm%jk<=92+t1?
zYQm8b9To+w5HwA~5zGd^ZXidj7mfV_8ElpxR#OKWn?ZBKjKJX^BqVfH+>S}7`=f5r
z0iO~wM3SF}9jtc at Spm@aR{q>3Y^+2P6B?JsB>DSe)<!}stVEs- at -m`5JWKQAfWu<_
zRXPac(;pfqZp&q^@WVbff;6!Fg4D45;i+PySA)j{3<XREdV1O5^$ytlFno=LIWC=N
ztcZrMV+M5r at 9N8OM0c@z{G9+5is2Kc4V{e}4$MAyQ=k?pJiBuD;kWJ7UsoG2a2jHk
zhOk!TDFl5Co)2LNn-3snuPFcNIb0fPV6~Qp6eCzx46QwQxvU5_P4i|bo&dZ0$p|ct
z7lj-}Oqg(4z-)nxe>?=dPL*dL{GN0~H+bHS&zv4GlluC at 6|ldC34_+6j>wK at H6Hwh
zC^8qUGog8ymcPB7{MQ3+lYft!$NQ#;44;ehd7myB^#b+i^045*(%;bKrN{WcrMF{w
za)Y2@&Q=U}Hj4wunYR+)IP*t|znpYlfj>s4!PNtMA6p&h!S-O5>hkv8?$hP%!`-OM
z+qdhX%iEu82^{-OmOgMr;Hto}yOq&cJrMS(GBWzALq<*G3<R&?L!E%j6bEn%;KcC;
zNU*oF5%3|y0eLClr-N`dVAA*j{9kD!c33|(IB`U&!H77~5XTr7yqga{<k4RatZgEH
zjUFQZ`=<;4|MCAHe?Bm~`OSZJhJ?b{5$4acz20}ccpE$W|5kZ6M*qG129L=o3<#?e
zHfIQIUyRQ&f5zqr355OL5Z{N6 at 1w@%3v3^?6rW4;`Eb5$#30#NeqM}^<m;8^$6>!4
zP~a05ufmU?$Hyc-HsoU)ejZzXyd9rA^7XIem(AqIW4}Eo0%6lT_FDrszplgh>BIPS
z4(Ia-zC4mI-^$mM1cdF#&*bAAetq)z<rVVdtNHb7;>&OG<>MrIeIe+x|2>ENKOQ_e
z at gI->ZB_hVdi?X|PoDgrHz)qj*8Bg><NtOW;2-}VHwA;IFh`4uOXb1D>>3dgp~8<Z
zc*x^9`8*z;)!B!q|9{H6wwS1nFg(lB7OicTge9~uJt0aoso>fsRSU|lM!-}9*u-^d
zst5-`S+pD$F|`&iX>8JmCT&w6#3F=^52TV-328Lgq-pwKQj!uwh#@}MNKyh#+pH1k
zZohwK&YsKe0c=Tn8}iNNKQr^s<^Ru~fwLbcJ9&B;^}p^N?^PW7Jr$Sz%k<hJ>-%Lo
zi2C36-0OLs{n#_vQ{&wK$xA#}g%6Ud at i)g6wr~8<`1;iPKa5S>eaM*aq-aThhoXD`
zWOsnw1MUKMfH7bc7y-TmZURF<KhO=F2R;DK0cU|u-~`YCv;jwfW}p$M2lfMR0Ivh(
zKq;_6)?Wv#1{5F%SOGBLx6kmr4om at efm^^$U|FW|d^v>j0B{-T2F?Q?0_T7Zpb-cH
z+kka|0;~Wsfw_y&0Zak+fIGk_a0~bbxDH$e`hg3;Ip73vRMxKtf<QU&GO!Zx0CQdF
z7nlOZfC1n_mpE_C_gwr}0`V;@W!c42$2vA+)EW95d-x^x<0{q|ZAYdA$d3{-TpzH`
z+1}q`<xbZhx4zT$zcbJ}buM4Ho>SQ0A7GXJ#=dNMI<P31_0xAli5r9N#PmJTPl0Q|
zDDW$g?V|Tua25o at E~CvRCY}e6G5)<nc9qfJju`s|ApY_qU?<RQTJS=N5!ljY_tpjR
zt_-87-;c5r$TrC)zZwwF+N6V0<%y4;WB;MB(D#@!Y{4yq-kzSB<EArHIzRs>2Jw3j
zHy{6M94qL&REm_&PDjNt!&J}3xrffB$TJzeo7xRIonMBK()ncsDb=4$(~nCeDRK&V
zriIhx^x3r9LB7F3UhW_dI>;L#@9??#zk<Ife<9TQs+)DYjIvVVW|<jAdF2*23z^ED
zemBc8)&Dqv>!vc+n6KfBT&oavFJmczOzHmp`)p!jg5AD-o87o^gPlElmIVR<wsGS|
z=JWa3nl)=!US1wODT{ALPb64hooGM(=feM`-Ap1O6J$(rB1N9Cv_m;A5 at bwqs+^bg
zb~B=^tFQ#S+}tiPI85clU^F_MKw*$#%3I}8O3dvZRo75gv(^4U2=U(P5YN|;JU%_c
z^CY3f+%BdRP`f at HvAkFMJUJeU87ue}^7u5D7x0)BSlZ2&?m)9gRX)Gp7pj77QtbDK
zO!5$qJv*%WJYEbvrg)=$9xv48P^?DYi+Xa=E~fB2znW{)myMZG at 3W<4rQ7jS28llq
z<oafl`G=);eHglzdWUBDL*9b)3YJmka}(+G`F%mDue>@a^m!16W>FIP9kW#_^UG1|
zJ*zxoe!0;opI<#`^?m}m$Ro?r>EmlCwU*0#wojK+?6d1rnAka*CH4&aDf2lVT~2Y1
zZGMl)6oie0$VLE}-&?KA5eN3z)>D{XT(`+M*`>@6Ms-`I*rpE#KL<-sx*U~sP$DAQ
zNvpo3N60k69YR3&3qnjOu(cDGucva3rg<jIR)3$?dlG3(tf^^TMVaPqZ6_>0A<}7K
zspfqYW&QSI(#I`75$7q7W4g5p8!~J&oj%#Cp|4Dj4coNo^YT6FBM;DZNlm0Rw=Zd%
zHhtOLCgAKLU7z#<uCJ`h=b(=n_ZF#7_qv$82RP~DHov#ZSCd9xwl8U$Df6S*kjjEI
z^AmT2bowgh9?I3*k!C%!L$mYh<NHRrXw<TQ-Yc<r<|lo<+V9B at 1k>0{8hza3g&le+
z&sKR-?K8EV at OWYKA>>g=;}6p*?c|4z at O8>Zhpm1ePx^wTUGgK&{9$L$Pp7tf%<!*i
z=VHcLp_JV$7f*t3yT0HlF`Kq=#v}P#ch^qNlDM05#A!UIa at N5H>tkc(J8U_7z;f86
zo1P(0n;7KwNOrPT&O&vJ^Y#(1P?O}{tCi(dy!H_rpPbJ27vW4=t(;+StK^p>)~sEe
zYs=uj6Z~I-%PGQHgajwlRz8C|&OoTwHFJ*%emrCT)^QhFCYzZxfcd_F`JQ3F^ZA}K
zE at sf%daUwEtY0JRWv)!I9*IPY?MXWWee|M_EPfcEI^slN-Jh%_-Jcc7{n^b|LNJsO
z<JED#24eNCJ7e(5S-C4WSrl|;ovIwh3GEttmd)7LBst&r*((^gaJ<v3(xP=6X981`
z9E`_p8gN>4T|ysun8zU&=Q_?j_PUF4cJXok>^RPgEMOXEh2wfGJZ5ZBX~6XqTMvC1
zd`z8eyJS^VIG0!>-!Uc0nw4q;OX;IA*sX2@^Z606z8#e8j=Fa^*@a;JY)JYod3mkm
zPZPbhN)|p9S9Fi8lgyI`dEb;f&U6k?!~dOaQRC~Nv$koRkGjq at qX6fHJDhUISlpe!
z+55Q_VJ*9`md~=E+&VXR8~W4OJ`?Ae$~jx<+pNsQT_&VHTaPa3A2ICdl-wU at yR+*s
zS>3#TQ0ceZmU{_p>w$x9>1(=OvQ}!Gx$gkKr?*+|Ec4%yk2&qhB7I-nw^#G~_LRx@
zi)%Ga4IgMeu5^T(TeOn=NNdX`t*%iG*J^9S`ujC&>yCss)oS6wc3-{{u5D>*P&NG}
zo05EA;kx|I&n#Dz;<na^#^0V?WN$Btc4(=obrE_6U6vtfRo{w0i%M^&w>LGb2URV3
z(R^9FP>X at QrnaliN;AbJ`L$X_OZ$<-YHPj{Y4X?6i(4i64Ykdhn!mZY$f2>JyvSL5
gagk-L#YM?Uqg-61ujl4Uf2Duhwu)V4!Jy3i0g*_Gf&c&j

diff --git a/distutils2/compat.py b/distutils2/compat.py
--- a/distutils2/compat.py
+++ b/distutils2/compat.py
@@ -1,10 +1,10 @@
-""" distutils2.compat
+"""Compatibility helpers.
 
-Used to provide classes, variables and imports which can be used to
-support distutils2 across versions(2.x and 3.x)
+This module provides classes, variables and imports which are used to
+support distutils2 across Python 2.x and 3.x.
 """
 
-import logging
+from distutils2 import logger
 
 
 # XXX Having two classes with the same name is not a good thing.
@@ -20,7 +20,6 @@
     _CONVERT = False
     _KLASS = object
 
-# marking public APIs
 __all__ = ['Mixin2to3']
 
 
@@ -31,6 +30,7 @@
     yet does nothing in particular with it.
     """
     if _CONVERT:
+
         def _run_2to3(self, files, doctests=[], fixers=[]):
             """ Takes a list of files and doctests, and performs conversion
             on those.
@@ -38,22 +38,20 @@
               - Second, the doctests in `files` are converted.
               - Thirdly, the doctests in `doctests` are converted.
             """
-            # if additional fixers are present, use them
             if fixers:
                 self.fixer_names = fixers
 
-            # Convert the ".py" files.
-            logging.info("Converting Python code")
+            logger.info('converting Python code')
             _KLASS.run_2to3(self, files)
 
-            # Convert the doctests in the ".py" files.
-            logging.info("Converting doctests with '.py' files")
+            logger.info('converting doctests in Python files')
             _KLASS.run_2to3(self, files, doctests_only=True)
 
             if doctests != []:
-                logging.info("Converting text files which contain doctests")
+                logger.info('converting doctest in text files')
                 _KLASS.run_2to3(self, doctests, doctests_only=True)
     else:
         # If run on Python 2.x, there is nothing to do.
+
         def _run_2to3(self, files, doctests=[], fixers=[]):
             pass
diff --git a/distutils2/compiler/__init__.py b/distutils2/compiler/__init__.py
--- a/distutils2/compiler/__init__.py
+++ b/distutils2/compiler/__init__.py
@@ -1,11 +1,26 @@
+"""Compiler abstraction model used by distutils2.
+
+An abstract base class is defined in the ccompiler submodule, and
+concrete implementations suitable for various platforms are defined in
+the other submodules.  The extension module is also placed in this
+package.
+
+In general, code should not instantiate compiler classes directly but
+use the new_compiler and customize_compiler functions provided in this
+module.
+
+The compiler system has a registration API: get_default_compiler,
+set_compiler, show_compilers.
+"""
+
 import os
 import sys
 import re
+import sysconfig
 
-from distutils2._backport import sysconfig
 from distutils2.util import resolve_name
-from distutils2.errors import DistutilsPlatformError
-
+from distutils2.errors import PackagingPlatformError
+from distutils2 import logger
 
 def customize_compiler(compiler):
     """Do any platform-specific customization of a CCompiler instance.
@@ -14,10 +29,10 @@
     varies across Unices and is stored in Python's Makefile.
     """
     if compiler.name == "unix":
-        (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
+        cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags = (
             sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
-                                       'CCSHARED', 'LDSHARED', 'SO', 'AR',
-                                       'ARFLAGS')
+                                      'CCSHARED', 'LDSHARED', 'SO', 'AR',
+                                      'ARFLAGS'))
 
         if 'CC' in os.environ:
             cc = os.environ['CC']
@@ -68,19 +83,16 @@
 # patterns. Order is important; platform mappings are preferred over
 # OS names.
 _default_compilers = (
-
     # Platform string mappings
 
     # on a cygwin built python we can use gcc like an ordinary UNIXish
     # compiler
     ('cygwin.*', 'unix'),
-    ('os2emx', 'emx'),
 
     # OS name mappings
     ('posix', 'unix'),
     ('nt', 'msvc'),
-
-    )
+)
 
 def get_default_compiler(osname=None, platform=None):
     """ Determine the default compiler to use for the given platform.
@@ -101,17 +113,19 @@
         if re.match(pattern, platform) is not None or \
            re.match(pattern, osname) is not None:
             return compiler
-    # Default to Unix compiler
+    # Defaults to Unix compiler
     return 'unix'
 
 
+# compiler mapping
+# XXX useful to expose them? (i.e. get_compiler_names)
 _COMPILERS = {
     'unix': 'distutils2.compiler.unixccompiler.UnixCCompiler',
     'msvc': 'distutils2.compiler.msvccompiler.MSVCCompiler',
     'cygwin': 'distutils2.compiler.cygwinccompiler.CygwinCCompiler',
     'mingw32': 'distutils2.compiler.cygwinccompiler.Mingw32CCompiler',
-    'bcpp': 'distutils2.compilers.bcppcompiler.BCPPCompiler'}
-
+    'bcpp': 'distutils2.compiler.bcppcompiler.BCPPCompiler',
+}
 
 def set_compiler(location):
     """Add or change a compiler"""
@@ -127,8 +141,8 @@
     from distutils2.fancy_getopt import FancyGetopt
     compilers = []
 
-    for name, cls in _COMPILERS.iteritems():
-        if isinstance(cls, str):
+    for name, cls in _COMPILERS.items():
+        if isinstance(cls, basestring):
             cls = resolve_name(cls)
             _COMPILERS[name] = cls
 
@@ -139,7 +153,8 @@
     pretty_printer.print_help("List of available compilers:")
 
 
-def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
+def new_compiler(plat=None, compiler=None, verbose=0, dry_run=False,
+                 force=False):
     """Generate an instance of some CCompiler subclass for the supplied
     platform/compiler combination.  'plat' defaults to 'os.name'
     (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
@@ -162,9 +177,9 @@
         msg = "don't know how to compile C/C++ code on platform '%s'" % plat
         if compiler is not None:
             msg = msg + " with '%s' compiler" % compiler
-        raise DistutilsPlatformError(msg)
+        raise PackagingPlatformError(msg)
 
-    if isinstance(cls, str):
+    if isinstance(cls, basestring):
         cls = resolve_name(cls)
         _COMPILERS[compiler] = cls
 
@@ -200,26 +215,24 @@
     pp_opts = []
     for macro in macros:
 
-        if not (isinstance(macro, tuple) and
-                1 <= len (macro) <= 2):
-            raise TypeError, \
-                  ("bad macro definition '%s': " +
-                   "each element of 'macros' list must be a 1- or 2-tuple") % \
-                  macro
+        if not isinstance(macro, tuple) and 1 <= len(macro) <= 2:
+            raise TypeError(
+                "bad macro definition '%s': each element of 'macros'"
+                "list must be a 1- or 2-tuple" % macro)
 
-        if len (macro) == 1:        # undefine this macro
-            pp_opts.append ("-U%s" % macro[0])
-        elif len (macro) == 2:
+        if len(macro) == 1:        # undefine this macro
+            pp_opts.append("-U%s" % macro[0])
+        elif len(macro) == 2:
             if macro[1] is None:    # define with no explicit value
-                pp_opts.append ("-D%s" % macro[0])
+                pp_opts.append("-D%s" % macro[0])
             else:
                 # XXX *don't* need to be clever about quoting the
                 # macro value here, because we're going to avoid the
                 # shell at all costs when we spawn the command!
-                pp_opts.append ("-D%s=%s" % macro)
+                pp_opts.append("-D%s=%s" % macro)
 
     for dir in include_dirs:
-        pp_opts.append ("-I%s" % dir)
+        pp_opts.append("-I%s" % dir)
 
     return pp_opts
 
@@ -258,7 +271,7 @@
             if lib_file is not None:
                 lib_opts.append(lib_file)
             else:
-                compiler.warn("no library file corresponding to "
+                logger.warning("no library file corresponding to "
                               "'%s' found (skipping)" % lib)
         else:
             lib_opts.append(compiler.library_option(lib))
diff --git a/distutils2/compiler/bcppcompiler.py b/distutils2/compiler/bcppcompiler.py
--- a/distutils2/compiler/bcppcompiler.py
+++ b/distutils2/compiler/bcppcompiler.py
@@ -1,8 +1,4 @@
-"""distutils.bcppcompiler
-
-Contains BorlandCCompiler, an implementation of the abstract CCompiler class
-for the Borland C++ compiler.
-"""
+"""CCompiler implementation for the Borland C++ compiler."""
 
 # This implementation by Lyle Johnson, based on the original msvccompiler.py
 # module and using the directions originally published by Gordon Williams.
@@ -10,10 +6,11 @@
 # XXX looks like there's a LOT of overlap between these two classes:
 # someone should sit down and factor out the common code as
 # WindowsCCompiler!  --GPW
-import os
 
-from distutils2.errors import (DistutilsExecError, CompileError, LibError,
-                               LinkError, UnknownFileError)
+import os, sys
+
+from distutils2.errors import (PackagingExecError, CompileError, LibError,
+                              LinkError, UnknownFileError)
 from distutils2.compiler.ccompiler import CCompiler
 from distutils2.compiler import gen_preprocess_options
 from distutils2.file_util import write_file
@@ -50,12 +47,8 @@
     exe_extension = '.exe'
 
 
-    def __init__ (self,
-                  verbose=0,
-                  dry_run=0,
-                  force=0):
-
-        CCompiler.__init__ (self, verbose, dry_run, force)
+    def __init__(self, verbose=0, dry_run=False, force=False):
+        CCompiler.__init__(self, verbose, dry_run, force)
 
         # These executables are assumed to all be in the path.
         # Borland doesn't seem to use any special registry settings to
@@ -79,18 +72,18 @@
     # -- Worker methods ------------------------------------------------
 
     def compile(self, sources,
-                output_dir=None, macros=None, include_dirs=None, debug=0,
+                output_dir=None, macros=None, include_dirs=None, debug=False,
                 extra_preargs=None, extra_postargs=None, depends=None):
 
         macros, objects, extra_postargs, pp_opts, build = \
                 self._setup_compile(output_dir, macros, include_dirs, sources,
                                     depends, extra_postargs)
         compile_opts = extra_preargs or []
-        compile_opts.append ('-c')
+        compile_opts.append('-c')
         if debug:
-            compile_opts.extend (self.compile_options_debug)
+            compile_opts.extend(self.compile_options_debug)
         else:
-            compile_opts.extend (self.compile_options)
+            compile_opts.extend(self.compile_options)
 
         for obj in objects:
             try:
@@ -110,9 +103,9 @@
             if ext == '.rc':
                 # This needs to be compiled to a .res file -- do it now.
                 try:
-                    self.spawn (["brcc32", "-fo", obj, src])
-                except DistutilsExecError, msg:
-                    raise CompileError, msg
+                    self.spawn(["brcc32", "-fo", obj, src])
+                except PackagingExecError:
+                    raise CompileError(sys.exc_info()[1])
                 continue # the 'for' loop
 
             # The next two are both for the real compiler.
@@ -132,72 +125,53 @@
             # Note that the source file names must appear at the end of
             # the command line.
             try:
-                self.spawn ([self.cc] + compile_opts + pp_opts +
-                            [input_opt, output_opt] +
-                            extra_postargs + [src])
-            except DistutilsExecError, msg:
-                raise CompileError, msg
+                self.spawn([self.cc] + compile_opts + pp_opts +
+                           [input_opt, output_opt] +
+                           extra_postargs + [src])
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
 
         return objects
 
-    # compile ()
 
+    def create_static_lib(self, objects, output_libname, output_dir=None,
+                          debug=False, target_lang=None):
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        output_filename = \
+            self.library_filename(output_libname, output_dir=output_dir)
 
-    def create_static_lib (self,
-                           objects,
-                           output_libname,
-                           output_dir=None,
-                           debug=0,
-                           target_lang=None):
-
-        (objects, output_dir) = self._fix_object_args (objects, output_dir)
-        output_filename = \
-            self.library_filename (output_libname, output_dir=output_dir)
-
-        if self._need_link (objects, output_filename):
+        if self._need_link(objects, output_filename):
             lib_args = [output_filename, '/u'] + objects
             if debug:
                 pass                    # XXX what goes here?
             try:
-                self.spawn ([self.lib] + lib_args)
-            except DistutilsExecError, msg:
-                raise LibError, msg
+                self.spawn([self.lib] + lib_args)
+            except PackagingExecError:
+                raise LibError(sys.exc_info()[1])
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
-    # create_static_lib ()
 
-
-    def link (self,
-              target_desc,
-              objects,
-              output_filename,
-              output_dir=None,
-              libraries=None,
-              library_dirs=None,
-              runtime_library_dirs=None,
-              export_symbols=None,
-              debug=0,
-              extra_preargs=None,
-              extra_postargs=None,
-              build_temp=None,
-              target_lang=None):
+    def link(self, target_desc, objects, output_filename, output_dir=None,
+             libraries=None, library_dirs=None, runtime_library_dirs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
+             extra_postargs=None, build_temp=None, target_lang=None):
 
         # XXX this ignores 'build_temp'!  should follow the lead of
         # msvccompiler.py
 
-        (objects, output_dir) = self._fix_object_args (objects, output_dir)
-        (libraries, library_dirs, runtime_library_dirs) = \
-            self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        libraries, library_dirs, runtime_library_dirs = \
+            self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
 
         if runtime_library_dirs:
             logger.warning("don't know what to do with "
                            "'runtime_library_dirs': %r", runtime_library_dirs)
 
         if output_dir is not None:
-            output_filename = os.path.join (output_dir, output_filename)
+            output_filename = os.path.join(output_dir, output_filename)
 
-        if self._need_link (objects, output_filename):
+        if self._need_link(objects, output_filename):
 
             # Figure out linker args based on type of target.
             if target_desc == CCompiler.EXECUTABLE:
@@ -218,10 +192,10 @@
             if export_symbols is None:
                 def_file = ''
             else:
-                head, tail = os.path.split (output_filename)
-                modname, ext = os.path.splitext (tail)
+                head, tail = os.path.split(output_filename)
+                modname, ext = os.path.splitext(tail)
                 temp_dir = os.path.dirname(objects[0]) # preserve tree structure
-                def_file = os.path.join (temp_dir, '%s.def' % modname)
+                def_file = os.path.join(temp_dir, '%s.def' % modname)
                 contents = ['EXPORTS']
                 for sym in (export_symbols or []):
                     contents.append('  %s=_%s' % (sym, sym))
@@ -229,13 +203,13 @@
                              "writing %s" % def_file)
 
             # Borland C++ has problems with '/' in paths
-            objects2 = map(os.path.normpath, objects)
+            objects2 = [os.path.normpath(o) for o in objects]
             # split objects in .obj and .res files
             # Borland C++ needs them at different positions in the command line
             objects = [startup_obj]
             resources = []
             for file in objects2:
-                (base, ext) = os.path.splitext(os.path.normcase(file))
+                base, ext = os.path.splitext(os.path.normcase(file))
                 if ext == '.res':
                     resources.append(file)
                 else:
@@ -260,7 +234,7 @@
             # them.  Arghghh!.  Apparently it works fine as coded...
 
             # name of dll/exe file
-            ld_args.extend([',',output_filename])
+            ld_args.extend((',',output_filename))
             # no map file and start libraries
             ld_args.append(',,')
 
@@ -276,11 +250,11 @@
                     ld_args.append(libfile)
 
             # some default libraries
-            ld_args.append ('import32')
-            ld_args.append ('cw32mt')
+            ld_args.append('import32')
+            ld_args.append('cw32mt')
 
             # def file for export symbols
-            ld_args.extend([',',def_file])
+            ld_args.extend((',',def_file))
             # add resource files
             ld_args.append(',')
             ld_args.extend(resources)
@@ -291,27 +265,25 @@
             if extra_postargs:
                 ld_args.extend(extra_postargs)
 
-            self.mkpath (os.path.dirname (output_filename))
+            self.mkpath(os.path.dirname(output_filename))
             try:
-                self.spawn ([self.linker] + ld_args)
-            except DistutilsExecError, msg:
-                raise LinkError, msg
+                self.spawn([self.linker] + ld_args)
+            except PackagingExecError:
+                raise LinkError(sys.exc_info()[1])
 
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
-    # link ()
-
     # -- Miscellaneous methods -----------------------------------------
 
 
-    def find_library_file (self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         # List of effective library names to try, in order of preference:
         # xxx_bcpp.lib is better than xxx.lib
         # and xxx_d.lib is better than xxx.lib if debug is set
         #
         # The "_bcpp" suffix is to handle a Python installation for people
-        # with multiple compilers (primarily Distutils hackers, I suspect
+        # with multiple compilers (primarily Packaging hackers, I suspect
         # ;-).  The idea is they'd have one static library for each
         # compiler they care about, since (almost?) every Windows compiler
         # seems to have a different format for static libraries.
@@ -331,43 +303,35 @@
             return None
 
     # overwrite the one from CCompiler to support rc and res-files
-    def object_filenames (self,
-                          source_filenames,
-                          strip_dir=0,
-                          output_dir=''):
-        if output_dir is None: output_dir = ''
+    def object_filenames(self, source_filenames, strip_dir=False,
+                         output_dir=''):
+        if output_dir is None:
+            output_dir = ''
         obj_names = []
         for src_name in source_filenames:
             # use normcase to make sure '.rc' is really '.rc' and not '.RC'
-            (base, ext) = os.path.splitext (os.path.normcase(src_name))
+            base, ext = os.path.splitext(os.path.normcase(src_name))
             if ext not in (self.src_extensions + ['.rc','.res']):
-                raise UnknownFileError, \
-                      "unknown file type '%s' (from '%s')" % \
-                      (ext, src_name)
+                raise UnknownFileError("unknown file type '%s' (from '%s')" % \
+                      (ext, src_name))
             if strip_dir:
-                base = os.path.basename (base)
+                base = os.path.basename(base)
             if ext == '.res':
                 # these can go unchanged
-                obj_names.append (os.path.join (output_dir, base + ext))
+                obj_names.append(os.path.join(output_dir, base + ext))
             elif ext == '.rc':
                 # these need to be compiled to .res-files
-                obj_names.append (os.path.join (output_dir, base + '.res'))
+                obj_names.append(os.path.join(output_dir, base + '.res'))
             else:
-                obj_names.append (os.path.join (output_dir,
-                                            base + self.obj_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.obj_extension))
         return obj_names
 
-    # object_filenames ()
 
-    def preprocess (self,
-                    source,
-                    output_file=None,
-                    macros=None,
-                    include_dirs=None,
-                    extra_preargs=None,
-                    extra_postargs=None):
-
-        (_, macros, include_dirs) = \
+    def preprocess(self, source, output_file=None, macros=None,
+                   include_dirs=None, extra_preargs=None,
+                   extra_postargs=None):
+        _, macros, include_dirs = \
             self._fix_compile_args(None, macros, include_dirs)
         pp_opts = gen_preprocess_options(macros, include_dirs)
         pp_args = ['cpp32.exe'] + pp_opts
@@ -387,8 +351,7 @@
                 self.mkpath(os.path.dirname(output_file))
             try:
                 self.spawn(pp_args)
-            except DistutilsExecError, msg:
-                print msg
-                raise CompileError, msg
-
-    # preprocess()
+            except PackagingExecError:
+                msg = sys.exc_info()[1]
+                print(msg)
+                raise CompileError(msg)
diff --git a/distutils2/compiler/ccompiler.py b/distutils2/compiler/ccompiler.py
--- a/distutils2/compiler/ccompiler.py
+++ b/distutils2/compiler/ccompiler.py
@@ -1,18 +1,15 @@
-"""distutils.ccompiler
+"""Abstract base class for compilers.
 
-Contains CCompiler, an abstract base class that defines the interface
-for the Distutils compiler abstraction model."""
+This modules contains CCompiler, an abstract base class that defines the
+interface for the compiler abstraction model used by distutils2.
+"""
 
-
+import os
 import sys
-import os
-import re
 from shutil import move
-
-from distutils2.errors import (CompileError, LinkError, UnknownFileError,
-                               DistutilsPlatformError, DistutilsModuleError)
+from distutils2 import logger
 from distutils2.util import split_quoted, execute, newer_group, spawn
-from distutils2 import logger
+from distutils2.errors import (CompileError, LinkError, UnknownFileError)
 from distutils2.compiler import gen_preprocess_options
 
 
@@ -75,15 +72,15 @@
     # what language to use when mixing source types. For example, if some
     # extension has two files with ".c" extension, and one with ".cpp", it
     # is still linked as c++.
-    language_map = {".c"   : "c",
-                    ".cc"  : "c++",
-                    ".cpp" : "c++",
-                    ".cxx" : "c++",
-                    ".m"   : "objc",
+    language_map = {".c": "c",
+                    ".cc": "c++",
+                    ".cpp": "c++",
+                    ".cxx": "c++",
+                    ".m": "objc",
                    }
     language_order = ["c++", "objc", "c"]
 
-    def __init__ (self, verbose=0, dry_run=0, force=0):
+    def __init__(self, verbose=0, dry_run=False, force=False):
         self.dry_run = dry_run
         self.force = force
         self.verbose = verbose
@@ -116,7 +113,7 @@
         # named library files) to include on any link
         self.objects = []
 
-        for key, value in self.executables.iteritems():
+        for key, value in self.executables.items():
             self.set_executable(key, value)
 
     def set_executables(self, **args):
@@ -145,15 +142,14 @@
         # discovered at run-time, since there are many different ways to do
         # basically the same things with Unix C compilers.
 
-        for key, value in args.iteritems():
+        for key, value in args.items():
             if key not in self.executables:
-                raise ValueError, \
-                      "unknown executable '%s' for class %s" % \
-                      (key, self.__class__.__name__)
+                raise ValueError("unknown executable '%s' for class %s" % \
+                      (key, self.__class__.__name__))
             self.set_executable(key, value)
 
     def set_executable(self, key, value):
-        if isinstance(value, str):
+        if isinstance(value, basestring):
             setattr(self, key, split_quoted(value))
         else:
             setattr(self, key, value)
@@ -173,14 +169,13 @@
         """
         for defn in definitions:
             if not (isinstance(defn, tuple) and
-                    (len (defn) == 1 or
-                     (len (defn) == 2 and
-                      (isinstance(defn[1], str) or defn[1] is None))) and
-                    isinstance(defn[0], str)):
-                raise TypeError, \
-                      ("invalid macro definition '%s': " % defn) + \
+                    (len(defn) == 1 or
+                     (len(defn) == 2 and
+                      (isinstance(defn[1], basestring) or defn[1] is None))) and
+                    isinstance(defn[0], basestring)):
+                raise TypeError(("invalid macro definition '%s': " % defn) + \
                       "must be tuple (string,), (string, string), or " + \
-                      "(string, None)"
+                      "(string, None)")
 
 
     # -- Bookkeeping methods -------------------------------------------
@@ -194,12 +189,12 @@
         """
         # Delete from the list of macro definitions/undefinitions if
         # already there (so that this one will take precedence).
-        i = self._find_macro (name)
+        i = self._find_macro(name)
         if i is not None:
             del self.macros[i]
 
         defn = (name, value)
-        self.macros.append (defn)
+        self.macros.append(defn)
 
     def undefine_macro(self, name):
         """Undefine a preprocessor macro for all compilations driven by
@@ -212,12 +207,12 @@
         """
         # Delete from the list of macro definitions/undefinitions if
         # already there (so that this one will take precedence).
-        i = self._find_macro (name)
+        i = self._find_macro(name)
         if i is not None:
             del self.macros[i]
 
         undefn = (name,)
-        self.macros.append (undefn)
+        self.macros.append(undefn)
 
     def add_include_dir(self, dir):
         """Add 'dir' to the list of directories that will be searched for
@@ -225,7 +220,7 @@
         the order in which they are supplied by successive calls to
         'add_include_dir()'.
         """
-        self.include_dirs.append (dir)
+        self.include_dirs.append(dir)
 
     def set_include_dirs(self, dirs):
         """Set the list of directories that will be searched to 'dirs' (a
@@ -251,7 +246,7 @@
         names; the linker will be instructed to link against libraries as
         many times as they are mentioned.
         """
-        self.libraries.append (libname)
+        self.libraries.append(libname)
 
     def set_libraries(self, libnames):
         """Set the list of libraries to be included in all links driven by
@@ -312,36 +307,35 @@
     # (here for the convenience of subclasses)
 
     # Helper method to prep compiler in subclass compile() methods
-
     def _setup_compile(self, outdir, macros, incdirs, sources, depends,
                        extra):
         """Process arguments and decide which source files to compile."""
         if outdir is None:
             outdir = self.output_dir
-        elif not isinstance(outdir, str):
-            raise TypeError, "'output_dir' must be a string or None"
+        elif not isinstance(outdir, basestring):
+            raise TypeError("'output_dir' must be a string or None")
 
         if macros is None:
             macros = self.macros
         elif isinstance(macros, list):
             macros = macros + (self.macros or [])
         else:
-            raise TypeError, "'macros' (if supplied) must be a list of tuples"
+            raise TypeError("'macros' (if supplied) must be a list of tuples")
 
         if incdirs is None:
             incdirs = self.include_dirs
         elif isinstance(incdirs, (list, tuple)):
             incdirs = list(incdirs) + (self.include_dirs or [])
         else:
-            raise TypeError, \
-                  "'include_dirs' (if supplied) must be a list of strings"
+            raise TypeError(
+                "'include_dirs' (if supplied) must be a list of strings")
 
         if extra is None:
             extra = []
 
         # Get the list of expected output (object) files
         objects = self.object_filenames(sources,
-                                        strip_dir=0,
+                                        strip_dir=False,
                                         output_dir=outdir)
         assert len(objects) == len(sources)
 
@@ -358,7 +352,7 @@
         return macros, objects, extra, pp_opts, build
 
     def _get_cc_args(self, pp_opts, debug, before):
-        # works for unixccompiler, emxccompiler, cygwinccompiler
+        # works for unixccompiler and cygwinccompiler
         cc_args = pp_opts + ['-c']
         if debug:
             cc_args[:0] = ['-g']
@@ -378,23 +372,23 @@
         """
         if output_dir is None:
             output_dir = self.output_dir
-        elif not isinstance(output_dir, str):
-            raise TypeError, "'output_dir' must be a string or None"
+        elif not isinstance(output_dir, basestring):
+            raise TypeError("'output_dir' must be a string or None")
 
         if macros is None:
             macros = self.macros
         elif isinstance(macros, list):
             macros = macros + (self.macros or [])
         else:
-            raise TypeError, "'macros' (if supplied) must be a list of tuples"
+            raise TypeError("'macros' (if supplied) must be a list of tuples")
 
         if include_dirs is None:
             include_dirs = self.include_dirs
         elif isinstance(include_dirs, (list, tuple)):
-            include_dirs = list (include_dirs) + (self.include_dirs or [])
+            include_dirs = list(include_dirs) + (self.include_dirs or [])
         else:
-            raise TypeError, \
-                  "'include_dirs' (if supplied) must be a list of strings"
+            raise TypeError(
+                "'include_dirs' (if supplied) must be a list of strings")
 
         return output_dir, macros, include_dirs
 
@@ -405,16 +399,15 @@
         'objects' and 'output_dir'.
         """
         if not isinstance(objects, (list, tuple)):
-            raise TypeError, \
-                  "'objects' must be a list or tuple of strings"
-        objects = list (objects)
+            raise TypeError("'objects' must be a list or tuple of strings")
+        objects = list(objects)
 
         if output_dir is None:
             output_dir = self.output_dir
-        elif not isinstance(output_dir, str):
-            raise TypeError, "'output_dir' must be a string or None"
+        elif not isinstance(output_dir, basestring):
+            raise TypeError("'output_dir' must be a string or None")
 
-        return (objects, output_dir)
+        return objects, output_dir
 
     def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
         """Typecheck and fix up some of the arguments supplied to the
@@ -426,37 +419,36 @@
         if libraries is None:
             libraries = self.libraries
         elif isinstance(libraries, (list, tuple)):
-            libraries = list (libraries) + (self.libraries or [])
+            libraries = list(libraries) + (self.libraries or [])
         else:
-            raise TypeError, \
-                  "'libraries' (if supplied) must be a list of strings"
+            raise TypeError(
+                "'libraries' (if supplied) must be a list of strings")
 
         if library_dirs is None:
             library_dirs = self.library_dirs
         elif isinstance(library_dirs, (list, tuple)):
-            library_dirs = list (library_dirs) + (self.library_dirs or [])
+            library_dirs = list(library_dirs) + (self.library_dirs or [])
         else:
-            raise TypeError, \
-                  "'library_dirs' (if supplied) must be a list of strings"
+            raise TypeError(
+                "'library_dirs' (if supplied) must be a list of strings")
 
         if runtime_library_dirs is None:
             runtime_library_dirs = self.runtime_library_dirs
         elif isinstance(runtime_library_dirs, (list, tuple)):
-            runtime_library_dirs = (list (runtime_library_dirs) +
+            runtime_library_dirs = (list(runtime_library_dirs) +
                                     (self.runtime_library_dirs or []))
         else:
-            raise TypeError, \
-                  "'runtime_library_dirs' (if supplied) " + \
-                  "must be a list of strings"
+            raise TypeError("'runtime_library_dirs' (if supplied) "
+                            "must be a list of strings")
 
-        return (libraries, library_dirs, runtime_library_dirs)
+        return libraries, library_dirs, runtime_library_dirs
 
     def _need_link(self, objects, output_file):
         """Return true if we need to relink the files listed in 'objects'
         to recreate 'output_file'.
         """
         if self.force:
-            return 1
+            return True
         else:
             if self.dry_run:
                 newer = newer_group(objects, output_file, missing='newer')
@@ -501,7 +493,7 @@
         pass
 
     def compile(self, sources, output_dir=None, macros=None,
-                include_dirs=None, debug=0, extra_preargs=None,
+                include_dirs=None, debug=False, extra_preargs=None,
                 extra_postargs=None, depends=None):
         """Compile one or more source files.
 
@@ -577,7 +569,7 @@
         pass
 
     def create_static_lib(self, objects, output_libname, output_dir=None,
-                          debug=0, target_lang=None):
+                          debug=False, target_lang=None):
         """Link a bunch of stuff together to create a static library file.
         The "bunch of stuff" consists of the list of object files supplied
         as 'objects', the extra object files supplied to
@@ -609,7 +601,7 @@
 
     def link(self, target_desc, objects, output_filename, output_dir=None,
              libraries=None, library_dirs=None, runtime_library_dirs=None,
-             export_symbols=None, debug=0, extra_preargs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
              extra_postargs=None, build_temp=None, target_lang=None):
         """Link a bunch of stuff together to create an executable or
         shared library file.
@@ -662,7 +654,7 @@
     def link_shared_lib(self, objects, output_libname, output_dir=None,
                         libraries=None, library_dirs=None,
                         runtime_library_dirs=None, export_symbols=None,
-                        debug=0, extra_preargs=None, extra_postargs=None,
+                        debug=False, extra_preargs=None, extra_postargs=None,
                         build_temp=None, target_lang=None):
         self.link(CCompiler.SHARED_LIBRARY, objects,
                   self.library_filename(output_libname, lib_type='shared'),
@@ -671,11 +663,10 @@
                   export_symbols, debug,
                   extra_preargs, extra_postargs, build_temp, target_lang)
 
-
     def link_shared_object(self, objects, output_filename, output_dir=None,
                            libraries=None, library_dirs=None,
                            runtime_library_dirs=None, export_symbols=None,
-                           debug=0, extra_preargs=None, extra_postargs=None,
+                           debug=False, extra_preargs=None, extra_postargs=None,
                            build_temp=None, target_lang=None):
         self.link(CCompiler.SHARED_OBJECT, objects,
                   output_filename, output_dir,
@@ -685,8 +676,9 @@
 
     def link_executable(self, objects, output_progname, output_dir=None,
                         libraries=None, library_dirs=None,
-                        runtime_library_dirs=None, debug=0, extra_preargs=None,
-                        extra_postargs=None, target_lang=None):
+                        runtime_library_dirs=None, debug=False,
+                        extra_preargs=None, extra_postargs=None,
+                        target_lang=None):
         self.link(CCompiler.EXECUTABLE, objects,
                   self.executable_filename(output_progname), output_dir,
                   libraries, library_dirs, runtime_library_dirs, None,
@@ -736,8 +728,7 @@
         if library_dirs is None:
             library_dirs = []
         fd, fname = tempfile.mkstemp(".c", funcname, text=True)
-        f = os.fdopen(fd, "w")
-        try:
+        with os.fdopen(fd, "w") as f:
             for incl in includes:
                 f.write("""#include "%s"\n""" % incl)
             f.write("""\
@@ -745,8 +736,6 @@
     %s();
 }
 """ % funcname)
-        finally:
-            f.close()
         try:
             objects = self.compile([fname], include_dirs=include_dirs)
         except CompileError:
@@ -760,10 +749,10 @@
             return False
         return True
 
-    def find_library_file (self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         """Search the specified list of directories for a static or shared
         library file 'lib' and return the full path to that file.  If
-        'debug' true, look for a debugging version (if that makes sense on
+        'debug' is true, look for a debugging version (if that makes sense on
         the current platform).  Return None if 'lib' wasn't found in any of
         the specified directories.
         """
@@ -803,44 +792,45 @@
     #   * exe_extension -
     #     extension for executable files, eg. '' or '.exe'
 
-    def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
+    def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
         if output_dir is None:
             output_dir = ''
         obj_names = []
         for src_name in source_filenames:
             base, ext = os.path.splitext(src_name)
-            base = os.path.splitdrive(base)[1] # Chop off the drive
+            base = os.path.splitdrive(base)[1]  # Chop off the drive
             base = base[os.path.isabs(base):]  # If abs, chop off leading /
             if ext not in self.src_extensions:
-                raise UnknownFileError, \
-                      "unknown file type '%s' (from '%s')" % (ext, src_name)
+                raise UnknownFileError("unknown file type '%s' (from '%s')" %
+                                       (ext, src_name))
             if strip_dir:
                 base = os.path.basename(base)
             obj_names.append(os.path.join(output_dir,
                                           base + self.obj_extension))
         return obj_names
 
-    def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
+    def shared_object_filename(self, basename, strip_dir=False, output_dir=''):
         assert output_dir is not None
         if strip_dir:
-            basename = os.path.basename (basename)
+            basename = os.path.basename(basename)
         return os.path.join(output_dir, basename + self.shared_lib_extension)
 
-    def executable_filename(self, basename, strip_dir=0, output_dir=''):
+    def executable_filename(self, basename, strip_dir=False, output_dir=''):
         assert output_dir is not None
         if strip_dir:
-            basename = os.path.basename (basename)
+            basename = os.path.basename(basename)
         return os.path.join(output_dir, basename + (self.exe_extension or ''))
 
     def library_filename(self, libname, lib_type='static',     # or 'shared'
-                         strip_dir=0, output_dir=''):
+                         strip_dir=False, output_dir=''):
         assert output_dir is not None
         if lib_type not in ("static", "shared", "dylib"):
-            raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\""
+            raise ValueError(
+                "'lib_type' must be 'static', 'shared' or 'dylib'")
         fmt = getattr(self, lib_type + "_lib_format")
         ext = getattr(self, lib_type + "_lib_extension")
 
-        dir, base = os.path.split (libname)
+        dir, base = os.path.split(libname)
         filename = fmt % (base, ext)
         if strip_dir:
             dir = ''
@@ -850,19 +840,6 @@
 
     # -- Utility methods -----------------------------------------------
 
-    # TODO use logging.info
-    def announce(self, msg, level=None):
-        logger.debug(msg)
-
-    def debug_print(self, msg):
-        from distutils2.debug import DEBUG
-        if DEBUG:
-            print msg
-
-    # TODO use logging.warn
-    def warn(self, msg):
-        sys.stderr.write("warning: %s\n" % msg)
-
     def execute(self, func, args, msg=None, level=1):
         execute(func, args, msg, self.dry_run)
 
@@ -870,11 +847,12 @@
         spawn(cmd, dry_run=self.dry_run)
 
     def move_file(self, src, dst):
+        logger.info("moving %r to %r", src, dst)
         if self.dry_run:
-            return # XXX log ?
+            return
         return move(src, dst)
 
-    def mkpath(self, name, mode=0777):
+    def mkpath(self, name, mode=0o777):
         name = os.path.normpath(name)
         if os.path.isdir(name) or name == '':
             return
diff --git a/distutils2/compiler/cygwinccompiler.py b/distutils2/compiler/cygwinccompiler.py
--- a/distutils2/compiler/cygwinccompiler.py
+++ b/distutils2/compiler/cygwinccompiler.py
@@ -1,9 +1,9 @@
-"""distutils.cygwinccompiler
+"""CCompiler implementations for Cygwin and mingw32 versions of GCC.
 
-Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
-handles the Cygwin port of the GNU C compiler to Windows.  It also contains
-the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
-cygwin in no-cygwin mode).
+This module contains the CygwinCCompiler class, a subclass of
+UnixCCompiler that handles the Cygwin port of the GNU C compiler to
+Windows, and the Mingw32CCompiler class which handles the mingw32 port
+of GCC (same as cygwin in no-cygwin mode).
 """
 
 # problems:
@@ -48,15 +48,13 @@
 
 import os
 import sys
-import copy
-import re
-from warnings import warn
 
+from distutils2 import logger
 from distutils2.compiler.unixccompiler import UnixCCompiler
 from distutils2.util import write_file
-from distutils2.errors import DistutilsExecError, CompileError, UnknownFileError
+from distutils2.errors import PackagingExecError, CompileError, UnknownFileError
 from distutils2.util import get_compiler_versions
-from distutils2._backport import sysconfig
+import sysconfig
 
 
 def get_msvcr():
@@ -94,13 +92,12 @@
     shared_lib_format = "%s%s"
     exe_extension = ".exe"
 
-    def __init__(self, verbose=0, dry_run=0, force=0):
+    def __init__(self, verbose=0, dry_run=False, force=False):
 
         UnixCCompiler.__init__(self, verbose, dry_run, force)
 
         status, details = check_config_h()
-        self.debug_print("Python's GCC status: %s (details: %s)" %
-                         (status, details))
+        logger.debug("Python's GCC status: %s (details: %s)", status, details)
         if status is not CONFIG_H_OK:
             self.warn(
                 "Python's pyconfig.h doesn't seem to support your compiler. "
@@ -110,10 +107,10 @@
 
         self.gcc_version, self.ld_version, self.dllwrap_version = \
             get_compiler_versions()
-        self.debug_print(self.name + ": gcc %s, ld %s, dllwrap %s\n" %
-                         (self.gcc_version,
-                          self.ld_version,
-                          self.dllwrap_version) )
+        logger.debug(self.name + ": gcc %s, ld %s, dllwrap %s\n",
+                     self.gcc_version,
+                     self.ld_version,
+                     self.dllwrap_version)
 
         # ld_version >= "2.10.90" and < "2.13" should also be able to use
         # gcc -mdll instead of dllwrap
@@ -154,29 +151,29 @@
             self.dll_libraries = get_msvcr()
 
     def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
-        """Compiles the source by spawing GCC and windres if needed."""
+        """Compile the source by spawning GCC and windres if needed."""
         if ext == '.rc' or ext == '.res':
             # gcc needs '.res' and '.rc' compiled to object files !!!
             try:
                 self.spawn(["windres", "-i", src, "-o", obj])
-            except DistutilsExecError, msg:
-                raise CompileError, msg
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
         else: # for other files use the C-compiler
             try:
                 self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
                            extra_postargs)
-            except DistutilsExecError, msg:
-                raise CompileError, msg
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
 
     def link(self, target_desc, objects, output_filename, output_dir=None,
              libraries=None, library_dirs=None, runtime_library_dirs=None,
-             export_symbols=None, debug=0, extra_preargs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
              extra_postargs=None, build_temp=None, target_lang=None):
         """Link the objects."""
         # use separate copies, so we can modify the lists
-        extra_preargs = copy.copy(extra_preargs or [])
-        libraries = copy.copy(libraries or [])
-        objects = copy.copy(objects or [])
+        extra_preargs = list(extra_preargs or [])
+        libraries = list(libraries or [])
+        objects = list(objects or [])
 
         # Additional libraries
         libraries.extend(self.dll_libraries)
@@ -195,7 +192,7 @@
             # where are the object files
             temp_dir = os.path.dirname(objects[0])
             # name of dll to give the helper files the same base name
-            (dll_name, dll_extension) = os.path.splitext(
+            dll_name, dll_extension = os.path.splitext(
                 os.path.basename(output_filename))
 
             # generate the filenames for these files
@@ -215,13 +212,13 @@
 
             # dllwrap uses different options than gcc/ld
             if self.linker_dll == "dllwrap":
-                extra_preargs.extend(["--output-lib", lib_file])
+                extra_preargs.extend(("--output-lib", lib_file))
                 # for dllwrap we have to use a special option
-                extra_preargs.extend(["--def", def_file])
+                extra_preargs.extend(("--def", def_file))
             # we use gcc/ld here and can be sure ld is >= 2.9.10
             else:
                 # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
-                #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file])
+                #extra_preargs.extend(("-Wl,--out-implib,%s" % lib_file))
                 # for gcc/ld the def-file is specified as any object files
                 objects.append(def_file)
 
@@ -246,7 +243,8 @@
 
     # -- Miscellaneous methods -----------------------------------------
 
-    def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
+    def object_filenames(self, source_filenames, strip_dir=False,
+                         output_dir=''):
         """Adds supports for rc and res files."""
         if output_dir is None:
             output_dir = ''
@@ -255,8 +253,7 @@
             # use normcase to make sure '.rc' is really '.rc' and not '.RC'
             base, ext = os.path.splitext(os.path.normcase(src_name))
             if ext not in (self.src_extensions + ['.rc','.res']):
-                raise UnknownFileError, \
-                      "unknown file type '%s' (from '%s')" % (ext, src_name)
+                raise UnknownFileError("unknown file type '%s' (from '%s')" % (ext, src_name))
             if strip_dir:
                 base = os.path.basename (base)
             if ext in ('.res', '.rc'):
@@ -275,7 +272,7 @@
     name = 'mingw32'
     description = 'MinGW32 compiler'
 
-    def __init__(self, verbose=0, dry_run=0, force=0):
+    def __init__(self, verbose=0, dry_run=False, force=False):
 
         CygwinCCompiler.__init__ (self, verbose, dry_run, force)
 
@@ -347,14 +344,12 @@
     # let's see if __GNUC__ is mentioned in python.h
     fn = sysconfig.get_config_h_filename()
     try:
-        config_h = open(fn)
-        try:
+        with open(fn) as config_h:
             if "__GNUC__" in config_h.read():
                 return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
             else:
                 return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
-        finally:
-            config_h.close()
-    except IOError, exc:
+    except IOError:
+        exc = sys.exc_info()[1]
         return (CONFIG_H_UNCERTAIN,
                 "couldn't read '%s': %s" % (fn, exc.strerror))
diff --git a/distutils2/compiler/extension.py b/distutils2/compiler/extension.py
--- a/distutils2/compiler/extension.py
+++ b/distutils2/compiler/extension.py
@@ -1,10 +1,6 @@
-"""distutils.extension
+"""Class representing C/C++ extension modules."""
 
-Provides the Extension class, used to describe C/C++ extension
-modules in setup scripts."""
-
-
-import warnings
+from distutils2 import logger
 
 # This class is really only used by the "build_ext" command, so it might
 # make sense to put it in distutils.command.build_ext.  However, that
@@ -82,33 +78,22 @@
         build process, but simply not install the failing extension.
     """
 
-    # When adding arguments to this constructor, be sure to update
-    # setup_keywords in core.py.
-    def __init__(self, name, sources,
-                  include_dirs=None,
-                  define_macros=None,
-                  undef_macros=None,
-                  library_dirs=None,
-                  libraries=None,
-                  runtime_library_dirs=None,
-                  extra_objects=None,
-                  extra_compile_args=None,
-                  extra_link_args=None,
-                  export_symbols=None,
-                  swig_opts=None,
-                  depends=None,
-                  language=None,
-                  optional=None,
-                  **kw # To catch unknown keywords
-                 ):
-        if not isinstance(name, str):
+    # **kwargs are allowed so that a warning is emitted instead of an
+    # exception
+    def __init__(self, name, sources, include_dirs=None, define_macros=None,
+                 undef_macros=None, library_dirs=None, libraries=None,
+                 runtime_library_dirs=None, extra_objects=None,
+                 extra_compile_args=None, extra_link_args=None,
+                 export_symbols=None, swig_opts=None, depends=None,
+                 language=None, optional=None, **kw):
+        if not isinstance(name, basestring):
             raise AssertionError("'name' must be a string")
 
         if not isinstance(sources, list):
             raise AssertionError("'sources' must be a list of strings")
 
         for v in sources:
-            if not isinstance(v, str):
+            if not isinstance(v, basestring):
                 raise AssertionError("'sources' must be a list of strings")
 
         self.name = name
@@ -132,5 +117,5 @@
         if len(kw) > 0:
             options = [repr(option) for option in kw]
             options = ', '.join(sorted(options))
-            msg = "Unknown Extension options: %s" % options
-            warnings.warn(msg)
+            logger.warning(
+                'unknown arguments given to Extension: %s', options)
diff --git a/distutils2/compiler/msvc9compiler.py b/distutils2/compiler/msvc9compiler.py
--- a/distutils2/compiler/msvc9compiler.py
+++ b/distutils2/compiler/msvc9compiler.py
@@ -1,10 +1,7 @@
-"""distutils.msvc9compiler
+"""CCompiler implementation for the Microsoft Visual Studio 2008 compiler.
 
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio 2008.
-
-The module is compatible with VS 2005 and VS 2008. You can find legacy support
-for older versions of VS in distutils.msvccompiler.
+The MSVCCompiler class is compatible with VS 2005 and VS 2008.  Legacy
+support for older versions of VS are in the msvccompiler module.
 """
 
 # Written by Perry Stoll
@@ -16,24 +13,24 @@
 import sys
 import re
 
-from distutils2.errors import (DistutilsExecError, DistutilsPlatformError,
-                               CompileError, LibError, LinkError)
+from distutils2.errors import (PackagingExecError, PackagingPlatformError,
+                              CompileError, LibError, LinkError)
 from distutils2.compiler.ccompiler import CCompiler
 from distutils2.compiler import gen_lib_options
 from distutils2 import logger
 from distutils2.util import get_platform
 
-import _winreg
+import winreg
 
-RegOpenKeyEx = _winreg.OpenKeyEx
-RegEnumKey = _winreg.EnumKey
-RegEnumValue = _winreg.EnumValue
-RegError = _winreg.error
+RegOpenKeyEx = winreg.OpenKeyEx
+RegEnumKey = winreg.EnumKey
+RegEnumValue = winreg.EnumValue
+RegError = winreg.error
 
-HKEYS = (_winreg.HKEY_USERS,
-         _winreg.HKEY_CURRENT_USER,
-         _winreg.HKEY_LOCAL_MACHINE,
-         _winreg.HKEY_CLASSES_ROOT)
+HKEYS = (winreg.HKEY_USERS,
+         winreg.HKEY_CURRENT_USER,
+         winreg.HKEY_LOCAL_MACHINE,
+         winreg.HKEY_CLASSES_ROOT)
 
 VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
 WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
@@ -132,11 +129,11 @@
             else:
                 raise KeyError("sdkinstallrootv2.0")
         except KeyError:
-            raise DistutilsPlatformError(
-            """Python was built with Visual Studio 2008;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2008 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
+            raise PackagingPlatformError(
+"""Python was built with Visual Studio 2008; extensions must be built with a
+compiler than can generate compatible binaries. Visual Studio 2008 was not
+found on this system. If you have Cygwin installed, you can try compiling
+with MingW32, by passing "-c mingw32" to pysetup.""")
 
         if version >= 9.0:
             self.set_macro("FrameworkVersion", self.vsbase, "clr version")
@@ -153,7 +150,7 @@
                 self.macros["$(FrameworkVersion)"] = d["version"]
 
     def sub(self, s):
-        for k, v in self.macros.iteritems():
+        for k, v in self.macros.items():
             s = s.replace(k, v)
         return s
 
@@ -247,7 +244,7 @@
     result = {}
 
     if vcvarsall is None:
-        raise DistutilsPlatformError("Unable to find vcvarsall.bat")
+        raise PackagingPlatformError("Unable to find vcvarsall.bat")
     logger.debug("calling 'vcvarsall.bat %s' (version=%s)", arch, version)
     popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
                              stdout=subprocess.PIPE,
@@ -255,7 +252,7 @@
 
     stdout, stderr = popen.communicate()
     if popen.wait() != 0:
-        raise DistutilsPlatformError(stderr.decode("mbcs"))
+        raise PackagingPlatformError(stderr.decode("mbcs"))
 
     stdout = stdout.decode("mbcs")
     for line in stdout.split("\n"):
@@ -278,7 +275,7 @@
 # More globals
 VERSION = get_build_version()
 if VERSION < 8.0:
-    raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
+    raise PackagingPlatformError("VC %0.1f is not supported by this module" % VERSION)
 # MACROS = MacroExpander(VERSION)
 
 class MSVCCompiler(CCompiler) :
@@ -312,8 +309,8 @@
     static_lib_format = shared_lib_format = '%s%s'
     exe_extension = '.exe'
 
-    def __init__(self, verbose=0, dry_run=0, force=0):
-        CCompiler.__init__ (self, verbose, dry_run, force)
+    def __init__(self, verbose=0, dry_run=False, force=False):
+        CCompiler.__init__(self, verbose, dry_run, force)
         self.__version = VERSION
         self.__root = r"Software\Microsoft\VisualStudio"
         # self.__macros = MACROS
@@ -331,7 +328,7 @@
         # sanity check for platforms to prevent obscure errors later.
         ok_plats = 'win32', 'win-amd64', 'win-ia64'
         if plat_name not in ok_plats:
-            raise DistutilsPlatformError("--plat-name must be one of %s" %
+            raise PackagingPlatformError("--plat-name must be one of %s" %
                                          (ok_plats,))
 
         if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
@@ -359,12 +356,12 @@
             vc_env = query_vcvarsall(VERSION, plat_spec)
 
             # take care to only use strings in the environment.
-            self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)
-            os.environ['lib'] = vc_env['lib'].encode('mbcs')
-            os.environ['include'] = vc_env['include'].encode('mbcs')
+            self.__paths = vc_env['path'].split(os.pathsep)
+            os.environ['lib'] = vc_env['lib']
+            os.environ['include'] = vc_env['include']
 
             if len(self.__paths) == 0:
-                raise DistutilsPlatformError("Python was built with %s, "
+                raise PackagingPlatformError("Python was built with %s, "
                        "and extensions need to be built with the same "
                        "version of the compiler, but it isn't installed."
                        % self.__product)
@@ -412,37 +409,37 @@
 
     def object_filenames(self,
                          source_filenames,
-                         strip_dir=0,
+                         strip_dir=False,
                          output_dir=''):
         # Copied from ccompiler.py, extended to return .res as 'object'-file
         # for .rc input file
         if output_dir is None: output_dir = ''
         obj_names = []
         for src_name in source_filenames:
-            (base, ext) = os.path.splitext (src_name)
+            base, ext = os.path.splitext(src_name)
             base = os.path.splitdrive(base)[1] # Chop off the drive
             base = base[os.path.isabs(base):]  # If abs, chop off leading /
             if ext not in self.src_extensions:
                 # Better to raise an exception instead of silently continuing
                 # and later complain about sources and targets having
                 # different lengths
-                raise CompileError ("Don't know how to compile %s" % src_name)
+                raise CompileError("Don't know how to compile %s" % src_name)
             if strip_dir:
-                base = os.path.basename (base)
+                base = os.path.basename(base)
             if ext in self._rc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.res_extension))
             elif ext in self._mc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.res_extension))
             else:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.obj_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.obj_extension))
         return obj_names
 
 
     def compile(self, sources,
-                output_dir=None, macros=None, include_dirs=None, debug=0,
+                output_dir=None, macros=None, include_dirs=None, debug=False,
                 extra_preargs=None, extra_postargs=None, depends=None):
 
         if not self.initialized:
@@ -452,7 +449,7 @@
         macros, objects, extra_postargs, pp_opts, build = compile_info
 
         compile_opts = extra_preargs or []
-        compile_opts.append ('/c')
+        compile_opts.append('/c')
         if debug:
             compile_opts.extend(self.compile_options_debug)
         else:
@@ -480,8 +477,8 @@
                 try:
                     self.spawn([self.rc] + pp_opts +
                                [output_opt] + [input_opt])
-                except DistutilsExecError, msg:
-                    raise CompileError(msg)
+                except PackagingExecError:
+                    raise CompileError(sys.exc_info()[1])
                 continue
             elif ext in self._mc_extensions:
                 # Compile .MC to .RC file to .RES file.
@@ -501,14 +498,14 @@
                     # first compile .MC to .RC and .H file
                     self.spawn([self.mc] +
                                ['-h', h_dir, '-r', rc_dir] + [src])
-                    base, _ = os.path.splitext (os.path.basename (src))
-                    rc_file = os.path.join (rc_dir, base + '.rc')
+                    base, _ = os.path.splitext(os.path.basename(src))
+                    rc_file = os.path.join(rc_dir, base + '.rc')
                     # then compile .RC to .RES file
                     self.spawn([self.rc] +
                                ["/fo" + obj] + [rc_file])
 
-                except DistutilsExecError, msg:
-                    raise CompileError(msg)
+                except PackagingExecError:
+                    raise CompileError(sys.exc_info()[1])
                 continue
             else:
                 # how to handle this file?
@@ -520,8 +517,8 @@
                 self.spawn([self.cc] + compile_opts + pp_opts +
                            [input_opt, output_opt] +
                            extra_postargs)
-            except DistutilsExecError, msg:
-                raise CompileError(msg)
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
 
         return objects
 
@@ -530,12 +527,12 @@
                           objects,
                           output_libname,
                           output_dir=None,
-                          debug=0,
+                          debug=False,
                           target_lang=None):
 
         if not self.initialized:
             self.initialize()
-        (objects, output_dir) = self._fix_object_args(objects, output_dir)
+        objects, output_dir = self._fix_object_args(objects, output_dir)
         output_filename = self.library_filename(output_libname,
                                                 output_dir=output_dir)
 
@@ -545,37 +542,26 @@
                 pass # XXX what goes here?
             try:
                 self.spawn([self.lib] + lib_args)
-            except DistutilsExecError, msg:
-                raise LibError(msg)
+            except PackagingExecError:
+                raise LibError(sys.exc_info()[1])
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
 
-    def link(self,
-             target_desc,
-             objects,
-             output_filename,
-             output_dir=None,
-             libraries=None,
-             library_dirs=None,
-             runtime_library_dirs=None,
-             export_symbols=None,
-             debug=0,
-             extra_preargs=None,
-             extra_postargs=None,
-             build_temp=None,
-             target_lang=None):
-
+    def link(self, target_desc, objects, output_filename, output_dir=None,
+             libraries=None, library_dirs=None, runtime_library_dirs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
+             extra_postargs=None, build_temp=None, target_lang=None):
         if not self.initialized:
             self.initialize()
-        (objects, output_dir) = self._fix_object_args(objects, output_dir)
+        objects, output_dir = self._fix_object_args(objects, output_dir)
         fixed_args = self._fix_lib_args(libraries, library_dirs,
                                         runtime_library_dirs)
-        (libraries, library_dirs, runtime_library_dirs) = fixed_args
+        libraries, library_dirs, runtime_library_dirs = fixed_args
 
         if runtime_library_dirs:
-            self.warn ("I don't know what to do with 'runtime_library_dirs': "
-                       + str (runtime_library_dirs))
+            self.warn("don't know what to do with 'runtime_library_dirs': "
+                      + str(runtime_library_dirs))
 
         lib_opts = gen_lib_options(self,
                                    library_dirs, runtime_library_dirs,
@@ -609,12 +595,12 @@
             # builds, they can go into the same directory.
             build_temp = os.path.dirname(objects[0])
             if export_symbols is not None:
-                (dll_name, dll_ext) = os.path.splitext(
+                dll_name, dll_ext = os.path.splitext(
                     os.path.basename(output_filename))
                 implib_file = os.path.join(
                     build_temp,
                     self.library_filename(dll_name))
-                ld_args.append ('/IMPLIB:' + implib_file)
+                ld_args.append('/IMPLIB:' + implib_file)
 
             # Embedded manifests are recommended - see MSDN article titled
             # "How to: Embed a Manifest Inside a C/C++ Application"
@@ -634,8 +620,8 @@
             self.mkpath(os.path.dirname(output_filename))
             try:
                 self.spawn([self.linker] + ld_args)
-            except DistutilsExecError, msg:
-                raise LinkError(msg)
+            except PackagingExecError:
+                raise LinkError(sys.exc_info()[1])
 
             # embed the manifest
             # XXX - this is somewhat fragile - if mt.exe fails, distutils
@@ -651,8 +637,8 @@
             try:
                 self.spawn(['mt.exe', '-nologo', '-manifest',
                             temp_manifest, out_arg])
-            except DistutilsExecError, msg:
-                raise LinkError(msg)
+            except PackagingExecError:
+                raise LinkError(sys.exc_info()[1])
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
@@ -664,11 +650,8 @@
             # runtimes are not in WinSxS folder, but in Python's own
             # folder), the runtimes do not need to be in every folder
             # with .pyd's.
-            manifest_f = open(manifest_file)
-            try:
+            with open(manifest_file) as manifest_f:
                 manifest_buf = manifest_f.read()
-            finally:
-                manifest_f.close()
             pattern = re.compile(
                 r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
                 r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
@@ -676,11 +659,8 @@
             manifest_buf = re.sub(pattern, "", manifest_buf)
             pattern = "<dependentAssembly>\s*</dependentAssembly>"
             manifest_buf = re.sub(pattern, "", manifest_buf)
-            manifest_f = open(manifest_file, 'w')
-            try:
+            with open(manifest_file, 'w') as manifest_f:
                 manifest_f.write(manifest_buf)
-            finally:
-                manifest_f.close()
         except IOError:
             pass
 
@@ -692,14 +672,14 @@
         return "/LIBPATH:" + dir
 
     def runtime_library_dir_option(self, dir):
-        raise DistutilsPlatformError(
+        raise PackagingPlatformError(
               "don't know how to set runtime library search path for MSVC++")
 
     def library_option(self, lib):
         return self.library_filename(lib)
 
 
-    def find_library_file(self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         # Prefer a debugging library if found (and requested), but deal
         # with it if we don't have one.
         if debug:
@@ -708,7 +688,7 @@
             try_names = [lib]
         for dir in dirs:
             for name in try_names:
-                libfile = os.path.join(dir, self.library_filename (name))
+                libfile = os.path.join(dir, self.library_filename(name))
                 if os.path.exists(libfile):
                     return libfile
         else:
diff --git a/distutils2/compiler/msvccompiler.py b/distutils2/compiler/msvccompiler.py
--- a/distutils2/compiler/msvccompiler.py
+++ b/distutils2/compiler/msvccompiler.py
@@ -1,7 +1,6 @@
-"""distutils.msvccompiler
+"""CCompiler implementation for old Microsoft Visual Studio compilers.
 
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio.
+For a compiler compatible with VS 2005 and 2008, use msvc9compiler.
 """
 
 # Written by Perry Stoll
@@ -11,31 +10,30 @@
 
 import sys
 import os
-import string
 
-from distutils2.errors import (DistutilsExecError, DistutilsPlatformError,
-                               CompileError, LibError, LinkError)
+from distutils2.errors import (PackagingExecError, PackagingPlatformError,
+                              CompileError, LibError, LinkError)
 from distutils2.compiler.ccompiler import CCompiler
 from distutils2.compiler import gen_lib_options
 from distutils2 import logger
 
-_can_read_reg = 0
+_can_read_reg = False
 try:
-    import _winreg
+    import winreg
 
-    _can_read_reg = 1
-    hkey_mod = _winreg
+    _can_read_reg = True
+    hkey_mod = winreg
 
-    RegOpenKeyEx = _winreg.OpenKeyEx
-    RegEnumKey = _winreg.EnumKey
-    RegEnumValue = _winreg.EnumValue
-    RegError = _winreg.error
+    RegOpenKeyEx = winreg.OpenKeyEx
+    RegEnumKey = winreg.EnumKey
+    RegEnumValue = winreg.EnumValue
+    RegError = winreg.error
 
 except ImportError:
     try:
         import win32api
         import win32con
-        _can_read_reg = 1
+        _can_read_reg = True
         hkey_mod = win32con
 
         RegOpenKeyEx = win32api.RegOpenKeyEx
@@ -55,6 +53,7 @@
              hkey_mod.HKEY_LOCAL_MACHINE,
              hkey_mod.HKEY_CLASSES_ROOT)
 
+
 def read_keys(base, key):
     """Return list of registry keys."""
 
@@ -64,7 +63,7 @@
         return None
     L = []
     i = 0
-    while 1:
+    while True:
         try:
             k = RegEnumKey(handle, i)
         except RegError:
@@ -73,6 +72,7 @@
         i = i + 1
     return L
 
+
 def read_values(base, key):
     """Return dict of registry keys and values.
 
@@ -84,7 +84,7 @@
         return None
     d = {}
     i = 0
-    while 1:
+    while True:
         try:
             name, value, type = RegEnumValue(handle, i)
         except RegError:
@@ -94,6 +94,7 @@
         i = i + 1
     return d
 
+
 def convert_mbcs(s):
     enc = getattr(s, "encode", None)
     if enc is not None:
@@ -103,6 +104,7 @@
             pass
     return s
 
+
 class MacroExpander(object):
 
     def __init__(self, version):
@@ -128,11 +130,11 @@
             else:
                 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
         except KeyError:
-            raise DistutilsPlatformError, \
-                  ("""Python was built with Visual Studio 2003;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2003 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
+            raise PackagingPlatformError(
+"""Python was built with Visual Studio 2003; extensions must be built with
+a compiler than can generate compatible binaries. Visual Studio 2003 was
+not found on this system. If you have Cygwin installed, you can try
+compiling with MingW32, by passing "-c mingw32" to pysetup.""")
 
         p = r"Software\Microsoft\NET Framework Setup\Product"
         for base in HKEYS:
@@ -145,10 +147,11 @@
             self.macros["$(FrameworkVersion)"] = d["version"]
 
     def sub(self, s):
-        for k, v in self.macros.iteritems():
-            s = string.replace(s, k, v)
+        for k, v in self.macros.items():
+            s = s.replace(k, v)
         return s
 
+
 def get_build_version():
     """Return the version of MSVC that was used to build Python.
 
@@ -157,7 +160,7 @@
     """
 
     prefix = "MSC v."
-    i = string.find(sys.version, prefix)
+    i = sys.version.find(prefix)
     if i == -1:
         return 6
     i = i + len(prefix)
@@ -172,6 +175,7 @@
     # else we don't know what version of the compiler this is
     return None
 
+
 def get_build_architecture():
     """Return the processor architecture.
 
@@ -179,12 +183,13 @@
     """
 
     prefix = " bit ("
-    i = string.find(sys.version, prefix)
+    i = sys.version.find(prefix)
     if i == -1:
         return "Intel"
-    j = string.find(sys.version, ")", i)
+    j = sys.version.find(")", i)
     return sys.version[i+len(prefix):j]
 
+
 def normalize_and_reduce_paths(paths):
     """Return a list of normalized paths with duplicates removed.
 
@@ -200,7 +205,7 @@
     return reduced_paths
 
 
-class MSVCCompiler (CCompiler) :
+class MSVCCompiler(CCompiler):
     """Concrete class that implements an interface to Microsoft Visual C++,
        as defined by the CCompiler abstract class."""
 
@@ -231,8 +236,8 @@
     static_lib_format = shared_lib_format = '%s%s'
     exe_extension = '.exe'
 
-    def __init__ (self, verbose=0, dry_run=0, force=0):
-        CCompiler.__init__ (self, verbose, dry_run, force)
+    def __init__(self, verbose=0, dry_run=False, force=False):
+        CCompiler.__init__(self, verbose, dry_run, force)
         self.__version = get_build_version()
         self.__arch = get_build_architecture()
         if self.__arch == "Intel":
@@ -251,7 +256,8 @@
 
     def initialize(self):
         self.__paths = []
-        if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
+        if ("DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and
+            self.find_exe("cl.exe")):
             # Assume that the SDK set up everything alright; don't try to be
             # smarter
             self.cc = "cl.exe"
@@ -262,11 +268,11 @@
         else:
             self.__paths = self.get_msvc_paths("path")
 
-            if len (self.__paths) == 0:
-                raise DistutilsPlatformError, \
-                      ("Python was built with %s, "
-                       "and extensions need to be built with the same "
-                       "version of the compiler, but it isn't installed." % self.__product)
+            if len(self.__paths) == 0:
+                raise PackagingPlatformError("Python was built with %s "
+                    "and extensions need to be built with the same "
+                    "version of the compiler, but it isn't installed." %
+                    self.__product)
 
             self.cc = self.find_exe("cl.exe")
             self.linker = self.find_exe("link.exe")
@@ -278,23 +284,23 @@
 
         # extend the MSVC path with the current path
         try:
-            for p in string.split(os.environ['path'], ';'):
+            for p in os.environ['path'].split(';'):
                 self.__paths.append(p)
         except KeyError:
             pass
         self.__paths = normalize_and_reduce_paths(self.__paths)
-        os.environ['path'] = string.join(self.__paths, ';')
+        os.environ['path'] = ';'.join(self.__paths)
 
         self.preprocess_options = None
         if self.__arch == "Intel":
-            self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
-                                     '/DNDEBUG']
+            self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GX',
+                                    '/DNDEBUG']
             self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
                                           '/Z7', '/D_DEBUG']
         else:
             # Win64
-            self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
-                                     '/DNDEBUG']
+            self.compile_options = ['/nologo', '/Ox', '/MD', '/W3', '/GS-',
+                                    '/DNDEBUG']
             self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
                                           '/Z7', '/D_DEBUG']
 
@@ -313,50 +319,46 @@
 
     # -- Worker methods ------------------------------------------------
 
-    def object_filenames (self,
-                          source_filenames,
-                          strip_dir=0,
-                          output_dir=''):
+    def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
         # Copied from ccompiler.py, extended to return .res as 'object'-file
         # for .rc input file
-        if output_dir is None: output_dir = ''
+        if output_dir is None:
+            output_dir = ''
         obj_names = []
         for src_name in source_filenames:
-            (base, ext) = os.path.splitext (src_name)
-            base = os.path.splitdrive(base)[1] # Chop off the drive
+            base, ext = os.path.splitext(src_name)
+            base = os.path.splitdrive(base)[1]  # Chop off the drive
             base = base[os.path.isabs(base):]  # If abs, chop off leading /
             if ext not in self.src_extensions:
                 # Better to raise an exception instead of silently continuing
                 # and later complain about sources and targets having
                 # different lengths
-                raise CompileError ("Don't know how to compile %s" % src_name)
+                raise CompileError("Don't know how to compile %s" % src_name)
             if strip_dir:
-                base = os.path.basename (base)
+                base = os.path.basename(base)
             if ext in self._rc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.res_extension))
             elif ext in self._mc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.res_extension))
             else:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.obj_extension))
+                obj_names.append(os.path.join(output_dir,
+                                              base + self.obj_extension))
         return obj_names
 
-    # object_filenames ()
-
-
     def compile(self, sources,
-                output_dir=None, macros=None, include_dirs=None, debug=0,
+                output_dir=None, macros=None, include_dirs=None, debug=False,
                 extra_preargs=None, extra_postargs=None, depends=None):
 
-        if not self.initialized: self.initialize()
+        if not self.initialized:
+            self.initialize()
         macros, objects, extra_postargs, pp_opts, build = \
                 self._setup_compile(output_dir, macros, include_dirs, sources,
                                     depends, extra_postargs)
 
         compile_opts = extra_preargs or []
-        compile_opts.append ('/c')
+        compile_opts.append('/c')
         if debug:
             compile_opts.extend(self.compile_options_debug)
         else:
@@ -382,10 +384,10 @@
                 input_opt = src
                 output_opt = "/fo" + obj
                 try:
-                    self.spawn ([self.rc] + pp_opts +
-                                [output_opt] + [input_opt])
-                except DistutilsExecError, msg:
-                    raise CompileError, msg
+                    self.spawn([self.rc] + pp_opts +
+                               [output_opt] + [input_opt])
+                except PackagingExecError:
+                    raise CompileError(sys.exc_info()[1])
                 continue
             elif ext in self._mc_extensions:
 
@@ -401,97 +403,78 @@
                 # the build directory for the RC file and message
                 # resources. This works at least for win32all.
 
-                h_dir = os.path.dirname (src)
-                rc_dir = os.path.dirname (obj)
+                h_dir = os.path.dirname(src)
+                rc_dir = os.path.dirname(obj)
                 try:
                     # first compile .MC to .RC and .H file
-                    self.spawn ([self.mc] +
-                                ['-h', h_dir, '-r', rc_dir] + [src])
-                    base, _ = os.path.splitext (os.path.basename (src))
-                    rc_file = os.path.join (rc_dir, base + '.rc')
+                    self.spawn([self.mc] +
+                               ['-h', h_dir, '-r', rc_dir] + [src])
+                    base, _ = os.path.splitext(os.path.basename(src))
+                    rc_file = os.path.join(rc_dir, base + '.rc')
                     # then compile .RC to .RES file
-                    self.spawn ([self.rc] +
+                    self.spawn([self.rc] +
                                 ["/fo" + obj] + [rc_file])
 
-                except DistutilsExecError, msg:
-                    raise CompileError, msg
+                except PackagingExecError:
+                    raise CompileError(sys.exc_info()[1])
                 continue
             else:
                 # how to handle this file?
-                raise CompileError (
-                    "Don't know how to compile %s to %s" % \
+                raise CompileError(
+                    "Don't know how to compile %s to %s" %
                     (src, obj))
 
             output_opt = "/Fo" + obj
             try:
-                self.spawn ([self.cc] + compile_opts + pp_opts +
-                            [input_opt, output_opt] +
-                            extra_postargs)
-            except DistutilsExecError, msg:
-                raise CompileError, msg
+                self.spawn([self.cc] + compile_opts + pp_opts +
+                           [input_opt, output_opt] +
+                           extra_postargs)
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
 
         return objects
 
-    # compile ()
+    def create_static_lib(self, objects, output_libname, output_dir=None,
+                          debug=False, target_lang=None):
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        output_filename = \
+            self.library_filename(output_libname, output_dir=output_dir)
 
-
-    def create_static_lib (self,
-                           objects,
-                           output_libname,
-                           output_dir=None,
-                           debug=0,
-                           target_lang=None):
-
-        if not self.initialized: self.initialize()
-        (objects, output_dir) = self._fix_object_args (objects, output_dir)
-        output_filename = \
-            self.library_filename (output_libname, output_dir=output_dir)
-
-        if self._need_link (objects, output_filename):
+        if self._need_link(objects, output_filename):
             lib_args = objects + ['/OUT:' + output_filename]
             if debug:
                 pass                    # XXX what goes here?
             try:
-                self.spawn ([self.lib] + lib_args)
-            except DistutilsExecError, msg:
-                raise LibError, msg
+                self.spawn([self.lib] + lib_args)
+            except PackagingExecError:
+                raise LibError(sys.exc_info()[1])
 
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
-    # create_static_lib ()
+    def link(self, target_desc, objects, output_filename, output_dir=None,
+             libraries=None, library_dirs=None, runtime_library_dirs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
+             extra_postargs=None, build_temp=None, target_lang=None):
 
-    def link (self,
-              target_desc,
-              objects,
-              output_filename,
-              output_dir=None,
-              libraries=None,
-              library_dirs=None,
-              runtime_library_dirs=None,
-              export_symbols=None,
-              debug=0,
-              extra_preargs=None,
-              extra_postargs=None,
-              build_temp=None,
-              target_lang=None):
-
-        if not self.initialized: self.initialize()
-        (objects, output_dir) = self._fix_object_args (objects, output_dir)
-        (libraries, library_dirs, runtime_library_dirs) = \
-            self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
+        if not self.initialized:
+            self.initialize()
+        objects, output_dir = self._fix_object_args(objects, output_dir)
+        libraries, library_dirs, runtime_library_dirs = \
+            self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
 
         if runtime_library_dirs:
-            self.warn ("I don't know what to do with 'runtime_library_dirs': "
-                       + str (runtime_library_dirs))
+            self.warn("don't know what to do with 'runtime_library_dirs': %s"
+                      % (runtime_library_dirs,))
 
-        lib_opts = gen_lib_options (self,
-                                    library_dirs, runtime_library_dirs,
-                                    libraries)
+        lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
+                                   libraries)
         if output_dir is not None:
-            output_filename = os.path.join (output_dir, output_filename)
+            output_filename = os.path.join(output_dir, output_filename)
 
-        if self._need_link (objects, output_filename):
+        if self._need_link(objects, output_filename):
 
             if target_desc == CCompiler.EXECUTABLE:
                 if debug:
@@ -517,46 +500,41 @@
             # directory. Since they have different names for debug and release
             # builds, they can go into the same directory.
             if export_symbols is not None:
-                (dll_name, dll_ext) = os.path.splitext(
+                dll_name, dll_ext = os.path.splitext(
                     os.path.basename(output_filename))
                 implib_file = os.path.join(
                     os.path.dirname(objects[0]),
                     self.library_filename(dll_name))
-                ld_args.append ('/IMPLIB:' + implib_file)
+                ld_args.append('/IMPLIB:' + implib_file)
 
             if extra_preargs:
                 ld_args[:0] = extra_preargs
             if extra_postargs:
                 ld_args.extend(extra_postargs)
 
-            self.mkpath (os.path.dirname (output_filename))
+            self.mkpath(os.path.dirname(output_filename))
             try:
-                self.spawn ([self.linker] + ld_args)
-            except DistutilsExecError, msg:
-                raise LinkError, msg
+                self.spawn([self.linker] + ld_args)
+            except PackagingExecError:
+                raise LinkError(sys.exc_info()[1])
 
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
-    # link ()
-
-
     # -- Miscellaneous methods -----------------------------------------
     # These are all used by the 'gen_lib_options() function, in
     # ccompiler.py.
 
-    def library_dir_option (self, dir):
+    def library_dir_option(self, dir):
         return "/LIBPATH:" + dir
 
-    def runtime_library_dir_option (self, dir):
-        raise DistutilsPlatformError, \
-              "don't know how to set runtime library search path for MSVC++"
+    def runtime_library_dir_option(self, dir):
+        raise PackagingPlatformError("don't know how to set runtime library search path for MSVC++")
 
-    def library_option (self, lib):
-        return self.library_filename (lib)
+    def library_option(self, lib):
+        return self.library_filename(lib)
 
-
-    def find_library_file (self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         # Prefer a debugging library if found (and requested), but deal
         # with it if we don't have one.
         if debug:
@@ -565,15 +543,13 @@
             try_names = [lib]
         for dir in dirs:
             for name in try_names:
-                libfile = os.path.join(dir, self.library_filename (name))
+                libfile = os.path.join(dir, self.library_filename(name))
                 if os.path.exists(libfile):
                     return libfile
         else:
             # Oops, didn't find it in *any* of 'dirs'
             return None
 
-    # find_library_file ()
-
     # Helper methods for using the MSVC registry settings
 
     def find_exe(self, exe):
@@ -592,8 +568,8 @@
                 return fn
 
         # didn't find it; try existing path
-        for p in string.split(os.environ['Path'],';'):
-            fn = os.path.join(os.path.abspath(p),exe)
+        for p in os.environ['Path'].split(';'):
+            fn = os.path.join(os.path.abspath(p), exe)
             if os.path.isfile(fn):
                 return fn
 
@@ -621,9 +597,9 @@
             d = read_values(base, key)
             if d:
                 if self.__version >= 7:
-                    return string.split(self.__macros.sub(d[path]), ";")
+                    return self.__macros.sub(d[path]).split(";")
                 else:
-                    return string.split(d[path], ";")
+                    return d[path].split(";")
         # MSVC 6 seems to create the registry entries we need only when
         # the GUI is run.
         if self.__version == 6:
@@ -648,7 +624,7 @@
         else:
             p = self.get_msvc_paths(name)
         if p:
-            os.environ[name] = string.join(p, ';')
+            os.environ[name] = ';'.join(p)
 
 
 if get_build_version() >= 8.0:
diff --git a/distutils2/compiler/unixccompiler.py b/distutils2/compiler/unixccompiler.py
--- a/distutils2/compiler/unixccompiler.py
+++ b/distutils2/compiler/unixccompiler.py
@@ -1,7 +1,7 @@
-"""distutils.unixccompiler
+"""CCompiler implementation for Unix compilers.
 
-Contains the UnixCCompiler class, a subclass of CCompiler that handles
-the "typical" Unix-style command-line C compiler:
+This module contains the UnixCCompiler class, a subclass of CCompiler
+that handles the "typical" Unix-style command-line C compiler:
   * macros defined with -Dname[=value]
   * macros undefined with -Uname
   * include search directories specified with -Idir
@@ -13,16 +13,15 @@
   * link shared library handled by 'cc -shared'
 """
 
-
 import os, sys
 
 from distutils2.util import newer
 from distutils2.compiler.ccompiler import CCompiler
 from distutils2.compiler import gen_preprocess_options, gen_lib_options
-from distutils2.errors import (DistutilsExecError, CompileError,
+from distutils2.errors import (PackagingExecError, CompileError,
                                LibError, LinkError)
 from distutils2 import logger
-from distutils2._backport import sysconfig
+import sysconfig
 
 
 # XXX Things not currently handled:
@@ -34,7 +33,7 @@
 #     we need some way for outsiders to feed preprocessor/compiler/linker
 #     flags in to us -- eg. a sysadmin might want to mandate certain flags
 #     via a site config file, or a user might want to set something for
-#     compiling this module distribution only via the setup.py command
+#     compiling this module distribution only via the pysetup command
 #     line, whatever.  As long as these options come from something on the
 #     current system, they can be as system-dependent as they like, and we
 #     should just happily stuff them into the preprocessor/compiler/linker
@@ -49,7 +48,7 @@
     build, without a way to remove an architecture. Furthermore GCC will
     barf if multiple '-isysroot' arguments are present.
     """
-    stripArch = stripSysroot = 0
+    stripArch = stripSysroot = False
 
     compiler_so = list(compiler_so)
     kernel_version = os.uname()[2] # 8.4.3
@@ -64,7 +63,7 @@
         stripSysroot = '-isysroot' in cc_args
 
     if stripArch or 'ARCHFLAGS' in os.environ:
-        while 1:
+        while True:
             try:
                 index = compiler_so.index('-arch')
                 # Strip this argument and the next one:
@@ -150,7 +149,7 @@
         pp_opts = gen_preprocess_options(macros, include_dirs)
         pp_args = self.preprocessor + pp_opts
         if output_file:
-            pp_args.extend(['-o', output_file])
+            pp_args.extend(('-o', output_file))
         if extra_preargs:
             pp_args[:0] = extra_preargs
         if extra_postargs:
@@ -166,8 +165,8 @@
                 self.mkpath(os.path.dirname(output_file))
             try:
                 self.spawn(pp_args)
-            except DistutilsExecError, msg:
-                raise CompileError, msg
+            except PackagingExecError:
+                raise CompileError(sys.exc_info()[1])
 
     def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
         compiler_so = self.compiler_so
@@ -176,11 +175,11 @@
         try:
             self.spawn(compiler_so + cc_args + [src, '-o', obj] +
                        extra_postargs)
-        except DistutilsExecError, msg:
-            raise CompileError, msg
+        except PackagingExecError:
+            raise CompileError(sys.exc_info()[1])
 
     def create_static_lib(self, objects, output_libname,
-                          output_dir=None, debug=0, target_lang=None):
+                          output_dir=None, debug=False, target_lang=None):
         objects, output_dir = self._fix_object_args(objects, output_dir)
 
         output_filename = \
@@ -200,15 +199,15 @@
             if self.ranlib:
                 try:
                     self.spawn(self.ranlib + [output_filename])
-                except DistutilsExecError, msg:
-                    raise LibError, msg
+                except PackagingExecError:
+                    raise LibError(sys.exc_info()[1])
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
     def link(self, target_desc, objects,
              output_filename, output_dir=None, libraries=None,
              library_dirs=None, runtime_library_dirs=None,
-             export_symbols=None, debug=0, extra_preargs=None,
+             export_symbols=None, debug=False, extra_preargs=None,
              extra_postargs=None, build_temp=None, target_lang=None):
         objects, output_dir = self._fix_object_args(objects, output_dir)
         libraries, library_dirs, runtime_library_dirs = \
@@ -216,8 +215,8 @@
 
         lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
                                    libraries)
-        if not isinstance(output_dir, (str, type(None))):
-            raise TypeError, "'output_dir' must be a string or None"
+        if type(output_dir) not in (str, type(None)):
+            raise TypeError("'output_dir' must be a string or None")
         if output_dir is not None:
             output_filename = os.path.join(output_dir, output_filename)
 
@@ -254,8 +253,8 @@
                     linker = _darwin_compiler_fixup(linker, ld_args)
 
                 self.spawn(linker + ld_args)
-            except DistutilsExecError, msg:
-                raise LinkError, msg
+            except PackagingExecError:
+                raise LinkError(sys.exc_info()[1])
         else:
             logger.debug("skipping %s (up-to-date)", output_filename)
 
@@ -316,7 +315,7 @@
     def library_option(self, lib):
         return "-l" + lib
 
-    def find_library_file(self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         shared_f = self.library_filename(lib, lib_type='shared')
         dylib_f = self.library_filename(lib, lib_type='dylib')
         static_f = self.library_filename(lib, lib_type='static')
diff --git a/distutils2/config.py b/distutils2/config.py
--- a/distutils2/config.py
+++ b/distutils2/config.py
@@ -1,21 +1,19 @@
-""" distutil2.config
+"""Utilities to find and read config files used by distutils2."""
 
-    Know how to read all config files Distutils2 uses.
-"""
-import os.path
+import codecs
 import os
 import sys
 import logging
+
+from shlex import split
 from ConfigParser import RawConfigParser
-from shlex import split
-
 from distutils2 import logger
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 from distutils2.compiler.extension import Extension
-from distutils2.util import check_environ, resolve_name, strtobool
+from distutils2.util import (check_environ, iglob, resolve_name, strtobool,
+                            split_multiline)
 from distutils2.compiler import set_compiler
 from distutils2.command import set_command
-from distutils2.resources import resources_dests
 from distutils2.markers import interpret
 
 
@@ -25,29 +23,54 @@
     if not vals_str:
         return
     fields = []
-    for field in vals_str.split(os.linesep):
+    # the line separator is \n for setup.cfg files
+    for field in vals_str.split('\n'):
         tmp_vals = field.split('--')
-        if (len(tmp_vals) == 2) and (not interpret(tmp_vals[1])):
+        if len(tmp_vals) == 2 and not interpret(tmp_vals[1]):
             continue
         fields.append(tmp_vals[0])
-    # Get bash options like `gcc -print-file-name=libgcc.a`
+    # Get bash options like `gcc -print-file-name=libgcc.a` XXX bash options?
     vals = split(' '.join(fields))
     if vals:
         return vals
 
 
+def _rel_path(base, path):
+    # normalizes and returns a lstripped-/-separated path
+    base = base.replace(os.path.sep, '/')
+    path = path.replace(os.path.sep, '/')
+    assert path.startswith(base)
+    return path[len(base):].lstrip('/')
+
+
+def get_resources_dests(resources_root, rules):
+    """Find destinations for resources files"""
+    destinations = {}
+    for base, suffix, dest in rules:
+        prefix = os.path.join(resources_root, base)
+        for abs_base in iglob(prefix):
+            abs_glob = os.path.join(abs_base, suffix)
+            for abs_path in iglob(abs_glob):
+                resource_file = _rel_path(resources_root, abs_path)
+                if dest is None:  # remove the entry if it was here
+                    destinations.pop(resource_file, None)
+                else:
+                    rel_path = _rel_path(abs_base, abs_path)
+                    rel_dest = dest.replace(os.path.sep, '/').rstrip('/')
+                    destinations[resource_file] = rel_dest + '/' + rel_path
+    return destinations
+
+
 class Config(object):
-    """Reads configuration files and work with the Distribution instance
-    """
+    """Class used to work with configuration files"""
     def __init__(self, dist):
         self.dist = dist
-        self.setup_hook = None
+        self.setup_hooks = []
 
-    def run_hook(self, config):
-        if self.setup_hook is None:
-            return
-        # the hook gets only the config
-        self.setup_hook(config)
+    def run_hooks(self, config):
+        """Run setup hooks in the order defined in the spec."""
+        for hook in self.setup_hooks:
+            hook(config)
 
     def find_config_files(self):
         """Find as many configuration files as should be processed for this
@@ -55,9 +78,9 @@
         should be parsed.  The filenames returned are guaranteed to exist
         (modulo nasty race conditions).
 
-        There are three possible config files: distutils.cfg in the
-        Distutils installation directory (ie. where the top-level
-        Distutils __inst__.py file lives), a file in the user's home
+        There are three possible config files: distutils2.cfg in the
+        Packaging installation directory (ie. where the top-level
+        Packaging __inst__.py file lives), a file in the user's home
         directory named .pydistutils.cfg on Unix and pydistutils.cfg
         on Windows/Mac; and setup.cfg in the current directory.
 
@@ -67,11 +90,11 @@
         files = []
         check_environ()
 
-        # Where to look for the system-wide Distutils config file
+        # Where to look for the system-wide Packaging config file
         sys_dir = os.path.dirname(sys.modules['distutils2'].__file__)
 
         # Look for the system config file
-        sys_file = os.path.join(sys_dir, "distutils.cfg")
+        sys_file = os.path.join(sys_dir, "distutils2.cfg")
         if os.path.isfile(sys_file):
             files.append(sys_file)
 
@@ -101,33 +124,41 @@
         # XXX
         return value
 
-    def _multiline(self, value):
-        value = [v for v in
-                [v.strip() for v in value.split('\n')]
-                if v != '']
-        return value
-
     def _read_setup_cfg(self, parser, cfg_filename):
         cfg_directory = os.path.dirname(os.path.abspath(cfg_filename))
         content = {}
         for section in parser.sections():
             content[section] = dict(parser.items(section))
 
-        # global:setup_hook is called *first*
+        # global setup hooks are called first
         if 'global' in content:
-            if 'setup_hook' in content['global']:
-                setup_hook = content['global']['setup_hook']
-                self.setup_hook = resolve_name(setup_hook)
-                self.run_hook(content)
+            if 'setup_hooks' in content['global']:
+                setup_hooks = split_multiline(content['global']['setup_hooks'])
+
+                # add project directory to sys.path, to allow hooks to be
+                # distributed with the project
+                sys.path.insert(0, cfg_directory)
+                try:
+                    for line in setup_hooks:
+                        try:
+                            hook = resolve_name(line)
+                        except ImportError:
+                            logger.warning('cannot find setup hook: %s',
+                                           sys.exc_info()[1].args[0])
+                        else:
+                            self.setup_hooks.append(hook)
+                    self.run_hooks(content)
+                finally:
+                    sys.path.pop(0)
 
         metadata = self.dist.metadata
 
         # setting the metadata values
         if 'metadata' in content:
-            for key, value in content['metadata'].iteritems():
+            for key, value in content['metadata'].items():
                 key = key.replace('_', '-')
                 if metadata.is_multi_field(key):
-                    value = self._multiline(value)
+                    value = split_multiline(value)
 
                 if key == 'project-url':
                     value = [(label.strip(), url.strip())
@@ -138,71 +169,74 @@
                     if 'description' in content['metadata']:
                         msg = ("description and description-file' are "
                                "mutually exclusive")
-                        raise DistutilsOptionError(msg)
+                        raise PackagingOptionError(msg)
 
-                    if isinstance(value, list):
-                        filenames = value
-                    else:
-                        filenames = value.split()
+                    filenames = value.split()
 
-                    # concatenate each files
-                    value = ''
+                    # concatenate all files
+                    value = []
                     for filename in filenames:
                         # will raise if file not found
-                        description_file = open(filename)
-                        try:
-                            value += description_file.read().strip() + '\n'
-                        finally:
-                            description_file.close()
+                        with open(filename) as description_file:
+                            value.append(description_file.read().strip())
                         # add filename as a required file
                         if filename not in metadata.requires_files:
                             metadata.requires_files.append(filename)
-                    value = value.strip()
+                    value = '\n'.join(value).strip()
                     key = 'description'
 
                 if metadata.is_metadata_field(key):
                     metadata[key] = self._convert_metadata(key, value)
 
+        if 'files' in content:
+            files = content['files']
+            self.dist.package_dir = files.pop('packages_root', None)
 
-        if 'files' in content:
-            def _convert(key, value):
-                if key not in ('packages_root',):
-                    value = self._multiline(value)
-                return value
+            files = dict((key, split_multiline(value)) for key, value in
+                         files.items())
 
-            files = dict([(key, _convert(key, value))
-                          for key, value in content['files'].iteritems()])
             self.dist.packages = []
-            self.dist.package_dir = files.get('packages_root')
 
             packages = files.get('packages', [])
-            if isinstance(packages, str):
+            if isinstance(packages, basestring):
                 packages = [packages]
 
             for package in packages:
+                if ':' in package:
+                    dir_, package = package.split(':')
+                    self.dist.package_dir[package] = dir_
                 self.dist.packages.append(package)
 
             self.dist.py_modules = files.get('modules', [])
-            if isinstance(self.dist.py_modules, str):
+            if isinstance(self.dist.py_modules, basestring):
                 self.dist.py_modules = [self.dist.py_modules]
             self.dist.scripts = files.get('scripts', [])
-            if isinstance(self.dist.scripts, str):
+            if isinstance(self.dist.scripts, basestring):
                 self.dist.scripts = [self.dist.scripts]
 
             self.dist.package_data = {}
             for data in files.get('package_data', []):
                 data = data.split('=')
                 if len(data) != 2:
-                    continue # XXX error should never pass silently
+                    continue  # XXX error should never pass silently
                 key, value = data
                 self.dist.package_data[key.strip()] = value.strip()
 
+            self.dist.data_files = []
+            for data in files.get('data_files', []):
+                data = data.split('=')
+                if len(data) != 2:
+                    continue
+                key, value = data
+                values = [v.strip() for v in value.split(',')]
+                self.dist.data_files.append((key, values))
+
             # manifest template
             self.dist.extra_files = files.get('extra_files', [])
 
             resources = []
             for rule in files.get('resources', []):
-                glob , destination  = rule.split('=', 1)
+                glob, destination = rule.split('=', 1)
                 rich_glob = glob.strip().split(' ', 1)
                 if len(rich_glob) == 2:
                     prefix, suffix = rich_glob
@@ -212,13 +246,15 @@
                     suffix = glob
                 if destination == '<exclude>':
                     destination = None
-                resources.append((prefix.strip(), suffix.strip(), destination.strip()))
-                self.dist.data_files = resources_dests(cfg_directory, resources)
+                resources.append(
+                    (prefix.strip(), suffix.strip(), destination.strip()))
+                self.dist.data_files = get_resources_dests(
+                    cfg_directory, resources)
 
         ext_modules = self.dist.ext_modules
         for section_key in content:
             labels = section_key.split('=')
-            if (len(labels) == 2) and (labels[0] == 'extension'):
+            if len(labels) == 2 and labels[0] == 'extension':
                 # labels[1] not used from now but should be implemented
                 # for extension build dependency
                 values_dct = content[section_key]
@@ -239,8 +275,7 @@
                     _pop_values(values_dct, 'depends'),
                     values_dct.pop('language', None),
                     values_dct.pop('optional', None),
-                    **values_dct
-                ))
+                    **values_dct))
 
     def parse_config_files(self, filenames=None):
         if filenames is None:
@@ -252,7 +287,8 @@
 
         for filename in filenames:
             logger.debug("  reading %s", filename)
-            parser.read(filename)
+            with codecs.open(filename, 'r', encoding='utf-8') as f:
+                parser.readfp(f)
 
             if os.path.split(filename)[-1] == 'setup.cfg':
                 self._read_setup_cfg(parser, filename)
@@ -275,8 +311,8 @@
                     opt = opt.replace('-', '_')
 
                     if opt == 'sub_commands':
-                        val = self._multiline(val)
-                        if isinstance(val, str):
+                        val = split_multiline(val)
+                        if isinstance(val, basestring):
                             val = [val]
 
                     # Hooks use a suffix system to prevent being overriden
@@ -287,8 +323,8 @@
                     if (opt.startswith("pre_hook.") or
                         opt.startswith("post_hook.")):
                         hook_type, alias = opt.split(".")
-                        hook_dict = opt_dict.setdefault(hook_type,
-                                                        (filename, {}))[1]
+                        hook_dict = opt_dict.setdefault(
+                            hook_type, (filename, {}))[1]
                         hook_dict[alias] = val
                     else:
                         opt_dict[opt] = filename, val
@@ -300,28 +336,28 @@
         # If there was a "global" section in the config file, use it
         # to set Distribution options.
         if 'global' in self.dist.command_options:
-            for (opt, (src, val)) in self.dist.command_options['global'].iteritems():
+            for opt, (src, val) in self.dist.command_options['global'].items():
                 alias = self.dist.negative_opt.get(opt)
                 try:
                     if alias:
                         setattr(self.dist, alias, not strtobool(val))
-                    elif opt in ('verbose', 'dry_run'):  # ugh!
+                    elif opt == 'dry_run':  # FIXME ugh!
                         setattr(self.dist, opt, strtobool(val))
                     else:
                         setattr(self.dist, opt, val)
-                except ValueError, msg:
-                    raise DistutilsOptionError(msg)
+                except ValueError:
+                    raise PackagingOptionError(sys.exc_info()[1])
 
     def _load_compilers(self, compilers):
-        compilers = self._multiline(compilers)
-        if isinstance(compilers, str):
+        compilers = split_multiline(compilers)
+        if isinstance(compilers, basestring):
             compilers = [compilers]
         for compiler in compilers:
             set_compiler(compiler.strip())
 
     def _load_commands(self, commands):
-        commands = self._multiline(commands)
-        if isinstance(commands, str):
+        commands = split_multiline(commands)
+        if isinstance(commands, basestring):
             commands = [commands]
         for command in commands:
             set_command(command.strip())
diff --git a/distutils2/create.py b/distutils2/create.py
new file mode 100644
--- /dev/null
+++ b/distutils2/create.py
@@ -0,0 +1,689 @@
+"""Interactive helper used to create a setup.cfg file.
+
+This script will generate a distutils2 configuration file by looking at
+the current directory and asking the user questions.  It is intended to
+be called as *pysetup create*.
+"""
+
+#  Original code by Sean Reifschneider <jafo at tummy.com>
+
+#  Original TODO list:
+#  Look for a license file and automatically add the category.
+#  When a .c file is found during the walk, can we add it as an extension?
+#  Ask if there is a maintainer different that the author
+#  Ask for the platform (can we detect this via "import win32" or something?)
+#  Ask for the dependencies.
+#  Ask for the Requires-Dist
+#  Ask for the Provides-Dist
+#  Ask for a description
+#  Detect scripts (not sure how.  #! outside of package?)
+
+import codecs
+import os
+import re
+import imp
+import sys
+import glob
+import shutil
+import sysconfig
+import tokenize
+from hashlib import md5
+from textwrap import dedent
+from distutils2.util import cmp_to_key, detect_encoding
+from ConfigParser import RawConfigParser
+# importing this with an underscore as it should be replaced by the
+# dict form or another structures for all purposes
+from distutils2._trove import all_classifiers as _CLASSIFIERS_LIST
+from distutils2.version import is_valid_version
+
+_FILENAME = 'setup.cfg'
+_DEFAULT_CFG = '.pypkgcreate'
+
+_helptext = {
+    'name': '''
+The name of the program to be packaged, usually a single word composed
+of lower-case characters such as "python", "sqlalchemy", or "CherryPy".
+''',
+    'version': '''
+Version number of the software, typically 2 or 3 numbers separated by dots
+such as "1.00", "0.6", or "3.02.01".  "0.1.0" is recommended for initial
+development.
+''',
+    'summary': '''
+A one-line summary of what this project is or does, typically a sentence 80
+characters or less in length.
+''',
+    'author': '''
+The full name of the author (typically you).
+''',
+    'author_email': '''
+E-mail address of the project author (typically you).
+''',
+    'do_classifier': '''
+Trove classifiers are optional identifiers that allow you to specify the
+intended audience by saying things like "Beta software with a text UI
+for Linux under the PSF license".  However, this can be a somewhat involved
+process.
+''',
+    'packages': '''
+You can provide a package name contained in your project.
+''',
+    'modules': '''
+You can provide a python module contained in your project.
+''',
+    'extra_files': '''
+You can provide extra files/dirs contained in your project.
+It has to follow the template syntax. XXX add help here.
+''',
+
+    'home_page': '''
+The home page for the project, typically starting with "http://".
+''',
+    'trove_license': '''
+Optionally you can specify a license.  Type a string that identifies a common
+license, and then you can select a list of license specifiers.
+''',
+    'trove_generic': '''
+Optionally, you can set other trove identifiers for things such as the
+human language, programming language, user interface, etc...
+''',
+    'setup.py found': '''
+The setup.py script will be executed to retrieve the metadata.
+An interactive helper will be run if you answer "n",
+''',
+}
+
+PROJECT_MATURITY = ['Development Status :: 1 - Planning',
+                    'Development Status :: 2 - Pre-Alpha',
+                    'Development Status :: 3 - Alpha',
+                    'Development Status :: 4 - Beta',
+                    'Development Status :: 5 - Production/Stable',
+                    'Development Status :: 6 - Mature',
+                    'Development Status :: 7 - Inactive']
+
+# XXX everything needs docstrings and tests (both low-level tests of various
+# methods and functional tests of running the script)
+
+
+def load_setup():
+    """run the setup script (i.e the setup.py file)
+
+    This function load the setup file in all cases (even if it have already
+    been loaded before, because we are monkey patching its setup function with
+    a particular one"""
+    with open("setup.py", "rb") as f:
+        encoding, lines = detect_encoding(f.readline)
+    with open("setup.py") as f:
+        imp.load_module("setup", f, "setup.py", (".py", "r", imp.PY_SOURCE))
+
+
+def ask_yn(question, default=None, helptext=None):
+    question += ' (y/n)'
+    while True:
+        answer = ask(question, default, helptext, required=True)
+        if answer and answer[0].lower() in 'yn':
+            return answer[0].lower()
+
+        print('\nERROR: You must select "Y" or "N".\n')
+
+
+def ask(question, default=None, helptext=None, required=True,
+        lengthy=False, multiline=False):
+    prompt = u'%s: ' % (question,)
+    if default:
+        prompt = u'%s [%s]: ' % (question, default)
+        if default and len(question) + len(default) > 70:
+            prompt = u'%s\n    [%s]: ' % (question, default)
+    if lengthy or multiline:
+        prompt += '\n   > '
+
+    if not helptext:
+        helptext = 'No additional help available.'
+
+    helptext = helptext.strip("\n")
+
+    while True:
+        sys.stdout.write(prompt)
+        sys.stdout.flush()
+
+        line = sys.stdin.readline().strip()
+        if line == '?':
+            print('=' * 70)
+            print(helptext)
+            print('=' * 70)
+            continue
+        if default and not line:
+            return default
+        if not line and required:
+            print('*' * 70)
+            print('This value cannot be empty.')
+            print('===========================')
+            if helptext:
+                print(helptext)
+            print('*' * 70)
+            continue
+        return line
+
+
+def convert_yn_to_bool(yn, yes=True, no=False):
+    """Convert a y/yes or n/no to a boolean value."""
+    if yn.lower().startswith('y'):
+        return yes
+    else:
+        return no
+
+
+def _build_classifiers_dict(classifiers):
+    d = {}
+    for key in classifiers:
+        subdict = d
+        for subkey in key.split(' :: '):
+            if subkey not in subdict:
+                subdict[subkey] = {}
+            subdict = subdict[subkey]
+    return d
+
+CLASSIFIERS = _build_classifiers_dict(_CLASSIFIERS_LIST)
+
+
+def _build_licences(classifiers):
+    res = []
+    for index, item in enumerate(classifiers):
+        if not item.startswith('License :: '):
+            continue
+        res.append((index, item.split(' :: ')[-1].lower()))
+    return res
+
+LICENCES = _build_licences(_CLASSIFIERS_LIST)
+
+
+class MainProgram(object):
+    """Make a project setup configuration file (setup.cfg)."""
+
+    def __init__(self):
+        self.configparser = None
+        self.classifiers = set()
+        self.data = {'name': '',
+                     'version': '1.0.0',
+                     'classifier': self.classifiers,
+                     'packages': [],
+                     'modules': [],
+                     'platform': [],
+                     'resources': [],
+                     'extra_files': [],
+                     'scripts': [],
+                     }
+        self._load_defaults()
+
+    def __call__(self):
+        setupcfg_defined = False
+        if self.has_setup_py() and self._prompt_user_for_conversion():
+            setupcfg_defined = self.convert_py_to_cfg()
+        if not setupcfg_defined:
+            self.define_cfg_values()
+        self._write_cfg()
+
+    def has_setup_py(self):
+        """Test for the existence of a setup.py file."""
+        return os.path.exists('setup.py')
+
+    def define_cfg_values(self):
+        self.inspect()
+        self.query_user()
+
+    def _lookup_option(self, key):
+        if not self.configparser.has_option('DEFAULT', key):
+            return None
+        return self.configparser.get('DEFAULT', key)
+
+    def _load_defaults(self):
+        # Load default values from a user configuration file
+        self.configparser = RawConfigParser()
+        # TODO replace with section in distutils config file
+        default_cfg = os.path.expanduser(os.path.join('~', _DEFAULT_CFG))
+        self.configparser.read(default_cfg)
+        self.data['author'] = self._lookup_option('author')
+        self.data['author_email'] = self._lookup_option('author_email')
+
+    def _prompt_user_for_conversion(self):
+        # Prompt the user about whether they would like to use the setup.py
+        # conversion utility to generate a setup.cfg or generate the setup.cfg
+        # from scratch
+        answer = ask_yn(('A legacy setup.py has been found.\n'
+                         'Would you like to convert it to a setup.cfg?'),
+                        default="y",
+                        helptext=_helptext['setup.py found'])
+        return convert_yn_to_bool(answer)
+
+    def _dotted_packages(self, data):
+        packages = sorted(data)
+        modified_pkgs = []
+        for pkg in packages:
+            pkg = pkg.lstrip('./')
+            pkg = pkg.replace('/', '.')
+            modified_pkgs.append(pkg)
+        return modified_pkgs
+
+    def _write_cfg(self):
+        if os.path.exists(_FILENAME):
+            if os.path.exists('%s.old' % _FILENAME):
+                print("ERROR: %(name)s.old backup exists, please check that "
+                      "current %(name)s is correct and remove %(name)s.old" %
+                      {'name': _FILENAME})
+                return
+            shutil.move(_FILENAME, '%s.old' % _FILENAME)
+
+        with codecs.open(_FILENAME, 'w', encoding='utf-8') as fp:
+            fp.write('[metadata]\n')
+            # TODO use metadata module instead of hard-coding field-specific
+            # behavior here
+
+            # simple string entries
+            for name in ('name', 'version', 'summary', 'download_url'):
+                fp.write('%s = %s\n' % (name, self.data.get(name, 'UNKNOWN')))
+
+            # optional string entries
+            if 'keywords' in self.data and self.data['keywords']:
+                fp.write('keywords = %s\n' % ' '.join(self.data['keywords']))
+            for name in ('home_page', 'author', 'author_email',
+                         'maintainer', 'maintainer_email', 'description-file'):
+                if name in self.data and self.data[name]:
+                    fp.write('%s = %s\n' % (name, self.data[name]))
+            if 'description' in self.data:
+                fp.write(
+                    'description = %s\n'
+                    % '\n       |'.join(self.data['description'].split('\n')))
+
+            # multiple use string entries
+            for name in ('platform', 'supported-platform', 'classifier',
+                         'requires-dist', 'provides-dist', 'obsoletes-dist',
+                         'requires-external'):
+                if not(name in self.data and self.data[name]):
+                    continue
+                fp.write('%s = ' % name)
+                fp.write(''.join('    %s\n' % val
+                                 for val in self.data[name]).lstrip())
+            fp.write('\n[files]\n')
+            for name in ('packages', 'modules', 'scripts',
+                         'package_data', 'extra_files'):
+                if not(name in self.data and self.data[name]):
+                    continue
+                fp.write('%s = %s\n'
+                         % (name, '\n    '.join(self.data[name]).strip()))
+            fp.write('\nresources =\n')
+            for src, dest in self.data['resources']:
+                fp.write('    %s = %s\n' % (src, dest))
+            fp.write('\n')
+
+        os.chmod(_FILENAME, 0o644)
+        print('Wrote "%s".' % _FILENAME)
+
+    def convert_py_to_cfg(self):
+        """Generate a setup.cfg from an existing setup.py.
+
+        It only exports the distutils metadata (setuptools specific metadata
+        is not currently supported).
+        """
+        data = self.data
+
+        def setup_mock(**attrs):
+            """Mock the setup(**attrs) in order to retrieve metadata."""
+
+            # TODO use config and metadata instead of Distribution
+            from distutils.dist import Distribution
+            dist = Distribution(attrs)
+            dist.parse_config_files()
+
+            # 1. retrieve metadata fields that are quite similar in
+            # PEP 314 and PEP 345
+            labels = (('name',) * 2,
+                      ('version',) * 2,
+                      ('author',) * 2,
+                      ('author_email',) * 2,
+                      ('maintainer',) * 2,
+                      ('maintainer_email',) * 2,
+                      ('description', 'summary'),
+                      ('long_description', 'description'),
+                      ('url', 'home_page'),
+                      ('platforms', 'platform'),
+                      # backport only for 2.5+
+                      ('provides', 'provides-dist'),
+                      ('obsoletes', 'obsoletes-dist'),
+                      ('requires', 'requires-dist'))
+
+            get = lambda lab: getattr(dist.metadata, lab.replace('-', '_'))
+            data.update((new, get(old)) for old, new in labels if get(old))
+
+            # 2. retrieve data that requires special processing
+            data['classifier'].update(dist.get_classifiers() or [])
+            data['scripts'].extend(dist.scripts or [])
+            data['packages'].extend(dist.packages or [])
+            data['modules'].extend(dist.py_modules or [])
+            # 2.1 data_files -> resources
+            if dist.data_files:
+                if (len(dist.data_files) < 2 or
+                    isinstance(dist.data_files[1], basestring)):
+                    dist.data_files = [('', dist.data_files)]
+                # add tokens in the destination paths
+                vars = {'distribution.name': data['name']}
+                path_tokens = list(sysconfig.get_paths(vars=vars).items())
+
+                # TODO replace this with a key function
+                def length_comparison(x, y):
+                    len_x = len(x[1])
+                    len_y = len(y[1])
+                    if len_x == len_y:
+                        return 0
+                    elif len_x < len_y:
+                        return -1
+                    else:
+                        return 1
+
+                # sort tokens to use the longest one first
+                path_tokens.sort(key=cmp_to_key(length_comparison))
+                for dest, srcs in (dist.data_files or []):
+                    dest = os.path.join(sys.prefix, dest)
+                    dest = dest.replace(os.path.sep, '/')
+                    for tok, path in path_tokens:
+                        path = path.replace(os.path.sep, '/')
+                        if not dest.startswith(path):
+                            continue
+
+                        dest = ('{%s}' % tok) + dest[len(path):]
+                        files = [('/ '.join(src.rsplit('/', 1)), dest)
+                                 for src in srcs]
+                        data['resources'].extend(files)
+
+            # 2.2 package_data -> extra_files
+            package_dirs = dist.package_dir or {}
+            for package, extras in dist.package_data.items() or []:
+                package_dir = package_dirs.get(package, package)
+                for file_ in extras:
+                    if package_dir:
+                        file_ = package_dir + '/' + file_
+                    data['extra_files'].append(file_)
+
+            # Use README file if its content is the desciption
+            if "description" in data:
+                ref = md5(re.sub('\s', '',
+                                 self.data['description']).lower().encode())
+                ref = ref.digest()
+                for readme in glob.glob('README*'):
+                    with codecs.open(readme, encoding='utf-8') as fp:
+                        contents = fp.read()
+                    contents = re.sub('\s', '', contents.lower()).encode()
+                    val = md5(contents).digest()
+                    if val == ref:
+                        del data['description']
+                        data['description-file'] = readme
+                        break
+
+        # apply monkey patch to distutils (v1) and setuptools (if needed)
+        # (abort the feature if distutils v1 has been killed)
+        try:
+            from distutils import core
+            core.setup  # make sure it's not d2 maskerading as d1
+        except (ImportError, AttributeError):
+            return
+        saved_setups = [(core, core.setup)]
+        core.setup = setup_mock
+        try:
+            import setuptools
+        except ImportError:
+            pass
+        else:
+            saved_setups.append((setuptools, setuptools.setup))
+            setuptools.setup = setup_mock
+        # get metadata by executing the setup.py with the patched setup(...)
+        success = False  # for python < 2.4
+        try:
+            load_setup()
+            success = True
+        finally:  # revert monkey patches
+            for patched_module, original_setup in saved_setups:
+                patched_module.setup = original_setup
+        if not self.data:
+            raise ValueError('Unable to load metadata from setup.py')
+        return success
+
+    def inspect(self):
+        """Inspect the current working diretory for a name and version.
+
+        This information is harvested in where the directory is named
+        like [name]-[version].
+        """
+        dir_name = os.path.basename(os.getcwd())
+        self.data['name'] = dir_name
+        match = re.match(r'(.*)-(\d.+)', dir_name)
+        if match:
+            self.data['name'] = match.group(1)
+            self.data['version'] = match.group(2)
+            # TODO needs testing!
+            if not is_valid_version(self.data['version']):
+                msg = "Invalid version discovered: %s" % self.data['version']
+                raise ValueError(msg)
+
+    def query_user(self):
+        self.data['name'] = ask('Project name', self.data['name'],
+              _helptext['name'])
+
+        self.data['version'] = ask('Current version number',
+              self.data.get('version'), _helptext['version'])
+        self.data['summary'] = ask('Package summary',
+              self.data.get('summary'), _helptext['summary'],
+              lengthy=True)
+        self.data['author'] = ask('Author name',
+              self.data.get('author'), _helptext['author'])
+        self.data['author_email'] = ask('Author e-mail address',
+              self.data.get('author_email'), _helptext['author_email'])
+        self.data['home_page'] = ask('Project home page',
+              self.data.get('home_page'), _helptext['home_page'],
+              required=False)
+
+        if ask_yn('Do you want me to automatically build the file list '
+              'with everything I can find in the current directory? '
+              'If you say no, you will have to define them manually.') == 'y':
+            self._find_files()
+        else:
+            while ask_yn('Do you want to add a single module?'
+                        ' (you will be able to add full packages next)',
+                    helptext=_helptext['modules']) == 'y':
+                self._set_multi('Module name', 'modules')
+
+            while ask_yn('Do you want to add a package?',
+                    helptext=_helptext['packages']) == 'y':
+                self._set_multi('Package name', 'packages')
+
+            while ask_yn('Do you want to add an extra file?',
+                        helptext=_helptext['extra_files']) == 'y':
+                self._set_multi('Extra file/dir name', 'extra_files')
+
+        if ask_yn('Do you want to set Trove classifiers?',
+                  helptext=_helptext['do_classifier']) == 'y':
+            self.set_classifier()
+
+    def _find_files(self):
+        # we are looking for python modules and packages,
+        # other stuff are added as regular files
+        pkgs = self.data['packages']
+        modules = self.data['modules']
+        extra_files = self.data['extra_files']
+
+        def is_package(path):
+            return os.path.exists(os.path.join(path, '__init__.py'))
+
+        curdir = os.getcwd()
+        scanned = []
+        _pref = ['lib', 'include', 'dist', 'build', '.', '~']
+        _suf = ['.pyc']
+
+        def to_skip(path):
+            path = relative(path)
+
+            for pref in _pref:
+                if path.startswith(pref):
+                    return True
+
+            for suf in _suf:
+                if path.endswith(suf):
+                    return True
+
+            return False
+
+        def relative(path):
+            return path[len(curdir) + 1:]
+
+        def dotted(path):
+            res = relative(path).replace(os.path.sep, '.')
+            if res.endswith('.py'):
+                res = res[:-len('.py')]
+            return res
+
+        # first pass: packages
+        for root, dirs, files in os.walk(curdir):
+            if to_skip(root):
+                continue
+            for dir_ in sorted(dirs):
+                if to_skip(dir_):
+                    continue
+                fullpath = os.path.join(root, dir_)
+                dotted_name = dotted(fullpath)
+                if is_package(fullpath) and dotted_name not in pkgs:
+                    pkgs.append(dotted_name)
+                    scanned.append(fullpath)
+
+        # modules and extra files
+        for root, dirs, files in os.walk(curdir):
+            if to_skip(root):
+                continue
+
+            if any(root.startswith(path) for path in scanned):
+                continue
+
+            for file in sorted(files):
+                fullpath = os.path.join(root, file)
+                if to_skip(fullpath):
+                    continue
+                # single module?
+                if os.path.splitext(file)[-1] == '.py':
+                    modules.append(dotted(fullpath))
+                else:
+                    extra_files.append(relative(fullpath))
+
+    def _set_multi(self, question, name):
+        existing_values = self.data[name]
+        value = ask(question, helptext=_helptext[name]).strip()
+        if value not in existing_values:
+            existing_values.append(value)
+
+    def set_classifier(self):
+        self.set_maturity_status(self.classifiers)
+        self.set_license(self.classifiers)
+        self.set_other_classifier(self.classifiers)
+
+    def set_other_classifier(self, classifiers):
+        if ask_yn('Do you want to set other trove identifiers?', 'n',
+                  _helptext['trove_generic']) != 'y':
+            return
+        self.walk_classifiers(classifiers, [CLASSIFIERS], '')
+
+    def walk_classifiers(self, classifiers, trovepath, desc):
+        trove = trovepath[-1]
+
+        if not trove:
+            return
+
+        for key in sorted(trove):
+            if len(trove[key]) == 0:
+                if ask_yn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y':
+                    classifiers.add(desc[4:] + ' :: ' + key)
+                continue
+
+            if ask_yn('Do you want to set items under\n   "%s" (%d sub-items)?'
+                      % (key, len(trove[key])), 'n',
+                      _helptext['trove_generic']) == 'y':
+                self.walk_classifiers(classifiers, trovepath + [trove[key]],
+                                      desc + ' :: ' + key)
+
+    def set_license(self, classifiers):
+        while True:
+            license = ask('What license do you use?',
+                          helptext=_helptext['trove_license'], required=False)
+            if not license:
+                return
+
+            license_words = license.lower().split(' ')
+            found_list = []
+
+            for index, licence in LICENCES:
+                for word in license_words:
+                    if word in licence:
+                        found_list.append(index)
+                        break
+
+            if len(found_list) == 0:
+                print('ERROR: Could not find a matching license for "%s"' %
+                      license)
+                continue
+
+            question = 'Matching licenses:\n\n'
+
+            for index, list_index in enumerate(found_list):
+                question += '   %s) %s\n' % (index + 1,
+                                             _CLASSIFIERS_LIST[list_index])
+
+            question += ('\nType the number of the license you wish to use or '
+                         '? to try again:')
+            choice = ask(question, required=False)
+
+            if choice == '?':
+                continue
+            if choice == '':
+                return
+
+            try:
+                index = found_list[int(choice) - 1]
+            except ValueError:
+                print("ERROR: Invalid selection, type a number from the list "
+                      "above.")
+
+            classifiers.add(_CLASSIFIERS_LIST[index])
+
+    def set_maturity_status(self, classifiers):
+        maturity_name = lambda mat: mat.split('- ')[-1]
+        maturity_question = '''\
+            Please select the project status:
+
+            %s
+
+            Status''' % '\n'.join('%s - %s' % (i, maturity_name(n))
+                                  for i, n in enumerate(PROJECT_MATURITY))
+        while True:
+            choice = ask(dedent(maturity_question), required=False)
+
+            if choice:
+                try:
+                    choice = int(choice) - 1
+                    key = PROJECT_MATURITY[choice]
+                    classifiers.add(key)
+                    return
+                except (IndexError, ValueError):
+                    print("ERROR: Invalid selection, type a single digit "
+                          "number.")
+
+
+def main():
+    """Main entry point."""
+    program = MainProgram()
+    # # uncomment when implemented
+    # if not program.load_existing_setup_script():
+    #     program.inspect_directory()
+    #     program.query_user()
+    #     program.update_config_file()
+    # program.write_setup_script()
+    # distutils2.util.cfg_to_args()
+    program()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/distutils2/database.py b/distutils2/database.py
new file mode 100644
--- /dev/null
+++ b/distutils2/database.py
@@ -0,0 +1,647 @@
+"""PEP 376 implementation."""
+
+from StringIO import StringIO
+import os
+import re
+import csv
+import sys
+import zipimport
+from hashlib import md5
+from distutils2 import logger
+from distutils2.errors import PackagingError
+from distutils2.version import suggest_normalized_version, VersionPredicate
+from distutils2.metadata import Metadata
+
+
+__all__ = [
+    'Distribution', 'EggInfoDistribution', 'distinfo_dirname',
+    'get_distributions', 'get_distribution', 'get_file_users',
+    'provides_distribution', 'obsoletes_distribution',
+    'enable_cache', 'disable_cache', 'clear_cache',
+    'get_file_path', 'get_file']
+
+
+# TODO update docs
+
+DIST_FILES = ('INSTALLER', 'METADATA', 'RECORD', 'REQUESTED', 'RESOURCES')
+
+# Cache
+_cache_name = {}  # maps names to Distribution instances
+_cache_name_egg = {}  # maps names to EggInfoDistribution instances
+_cache_path = {}  # maps paths to Distribution instances
+_cache_path_egg = {}  # maps paths to EggInfoDistribution instances
+_cache_generated = False  # indicates if .dist-info distributions are cached
+_cache_generated_egg = False  # indicates if .dist-info and .egg are cached
+_cache_enabled = True
+
+
+def enable_cache():
+    """
+    Enables the internal cache.
+
+    Note that this function will not clear the cache in any case, for that
+    functionality see :func:`clear_cache`.
+    """
+    global _cache_enabled
+
+    _cache_enabled = True
+
+
+def disable_cache():
+    """
+    Disables the internal cache.
+
+    Note that this function will not clear the cache in any case, for that
+    functionality see :func:`clear_cache`.
+    """
+    global _cache_enabled
+
+    _cache_enabled = False
+
+
+def clear_cache():
+    """ Clears the internal cache. """
+    global _cache_generated, _cache_generated_egg
+
+    _cache_name.clear()
+    _cache_name_egg.clear()
+    _cache_path.clear()
+    _cache_path_egg.clear()
+    _cache_generated = False
+    _cache_generated_egg = False
+
+
+def _yield_distributions(include_dist, include_egg, paths):
+    """
+    Yield .dist-info and .egg(-info) distributions, based on the arguments
+
+    :parameter include_dist: yield .dist-info distributions
+    :parameter include_egg: yield .egg(-info) distributions
+    """
+    for path in paths:
+        realpath = os.path.realpath(path)
+        if not os.path.isdir(realpath):
+            continue
+        for dir in os.listdir(realpath):
+            dist_path = os.path.join(realpath, dir)
+            if include_dist and dir.endswith('.dist-info'):
+                yield Distribution(dist_path)
+            elif include_egg and (dir.endswith('.egg-info') or
+                                  dir.endswith('.egg')):
+                yield EggInfoDistribution(dist_path)
+
+
+def _generate_cache(use_egg_info, paths):
+    global _cache_generated, _cache_generated_egg
+
+    if _cache_generated_egg or (_cache_generated and not use_egg_info):
+        return
+    else:
+        gen_dist = not _cache_generated
+        gen_egg = use_egg_info
+
+        for dist in _yield_distributions(gen_dist, gen_egg, paths):
+            if isinstance(dist, Distribution):
+                _cache_path[dist.path] = dist
+                if dist.name not in _cache_name:
+                    _cache_name[dist.name] = []
+                _cache_name[dist.name].append(dist)
+            else:
+                _cache_path_egg[dist.path] = dist
+                if dist.name not in _cache_name_egg:
+                    _cache_name_egg[dist.name] = []
+                _cache_name_egg[dist.name].append(dist)
+
+        if gen_dist:
+            _cache_generated = True
+        if gen_egg:
+            _cache_generated_egg = True
+
+
+class Distribution(object):
+    """Created with the *path* of the ``.dist-info`` directory provided to the
+    constructor. It reads the metadata contained in ``METADATA`` when it is
+    instantiated."""
+
+    name = ''
+    """The name of the distribution."""
+
+    version = ''
+    """The version of the distribution."""
+
+    metadata = None
+    """A :class:`distutils2.metadata.Metadata` instance loaded with
+    the distribution's ``METADATA`` file."""
+
+    requested = False
+    """A boolean that indicates whether the ``REQUESTED`` metadata file is
+    present (in other words, whether the package was installed by user
+    request or it was installed as a dependency)."""
+
+    def __init__(self, path):
+        if _cache_enabled and path in _cache_path:
+            self.metadata = _cache_path[path].metadata
+        else:
+            metadata_path = os.path.join(path, 'METADATA')
+            self.metadata = Metadata(path=metadata_path)
+
+        self.name = self.metadata['Name']
+        self.version = self.metadata['Version']
+        self.path = path
+
+        if _cache_enabled and path not in _cache_path:
+            _cache_path[path] = self
+
+    def __repr__(self):
+        return '<Distribution %r %s at %r>' % (
+            self.name, self.version, self.path)
+
+    def _get_records(self, local=False):
+        results = []
+        with self.get_distinfo_file('RECORD') as record:
+            record_reader = csv.reader(record, delimiter=',',
+                                       lineterminator='\n')
+            for row in record_reader:
+                missing = [None for i in range(len(row), 3)]
+                path, checksum, size = row + missing
+                if local:
+                    path = path.replace('/', os.sep)
+                    path = os.path.join(sys.prefix, path)
+                results.append((path, checksum, size))
+        return results
+
+    def get_resource_path(self, relative_path):
+        with self.get_distinfo_file('RESOURCES') as resources_file:
+            resources_reader = csv.reader(resources_file, delimiter=',',
+                                           lineterminator='\n')
+            for relative, destination in resources_reader:
+                if relative == relative_path:
+                    return destination
+        raise KeyError(
+            'no resource file with relative path %r is installed' %
+            relative_path)
+
+    def list_installed_files(self, local=False):
+        """
+        Iterates over the ``RECORD`` entries and returns a tuple
+        ``(path, md5, size)`` for each line. If *local* is ``True``,
+        the returned path is transformed into a local absolute path.
+        Otherwise the raw value from RECORD is returned.
+
+        A local absolute path is an absolute path in which occurrences of
+        ``'/'`` have been replaced by the system separator given by ``os.sep``.
+
+        :parameter local: flag to say if the path should be returned a local
+                          absolute path
+
+        :type local: boolean
+        :returns: iterator of (path, md5, size)
+        """
+        for result in self._get_records(local):
+            yield result
+
+    def uses(self, path):
+        """
+        Returns ``True`` if path is listed in ``RECORD``. *path* can be a local
+        absolute path or a relative ``'/'``-separated path.
+
+        :rtype: boolean
+        """
+        for p, checksum, size in self._get_records():
+            local_absolute = os.path.join(sys.prefix, p)
+            if path == p or path == local_absolute:
+                return True
+        return False
+
+    def get_distinfo_file(self, path, binary=False):
+        """
+        Returns a file located under the ``.dist-info`` directory. Returns a
+        ``file`` instance for the file pointed by *path*.
+
+        :parameter path: a ``'/'``-separated path relative to the
+                         ``.dist-info`` directory or an absolute path;
+                         If *path* is an absolute path and doesn't start
+                         with the ``.dist-info`` directory path,
+                         a :class:`PackagingError` is raised
+        :type path: string
+        :parameter binary: If *binary* is ``True``, opens the file in read-only
+                           binary mode (``rb``), otherwise opens it in
+                           read-only mode (``r``).
+        :rtype: file object
+        """
+        open_flags = 'r'
+        if binary:
+            open_flags += 'b'
+
+        # Check if it is an absolute path  # XXX use relpath, add tests
+        if path.find(os.sep) >= 0:
+            # it's an absolute path?
+            distinfo_dirname, path = path.split(os.sep)[-2:]
+            if distinfo_dirname != self.path.split(os.sep)[-1]:
+                raise PackagingError(
+                    'dist-info file %r does not belong to the %r %s '
+                    'distribution' % (path, self.name, self.version))
+
+        # The file must be relative
+        if path not in DIST_FILES:
+            raise PackagingError('invalid path for a dist-info file: %r' %
+                                 path)
+
+        path = os.path.join(self.path, path)
+        return open(path, open_flags)
+
+    def list_distinfo_files(self, local=False):
+        """
+        Iterates over the ``RECORD`` entries and returns paths for each line if
+        the path is pointing to a file located in the ``.dist-info`` directory
+        or one of its subdirectories.
+
+        :parameter local: If *local* is ``True``, each returned path is
+                          transformed into a local absolute path. Otherwise the
+                          raw value from ``RECORD`` is returned.
+        :type local: boolean
+        :returns: iterator of paths
+        """
+        for path, checksum, size in self._get_records(local):
+            yield path
+
+    def __eq__(self, other):
+        return isinstance(other, Distribution) and self.path == other.path
+
+    # See http://docs.python.org/reference/datamodel#object.__hash__
+    __hash__ = object.__hash__
+
+
+class EggInfoDistribution(object):
+    """Created with the *path* of the ``.egg-info`` directory or file provided
+    to the constructor. It reads the metadata contained in the file itself, or
+    if the given path happens to be a directory, the metadata is read from the
+    file ``PKG-INFO`` under that directory."""
+
+    name = ''
+    """The name of the distribution."""
+
+    version = ''
+    """The version of the distribution."""
+
+    metadata = None
+    """A :class:`distutils2.metadata.Metadata` instance loaded with
+    the distribution's ``METADATA`` file."""
+
+    _REQUIREMENT = re.compile(
+        r'(?P<name>[-A-Za-z0-9_.]+)\s*'
+        r'(?P<first>(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)?\s*'
+        r'(?P<rest>(?:\s*,\s*(?:<|<=|!=|==|>=|>)[-A-Za-z0-9_.]+)*)\s*'
+        r'(?P<extras>\[.*\])?')
+
+    def __init__(self, path):
+        self.path = path
+        if _cache_enabled and path in _cache_path_egg:
+            self.metadata = _cache_path_egg[path].metadata
+            self.name = self.metadata['Name']
+            self.version = self.metadata['Version']
+            return
+
+        # reused from Distribute's pkg_resources
+        def yield_lines(strs):
+            """Yield non-empty/non-comment lines of a ``basestring``
+            or sequence"""
+            if isinstance(strs, basestring):
+                for s in strs.splitlines():
+                    s = s.strip()
+                    # skip blank lines/comments
+                    if s and not s.startswith('#'):
+                        yield s
+            else:
+                for ss in strs:
+                    for s in yield_lines(ss):
+                        yield s
+
+        requires = None
+
+        if path.endswith('.egg'):
+            if os.path.isdir(path):
+                meta_path = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
+                self.metadata = Metadata(path=meta_path)
+                try:
+                    req_path = os.path.join(path, 'EGG-INFO', 'requires.txt')
+                    with open(req_path, 'r') as fp:
+                        requires = fp.read()
+                except IOError:
+                    requires = None
+            else:
+                # FIXME handle the case where zipfile is not available
+                zipf = zipimport.zipimporter(path)
+                fileobj = StringIO(
+                    zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8'))
+                self.metadata = Metadata(fileobj=fileobj)
+                try:
+                    requires = zipf.get_data('EGG-INFO/requires.txt')
+                except IOError:
+                    requires = None
+            self.name = self.metadata['Name']
+            self.version = self.metadata['Version']
+
+        elif path.endswith('.egg-info'):
+            if os.path.isdir(path):
+                path = os.path.join(path, 'PKG-INFO')
+                try:
+                    with open(os.path.join(path, 'requires.txt'), 'r') as fp:
+                        requires = fp.read()
+                except IOError:
+                    requires = None
+            self.metadata = Metadata(path=path)
+            self.name = self.metadata['Name']
+            self.version = self.metadata['Version']
+
+        else:
+            raise ValueError('path must end with .egg-info or .egg, got %r' %
+                             path)
+
+        if requires is not None:
+            if self.metadata['Metadata-Version'] == '1.1':
+                # we can't have 1.1 metadata *and* Setuptools requires
+                for field in ('Obsoletes', 'Requires', 'Provides'):
+                    del self.metadata[field]
+
+        reqs = []
+
+        if requires is not None:
+            for line in yield_lines(requires):
+                if line.startswith('['):
+                    logger.warning(
+                        'extensions in requires.txt are not supported '
+                        '(used by %r %s)', self.name, self.version)
+                    break
+                else:
+                    match = self._REQUIREMENT.match(line.strip())
+                    if not match:
+                        # this happens when we encounter extras; since they
+                        # are written at the end of the file we just exit
+                        break
+                    else:
+                        if match.group('extras'):
+                            msg = ('extra requirements are not supported '
+                                   '(used by %r %s)', self.name, self.version)
+                            logger.warning(msg, self.name)
+                        name = match.group('name')
+                        version = None
+                        if match.group('first'):
+                            version = match.group('first')
+                            if match.group('rest'):
+                                version += match.group('rest')
+                            version = version.replace(' ', '')  # trim spaces
+                        if version is None:
+                            reqs.append(name)
+                        else:
+                            reqs.append('%s (%s)' % (name, version))
+
+            if len(reqs) > 0:
+                self.metadata['Requires-Dist'] += reqs
+
+        if _cache_enabled:
+            _cache_path_egg[self.path] = self
+
+    def __repr__(self):
+        return '<EggInfoDistribution %r %s at %r>' % (
+            self.name, self.version, self.path)
+
+    def list_installed_files(self, local=False):
+
+        def _md5(path):
+            with open(path, 'rb') as f:
+                content = f.read()
+            return md5(content).hexdigest()
+
+        def _size(path):
+            return os.stat(path).st_size
+
+        path = self.path
+        if local:
+            path = path.replace('/', os.sep)
+
+        # XXX What about scripts and data files ?
+        if os.path.isfile(path):
+            return [(path, _md5(path), _size(path))]
+        else:
+            files = []
+            for root, dir, files_ in os.walk(path):
+                for item in files_:
+                    item = os.path.join(root, item)
+                    files.append((item, _md5(item), _size(item)))
+            return files
+
+        return []
+
+    def uses(self, path):
+        return False
+
+    def __eq__(self, other):
+        return (isinstance(other, EggInfoDistribution) and
+                self.path == other.path)
+
+    # See http://docs.python.org/reference/datamodel#object.__hash__
+    __hash__ = object.__hash__
+
+
+def distinfo_dirname(name, version):
+    """
+    The *name* and *version* parameters are converted into their
+    filename-escaped form, i.e. any ``'-'`` characters are replaced
+    with ``'_'`` other than the one in ``'dist-info'`` and the one
+    separating the name from the version number.
+
+    :parameter name: is converted to a standard distribution name by replacing
+                     any runs of non- alphanumeric characters with a single
+                     ``'-'``.
+    :type name: string
+    :parameter version: is converted to a standard version string. Spaces
+                        become dots, and all other non-alphanumeric characters
+                        (except dots) become dashes, with runs of multiple
+                        dashes condensed to a single dash.
+    :type version: string
+    :returns: directory name
+    :rtype: string"""
+    file_extension = '.dist-info'
+    name = name.replace('-', '_')
+    normalized_version = suggest_normalized_version(version)
+    # Because this is a lookup procedure, something will be returned even if
+    #   it is a version that cannot be normalized
+    if normalized_version is None:
+        # Unable to achieve normality?
+        normalized_version = version
+    return '-'.join([name, normalized_version]) + file_extension
+
+
+def get_distributions(use_egg_info=False, paths=None):
+    """
+    Provides an iterator that looks for ``.dist-info`` directories in
+    ``sys.path`` and returns :class:`Distribution` instances for each one of
+    them. If the parameters *use_egg_info* is ``True``, then the ``.egg-info``
+    files and directores are iterated as well.
+
+    :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution`
+            instances
+    """
+    if paths is None:
+        paths = sys.path
+
+    if not _cache_enabled:
+        for dist in _yield_distributions(True, use_egg_info, paths):
+            yield dist
+    else:
+        _generate_cache(use_egg_info, paths)
+
+        for dist in _cache_path.values():
+            yield dist
+
+        if use_egg_info:
+            for dist in _cache_path_egg.values():
+                yield dist
+
+
+def get_distribution(name, use_egg_info=False, paths=None):
+    """
+    Scans all elements in ``sys.path`` and looks for all directories
+    ending with ``.dist-info``. Returns a :class:`Distribution`
+    corresponding to the ``.dist-info`` directory that contains the
+    ``METADATA`` that matches *name* for the *name* metadata field.
+    If no distribution exists with the given *name* and the parameter
+    *use_egg_info* is set to ``True``, then all files and directories ending
+    with ``.egg-info`` are scanned. A :class:`EggInfoDistribution` instance is
+    returned if one is found that has metadata that matches *name* for the
+    *name* metadata field.
+
+    This function only returns the first result found, as no more than one
+    value is expected. If the directory is not found, ``None`` is returned.
+
+    :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None
+    """
+    if paths is None:
+        paths = sys.path
+
+    if not _cache_enabled:
+        for dist in _yield_distributions(True, use_egg_info, paths):
+            if dist.name == name:
+                return dist
+    else:
+        _generate_cache(use_egg_info, paths)
+
+        if name in _cache_name:
+            return _cache_name[name][0]
+        elif use_egg_info and name in _cache_name_egg:
+            return _cache_name_egg[name][0]
+        else:
+            return None
+
+
+def obsoletes_distribution(name, version=None, use_egg_info=False):
+    """
+    Iterates over all distributions to find which distributions obsolete
+    *name*.
+
+    If a *version* is provided, it will be used to filter the results.
+    If the argument *use_egg_info* is set to ``True``, then ``.egg-info``
+    distributions will be considered as well.
+
+    :type name: string
+    :type version: string
+    :parameter name:
+    """
+    for dist in get_distributions(use_egg_info):
+        obsoleted = (dist.metadata['Obsoletes-Dist'] +
+                     dist.metadata['Obsoletes'])
+        for obs in obsoleted:
+            o_components = obs.split(' ', 1)
+            if len(o_components) == 1 or version is None:
+                if name == o_components[0]:
+                    yield dist
+                    break
+            else:
+                try:
+                    predicate = VersionPredicate(obs)
+                except ValueError:
+                    raise PackagingError(
+                        'distribution %r has ill-formed obsoletes field: '
+                        '%r' % (dist.name, obs))
+                if name == o_components[0] and predicate.match(version):
+                    yield dist
+                    break
+
+
+def provides_distribution(name, version=None, use_egg_info=False):
+    """
+    Iterates over all distributions to find which distributions provide *name*.
+    If a *version* is provided, it will be used to filter the results. Scans
+    all elements in ``sys.path``  and looks for all directories ending with
+    ``.dist-info``. Returns a :class:`Distribution`  corresponding to the
+    ``.dist-info`` directory that contains a ``METADATA`` that matches *name*
+    for the name metadata. If the argument *use_egg_info* is set to ``True``,
+    then all files and directories ending with ``.egg-info`` are considered
+    as well and returns an :class:`EggInfoDistribution` instance.
+
+    This function only returns the first result found, since no more than
+    one values are expected. If the directory is not found, returns ``None``.
+
+    :parameter version: a version specifier that indicates the version
+                        required, conforming to the format in ``PEP-345``
+
+    :type name: string
+    :type version: string
+    """
+    predicate = None
+    if not version is None:
+        try:
+            predicate = VersionPredicate(name + ' (' + version + ')')
+        except ValueError:
+            raise PackagingError('invalid name or version: %r, %r' %
+                                 (name, version))
+
+    for dist in get_distributions(use_egg_info):
+        provided = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
+
+        for p in provided:
+            p_components = p.rsplit(' ', 1)
+            if len(p_components) == 1 or predicate is None:
+                if name == p_components[0]:
+                    yield dist
+                    break
+            else:
+                p_name, p_ver = p_components
+                if len(p_ver) < 2 or p_ver[0] != '(' or p_ver[-1] != ')':
+                    raise PackagingError(
+                        'distribution %r has invalid Provides field: %r' %
+                        (dist.name, p))
+                p_ver = p_ver[1:-1]  # trim off the parenthesis
+                if p_name == name and predicate.match(p_ver):
+                    yield dist
+                    break
+
+
+def get_file_users(path):
+    """
+    Iterates over all distributions to find out which distributions use
+    *path*.
+
+    :parameter path: can be a local absolute path or a relative
+                     ``'/'``-separated path.
+    :type path: string
+    :rtype: iterator of :class:`Distribution` instances
+    """
+    for dist in get_distributions():
+        if dist.uses(path):
+            yield dist
+
+
+def get_file_path(distribution_name, relative_path):
+    """Return the path to a resource file."""
+    dist = get_distribution(distribution_name)
+    if dist is not None:
+        return dist.get_resource_path(relative_path)
+    raise LookupError('no distribution named %r found' % distribution_name)
+
+
+def get_file(distribution_name, relative_path, *args, **kwargs):
+    """Open and return a resource file."""
+    return open(get_file_path(distribution_name, relative_path),
+                *args, **kwargs)
diff --git a/distutils2/depgraph.py b/distutils2/depgraph.py
--- a/distutils2/depgraph.py
+++ b/distutils2/depgraph.py
@@ -1,29 +1,34 @@
-"""Analyse the relationships between the distributions in the system
-and generate a dependency graph.
+"""Class and functions dealing with dependencies between distributions.
+
+This module provides a DependencyGraph class to represent the
+dependencies between distributions.  Auxiliary functions can generate a
+graph, find reverse dependencies, and print a graph in DOT format.
 """
+
 import sys
+
 from StringIO import StringIO
-from distutils2.errors import DistutilsError
+from distutils2.errors import PackagingError
 from distutils2.version import VersionPredicate, IrrationalVersionError
 
 __all__ = ['DependencyGraph', 'generate_graph', 'dependent_dists',
            'graph_to_dot']
 
 
-class DependencyGraph(object):
+class DependencyGraph:
     """
     Represents a dependency graph between distributions.
 
     The dependency relationships are stored in an ``adjacency_list`` that maps
     distributions to a list of ``(other, label)`` tuples where  ``other``
-    is a distribution and the edge is labelled with ``label`` (i.e. the version
+    is a distribution and the edge is labeled with ``label`` (i.e. the version
     specifier, if such was provided). Also, for more efficient traversal, for
     every distribution ``x``, a list of predecessors is kept in
     ``reverse_list[x]``. An edge from distribution ``a`` to
     distribution ``b`` means that ``a`` depends on ``b``. If any missing
-    depencies are found, they are stored in ``missing``, which is a dictionary
-    that maps distributions to a list of requirements that were not provided by
-    any other distributions.
+    dependencies are found, they are stored in ``missing``, which is a
+    dictionary that maps distributions to a list of requirements that were not
+    provided by any other distributions.
     """
 
     def __init__(self):
@@ -34,40 +39,40 @@
     def add_distribution(self, distribution):
         """Add the *distribution* to the graph.
 
-        :type distribution: :class:`pkgutil.Distribution` or
-                            :class:`pkgutil.EggInfoDistribution`
+        :type distribution: :class:`distutils2.database.Distribution` or
+                            :class:`distutils2.database.EggInfoDistribution`
         """
-        self.adjacency_list[distribution] = list()
-        self.reverse_list[distribution] = list()
-        self.missing[distribution] = list()
+        self.adjacency_list[distribution] = []
+        self.reverse_list[distribution] = []
+        self.missing[distribution] = []
 
     def add_edge(self, x, y, label=None):
         """Add an edge from distribution *x* to distribution *y* with the given
         *label*.
 
-        :type x: :class:`pkgutil.Distribution` or
-                 :class:`pkgutil.EggInfoDistribution`
-        :type y: :class:`pkgutil.Distribution` or
-                 :class:`pkgutil.EggInfoDistribution`
+        :type x: :class:`distutils2.database.Distribution` or
+                 :class:`distutils2.database.EggInfoDistribution`
+        :type y: :class:`distutils2.database.Distribution` or
+                 :class:`distutils2.database.EggInfoDistribution`
         :type label: ``str`` or ``None``
         """
         self.adjacency_list[x].append((y, label))
         # multiple edges are allowed, so be careful
-        if not x in self.reverse_list[y]:
+        if x not in self.reverse_list[y]:
             self.reverse_list[y].append(x)
 
     def add_missing(self, distribution, requirement):
         """
         Add a missing *requirement* for the given *distribution*.
 
-        :type distribution: :class:`pkgutil.Distribution` or
-                            :class:`pkgutil.EggInfoDistribution`
+        :type distribution: :class:`distutils2.database.Distribution` or
+                            :class:`distutils2.database.EggInfoDistribution`
         :type requirement: ``str``
         """
         self.missing[distribution].append(requirement)
 
     def _repr_dist(self, dist):
-        return '%s %s' % (dist.name, dist.metadata['Version'])
+        return '%r %s' % (dist.name, dist.version)
 
     def repr_node(self, dist, level=1):
         """Prints only a subgraph"""
@@ -77,7 +82,7 @@
             dist = self._repr_dist(other)
             if label is not None:
                 dist = '%s [%s]' % (dist, label)
-            output.append('    ' * level + '%s' % dist)
+            output.append('    ' * level + str(dist))
             suboutput = self.repr_node(other, level + 1)
             subs = suboutput.split('\n')
             output.extend(subs[1:])
@@ -86,7 +91,7 @@
     def __repr__(self):
         """Representation of the graph"""
         output = []
-        for dist, adjs in self.adjacency_list.iteritems():
+        for dist, adjs in self.adjacency_list.items():
             output.append(self.repr_node(dist))
         return '\n'.join(output)
 
@@ -102,46 +107,45 @@
     """
     disconnected = []
 
-    f.write("digraph dependencies {\n")
-    for dist, adjs in graph.adjacency_list.iteritems():
+    f.write(u"digraph dependencies {\n")
+    for dist, adjs in graph.adjacency_list.items():
         if len(adjs) == 0 and not skip_disconnected:
             disconnected.append(dist)
-        for (other, label) in adjs:
+        for other, label in adjs:
             if not label is None:
-                f.write('"%s" -> "%s" [label="%s"]\n' %
+                f.write(u'"%s" -> "%s" [label="%s"]\n' %
                                             (dist.name, other.name, label))
             else:
-                f.write('"%s" -> "%s"\n' % (dist.name, other.name))
+                f.write(u'"%s" -> "%s"\n' % (dist.name, other.name))
     if not skip_disconnected and len(disconnected) > 0:
-        f.write('subgraph disconnected {\n')
-        f.write('label = "Disconnected"\n')
-        f.write('bgcolor = red\n')
+        f.write(u'subgraph disconnected {\n')
+        f.write(u'label = "Disconnected"\n')
+        f.write(u'bgcolor = red\n')
 
         for dist in disconnected:
-            f.write('"%s"' % dist.name)
-            f.write('\n')
-        f.write('}\n')
-    f.write('}\n')
+            f.write(u'"%s"' % dist.name)
+            f.write(u'\n')
+        f.write(u'}\n')
+    f.write(u'}\n')
 
 
 def generate_graph(dists):
     """Generates a dependency graph from the given distributions.
 
     :parameter dists: a list of distributions
-    :type dists: list of :class:`pkgutil.Distribution` and
-                         :class:`pkgutil.EggInfoDistribution` instances
-    :rtype: an :class:`DependencyGraph` instance
+    :type dists: list of :class:`distutils2.database.Distribution` and
+                 :class:`distutils2.database.EggInfoDistribution` instances
+    :rtype: a :class:`DependencyGraph` instance
     """
     graph = DependencyGraph()
     provided = {}  # maps names to lists of (version, dist) tuples
-    dists = list(dists)  # maybe use generator_tools in future
 
     # first, build the graph and find out the provides
     for dist in dists:
         graph.add_distribution(dist)
         provides = (dist.metadata['Provides-Dist'] +
                     dist.metadata['Provides'] +
-                    ['%s (%s)' % (dist.name, dist.metadata['Version'])])
+                    ['%s (%s)' % (dist.name, dist.version)])
 
         for p in provides:
             comps = p.strip().rsplit(" ", 1)
@@ -150,10 +154,10 @@
             if len(comps) == 2:
                 version = comps[1]
                 if len(version) < 3 or version[0] != '(' or version[-1] != ')':
-                    raise DistutilsError('Distribution %s has ill formed' \
-                                         'provides field: %s' % (dist.name, p))
+                    raise PackagingError('distribution %r has ill-formed'
+                                         'provides field: %r' % (dist.name, p))
                 version = version[1:-1]  # trim off parenthesis
-            if not name in provided:
+            if name not in provided:
                 provided[name] = []
             provided[name].append((version, dist))
 
@@ -170,7 +174,7 @@
 
             name = predicate.name
 
-            if not name in provided:
+            if name not in provided:
                 graph.add_missing(dist, req)
             else:
                 matched = False
@@ -200,8 +204,9 @@
     :param dists: a list of distributions
     :param dist: a distribution, member of *dists* for which we are interested
     """
-    if not dist in dists:
-        raise ValueError('The given distribution is not a member of the list')
+    if dist not in dists:
+        raise ValueError('given distribution %r is not a member of the list' %
+                         dist.name)
     graph = generate_graph(dists)
 
     dep = [dist]  # dependent distributions
@@ -211,7 +216,7 @@
         node = fringe.pop()
         dep.append(node)
         for prev in graph.reverse_list[node]:
-            if not prev in dep:
+            if prev not in dep:
                 fringe.append(prev)
 
     dep.pop(0)  # remove dist from dep, was there to prevent infinite loops
@@ -219,7 +224,7 @@
 
 
 def main():
-    from distutils2._backport.pkgutil import get_distributions
+    from distutils2.database import get_distributions
     tempout = StringIO()
     try:
         old = sys.stderr
@@ -229,20 +234,23 @@
             graph = generate_graph(dists)
         finally:
             sys.stderr = old
-    except Exception, e:
+    except Exception:
+        e = sys.exc_info()[1]
         tempout.seek(0)
         tempout = tempout.read()
-        print('Could not generate the graph\n%s\n%s\n' % (tempout, str(e)))
+        print(u'Could not generate the graph')
+        print(tempout)
+        print(e)
         sys.exit(1)
 
-    for dist, reqs in graph.missing.iteritems():
+    for dist, reqs in graph.missing.items():
         if len(reqs) > 0:
-            print("Warning: Missing dependencies for %s: %s" % (dist.name,
-                                                       ", ".join(reqs)))
+            print(u"Warning: Missing dependencies for %r:" % dist.name,
+                  ", ".join(reqs))
     # XXX replace with argparse
     if len(sys.argv) == 1:
-        print('Dependency graph:')
-        print('    ' + repr(graph).replace('\n', '\n    '))
+        print(u'Dependency graph:')
+        print(u'   ', repr(graph).replace(u'\n', u'\n    '))
         sys.exit(0)
     elif len(sys.argv) > 1 and sys.argv[1] in ('-d', '--dot'):
         if len(sys.argv) > 2:
@@ -250,15 +258,12 @@
         else:
             filename = 'depgraph.dot'
 
-        f = open(filename, 'w')
-        try:
+        with open(filename, 'w') as f:
             graph_to_dot(graph, f, True)
-        finally:
-            f.close()
         tempout.seek(0)
         tempout = tempout.read()
         print(tempout)
-        print('Dot file written at "%s"' % filename)
+        print('Dot file written at %r' % filename)
         sys.exit(0)
     else:
         print('Supported option: -d [filename]')
diff --git a/distutils2/dist.py b/distutils2/dist.py
--- a/distutils2/dist.py
+++ b/distutils2/dist.py
@@ -1,17 +1,11 @@
-"""distutils.dist
-
-Provides the Distribution class, which represents the module distribution
-being built/installed/distributed.
-"""
-
+"""Class representing the distribution being built/installed/etc."""
 
 import os
 import re
-import warnings
-import logging
+import sys
 
-from distutils2.errors import (DistutilsOptionError, DistutilsArgError,
-                               DistutilsModuleError, DistutilsClassError)
+from distutils2.errors import (PackagingOptionError, PackagingArgError,
+                              PackagingModuleError, PackagingClassError)
 from distutils2.fancy_getopt import FancyGetopt
 from distutils2.util import strtobool, resolve_name
 from distutils2 import logger
@@ -19,7 +13,7 @@
 from distutils2.config import Config
 from distutils2.command import get_command_class, STANDARD_COMMANDS
 
-# Regex to define acceptable Distutils command names.  This is not *quite*
+# Regex to define acceptable Packaging command names.  This is not *quite*
 # the same as a Python NAME -- I don't allow leading underscores.  The fact
 # that they're very similar is no coincidence; the default naming scheme is
 # to look for a Python module named after the command.
@@ -32,14 +26,16 @@
    or: %(script)s cmd --help
 """
 
+
 def gen_usage(script_name):
     script = os.path.basename(script_name)
     return USAGE % {'script': script}
 
+
 class Distribution(object):
-    """The core of the Distutils.  Most of the work hiding behind 'setup'
+    """The core of the Packaging.  Most of the work hiding behind 'setup'
     is really done within a Distribution instance, which farms the work out
-    to the Distutils commands specified on the command line.
+    to the Packaging commands specified on the command line.
 
     Setup scripts will almost never instantiate Distribution directly,
     unless the 'setup()' function is totally inadequate to their needs.
@@ -52,33 +48,31 @@
 
     # 'global_options' describes the command-line options that may be
     # supplied to the setup script prior to any actual commands.
-    # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of
+    # Eg. "pysetup -n" or "pysetup --dry-run" both take advantage of
     # these global options.  This list should be kept to a bare minimum,
     # since every global option is also valid as a command option -- and we
     # don't want to pollute the commands with too many options that they
     # have minimal control over.
-    # The fourth entry for verbose means that it can be repeated.
-    global_options = [('verbose', 'v', "run verbosely (default)", 1),
-                      ('quiet', 'q', "run quietly (turns verbosity off)"),
-                      ('dry-run', 'n', "don't actually do anything"),
-                      ('help', 'h', "show detailed help message"),
-                      ('no-user-cfg', None,
-                       'ignore pydistutils.cfg in your home directory'),
+    global_options = [
+        ('dry-run', 'n', "don't actually do anything"),
+        ('help', 'h', "show detailed help message"),
+        ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
     ]
 
     # 'common_usage' is a short (2-3 line) string describing the common
     # usage of the setup script.
-    common_usage = """\
+    common_usage = u"""\
 Common commands: (see '--help-commands' for more)
 
-  setup.py build      will build the package underneath 'build/'
-  setup.py install    will install the package
+  pysetup run build      will build the package underneath 'build/'
+  pysetup run install    will install the package
 """
 
     # options that are not propagated to the commands
     display_options = [
         ('help-commands', None,
          "list all available commands"),
+        # XXX this is obsoleted by the pysetup metadata action
         ('name', None,
          "print package name"),
         ('version', 'V',
@@ -127,7 +121,7 @@
     display_option_names = [x[0].replace('-', '_') for x in display_options]
 
     # negative options are options that exclude other options
-    negative_opt = {'quiet': 'verbose'}
+    negative_opt = {}
 
     # -- Creation/initialization methods -------------------------------
     def __init__(self, attrs=None):
@@ -142,11 +136,10 @@
         """
 
         # Default values for our command-line options
-        self.verbose = 1
-        self.dry_run = 0
-        self.help = 0
+        self.dry_run = False
+        self.help = False
         for attr in self.display_option_names:
-            setattr(self, attr, 0)
+            setattr(self, attr, False)
 
         # Store the configuration
         self.config = Config(self)
@@ -237,21 +230,21 @@
             options = attrs.get('options')
             if options is not None:
                 del attrs['options']
-                for (command, cmd_options) in options.iteritems():
+                for command, cmd_options in options.items():
                     opt_dict = self.get_option_dict(command)
-                    for (opt, val) in cmd_options.iteritems():
+                    for opt, val in cmd_options.items():
                         opt_dict[opt] = ("setup script", val)
 
             # Now work on the rest of the attributes.  Any attribute that's
             # not already defined is invalid!
-            for key, val in attrs.iteritems():
+            for key, val in attrs.items():
                 if self.metadata.is_metadata_field(key):
                     self.metadata[key] = val
                 elif hasattr(self, key):
                     setattr(self, key, val)
                 else:
-                    msg = "Unknown distribution option: %r" % key
-                    warnings.warn(msg)
+                    logger.warning(
+                        'unknown argument given to Distribution: %r', key)
 
         # no-user-cfg is handled before other command line args
         # because other args override the config files, and this
@@ -292,24 +285,23 @@
             commands = sorted(self.command_options)
 
         if header is not None:
-            self.announce(indent + header)
+            logger.info(indent + header)
             indent = indent + "  "
 
         if not commands:
-            self.announce(indent + "no commands known yet")
+            logger.info(indent + "no commands known yet")
             return
 
         for cmd_name in commands:
             opt_dict = self.command_options.get(cmd_name)
             if opt_dict is None:
-                self.announce(indent +
-                              "no option dict for %r command" % cmd_name)
+                logger.info(indent + "no option dict for %r command",
+                            cmd_name)
             else:
-                self.announce(indent +
-                              "option dict for %r command:" % cmd_name)
+                logger.info(indent + "option dict for %r command:", cmd_name)
                 out = pformat(opt_dict)
                 for line in out.split('\n'):
-                    self.announce(indent + "  " + line)
+                    logger.info(indent + "  " + line)
 
     # -- Config file finding/parsing methods ---------------------------
     # XXX to be removed
@@ -326,15 +318,15 @@
         'script_args' instance attribute (which defaults to 'sys.argv[1:]'
         -- see 'setup()' in run.py).  This list is first processed for
         "global options" -- options that set attributes of the Distribution
-        instance.  Then, it is alternately scanned for Distutils commands
+        instance.  Then, it is alternately scanned for Packaging commands
         and options for that command.  Each new command terminates the
         options for the previous command.  The allowed options for a
         command are determined by the 'user_options' attribute of the
         command class -- thus, we have to be able to load command classes
         in order to parse the command line.  Any error in that 'options'
-        attribute raises DistutilsGetoptError; any error on the
-        command line raises DistutilsArgError.  If no Distutils commands
-        were found on the command line, raises DistutilsArgError.  Return
+        attribute raises PackagingGetoptError; any error on the
+        command line raises PackagingArgError.  If no Packaging commands
+        were found on the command line, raises PackagingArgError.  Return
         true if command line was successfully parsed and we should carry
         on with executing commands; false if no errors but we shouldn't
         execute commands (currently, this only happens if user asks for
@@ -360,14 +352,6 @@
         args = parser.getopt(args=self.script_args, object=self)
         option_order = parser.get_option_order()
 
-        handler = logging.StreamHandler()
-        logger.addHandler(handler)
-
-        if self.verbose:
-            handler.setLevel(logging.DEBUG)
-        else:
-            handler.setLevel(logging.INFO)
-
         # for display options we return immediately
         if self.handle_display_options(option_order):
             return
@@ -378,8 +362,8 @@
                 return
 
         # Handle the cases of --help as a "global" option, ie.
-        # "setup.py --help" and "setup.py --help command ...".  For the
-        # former, we show global options (--verbose, --dry-run, etc.)
+        # "pysetup run --help" and "pysetup run --help command ...".  For the
+        # former, we show global options (--dry-run, etc.)
         # and display-only options (--name, --version, etc.); for the
         # latter, we omit the display-only options and show help for
         # each command listed on the command line.
@@ -419,24 +403,24 @@
         # it takes.
         try:
             cmd_class = get_command_class(command)
-        except DistutilsModuleError, msg:
-            raise DistutilsArgError(msg)
+        except PackagingModuleError:
+            raise PackagingArgError(sys.exc_info()[1])
 
-        # XXX We want to push this in distutils.command
+        # XXX We want to push this in distutils2.command
         #
         # Require that the command class be derived from Command -- want
         # to be sure that the basic "command" interface is implemented.
         for meth in ('initialize_options', 'finalize_options', 'run'):
             if hasattr(cmd_class, meth):
                 continue
-            raise DistutilsClassError(
+            raise PackagingClassError(
                 'command %r must implement %r' % (cmd_class, meth))
 
         # Also make sure that the command object provides a list of its
         # known options.
         if not (hasattr(cmd_class, 'user_options') and
                 isinstance(cmd_class.user_options, list)):
-            raise DistutilsClassError(
+            raise PackagingClassError(
                 "command class %s must provide "
                 "'user_options' attribute (a list of tuples)" % cmd_class)
 
@@ -451,7 +435,7 @@
         # format (tuple of four) so we need to preprocess them here.
         if (hasattr(cmd_class, 'help_options') and
             isinstance(cmd_class.help_options, list)):
-            help_options = fix_help_options(cmd_class.help_options)
+            help_options = cmd_class.help_options[:]
         else:
             help_options = []
 
@@ -461,21 +445,22 @@
                                 cmd_class.user_options +
                                 help_options)
         parser.set_negative_aliases(negative_opt)
-        (args, opts) = parser.getopt(args[1:])
+        args, opts = parser.getopt(args[1:])
         if hasattr(opts, 'help') and opts.help:
-            self._show_help(parser, display_options=0, commands=[cmd_class])
+            self._show_help(parser, display_options=False,
+                            commands=[cmd_class])
             return
 
         if (hasattr(cmd_class, 'help_options') and
             isinstance(cmd_class.help_options, list)):
-            help_option_found = 0
-            for (help_option, short, desc, func) in cmd_class.help_options:
+            help_option_found = False
+            for help_option, short, desc, func in cmd_class.help_options:
                 if hasattr(opts, help_option.replace('-', '_')):
-                    help_option_found = 1
+                    help_option_found = True
                     if hasattr(func, '__call__'):
                         func()
                     else:
-                        raise DistutilsClassError(
+                        raise PackagingClassError(
                             "invalid help function %r for help option %r: "
                             "must be a callable object (function, etc.)"
                             % (func, help_option))
@@ -486,7 +471,7 @@
         # Put the options from the command line into their official
         # holding pen, the 'command_options' dictionary.
         opt_dict = self.get_option_dict(command)
-        for (name, value) in vars(opts).iteritems():
+        for name, value in vars(opts).items():
             opt_dict[name] = ("command line", value)
 
         return args
@@ -502,7 +487,7 @@
         else:
             self.convert_2to3_doctests = []
 
-    def _show_help(self, parser, global_options=1, display_options=1,
+    def _show_help(self, parser, global_options=True, display_options=True,
                    commands=[]):
         """Show help for the setup script command line in the form of
         several lists of command-line options.  'parser' should be a
@@ -511,7 +496,7 @@
         generate the correct help text.
 
         If 'global_options' is true, lists the global options:
-        --verbose, --dry-run, etc.  If 'display_options' is true, lists
+        --dry-run, etc.  If 'display_options' is true, lists
         the "display-only" options: --name, --version, etc.  Finally,
         lists per-command help for every command name or command class
         in 'commands'.
@@ -526,14 +511,14 @@
                 options = self.global_options
             parser.set_option_table(options)
             parser.print_help(self.common_usage + "\nGlobal options:")
-            print('')
+            print(u'')
 
         if display_options:
             parser.set_option_table(self.display_options)
             parser.print_help(
                 "Information display options (just display " +
                 "information, ignore any commands)")
-            print('')
+            print(u'')
 
         for command in self.commands:
             if isinstance(command, type) and issubclass(command, Command):
@@ -542,12 +527,11 @@
                 cls = get_command_class(command)
             if (hasattr(cls, 'help_options') and
                 isinstance(cls.help_options, list)):
-                parser.set_option_table(cls.user_options +
-                                        fix_help_options(cls.help_options))
+                parser.set_option_table(cls.user_options + cls.help_options)
             else:
                 parser.set_option_table(cls.user_options)
             parser.print_help("Options for %r command:" % cls.__name__)
-            print('')
+            print(u'')
 
         print(gen_usage(self.script_name))
 
@@ -562,30 +546,30 @@
         # we ignore "foo bar").
         if self.help_commands:
             self.print_commands()
-            print('')
+            print()
             print(gen_usage(self.script_name))
             return 1
 
         # If user supplied any of the "display metadata" options, then
         # display that metadata in the order in which the user supplied the
         # metadata options.
-        any_display_options = 0
-        is_display_option = {}
+        any_display_options = False
+        is_display_option = set()
         for option in self.display_options:
-            is_display_option[option[0]] = 1
+            is_display_option.add(option[0])
 
         for opt, val in option_order:
-            if val and is_display_option.get(opt):
+            if val and opt in is_display_option:
                 opt = opt.replace('-', '_')
                 value = self.metadata[opt]
-                if opt in ['keywords', 'platform']:
+                if opt in ('keywords', 'platform'):
                     print(','.join(value))
                 elif opt in ('classifier', 'provides', 'requires',
                              'obsoletes'):
                     print('\n'.join(value))
                 else:
                     print(value)
-                any_display_options = 1
+                any_display_options = True
 
         return any_display_options
 
@@ -630,14 +614,14 @@
                                 "Standard commands",
                                 max_length)
         if extra_commands:
-            print
+            print()
             self.print_command_list(extra_commands,
                                     "Extra commands",
                                     max_length)
 
     # -- Command class/object methods ----------------------------------
 
-    def get_command_obj(self, command, create=1):
+    def get_command_obj(self, command, create=True):
         """Return the command object for 'command'.  Normally this object
         is cached on a previous call to 'get_command_obj()'; if no command
         object for 'command' is in the cache, then we either create and
@@ -660,7 +644,6 @@
             options = self.command_options.get(command)
             if options:
                 self._set_command_options(cmd_obj, options)
-
         return cmd_obj
 
     def _set_command_options(self, command_obj, option_dict=None):
@@ -678,7 +661,7 @@
 
         logger.debug("  setting options for %r command:", command_name)
 
-        for (option, (source, value)) in option_dict.iteritems():
+        for option, (source, value) in option_dict.items():
             logger.debug("    %s = %s (from %s)", option, value, source)
             try:
                 bool_opts = [x.replace('-', '_')
@@ -691,7 +674,7 @@
                 neg_opt = {}
 
             try:
-                is_string = isinstance(value, str)
+                is_string = isinstance(value, basestring)
                 if option in neg_opt and is_string:
                     setattr(command_obj, neg_opt[option], not strtobool(value))
                 elif option in bool_opts and is_string:
@@ -699,13 +682,13 @@
                 elif hasattr(command_obj, option):
                     setattr(command_obj, option, value)
                 else:
-                    raise DistutilsOptionError(
+                    raise PackagingOptionError(
                         "error in %s: command %r has no such option %r" %
                         (source, command_name, option))
-            except ValueError, msg:
-                raise DistutilsOptionError(msg)
+            except ValueError:
+                raise PackagingOptionError(sys.exc_info()[1])
 
-    def get_reinitialized_command(self, command, reinit_subcommands=0):
+    def get_reinitialized_command(self, command, reinit_subcommands=False):
         """Reinitializes a command to the state it was in when first
         returned by 'get_command_obj()': ie., initialized but not yet
         finalized.  This provides the opportunity to sneak option
@@ -734,8 +717,8 @@
         if not command.finalized:
             return command
         command.initialize_options()
-        command.finalized = 0
         self.have_run[command_name] = 0
+        command.finalized = False
         self._set_command_options(command)
 
         if reinit_subcommands:
@@ -746,9 +729,6 @@
 
     # -- Methods that operate on the Distribution ----------------------
 
-    def announce(self, msg, level=logging.INFO):
-        logger.log(level, msg)
-
     def run_commands(self):
         """Run each command that was seen on the setup script command line.
         Uses the list of commands found and cache of command objects
@@ -796,17 +776,17 @@
         if hooks is None:
             return
 
-        for hook in hooks.itervalues():
+        for hook in hooks.values():
             if isinstance(hook, basestring):
                 try:
                     hook_obj = resolve_name(hook)
-                except ImportError, e:
-                    raise DistutilsModuleError(e)
+                except ImportError:
+                    raise PackagingModuleError(sys.exc_info()[1])
             else:
                 hook_obj = hook
 
             if not hasattr(hook_obj, '__call__'):
-                raise DistutilsOptionError('hook %r is not callable' % hook)
+                raise PackagingOptionError('hook %r is not callable' % hook)
 
             logger.info('running %s %s for command %s',
                         hook_kind, hook, cmd_obj.get_command_name())
@@ -838,15 +818,3 @@
         return (self.has_pure_modules() and
                 not self.has_ext_modules() and
                 not self.has_c_libraries())
-
-
-# XXX keep for compat or remove?
-
-def fix_help_options(options):
-    """Convert a 4-tuple 'help_options' list as found in various command
-    classes to the 3-tuple form required by FancyGetopt.
-    """
-    new_options = []
-    for help_tuple in options:
-        new_options.append(help_tuple[0:3])
-    return new_options
diff --git a/distutils2/errors.py b/distutils2/errors.py
--- a/distutils2/errors.py
+++ b/distutils2/errors.py
@@ -1,84 +1,82 @@
-"""distutils.errors
+"""Exceptions used throughout the package.
 
-Provides exceptions used by the Distutils modules.  Note that Distutils
-modules may raise standard exceptions; in particular, SystemExit is
-usually raised for errors that are obviously the end-user's fault
-(eg. bad command-line arguments).
+Submodules of distutils2 may raise exceptions defined in this module as
+well as standard exceptions; in particular, SystemExit is usually raised
+for errors that are obviously the end-user's fault (e.g. bad
+command-line arguments).
+"""
 
-This module is safe to use in "from ... import *" mode; it only exports
-symbols whose names start with "Distutils" and end with "Error"."""
 
+class PackagingError(Exception):
+    """The root of all Packaging evil."""
 
-class DistutilsError(Exception):
-    """The root of all Distutils evil."""
 
-
-class DistutilsModuleError(DistutilsError):
+class PackagingModuleError(PackagingError):
     """Unable to load an expected module, or to find an expected class
     within some module (in particular, command modules and classes)."""
 
 
-class DistutilsClassError(DistutilsError):
+class PackagingClassError(PackagingError):
     """Some command class (or possibly distribution class, if anyone
     feels a need to subclass Distribution) is found not to be holding
     up its end of the bargain, ie. implementing some part of the
     "command "interface."""
 
 
-class DistutilsGetoptError(DistutilsError):
+class PackagingGetoptError(PackagingError):
     """The option table provided to 'fancy_getopt()' is bogus."""
 
 
-class DistutilsArgError(DistutilsError):
+class PackagingArgError(PackagingError):
     """Raised by fancy_getopt in response to getopt.error -- ie. an
     error in the command line usage."""
 
 
-class DistutilsFileError(DistutilsError):
+class PackagingFileError(PackagingError):
     """Any problems in the filesystem: expected file not found, etc.
     Typically this is for problems that we detect before IOError or
     OSError could be raised."""
 
 
-class DistutilsOptionError(DistutilsError):
+class PackagingOptionError(PackagingError):
     """Syntactic/semantic errors in command options, such as use of
     mutually conflicting options, or inconsistent options,
     badly-spelled values, etc.  No distinction is made between option
     values originating in the setup script, the command line, config
     files, or what-have-you -- but if we *know* something originated in
-    the setup script, we'll raise DistutilsSetupError instead."""
+    the setup script, we'll raise PackagingSetupError instead."""
 
 
-class DistutilsSetupError(DistutilsError):
+class PackagingSetupError(PackagingError):
     """For errors that can be definitely blamed on the setup script,
     such as invalid keyword arguments to 'setup()'."""
 
 
-class DistutilsPlatformError(DistutilsError):
+class PackagingPlatformError(PackagingError):
     """We don't know how to do something on the current platform (but
     we do know how to do it on some platform) -- eg. trying to compile
     C files on a platform not supported by a CCompiler subclass."""
 
 
-class DistutilsExecError(DistutilsError):
+class PackagingExecError(PackagingError):
     """Any problems executing an external program (such as the C
     compiler, when compiling C files)."""
 
 
-class DistutilsInternalError(DistutilsError):
+class PackagingInternalError(PackagingError):
     """Internal inconsistencies or impossibilities (obviously, this
     should never be seen if the code is working!)."""
 
 
-class DistutilsTemplateError(DistutilsError):
+class PackagingTemplateError(PackagingError):
     """Syntax error in a file list template."""
 
 
-class DistutilsByteCompileError(DistutilsError):
+class PackagingByteCompileError(PackagingError):
     """Byte compile error."""
 
 
-class DistutilsIndexError(DistutilsError):
+class PackagingPyPIError(PackagingError):
     """Any problem occuring during using the indexes."""
 
 
@@ -109,15 +107,15 @@
     """Attempt to process an unknown file type."""
 
 
-class MetadataMissingError(DistutilsError):
+class MetadataMissingError(PackagingError):
     """A required metadata is missing"""
 
 
-class MetadataConflictError(DistutilsError):
+class MetadataConflictError(PackagingError):
     """Attempt to read or write metadata fields that are conflictual."""
 
 
-class MetadataUnrecognizedVersionError(DistutilsError):
+class MetadataUnrecognizedVersionError(PackagingError):
     """Unknown metadata version number."""
 
 
diff --git a/distutils2/fancy_getopt.py b/distutils2/fancy_getopt.py
--- a/distutils2/fancy_getopt.py
+++ b/distutils2/fancy_getopt.py
@@ -1,22 +1,23 @@
-"""distutils.fancy_getopt
+"""Command line parsing machinery.
 
-Wrapper around the standard getopt module that provides the following
-additional features:
+The FancyGetopt class is a Wrapper around the getopt module that
+provides the following additional features:
   * short and long options are tied together
   * options have help strings, so fancy_getopt could potentially
     create a complete usage summary
-  * options set attributes of a passed-in object
+  * options set attributes of a passed-in object.
+
+It is used under the hood by the command classes.  Do not use directly.
 """
 
+import getopt
+import re
+import sys
+import textwrap
 
-import sys
-import string
-import re
-import getopt
-import textwrap
-from distutils2.errors import DistutilsGetoptError, DistutilsArgError
+from distutils2.errors import PackagingGetoptError, PackagingArgError
 
-# Much like command_re in distutils.core, this is close to but not quite
+# Much like command_re in distutils2.core, this is close to but not quite
 # the same as a Python NAME -- except, in the spirit of most GNU
 # utilities, we use '-' in place of '_'.  (The spirit of LISP lives on!)
 # The similarities to NAME are again not a coincidence...
@@ -38,6 +39,7 @@
         --quiet is the "negative alias" of --verbose, then "--quiet"
         on the command line sets 'verbose' to false
     """
+
     def __init__(self, option_table=None):
 
         # The option table is (currently) a list of tuples.  The
@@ -90,7 +92,7 @@
 
     def add_option(self, long_option, short_option=None, help_string=None):
         if long_option in self.option_index:
-            raise DistutilsGetoptError(
+            raise PackagingGetoptError(
                   "option conflict: already an option '%s'" % long_option)
         else:
             option = (long_option, short_option, help_string)
@@ -104,13 +106,13 @@
 
     def _check_alias_dict(self, aliases, what):
         assert isinstance(aliases, dict)
-        for (alias, opt) in aliases.iteritems():
+        for alias, opt in aliases.items():
             if alias not in self.option_index:
-                raise DistutilsGetoptError(
+                raise PackagingGetoptError(
                       ("invalid %s '%s': "
                        "option '%s' not defined") % (what, alias, alias))
             if opt not in self.option_index:
-                raise DistutilsGetoptError(
+                raise PackagingGetoptError(
                       ("invalid %s '%s': "
                        "aliased option '%s' not defined") % (what, alias, opt))
 
@@ -139,76 +141,76 @@
 
         for option in self.option_table:
             if len(option) == 3:
-                long, short, help = option
+                longopt, short, help = option
                 repeat = 0
             elif len(option) == 4:
-                long, short, help, repeat = option
+                longopt, short, help, repeat = option
             else:
                 # the option table is part of the code, so simply
                 # assert that it is correct
                 raise ValueError("invalid option tuple: %r" % option)
 
             # Type- and value-check the option names
-            if not isinstance(long, str) or len(long) < 2:
-                raise DistutilsGetoptError(
+            if not isinstance(longopt, basestring) or len(longopt) < 2:
+                raise PackagingGetoptError(
                       ("invalid long option '%s': "
-                       "must be a string of length >= 2") % long)
+                       "must be a string of length >= 2") % longopt)
 
             if (not ((short is None) or
-                     (isinstance(short, str) and len(short) == 1))):
-                raise DistutilsGetoptError(
+                     (isinstance(short, basestring) and len(short) == 1))):
+                raise PackagingGetoptError(
                       ("invalid short option '%s': "
-                       "must a single character or None") % short)
+                       "must be a single character or None") % short)
 
-            self.repeat[long] = repeat
-            self.long_opts.append(long)
+            self.repeat[longopt] = repeat
+            self.long_opts.append(longopt)
 
-            if long[-1] == '=':             # option takes an argument?
+            if longopt[-1] == '=':             # option takes an argument?
                 if short:
                     short = short + ':'
-                long = long[0:-1]
-                self.takes_arg[long] = 1
+                longopt = longopt[0:-1]
+                self.takes_arg[longopt] = 1
             else:
 
                 # Is option is a "negative alias" for some other option (eg.
                 # "quiet" == "!verbose")?
-                alias_to = self.negative_alias.get(long)
+                alias_to = self.negative_alias.get(longopt)
                 if alias_to is not None:
                     if self.takes_arg[alias_to]:
-                        raise DistutilsGetoptError(
+                        raise PackagingGetoptError(
                               ("invalid negative alias '%s': "
                                "aliased option '%s' takes a value") % \
-                               (long, alias_to))
+                               (longopt, alias_to))
 
-                    self.long_opts[-1] = long   # XXX redundant?!
-                    self.takes_arg[long] = 0
+                    self.long_opts[-1] = longopt   # XXX redundant?!
+                    self.takes_arg[longopt] = 0
 
                 else:
-                    self.takes_arg[long] = 0
+                    self.takes_arg[longopt] = 0
 
             # If this is an alias option, make sure its "takes arg" flag is
             # the same as the option it's aliased to.
-            alias_to = self.alias.get(long)
+            alias_to = self.alias.get(longopt)
             if alias_to is not None:
-                if self.takes_arg[long] != self.takes_arg[alias_to]:
-                    raise DistutilsGetoptError(
+                if self.takes_arg[longopt] != self.takes_arg[alias_to]:
+                    raise PackagingGetoptError(
                           ("invalid alias '%s': inconsistent with "
                            "aliased option '%s' (one of them takes a value, "
-                           "the other doesn't") % (long, alias_to))
+                           "the other doesn't") % (longopt, alias_to))
 
             # Now enforce some bondage on the long option name, so we can
             # later translate it to an attribute name on some object.  Have
             # to do this a bit late to make sure we've removed any trailing
             # '='.
-            if not longopt_re.match(long):
-                raise DistutilsGetoptError(
+            if not longopt_re.match(longopt):
+                raise PackagingGetoptError(
                       ("invalid long option name '%s' " +
-                       "(must be letters, numbers, hyphens only") % long)
+                       "(must be letters, numbers, hyphens only") % longopt)
 
-            self.attr_name[long] = long.replace('-', '_')
+            self.attr_name[longopt] = longopt.replace('-', '_')
             if short:
                 self.short_opts.append(short)
-                self.short2long[short[0]] = long
+                self.short2long[short[0]] = longopt
 
     def getopt(self, args=None, object=None):
         """Parse command-line options in args. Store as attributes on object.
@@ -231,11 +233,12 @@
 
         self._grok_option_table()
 
-        short_opts = string.join(self.short_opts)
+        short_opts = ' '.join(self.short_opts)
+
         try:
             opts, args = getopt.getopt(args, short_opts, self.long_opts)
-        except getopt.error, msg:
-            raise DistutilsArgError(msg)
+        except getopt.error:
+            raise PackagingArgError(sys.exc_info()[1])
 
         for opt, val in opts:
             if len(opt) == 2 and opt[0] == '-':   # it's a short option
@@ -278,6 +281,8 @@
         """
         if self.option_order is None:
             raise RuntimeError("'getopt()' hasn't been called yet")
+        else:
+            return self.option_order
 
         return self.option_order
 
@@ -291,10 +296,10 @@
         # First pass: determine maximum length of long option names
         max_opt = 0
         for option in self.option_table:
-            long = option[0]
+            longopt = option[0]
             short = option[1]
-            l = len(long)
-            if long[-1] == '=':
+            l = len(longopt)
+            if longopt[-1] == '=':
                 l = l - 1
             if short is not None:
                 l = l + 5                   # " (-x)" where short == 'x'
@@ -334,22 +339,20 @@
             lines = ['Option summary:']
 
         for option in self.option_table:
-            long, short, help = option[:3]
+            longopt, short, help = option[:3]
             text = textwrap.wrap(help, text_width)
-            if long[-1] == '=':
-                long = long[0:-1]
 
             # Case 1: no short option at all (makes life easy)
             if short is None:
                 if text:
-                    lines.append("  --%-*s  %s" % (max_opt, long, text[0]))
+                    lines.append("  --%-*s  %s" % (max_opt, longopt, text[0]))
                 else:
-                    lines.append("  --%-*s  " % (max_opt, long))
+                    lines.append("  --%-*s  " % (max_opt, longopt))
 
             # Case 2: we have a short option, so we have to include it
             # just after the long option
             else:
-                opt_names = "%s (-%s)" % (long, short)
+                opt_names = "%s (-%s)" % (longopt, short)
                 if text:
                     lines.append("  --%-*s  %s" %
                                  (max_opt, opt_names, text[0]))
@@ -365,7 +368,7 @@
         if file is None:
             file = sys.stdout
         for line in self.generate_help(header):
-            file.write(line + "\n")
+            file.write(line + u"\n")
 
 
 def fancy_getopt(options, negative_opt, object, args):
@@ -374,71 +377,6 @@
     return parser.getopt(args, object)
 
 
-if 'maketrans' in str.__dict__ :
-    # Python 3.2+
-    WS_TRANS = str.maketrans(string.whitespace, ' ' * len(string.whitespace))
-else :
-    # Depreciated syntax
-    WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace))
-
-
-def wrap_text(text, width):
-    """wrap_text(text : string, width : int) -> [string]
-
-    Split 'text' into multiple lines of no more than 'width' characters
-    each, and return the list of strings that results.
-    """
-
-    if text is None:
-        return []
-    if len(text) <= width:
-        return [text]
-
-    text = string.expandtabs(text)
-    text = string.translate(text, WS_TRANS)
-    chunks = re.split(r'( +|-+)', text)
-    chunks = filter(None, chunks)      # ' - ' results in empty strings
-    lines = []
-
-    while chunks:
-
-        cur_line = []                   # list of chunks (to-be-joined)
-        cur_len = 0                     # length of current line
-
-        while chunks:
-            l = len(chunks[0])
-            if cur_len + l <= width:    # can squeeze (at least) this chunk in
-                cur_line.append(chunks[0])
-                del chunks[0]
-                cur_len = cur_len + l
-            else:                       # this line is full
-                # drop last chunk if all space
-                if cur_line and cur_line[-1][0] == ' ':
-                    del cur_line[-1]
-                break
-
-        if chunks:                      # any chunks left to process?
-
-            # if the current line is still empty, then we had a single
-            # chunk that's too big too fit on a line -- so we break
-            # down and break it up at the line width
-            if cur_len == 0:
-                cur_line.append(chunks[0][0:width])
-                chunks[0] = chunks[0][width:]
-
-            # all-whitespace chunks at the end of a line can be discarded
-            # (and we know from the re.split above that if a chunk has
-            # *any* whitespace, it is *all* whitespace)
-            if chunks[0][0] == ' ':
-                del chunks[0]
-
-        # and store this line in the list-of-all-lines -- as a single
-        # string, of course!
-        lines.append(string.join(cur_line, ''))
-
-    return lines
-
-
 class OptionDummy(object):
     """Dummy class just used as a place to hold command-line option
     values as instance attributes."""
diff --git a/distutils2/install.py b/distutils2/install.py
--- a/distutils2/install.py
+++ b/distutils2/install.py
@@ -1,34 +1,37 @@
-"""Provides installations scripts.
+"""Building blocks for installers.
 
-The goal of this script is to install a release from the indexes (eg.
-PyPI), including the dependencies of the releases if needed.
+When used as a script, this module installs a release thanks to info
+obtained from an index (e.g. PyPI), with dependencies.
 
-It uses the work made in pkgutil and by the index crawlers to browse the
-installed distributions, and rely on the instalation commands to install.
+This is a higher-level module built on distutils2.database and
+distutils2.pypi.
 """
-import shutil
 import os
 import sys
 import stat
 import errno
-import itertools
+import shutil
 import logging
 import tempfile
+from sysconfig import get_config_var, get_path, is_python_build
 
 from distutils2 import logger
-from distutils2._backport.pkgutil import get_distributions
-from distutils2._backport.pkgutil import get_distribution
-from distutils2._backport.sysconfig import get_config_var
+from distutils2.dist import Distribution
+from distutils2.util import (_is_archive_file, ask, get_install_method,
+                            egginfo_to_distinfo, unpack_archive)
+from distutils2.pypi import wrapper
+from distutils2.version import get_version_predicate
+from distutils2.database import get_distributions, get_distribution
 from distutils2.depgraph import generate_graph
-from distutils2.index import wrapper
-from distutils2.index.errors import ProjectNotFound, ReleaseNotFound
-from distutils2.errors import (DistutilsError, InstallationException,
-                               InstallationConflict)
-from distutils2.version import get_version_predicate
+
+from distutils2.errors import (PackagingError, InstallationException,
+                              InstallationConflict, CCompilerError)
+from distutils2.pypi.errors import ProjectNotFound, ReleaseNotFound
+from distutils2 import database
 
 
 __all__ = ['install_dists', 'install_from_infos', 'get_infos', 'remove',
-           'install']
+           'install', 'install_local_project']
 
 
 def _move_files(files, destination):
@@ -40,42 +43,53 @@
     :param files: a list of files to move.
     :param destination: the destination directory to put on the files.
     """
+
     for old in files:
-        # not using os.path.join() because basename() might not be
-        # unique in destination
-        new = "%s%s" % (destination, old)
-
+        filename = os.path.split(old)[-1]
+        new = os.path.join(destination, filename)
         # try to make the paths.
         try:
             os.makedirs(os.path.dirname(new))
-        except OSError, e:
-            if e.errno == errno.EEXIST:
-                pass
-            else:
-                raise e
+        except OSError:
+            e = sys.exc_info()[1]
+            if e.errno != errno.EEXIST:
+                raise
         os.rename(old, new)
         yield old, new
 
 
-def _run_d1_install(archive_dir, path):
+def _run_distutils_install(path):
     # backward compat: using setuptools or plain-distutils
-    cmd = '%s setup.py install --root=%s --record=%s'
-    setup_py = os.path.join(archive_dir, 'setup.py')
-    if 'setuptools' in open(setup_py).read():
-        cmd += ' --single-version-externally-managed'
-
-    # how to place this file in the egg-info dir
-    # for non-distutils2 projects ?
-    record_file = os.path.join(archive_dir, 'RECORD')
-    os.system(cmd % (sys.executable, path, record_file))
+    cmd = '%s setup.py install --record=%s'
+    record_file = os.path.join(path, 'RECORD')
+    os.system(cmd % (sys.executable, record_file))
     if not os.path.exists(record_file):
         raise ValueError('failed to install')
-    return open(record_file).read().split('\n')
+    else:
+        egginfo_to_distinfo(record_file, remove_egginfo=True)
 
 
-def _run_d2_install(archive_dir, path):
-    # using our own install command
-    raise NotImplementedError()
+def _run_setuptools_install(path):
+    cmd = '%s setup.py install --record=%s --single-version-externally-managed'
+    record_file = os.path.join(path, 'RECORD')
+
+    os.system(cmd % (sys.executable, record_file))
+    if not os.path.exists(record_file):
+        raise ValueError('failed to install')
+    else:
+        egginfo_to_distinfo(record_file, remove_egginfo=True)
+
+
+def _run_packaging_install(path):
+    # XXX check for a valid setup.cfg?
+    dist = Distribution()
+    dist.parse_config_files()
+    try:
+        dist.run_command('install_dist')
+        name = dist.metadata['Name']
+        return database.get_distribution(name) is not None
+    except (IOError, os.error, PackagingError, CCompilerError):
+        raise ValueError("Failed to install, " + str(sys.exc_info()[1]))
 
 
 def _install_dist(dist, path):
@@ -87,64 +101,105 @@
     * copy the files in "path"
     * determine if the distribution is distutils2 or distutils1.
     """
-    where = dist.unpack(path)
+    where = dist.unpack()
 
-    # get into the dir
-    archive_dir = None
-    for item in os.listdir(where):
-        fullpath = os.path.join(where, item)
-        if os.path.isdir(fullpath):
-            archive_dir = fullpath
-            break
-
-    if archive_dir is None:
+    if where is None:
         raise ValueError('Cannot locate the unpacked archive')
 
-    # install
+    return _run_install_from_archive(where)
+
+
+def install_local_project(path):
+    """Install a distribution from a source directory.
+
+    If the source directory contains a setup.py install using distutils1.
+    If a setup.cfg is found, install using the install_dist command.
+
+    Returns True on success, False on Failure.
+    """
+    path = os.path.abspath(path)
+    if os.path.isdir(path):
+        logger.info('Installing from source directory: %r', path)
+        return _run_install_from_dir(path)
+    elif _is_archive_file(path):
+        logger.info('Installing from archive: %r', path)
+        _unpacked_dir = tempfile.mkdtemp()
+        try:
+            unpack_archive(path, _unpacked_dir)
+            return _run_install_from_archive(_unpacked_dir)
+        finally:
+            shutil.rmtree(_unpacked_dir)
+    else:
+        logger.warning('No project to install.')
+        return False
+
+
+def _run_install_from_archive(source_dir):
+    # XXX need a better way
+    for item in os.listdir(source_dir):
+        fullpath = os.path.join(source_dir, item)
+        if os.path.isdir(fullpath):
+            source_dir = fullpath
+            break
+    return _run_install_from_dir(source_dir)
+
+
+install_methods = {
+    'distutils2': _run_packaging_install,
+    'setuptools': _run_setuptools_install,
+    'distutils': _run_distutils_install}
+
+
+def _run_install_from_dir(source_dir):
     old_dir = os.getcwd()
-    os.chdir(archive_dir)
+    os.chdir(source_dir)
+    install_method = get_install_method(source_dir)
+    func = install_methods[install_method]
     try:
-        # distutils2 or distutils1 ?
-        if 'setup.py' in os.listdir(archive_dir):
-            return _run_d1_install(archive_dir, path)
-        else:
-            return _run_d2_install(archive_dir, path)
+        func = install_methods[install_method]
+        try:
+            func(source_dir)
+            return True
+        except ValueError:
+            # failed to install
+            logger.info(str(sys.exc_info()[1]))
+            return False
     finally:
         os.chdir(old_dir)
 
 
-def install_dists(dists, path, paths=sys.path):
+def install_dists(dists, path, paths=None):
     """Install all distributions provided in dists, with the given prefix.
 
     If an error occurs while installing one of the distributions, uninstall all
     the installed distribution (in the context if this function).
 
-    Return a list of installed files.
+    Return a list of installed dists.
 
     :param dists: distributions to install
     :param path: base path to install distribution in
     :param paths: list of paths (defaults to sys.path) to look for info
     """
 
-    installed_dists, installed_files = [], []
+    installed_dists = []
     for dist in dists:
-        logger.info('installing %s %s', dist.name, dist.version)
+        logger.info('Installing %r %s...', dist.name, dist.version)
         try:
-            installed_files.extend(_install_dist(dist, path))
+            _install_dist(dist, path)
             installed_dists.append(dist)
-        except Exception, e:
-            logger.info('failed: %s', e)
+        except Exception:
+            logger.info('Failed: %s', sys.exc_info()[1])
 
             # reverting
             for installed_dist in installed_dists:
-                _remove_dist(installed_dist, paths)
+                logger.info('Reverting %r', installed_dist)
+                remove(installed_dist.name, paths)
             raise e
-
-    return installed_files
+    return installed_dists
 
 
 def install_from_infos(install_path=None, install=[], remove=[], conflicts=[],
-                       paths=sys.path):
+                       paths=None):
     """Install and remove the given distributions.
 
     The function signature is made to be compatible with the one of get_infos.
@@ -188,7 +243,7 @@
     if remove:
         temp_dir = tempfile.mkdtemp()
         for dist in remove:
-            files = dist.get_installed_files()
+            files = dist.list_installed_files()
             temp_files[dist] = _move_files(files, temp_dir)
     try:
         if install:
@@ -236,23 +291,31 @@
     Conflict contains all the conflicting distributions, if there is a
     conflict.
     """
+    # this function does several things:
+    # 1. get a release specified by the requirements
+    # 2. gather its metadata, using setuptools compatibility if needed
+    # 3. compare this tree with what is currently installed on the system,
+    #    return the requirements of what is missing
+    # 4. do that recursively and merge back the results
+    # 5. return a dict containing information about what is needed to install
+    #    or remove
+
     if not installed:
-        logger.info('reading installed distributions')
-        installed = get_distributions(use_egg_info=True)
+        logger.debug('Reading installed distributions')
+        installed = list(get_distributions(use_egg_info=True))
 
     infos = {'install': [], 'remove': [], 'conflict': []}
-    # Is a compatible version of the project is already installed ?
+    # Is a compatible version of the project already installed ?
     predicate = get_version_predicate(requirements)
     found = False
-    installed = list(installed)
 
-    # check that the project isnt already installed
+    # check that the project isn't already installed
     for installed_project in installed:
         # is it a compatible project ?
         if predicate.name.lower() != installed_project.name.lower():
             continue
         found = True
-        logger.info('found %s %s', installed_project.name,
+        logger.info('Found %r %s', installed_project.name,
                     installed_project.version)
 
         # if we already have something installed, check it matches the
@@ -262,50 +325,45 @@
         break
 
     if not found:
-        logger.info('project not installed')
+        logger.debug('Project not installed')
 
     if not index:
         index = wrapper.ClientWrapper()
 
+    if not installed:
+        installed = get_distributions(use_egg_info=True)
+
     # Get all the releases that match the requirements
     try:
-        releases = index.get_releases(requirements)
+        release = index.get_release(requirements)
     except (ReleaseNotFound, ProjectNotFound):
-        raise InstallationException('Release not found: "%s"' % requirements)
-
-    # Pick up a release, and try to get the dependency tree
-    release = releases.get_last(requirements, prefer_final=prefer_final)
+        raise InstallationException('Release not found: %r' % requirements)
 
     if release is None:
-        logger.info('could not find a matching project')
+        logger.info('Could not find a matching project')
         return infos
 
-    # this works for Metadata 1.2
     metadata = release.fetch_metadata()
 
-    # for earlier, we need to build setuptools deps if any
+    # we need to build setuptools deps if any
     if 'requires_dist' not in metadata:
-        deps = _get_setuptools_deps(release)
-    else:
-        deps = metadata['requires_dist']
+        metadata['requires_dist'] = _get_setuptools_deps(release)
 
-    # XXX deps not used
+    # build the dependency graph with local and required dependencies
+    dists = list(installed)
+    dists.append(release)
+    depgraph = generate_graph(dists)
 
-    distributions = itertools.chain(installed, [release])
-    depgraph = generate_graph(distributions)
-
-    # Store all the already_installed packages in a list, in case of rollback.
     # Get what the missing deps are
     dists = depgraph.missing[release]
     if dists:
-        logger.info("missing dependencies found, retrieving metadata")
+        logger.info("Missing dependencies found, retrieving metadata")
         # we have missing deps
         for dist in dists:
             _update_infos(infos, get_infos(dist, index, installed))
 
     # Fill in the infos
     existing = [d for d in installed if d.name == release.name]
-
     if existing:
         infos['remove'].append(existing[0])
         infos['conflict'].extend(depgraph.reverse_list[existing[0]])
@@ -322,26 +380,38 @@
             infos[key].extend(new_infos[key])
 
 
-def _remove_dist(dist, paths=sys.path):
-    remove(dist.name, paths)
+def remove(project_name, paths=None, auto_confirm=True):
+    """Removes a single project from the installation.
 
-
-def remove(project_name, paths=sys.path):
-    """Removes a single project from the installation"""
+    Returns True on success
+    """
     dist = get_distribution(project_name, use_egg_info=True, paths=paths)
     if dist is None:
-        raise DistutilsError('Distribution "%s" not found' % project_name)
-    files = dist.get_installed_files(local=True)
+        raise PackagingError('Distribution %r not found' % project_name)
+    files = dist.list_installed_files(local=True)
     rmdirs = []
     rmfiles = []
     tmp = tempfile.mkdtemp(prefix=project_name + '-uninstall')
+
+    def _move_file(source, target):
+        try:
+            os.rename(source, target)
+        except OSError:
+            return sys.exc_info()[1]
+        return None
+
+    success = True
+    error = None
     try:
         for file_, md5, size in files:
             if os.path.isfile(file_):
                 dirname, filename = os.path.split(file_)
                 tmpfile = os.path.join(tmp, filename)
                 try:
-                    os.rename(file_, tmpfile)
+                    error = _move_file(file_, tmpfile)
+                    if error is not None:
+                        success = False
+                        break
                 finally:
                     if not os.path.isfile(file_):
                         os.rename(tmpfile, file_)
@@ -352,72 +422,119 @@
     finally:
         shutil.rmtree(tmp)
 
-    logger.info('removing %r...', project_name)
+    if not success:
+        logger.info('%r cannot be removed.', project_name)
+        logger.info('Error: %s', error)
+        return False
 
-    file_count = 0
+    logger.info('Removing %r: ', project_name)
+
     for file_ in rmfiles:
-        os.remove(file_)
-        file_count += 1
+        logger.info('  %s', file_)
 
-    dir_count = 0
-    for dirname in rmdirs:
-        if not os.path.exists(dirname):
-            # could
-            continue
+    # Taken from the pip project
+    if auto_confirm:
+        response = 'y'
+    else:
+        response = ask('Proceed (y/n)? ', ('y', 'n'))
 
-        files_count = 0
-        for root, dir, files in os.walk(dirname):
-            files_count += len(files)
+    if response == 'y':
+        file_count = 0
+        for file_ in rmfiles:
+            os.remove(file_)
+            file_count += 1
 
-        if files_count > 0:
-            # XXX Warning
-            continue
+        dir_count = 0
+        for dirname in rmdirs:
+            if not os.path.exists(dirname):
+                # could
+                continue
 
-        # empty dirs with only empty dirs
-        if bool(os.stat(dirname).st_mode & stat.S_IWUSR):
-            # XXX Add a callable in shutil.rmtree to count
-            # the number of deleted elements
-            shutil.rmtree(dirname)
-            dir_count += 1
+            files_count = 0
+            for root, dir, files in os.walk(dirname):
+                files_count += len(files)
 
-    # removing the top path
-    # XXX count it ?
-    if os.path.exists(dist.path):
-        shutil.rmtree(dist.path)
+            if files_count > 0:
+                # XXX Warning
+                continue
 
-    logger.info('success: removed %d files and %d dirs',
-                file_count, dir_count)
+            # empty dirs with only empty dirs
+            if os.stat(dirname).st_mode & stat.S_IWUSR:
+                # XXX Add a callable in shutil.rmtree to count
+                # the number of deleted elements
+                shutil.rmtree(dirname)
+                dir_count += 1
+
+        # removing the top path
+        # XXX count it ?
+        if os.path.exists(dist.path):
+            shutil.rmtree(dist.path)
+
+        logger.info('Success: removed %d files and %d dirs',
+                    file_count, dir_count)
+
+    return True
 
 
 def install(project):
-    logger.info('getting information about %r', project)
+    """Installs a project.
+
+    Returns True on success, False on failure
+    """
+    if is_python_build():
+        # Python would try to install into the site-packages directory under
+        # $PREFIX, but when running from an uninstalled code checkout we don't
+        # want to create directories under the installation root
+        message = ('installing third-party projects from an uninstalled '
+                   'Python is not supported')
+        logger.error(message)
+        return False
+
+    logger.info('Checking the installation location...')
+    purelib_path = get_path('purelib')
+
+    # trying to write a file there
+    try:
+        with tempfile.NamedTemporaryFile(suffix=project,
+                                         dir=purelib_path) as testfile:
+            testfile.write(b'test')
+    except OSError:
+        # FIXME this should check the errno, or be removed altogether (race
+        # condition: the directory permissions could be changed between here
+        # and the actual install)
+        logger.info('Unable to write in "%s". Do you have the permissions ?'
+                    % purelib_path)
+        return False
+
+    logger.info('Getting information about %r...', project)
     try:
         info = get_infos(project)
     except InstallationException:
-        logger.info('cound not find %r', project)
-        return
+        logger.info('Cound not find %r', project)
+        return False
 
     if info['install'] == []:
-        logger.info('nothing to install')
-        return
+        logger.info('Nothing to install')
+        return False
 
     install_path = get_config_var('base')
     try:
         install_from_infos(install_path,
                            info['install'], info['remove'], info['conflict'])
 
-    except InstallationConflict, e:
+    except InstallationConflict:
+        e = sys.exc_info()[1]
         if logger.isEnabledFor(logging.INFO):
-            projects = ['%s %s' % (p.name, p.version) for p in e.args[0]]
+            projects = ('%r %s' % (p.name, p.version) for p in e.args[0])
             logger.info('%r conflicts with %s', project, ','.join(projects))
 
+    return True
+
 
 def _main(**attrs):
     if 'script_args' not in attrs:
-        import sys
         attrs['requirements'] = sys.argv[1]
     get_infos(**attrs)
 
-
 if __name__ == '__main__':
     _main()
diff --git a/distutils2/manifest.py b/distutils2/manifest.py
--- a/distutils2/manifest.py
+++ b/distutils2/manifest.py
@@ -1,22 +1,20 @@
-"""distutils2.manifest
+"""Class representing the list of files in a distribution.
 
-Provides a Manifest class that can be used to:
+The Manifest class can be used to:
 
  - read or write a MANIFEST file
  - read a template file and find out the file list
-
-Basically, Manifest *is* the file list.
-
-XXX todo: document + add tests
 """
+# XXX todo: document + add tests
 import re
 import os
+import sys
 import fnmatch
-import logging
 
+from distutils2 import logger
 from distutils2.util import write_file, convert_path
-from distutils2.errors import (DistutilsTemplateError,
-                               DistutilsInternalError)
+from distutils2.errors import (PackagingTemplateError,
+                              PackagingInternalError)
 
 __all__ = ['Manifest']
 
@@ -24,6 +22,7 @@
 _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M)
 _COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S)
 
+
 class Manifest(object):
     """A list of files built by on exploring the filesystem and filtered by
     applying various patterns to what we find there.
@@ -48,11 +47,8 @@
 
     def sort(self):
         # Not a strict lexical sort!
-        sortable_files = map(os.path.split, self.files)
-        sortable_files.sort()
-        self.files = []
-        for sort_tuple in sortable_files:
-            self.files.append(os.path.join(*sort_tuple))
+        self.files = [os.path.join(*path_tuple) for path_tuple in
+                      sorted(os.path.split(path) for path in self.files)]
 
     def clear(self):
         """Clear all collected files."""
@@ -72,7 +68,7 @@
 
         Updates the list accordingly.
         """
-        if isinstance(path_or_file, str):
+        if isinstance(path_or_file, basestring):
             f = open(path_or_file)
         else:
             f = path_or_file
@@ -94,8 +90,8 @@
                 continue
             try:
                 self._process_template_line(line)
-            except DistutilsTemplateError, msg:
-                logging.warning("%s, %s", path_or_file, msg)
+            except PackagingTemplateError:
+                logger.warning("%s, %s", path_or_file, sys.exc_info()[1])
 
     def write(self, path):
         """Write the file list in 'self.filelist' (presumably as filled in
@@ -103,22 +99,19 @@
         named by 'self.manifest'.
         """
         if os.path.isfile(path):
-            fp = open(path)
-            try:
+            with open(path) as fp:
                 first_line = fp.readline()
-            finally:
-                fp.close()
 
-            if first_line != '# file GENERATED by distutils, do NOT edit\n':
-                logging.info("not writing to manually maintained "
-                             "manifest file %r", path)
+            if first_line != '# file GENERATED by distutils2, do NOT edit\n':
+                logger.info("not writing to manually maintained "
+                            "manifest file %r", path)
                 return
 
         self.sort()
         self.remove_duplicates()
         content = self.files[:]
-        content.insert(0, '# file GENERATED by distutils, do NOT edit')
-        logging.info("writing manifest file %r", path)
+        content.insert(0, '# file GENERATED by distutils2, do NOT edit')
+        logger.info("writing manifest file %r", path)
         write_file(path, content)
 
     def read(self, path):
@@ -126,28 +119,26 @@
         fill in 'self.filelist', the list of files to include in the source
         distribution.
         """
-        logging.info("reading manifest file %r", path)
-        manifest = open(path)
-        try:
+        logger.info("reading manifest file %r", path)
+        with open(path) as manifest:
             for line in manifest.readlines():
                 self.append(line)
-        finally:
-            manifest.close()
 
-    def exclude_pattern(self, pattern, anchor=1, prefix=None, is_regex=0):
+    def exclude_pattern(self, pattern, anchor=True, prefix=None,
+                        is_regex=False):
         """Remove strings (presumably filenames) from 'files' that match
         'pattern'.
 
         Other parameters are the same as for 'include_pattern()', above.
-        The list 'self.files' is modified in place. Return 1 if files are
+        The list 'self.files' is modified in place. Return True if files are
         found.
         """
-        files_found = 0
+        files_found = False
         pattern_re = _translate_pattern(pattern, anchor, prefix, is_regex)
-        for i in range(len(self.files)-1, -1, -1):
+        for i in range(len(self.files) - 1, -1, -1):
             if pattern_re.search(self.files[i]):
                 del self.files[i]
-                files_found = 1
+                files_found = True
 
         return files_found
 
@@ -167,28 +158,28 @@
         if action in ('include', 'exclude',
                       'global-include', 'global-exclude'):
             if len(words) < 2:
-                raise DistutilsTemplateError(
+                raise PackagingTemplateError(
                       "%r expects <pattern1> <pattern2> ..." % action)
 
-            patterns = map(convert_path, words[1:])
+            patterns = [convert_path(word) for word in words[1:]]
 
         elif action in ('recursive-include', 'recursive-exclude'):
             if len(words) < 3:
-                raise DistutilsTemplateError(
+                raise PackagingTemplateError(
                       "%r expects <dir> <pattern1> <pattern2> ..." % action)
 
             dir = convert_path(words[1])
-            patterns = map(convert_path, words[2:])
+            patterns = [convert_path(word) for word in words[2:]]
 
         elif action in ('graft', 'prune'):
             if len(words) != 2:
-                raise DistutilsTemplateError(
+                raise PackagingTemplateError(
                      "%r expects a single <dir_pattern>" % action)
 
             dir_pattern = convert_path(words[1])
 
         else:
-            raise DistutilsTemplateError("unknown action %r" % action)
+            raise PackagingTemplateError("unknown action %r" % action)
 
         return action, patterns, dir, dir_pattern
 
@@ -205,55 +196,56 @@
         # can proceed with minimal error-checking.
         if action == 'include':
             for pattern in patterns:
-                if not self._include_pattern(pattern, anchor=1):
-                    logging.warning("no files found matching %r", pattern)
+                if not self._include_pattern(pattern, anchor=True):
+                    logger.warning("no files found matching %r", pattern)
 
         elif action == 'exclude':
             for pattern in patterns:
-                if not self.exclude_pattern(pattern, anchor=1):
-                    logging.warning("no previously-included files "
-                                    "found matching %r", pattern)
+                if not self.exclude_pattern(pattern, anchor=True):
+                    logger.warning("no previously-included files "
+                                   "found matching %r", pattern)
 
         elif action == 'global-include':
             for pattern in patterns:
-                if not self._include_pattern(pattern, anchor=0):
-                    logging.warning("no files found matching %r "
-                                    "anywhere in distribution", pattern)
+                if not self._include_pattern(pattern, anchor=False):
+                    logger.warning("no files found matching %r "
+                                   "anywhere in distribution", pattern)
 
         elif action == 'global-exclude':
             for pattern in patterns:
-                if not self.exclude_pattern(pattern, anchor=0):
-                    logging.warning("no previously-included files "
-                                    "matching %r found anywhere in "
-                                    "distribution", pattern)
+                if not self.exclude_pattern(pattern, anchor=False):
+                    logger.warning("no previously-included files "
+                                   "matching %r found anywhere in "
+                                   "distribution", pattern)
 
         elif action == 'recursive-include':
             for pattern in patterns:
                 if not self._include_pattern(pattern, prefix=dir):
-                    logging.warning("no files found matching %r "
-                                    "under directory %r", pattern, dir)
+                    logger.warning("no files found matching %r "
+                                   "under directory %r", pattern, dir)
 
         elif action == 'recursive-exclude':
             for pattern in patterns:
                 if not self.exclude_pattern(pattern, prefix=dir):
-                    logging.warning("no previously-included files "
-                                    "matching %r found under directory %r",
-                                    pattern, dir)
+                    logger.warning("no previously-included files "
+                                   "matching %r found under directory %r",
+                                   pattern, dir)
 
         elif action == 'graft':
             if not self._include_pattern(None, prefix=dir_pattern):
-                logging.warning("no directories found matching %r",
-                                dir_pattern)
+                logger.warning("no directories found matching %r",
+                               dir_pattern)
 
         elif action == 'prune':
             if not self.exclude_pattern(None, prefix=dir_pattern):
-                logging.warning("no previously-included directories found "
-                                "matching %r", dir_pattern)
+                logger.warning("no previously-included directories found "
+                               "matching %r", dir_pattern)
         else:
-            raise DistutilsInternalError(
-                  "this cannot happen: invalid action %r" % action)
+            raise PackagingInternalError(
+                "this cannot happen: invalid action %r" % action)
 
-    def _include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0):
+    def _include_pattern(self, pattern, anchor=True, prefix=None,
+                         is_regex=False):
         """Select strings (presumably filenames) from 'self.files' that
         match 'pattern', a Unix-style wildcard (glob) pattern.
 
@@ -277,9 +269,9 @@
 
         Selected strings will be added to self.files.
 
-        Return 1 if files are found.
+        Return True if files are found.
         """
-        files_found = 0
+        files_found = False
         pattern_re = _translate_pattern(pattern, anchor, prefix, is_regex)
 
         # delayed loading of allfiles list
@@ -289,21 +281,19 @@
         for name in self.allfiles:
             if pattern_re.search(name):
                 self.files.append(name)
-                files_found = 1
+                files_found = True
 
         return files_found
 
 
-
 #
 # Utility functions
 #
-
 def _findall(dir=os.curdir):
     """Find all files under 'dir' and return the list of full filenames
     (relative to 'dir').
     """
-    from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK
+    from stat import S_ISREG, S_ISDIR, S_ISLNK
 
     list = []
     stack = [dir]
@@ -322,7 +312,7 @@
 
             # Avoid excess stat calls -- just one will do, thank you!
             stat = os.stat(fullname)
-            mode = stat[ST_MODE]
+            mode = stat.st_mode
             if S_ISREG(mode):
                 list.append(fullname)
             elif S_ISDIR(mode) and not S_ISLNK(mode):
@@ -331,7 +321,6 @@
     return list
 
 
-
 def _glob_to_re(pattern):
     """Translate a shell-like glob pattern to a regular expression.
 
@@ -353,7 +342,7 @@
     return pattern_re
 
 
-def _translate_pattern(pattern, anchor=1, prefix=None, is_regex=0):
+def _translate_pattern(pattern, anchor=True, prefix=None, is_regex=False):
     """Translate a shell-like wildcard pattern to a compiled regular
     expression.
 
@@ -362,7 +351,7 @@
     or just returned as-is (assumes it's a regex object).
     """
     if is_regex:
-        if isinstance(pattern, str):
+        if isinstance(pattern, basestring):
             return re.compile(pattern)
         else:
             return pattern
diff --git a/distutils2/markers.py b/distutils2/markers.py
--- a/distutils2/markers.py
+++ b/distutils2/markers.py
@@ -1,10 +1,11 @@
-""" Micro-language for PEP 345 environment markers
-"""
+"""Parser for the environment markers micro-language defined in PEP 345."""
+
 import sys
 import platform
 import os
-from tokenize import tokenize, NAME, OP, STRING, ENDMARKER
-from StringIO import StringIO
+
+from tokenize import generate_tokens, NAME, OP, STRING, ENDMARKER
+from StringIO import StringIO as BytesIO
 
 __all__ = ['interpret']
 
@@ -30,7 +31,8 @@
          'python_full_version': sys.version.split(' ', 1)[0],
          'os.name': os.name,
          'platform.version': platform.version(),
-         'platform.machine': platform.machine()}
+         'platform.machine': platform.machine(),
+         'platform.python_implementation': platform.python_implementation()}
 
 
 class _Operation(object):
@@ -124,39 +126,39 @@
         return self.left() and self.right()
 
 
-class _CHAIN(object):
-
-    def __init__(self, execution_context=None):
-        self.ops = []
-        self.op_starting = True
-        self.execution_context = execution_context
-
-    def eat(self, toktype, tokval, rowcol, line, logical_line):
+def interpret(marker, execution_context=None):
+    """Interpret a marker and return a result depending on environment."""
+    marker = marker.strip().encode()
+    ops = []
+    op_starting = True
+    for token in generate_tokens(BytesIO(marker).readline):
+        # Unpack token
+        toktype, tokval, rowcol, line, logical_line = token
         if toktype not in (NAME, OP, STRING, ENDMARKER):
             raise SyntaxError('Type not supported "%s"' % tokval)
 
-        if self.op_starting:
-            op = _Operation(self.execution_context)
-            if len(self.ops) > 0:
-                last = self.ops[-1]
+        if op_starting:
+            op = _Operation(execution_context)
+            if len(ops) > 0:
+                last = ops[-1]
                 if isinstance(last, (_OR, _AND)) and not last.filled():
                     last.right = op
                 else:
-                    self.ops.append(op)
+                    ops.append(op)
             else:
-                self.ops.append(op)
-            self.op_starting = False
+                ops.append(op)
+            op_starting = False
         else:
-            op = self.ops[-1]
+            op = ops[-1]
 
         if (toktype == ENDMARKER or
             (toktype == NAME and tokval in ('and', 'or'))):
             if toktype == NAME and tokval == 'and':
-                self.ops.append(_AND(self.ops.pop()))
+                ops.append(_AND(ops.pop()))
             elif toktype == NAME and tokval == 'or':
-                self.ops.append(_OR(self.ops.pop()))
-            self.op_starting = True
-            return
+                ops.append(_OR(ops.pop()))
+            op_starting = True
+            continue
 
         if isinstance(op, (_OR, _AND)) and op.right is not None:
             op = op.right
@@ -179,16 +181,7 @@
             else:
                 op.op = tokval
 
-    def result(self):
-        for op in self.ops:
-            if not op():
-                return False
-        return True
-
-
-def interpret(marker, execution_context=None):
-    """Interpret a marker and return a result depending on environment."""
-    marker = marker.strip()
-    operations = _CHAIN(execution_context)
-    tokenize(StringIO(marker).readline, operations.eat)
-    return operations.result()
+    for op in ops:
+        if not op():
+            return False
+    return True
diff --git a/distutils2/metadata.py b/distutils2/metadata.py
--- a/distutils2/metadata.py
+++ b/distutils2/metadata.py
@@ -3,17 +3,19 @@
 Supports all metadata formats (1.0, 1.1, 1.2).
 """
 
+import codecs
 import re
+import logging
+
 from StringIO import StringIO
 from email import message_from_file
-
 from distutils2 import logger
 from distutils2.markers import interpret
 from distutils2.version import (is_valid_predicate, is_valid_version,
-                                is_valid_versions)
+                               is_valid_versions)
 from distutils2.errors import (MetadataMissingError,
-                               MetadataConflictError,
-                               MetadataUnrecognizedVersionError)
+                              MetadataConflictError,
+                              MetadataUnrecognizedVersionError)
 
 try:
     # docutils is installed
@@ -39,8 +41,7 @@
     _HAS_DOCUTILS = False
 
 # public API of this module
-__all__ = ['Metadata', 'get_metadata_version', 'metadata_to_dict',
-           'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION']
+__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION']
 
 # Encoding used for the PKG-INFO files
 PKG_INFO_ENCODING = 'utf-8'
@@ -99,7 +100,7 @@
                 return True
         return False
 
-    keys = fields.keys()
+    keys = list(fields)
     possible_versions = ['1.0', '1.1', '1.2']
 
     # first let's try to see if a field is not part of one of the version
@@ -138,52 +139,6 @@
     return '1.2'
 
 
-def get_metadata_version(metadata):
-    """Return the Metadata-Version attribute
-
-    - *metadata* give a METADATA object
-    """
-    return metadata['Metadata-Version']
-
-
-def metadata_to_dict(metadata):
-    """Convert a metadata object to a dict
-
-    - *metadata* give a METADATA object
-    """
-    data = {
-        'metadata_version': metadata['Metadata-Version'],
-        'name': metadata['Name'],
-        'version': metadata['Version'],
-        'summary': metadata['Summary'],
-        'home_page': metadata['Home-page'],
-        'author': metadata['Author'],
-        'author_email': metadata['Author-email'],
-        'license': metadata['License'],
-        'description': metadata['Description'],
-        'keywords': metadata['Keywords'],
-        'platform': metadata['Platform'],
-        'classifier': metadata['Classifier'],
-        'download_url': metadata['Download-URL'],
-    }
-
-    if metadata['Metadata-Version'] == '1.2':
-        data['requires_dist'] = metadata['Requires-Dist']
-        data['requires_python'] = metadata['Requires-Python']
-        data['requires_external'] = metadata['Requires-External']
-        data['provides_dist'] = metadata['Provides-Dist']
-        data['obsoletes_dist'] = metadata['Obsoletes-Dist']
-        data['project_url'] = [','.join(url) for url in
-                               metadata['Project-URL']]
-
-    elif metadata['Metadata-Version'] == '1.1':
-        data['provides'] = metadata['Provides']
-        data['requires'] = metadata['Requires']
-        data['obsoletes'] = metadata['Obsoletes']
-
-    return data
-
-
 _ATTR2FIELD = {
     'metadata_version': 'Metadata-Version',
     'name': 'Name',
@@ -225,6 +180,8 @@
 
 _UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description')
 
+_MISSING = object()
+
 
 class NoDefault(object):
     """Marker object used for clean representation"""
@@ -248,10 +205,8 @@
     # also document the mapping API and UNKNOWN default key
 
     def __init__(self, path=None, platform_dependent=False,
-                 execution_context=None, fileobj=None, mapping=None,
-                 display_warnings=False):
+                 execution_context=None, fileobj=None, mapping=None):
         self._fields = {}
-        self.display_warnings = display_warnings
         self.requires_files = []
         self.docutils_support = _HAS_DOCUTILS
         self.platform_dependent = platform_dependent
@@ -269,12 +224,7 @@
         self._fields['Metadata-Version'] = _best_version(self._fields)
 
     def _write_field(self, file, name, value):
-        file.write('%s: %s\n' % (name, value))
-
-    def _encode_field(self, value):
-        if isinstance(value, unicode):
-            return value.encode(PKG_INFO_ENCODING)
-        return str(value)
+        file.write(u'%s: %s\n' % (name, value))
 
     def __getitem__(self, name):
         return self.get(name)
@@ -358,7 +308,8 @@
 
     def read(self, filepath):
         """Read the metadata values from a file path."""
-        self.read_file(open(filepath))
+        with codecs.open(filepath, 'r', encoding='utf-8') as fp:
+            self.read_file(fp)
 
     def read_file(self, fileob):
         """Read the metadata values from a file object."""
@@ -380,11 +331,8 @@
 
     def write(self, filepath):
         """Write the metadata fields to filepath."""
-        pkg_info = open(filepath, 'w')
-        try:
-            self.write_file(pkg_info)
-        finally:
-            pkg_info.close()
+        with codecs.open(filepath, 'w', encoding='utf-8') as fp:
+            self.write_file(fp)
 
     def write_file(self, fileobject):
         """Write the PKG-INFO format data to a file object."""
@@ -437,36 +385,38 @@
 
         if ((name in _ELEMENTSFIELD or name == 'Platform') and
             not isinstance(value, (list, tuple))):
-            if isinstance(value, str):
+            if isinstance(value, basestring):
                 value = [v.strip() for v in value.split(',')]
             else:
                 value = []
         elif (name in _LISTFIELDS and
               not isinstance(value, (list, tuple))):
-            if isinstance(value, str):
+            if isinstance(value, basestring):
                 value = [value]
             else:
                 value = []
 
-        if self.display_warnings:
+        if logger.isEnabledFor(logging.WARNING):
+            project_name = self['Name']
+
             if name in _PREDICATE_FIELDS and value is not None:
                 for v in value:
                     # check that the values are valid predicates
                     if not is_valid_predicate(v.split(';')[0]):
-                        logger.warn('"%s" is not a valid predicate (field "%s")' %
-                            (v, name))
+                        logger.warning(
+                            '%r: %r is not a valid predicate (field %r)',
+                            project_name, v, name)
             # FIXME this rejects UNKNOWN, is that right?
             elif name in _VERSIONS_FIELDS and value is not None:
                 if not is_valid_versions(value):
-                    logger.warn('"%s" is not a valid version (field "%s")' %
-                        (value, name))
+                    logger.warning('%r: %r is not a valid version (field %r)',
+                                   project_name, value, name)
             elif name in _VERSION_FIELDS and value is not None:
                 if not is_valid_version(value):
-                    logger.warn('"%s" is not a valid version (field "%s")' %
-                        (value, name))
+                    logger.warning('%r: %r is not a valid version (field %r)',
+                                   project_name, value, name)
 
         if name in _UNICODEFIELDS:
-            value = self._encode_field(value)
             if name == 'Description':
                 value = self._remove_line_prefix(value)
 
@@ -482,7 +432,7 @@
             return default
         if name in _UNICODEFIELDS:
             value = self._fields[name]
-            return self._encode_field(value)
+            return value
         elif name in _LISTFIELDS:
             value = self._fields[name]
             if value is None:
@@ -493,17 +443,17 @@
                 if not valid:
                     continue
                 if name not in _LISTTUPLEFIELDS:
-                    res.append(self._encode_field(val))
+                    res.append(val)
                 else:
                     # That's for Project-URL
-                    res.append((self._encode_field(val[0]), val[1]))
+                    res.append((val[0], val[1]))
             return res
 
         elif name in _ELEMENTSFIELD:
             valid, value = self._platform(self._fields[name])
             if not valid:
                 return []
-            if isinstance(value, str):
+            if isinstance(value, basestring):
                 return value.split(',')
         valid, value = self._platform(self._fields[name])
         if not valid:
@@ -551,13 +501,55 @@
 
         return missing, warnings
 
+    def todict(self):
+        """Return fields as a dict.
+
+        Field names will be converted to use the underscore-lowercase style
+        instead of hyphen-mixed case (i.e. home_page instead of Home-page).
+        """
+        data = {
+            'metadata_version': self['Metadata-Version'],
+            'name': self['Name'],
+            'version': self['Version'],
+            'summary': self['Summary'],
+            'home_page': self['Home-page'],
+            'author': self['Author'],
+            'author_email': self['Author-email'],
+            'license': self['License'],
+            'description': self['Description'],
+            'keywords': self['Keywords'],
+            'platform': self['Platform'],
+            'classifier': self['Classifier'],
+            'download_url': self['Download-URL'],
+        }
+
+        if self['Metadata-Version'] == '1.2':
+            data['requires_dist'] = self['Requires-Dist']
+            data['requires_python'] = self['Requires-Python']
+            data['requires_external'] = self['Requires-External']
+            data['provides_dist'] = self['Provides-Dist']
+            data['obsoletes_dist'] = self['Obsoletes-Dist']
+            data['project_url'] = [','.join(url) for url in
+                                   self['Project-URL']]
+
+        elif self['Metadata-Version'] == '1.1':
+            data['provides'] = self['Provides']
+            data['requires'] = self['Requires']
+            data['obsoletes'] = self['Obsoletes']
+
+        return data
+
     # Mapping API
 
     def keys(self):
         return _version2fieldlist(self['Metadata-Version'])
 
+    def __iter__(self):
+        for key in self.keys():
+            yield key
+
     def values(self):
-        return [self[key] for key in self.keys()]
+        return [self[key] for key in list(self.keys())]
 
     def items(self):
-        return [(key, self[key]) for key in self.keys()]
+        return [(key, self[key]) for key in list(self.keys())]
diff --git a/distutils2/mkcfg.py b/distutils2/mkcfg.py
deleted file mode 100644
--- a/distutils2/mkcfg.py
+++ /dev/null
@@ -1,657 +0,0 @@
-#!/usr/bin/env python
-#
-#  Helper for automating the creation of a package by looking at you
-#  current directory and asking the user questions.
-#
-#  Available as either a stand-alone file or callable from the distutils2
-#  package:
-#
-#     python -m distutils2.mkcfg
-#  or:
-#     python mkcfg.py
-#
-#  Written by Sean Reifschneider <jafo at tummy.com>
-#
-#  Original TODO list:
-#  Look for a license file and automatically add the category.
-#  When a .c file is found during the walk, can we add it as an extension?
-#  Ask if there is a maintainer different that the author
-#  Ask for the platform (can we detect this via "import win32" or something?)
-#  Ask for the dependencies.
-#  Ask for the Requires-Dist
-#  Ask for the Provides-Dist
-#  Ask for a description
-#  Detect scripts (not sure how.  #! outside of package?)
-
-import os
-import sys
-import glob
-import re
-import shutil
-from ConfigParser import RawConfigParser
-from textwrap import dedent
-try:
-    from hashlib import md5
-except ImportError:
-    from distutils2._backport.hashlib  import md5
-# importing this with an underscore as it should be replaced by the
-# dict form or another structures for all purposes
-from distutils2._trove import all_classifiers as _CLASSIFIERS_LIST
-from distutils2._backport import sysconfig
-
-_FILENAME = 'setup.cfg'
-
-_helptext = {
-    'name': '''
-The name of the program to be packaged, usually a single word composed
-of lower-case characters such as "python", "sqlalchemy", or "CherryPy".
-''',
-    'version': '''
-Version number of the software, typically 2 or 3 numbers separated by dots
-such as "1.00", "0.6", or "3.02.01".  "0.1.0" is recommended for initial
-development.
-''',
-    'summary': '''
-A one-line summary of what this project is or does, typically a sentence 80
-characters or less in length.
-''',
-    'author': '''
-The full name of the author (typically you).
-''',
-    'author_email': '''
-E-mail address of the project author (typically you).
-''',
-    'do_classifier': '''
-Trove classifiers are optional identifiers that allow you to specify the
-intended audience by saying things like "Beta software with a text UI
-for Linux under the PSF license.  However, this can be a somewhat involved
-process.
-''',
-    'packages': '''
-You can provide a package name contained in your project.
-''',
-    'modules': '''
-You can provide a python module contained in your project.
-''',
-    'extra_files': '''
-You can provide extra files/dirs contained in your project.
-It has to follow the template syntax. XXX add help here.
-''',
-
-    'home_page': '''
-The home page for the project, typically starting with "http://".
-''',
-    'trove_license': '''
-Optionally you can specify a license.  Type a string that identifies a common
-license, and then you can select a list of license specifiers.
-''',
-    'trove_generic': '''
-Optionally, you can set other trove identifiers for things such as the
-human language, programming language, user interface, etc...
-''',
-    'setup.py found': '''
-The setup.py script will be executed to retrieve the metadata.
-A wizard will be run if you answer "n",
-''',
-}
-
-# XXX everything needs docstrings and tests (both low-level tests of various
-# methods and functional tests of running the script)
-
-
-def ask_yn(question, default=None, helptext=None):
-    question += ' (y/n)'
-    while True:
-        answer = ask(question, default, helptext, required=True)
-        if answer and answer[0].lower() in 'yn':
-            return answer[0].lower()
-
-        print '\nERROR: You must select "Y" or "N".\n'
-
-
-def ask(question, default=None, helptext=None, required=True,
-        lengthy=False, multiline=False):
-    prompt = '%s: ' % (question,)
-    if default:
-        prompt = '%s [%s]: ' % (question, default)
-        if default and len(question) + len(default) > 70:
-            prompt = '%s\n    [%s]: ' % (question, default)
-    if lengthy or multiline:
-        prompt += '\n   > '
-
-    if not helptext:
-        helptext = 'No additional help available.'
-
-    helptext = helptext.strip("\n")
-
-    while True:
-        sys.stdout.write(prompt)
-        sys.stdout.flush()
-
-        line = sys.stdin.readline().strip()
-        if line == '?':
-            print '=' * 70
-            print helptext
-            print '=' * 70
-            continue
-        if default and not line:
-            return default
-        if not line and required:
-            print '*' * 70
-            print 'This value cannot be empty.'
-            print '==========================='
-            if helptext:
-                print helptext
-            print '*' * 70
-            continue
-        return line
-
-
-def _build_classifiers_dict(classifiers):
-    d = {}
-    for key in classifiers:
-        subDict = d
-        for subkey in key.split(' :: '):
-            if not subkey in subDict:
-                subDict[subkey] = {}
-            subDict = subDict[subkey]
-    return d
-
-CLASSIFIERS = _build_classifiers_dict(_CLASSIFIERS_LIST)
-
-
-def _build_licences(classifiers):
-    res = []
-    for index, item in enumerate(classifiers):
-        if not item.startswith('License :: '):
-            continue
-        res.append((index, item.split(' :: ')[-1].lower()))
-    return res
-
-LICENCES = _build_licences(_CLASSIFIERS_LIST)
-
-
-class MainProgram(object):
-    def __init__(self):
-        self.configparser = None
-        self.classifiers = set([])
-        self.data = {}
-        self.data['classifier'] = self.classifiers
-        self.data['packages'] = []
-        self.data['modules'] = []
-        self.data['platform'] = []
-        self.data['resources'] = []
-        self.data['extra_files'] = []
-        self.data['scripts'] = []
-        self.load_config_file()
-
-    def lookup_option(self, key):
-        if not self.configparser.has_option('DEFAULT', key):
-            return None
-        return self.configparser.get('DEFAULT', key)
-
-    def load_config_file(self):
-        self.configparser = RawConfigParser()
-        # TODO replace with section in distutils config file
-        #XXX freedesktop
-        self.configparser.read(os.path.expanduser('~/.mkcfg'))
-        self.data['author'] = self.lookup_option('author')
-        self.data['author_email'] = self.lookup_option('author_email')
-
-    def update_config_file(self):
-        valuesDifferent = False
-        # FIXME looking only for those two fields seems wrong
-        for compareKey in ('author', 'author_email'):
-            if self.lookup_option(compareKey) != self.data[compareKey]:
-                valuesDifferent = True
-                self.configparser.set('DEFAULT', compareKey,
-                                      self.data[compareKey])
-
-        if not valuesDifferent:
-            return
-
-        #XXX freedesktop
-        fp = open(os.path.expanduser('~/.mkcfgpy'), 'w')
-        try:
-            self.configparser.write(fp)
-        finally:
-            fp.close()
-
-    def load_existing_setup_script(self):
-        """ Generate a setup.cfg from an existing setup.py.
-
-        It only exports the distutils metadata (setuptools specific metadata
-        is not actually supported).
-        """
-        setuppath = 'setup.py'
-        if not os.path.exists(setuppath):
-            return
-        else:
-            ans = ask_yn(('A legacy setup.py has been found.\n'
-                          'Would you like to convert it to a setup.cfg ?'),
-                         'y',
-                         _helptext['setup.py found'])
-            if ans != 'y':
-                return
-
-        data = self.data
-
-        def setup(**attrs):
-            """Mock the setup(**attrs) in order to retrive metadata."""
-            # use the distutils v1 processings to correctly parse metadata.
-            #XXX we could also use the setuptools distibution ???
-            from distutils.dist import Distribution
-            dist = Distribution(attrs)
-            dist.parse_config_files()
-            # 1. retrieves metadata that are quite similar PEP314<->PEP345
-            labels = (('name',) * 2,
-                      ('version',) * 2,
-                      ('author',) * 2,
-                      ('author_email',) * 2,
-                      ('maintainer',) * 2,
-                      ('maintainer_email',) * 2,
-                      ('description', 'summary'),
-                      ('long_description', 'description'),
-                      ('url', 'home_page'),
-                      ('platforms', 'platform'))
-
-            if sys.version[:3] >= '2.5':
-                labels += (('provides', 'provides-dist'),
-                           ('obsoletes', 'obsoletes-dist'),
-                           ('requires', 'requires-dist'),)
-            get = lambda lab: getattr(dist.metadata, lab.replace('-', '_'))
-            data.update((new, get(old)) for (old, new) in labels if get(old))
-            # 2. retrieves data that requires special processings.
-            data['classifier'].update(dist.get_classifiers() or [])
-            data['scripts'].extend(dist.scripts or [])
-            data['packages'].extend(dist.packages or [])
-            data['modules'].extend(dist.py_modules or [])
-            # 2.1 data_files -> resources.
-            if dist.data_files:
-                if len(dist.data_files) < 2 or \
-                   isinstance(dist.data_files[1], str):
-                    dist.data_files = [('', dist.data_files)]
-                # add tokens in the destination paths
-                vars = {'distribution.name': data['name']}
-                path_tokens = sysconfig.get_paths(vars=vars).items()
-                # sort tokens to use the longest one first
-                # TODO chain two sorted with key arguments, remove cmp
-                path_tokens.sort(cmp=lambda x, y: cmp(len(y), len(x)),
-                                 key=lambda x: x[1])
-                for dest, srcs in (dist.data_files or []):
-                    dest = os.path.join(sys.prefix, dest)
-                    for tok, path in path_tokens:
-                        if dest.startswith(path):
-                            dest = ('{%s}' % tok) + dest[len(path):]
-                            files = [('/ '.join(src.rsplit('/', 1)), dest)
-                                     for src in srcs]
-                            data['resources'].extend(files)
-                            continue
-            # 2.2 package_data -> extra_files
-            package_dirs = dist.package_dir or {}
-            for package, extras in dist.package_data.iteritems() or []:
-                package_dir = package_dirs.get(package, package)
-                files = [os.path.join(package_dir, f) for f in extras]
-                data['extra_files'].extend(files)
-
-            # Use README file if its content is the desciption
-            if "description" in data:
-                ref = md5(re.sub('\s', '', self.data['description']).lower())
-                ref = ref.digest()
-                for readme in glob.glob('README*'):
-                    fp = open(readme)
-                    try:
-                        contents = fp.read()
-                    finally:
-                        fp.close()
-                    val = md5(re.sub('\s', '', contents.lower())).digest()
-                    if val == ref:
-                        del data['description']
-                        data['description-file'] = readme
-                        break
-
-        # apply monkey patch to distutils (v1) and setuptools (if needed)
-        # (abord the feature if distutils v1 has been killed)
-        try:
-            import distutils.core as DC
-            DC.setup  # ensure distutils v1
-        except (ImportError, AttributeError):
-            return
-        saved_setups = [(DC, DC.setup)]
-        DC.setup = setup
-        try:
-            import setuptools
-            saved_setups.append((setuptools, setuptools.setup))
-            setuptools.setup = setup
-        except (ImportError, AttributeError):
-            pass
-        # get metadata by executing the setup.py with the patched setup(...)
-        success = False  # for python < 2.4
-        try:
-            pyenv = globals().copy()
-            execfile(setuppath, pyenv)
-            success = True
-        finally:  # revert monkey patches
-            for patched_module, original_setup in saved_setups:
-                patched_module.setup = original_setup
-        if not self.data:
-            raise ValueError('Unable to load metadata from setup.py')
-        return success
-
-    def inspect_file(self, path):
-        fp = open(path, 'r')
-        try:
-            for _ in xrange(10):
-                line = fp.readline()
-                m = re.match(r'^#!.*python((?P<major>\d)(\.\d+)?)?$', line)
-                if m:
-                    if m.group('major') == '3':
-                        self.classifiers.add(
-                            'Programming Language :: Python :: 3')
-                    else:
-                        self.classifiers.add(
-                        'Programming Language :: Python :: 2')
-        finally:
-            fp.close()
-
-    def inspect_directory(self):
-        dirName = os.path.basename(os.getcwd())
-        self.data['name'] = dirName
-        m = re.match(r'(.*)-(\d.+)', dirName)
-        if m:
-            self.data['name'] = m.group(1)
-            self.data['version'] = m.group(2)
-
-    def query_user(self):
-        self.data['name'] = ask('Project name', self.data['name'],
-              _helptext['name'])
-        self.data['version'] = ask('Current version number',
-              self.data.get('version'), _helptext['version'])
-        self.data['summary'] = ask('Package summary',
-              self.data.get('summary'), _helptext['summary'],
-              lengthy=True)
-        self.data['author'] = ask('Author name',
-              self.data.get('author'), _helptext['author'])
-        self.data['author_email'] = ask('Author e-mail address',
-              self.data.get('author_email'), _helptext['author_email'])
-        self.data['home_page'] = ask('Project Home Page',
-              self.data.get('home_page'), _helptext['home_page'],
-              required=False)
-
-        if ask_yn('Do you want me to automatically build the file list '
-              'with everything I can find in the current directory ? '
-              'If you say no, you will have to define them manually.') == 'y':
-            self._find_files()
-        else:
-            while ask_yn('Do you want to add a single module ?'
-                        ' (you will be able to add full packages next)',
-                    helptext=_helptext['modules']) == 'y':
-                self._set_multi('Module name', 'modules')
-
-            while ask_yn('Do you want to add a package ?',
-                    helptext=_helptext['packages']) == 'y':
-                self._set_multi('Package name', 'packages')
-
-            while ask_yn('Do you want to add an extra file ?',
-                        helptext=_helptext['extra_files']) == 'y':
-                self._set_multi('Extra file/dir name', 'extra_files')
-
-        if ask_yn('Do you want to set Trove classifiers?',
-                  helptext=_helptext['do_classifier']) == 'y':
-            self.set_classifier()
-
-    def _find_files(self):
-        # we are looking for python modules and packages,
-        # other stuff are added as regular files
-        pkgs = self.data['packages']
-        modules = self.data['modules']
-        extra_files = self.data['extra_files']
-
-        def is_package(path):
-            return os.path.exists(os.path.join(path, '__init__.py'))
-
-        curdir = os.getcwd()
-        scanned = []
-        _pref = ['lib', 'include', 'dist', 'build', '.', '~']
-        _suf = ['.pyc']
-
-        def to_skip(path):
-            path = relative(path)
-
-            for pref in _pref:
-                if path.startswith(pref):
-                    return True
-
-            for suf in _suf:
-                if path.endswith(suf):
-                    return True
-
-            return False
-
-        def relative(path):
-            return path[len(curdir) + 1:]
-
-        def dotted(path):
-            res = relative(path).replace(os.path.sep, '.')
-            if res.endswith('.py'):
-                res = res[:-len('.py')]
-            return res
-
-        # first pass : packages
-        for root, dirs, files in os.walk(curdir):
-            if to_skip(root):
-                continue
-            for dir_ in sorted(dirs):
-                if to_skip(dir_):
-                    continue
-                fullpath = os.path.join(root, dir_)
-                dotted_name = dotted(fullpath)
-                if is_package(fullpath) and dotted_name not in pkgs:
-                    pkgs.append(dotted_name)
-                    scanned.append(fullpath)
-
-        # modules and extra files
-        for root, dirs, files in os.walk(curdir):
-            if to_skip(root):
-                continue
-
-            if True in [root.startswith(path) for path in scanned]:
-                continue
-
-            for file in sorted(files):
-                fullpath = os.path.join(root, file)
-                if to_skip(fullpath):
-                    continue
-                # single module ?
-                if os.path.splitext(file)[-1] == '.py':
-                    modules.append(dotted(fullpath))
-                else:
-                    extra_files.append(relative(fullpath))
-
-    def _set_multi(self, question, name):
-        existing_values = self.data[name]
-        value = ask(question, helptext=_helptext[name]).strip()
-        if value not in existing_values:
-            existing_values.append(value)
-
-    def set_classifier(self):
-        self.set_devel_status(self.classifiers)
-        self.set_license(self.classifiers)
-        self.set_other_classifier(self.classifiers)
-
-    def set_other_classifier(self, classifiers):
-        if ask_yn('Do you want to set other trove identifiers', 'n',
-                  _helptext['trove_generic']) != 'y':
-            return
-        self.walk_classifiers(classifiers, [CLASSIFIERS], '')
-
-    def walk_classifiers(self, classifiers, trovepath, desc):
-        trove = trovepath[-1]
-
-        if not trove:
-            return
-
-        for key in sorted(trove):
-            if len(trove[key]) == 0:
-                if ask_yn('Add "%s"' % desc[4:] + ' :: ' + key, 'n') == 'y':
-                    classifiers.add(desc[4:] + ' :: ' + key)
-                continue
-
-            if ask_yn('Do you want to set items under\n   "%s" (%d sub-items)'
-                      % (key, len(trove[key])), 'n',
-                      _helptext['trove_generic']) == 'y':
-                self.walk_classifiers(classifiers, trovepath + [trove[key]],
-                                      desc + ' :: ' + key)
-
-    def set_license(self, classifiers):
-        while True:
-            license = ask('What license do you use',
-                          helptext=_helptext['trove_license'], required=False)
-            if not license:
-                return
-
-            license_words = license.lower().split(' ')
-            found_list = []
-
-            for index, licence in LICENCES:
-                for word in license_words:
-                    if word in licence:
-                        found_list.append(index)
-                        break
-
-            if len(found_list) == 0:
-                print('ERROR: Could not find a matching license for "%s"' % \
-                      license)
-                continue
-
-            question = 'Matching licenses:\n\n'
-
-            for index, list_index in enumerate(found_list):
-                question += '   %s) %s\n' % (index + 1,
-                                             _CLASSIFIERS_LIST[list_index])
-
-            question += ('\nType the number of the license you wish to use or '
-                         '? to try again:')
-            choice = ask(question, required=False)
-
-            if choice == '?':
-                continue
-            if choice == '':
-                return
-
-            try:
-                index = found_list[int(choice) - 1]
-            except ValueError:
-                print ("ERROR: Invalid selection, type a number from the list "
-                       "above.")
-
-            classifiers.add(_CLASSIFIERS_LIST[index])
-            return
-
-    def set_devel_status(self, classifiers):
-        while True:
-            choice = ask(dedent('''\
-                Please select the project status:
-
-                1 - Planning
-                2 - Pre-Alpha
-                3 - Alpha
-                4 - Beta
-                5 - Production/Stable
-                6 - Mature
-                7 - Inactive
-
-                Status'''), required=False)
-            if choice:
-                try:
-                    choice = int(choice) - 1
-                    key = ['Development Status :: 1 - Planning',
-                           'Development Status :: 2 - Pre-Alpha',
-                           'Development Status :: 3 - Alpha',
-                           'Development Status :: 4 - Beta',
-                           'Development Status :: 5 - Production/Stable',
-                           'Development Status :: 6 - Mature',
-                           'Development Status :: 7 - Inactive'][choice]
-                    classifiers.add(key)
-                    return
-                except (IndexError, ValueError):
-                    print ("ERROR: Invalid selection, type a single digit "
-                           "number.")
-
-    def _dotted_packages(self, data):
-        packages = sorted(data)
-        modified_pkgs = []
-        for pkg in packages:
-            pkg = pkg.lstrip('./')
-            pkg = pkg.replace('/', '.')
-            modified_pkgs.append(pkg)
-        return modified_pkgs
-
-    def write_setup_script(self):
-        if os.path.exists(_FILENAME):
-            if os.path.exists('%s.old' % _FILENAME):
-                print("ERROR: %(name)s.old backup exists, please check that "
-                    "current %(name)s is correct and remove %(name)s.old" % \
-                    {'name': _FILENAME})
-                return
-            shutil.move(_FILENAME, '%s.old' % _FILENAME)
-
-        fp = open(_FILENAME, 'w')
-        try:
-            fp.write('[metadata]\n')
-            # simple string entries
-            for name in ('name', 'version', 'summary', 'download_url'):
-                fp.write('%s = %s\n' % (name, self.data.get(name, 'UNKNOWN')))
-            # optional string entries
-            if 'keywords' in self.data and self.data['keywords']:
-                fp.write('keywords = %s\n' % ' '.join(self.data['keywords']))
-            for name in ('home_page', 'author', 'author_email',
-                         'maintainer', 'maintainer_email', 'description-file'):
-                if name in self.data and self.data[name]:
-                    fp.write('%s = %s\n' % (name, self.data[name]))
-            if 'description' in self.data:
-                fp.write(
-                    'description = %s\n'
-                    % '\n       |'.join(self.data['description'].split('\n')))
-            # multiple use string entries
-            for name in ('platform', 'supported-platform', 'classifier',
-                         'requires-dist', 'provides-dist', 'obsoletes-dist',
-                         'requires-external'):
-                if not(name in self.data and self.data[name]):
-                    continue
-                fp.write('%s = ' % name)
-                fp.write(''.join('    %s\n' % val
-                                 for val in self.data[name]).lstrip())
-            fp.write('\n[files]\n')
-            for name in ('packages', 'modules', 'scripts',
-                         'package_data', 'extra_files'):
-                if not(name in self.data and self.data[name]):
-                    continue
-                fp.write('%s = %s\n'
-                         % (name, '\n    '.join(self.data[name]).strip()))
-            fp.write('\nresources =\n')
-            for src, dest in self.data['resources']:
-                fp.write('    %s = %s\n' % (src, dest))
-            fp.write('\n')
-
-        finally:
-            fp.close()
-
-        os.chmod(_FILENAME, 0644)
-        print 'Wrote "%s".' % _FILENAME
-
-
-def main():
-    """Main entry point."""
-    program = MainProgram()
-    # uncomment when implemented
-    if not program.load_existing_setup_script():
-        program.inspect_directory()
-        program.query_user()
-        program.update_config_file()
-    program.write_setup_script()
-    # distutils2.util.cfg_to_args()
-
-
-if __name__ == '__main__':
-    main()
diff --git a/distutils2/index/__init__.py b/distutils2/pypi/__init__.py
rename from distutils2/index/__init__.py
rename to distutils2/pypi/__init__.py
--- a/distutils2/index/__init__.py
+++ b/distutils2/pypi/__init__.py
@@ -1,6 +1,4 @@
-"""Package containing ways to interact with Index APIs.
-
-"""
+"""Low-level and high-level APIs to interact with project indexes."""
 
 __all__ = ['simple',
            'xmlrpc',
@@ -8,4 +6,4 @@
            'errors',
            'mirrors']
 
-from dist import ReleaseInfo, ReleasesList, DistInfo
+from distutils2.pypi.dist import ReleaseInfo, ReleasesList, DistInfo
diff --git a/distutils2/index/base.py b/distutils2/pypi/base.py
rename from distutils2/index/base.py
rename to distutils2/pypi/base.py
--- a/distutils2/index/base.py
+++ b/distutils2/pypi/base.py
@@ -1,4 +1,6 @@
-from distutils2.index.dist import ReleasesList
+"""Base class for index crawlers."""
+
+from distutils2.pypi.dist import ReleasesList
 
 
 class BaseClient(object):
diff --git a/distutils2/index/dist.py b/distutils2/pypi/dist.py
rename from distutils2/index/dist.py
rename to distutils2/pypi/dist.py
--- a/distutils2/index/dist.py
+++ b/distutils2/pypi/dist.py
@@ -1,35 +1,25 @@
-"""distutils2.index.dist
+"""Classes representing releases and distributions retrieved from indexes.
 
-Provides useful classes to represent the release and distributions retrieved
-from indexes.
+A project (= unique name) can have several releases (= versions) and
+each release can have several distributions (= sdist and bdists).
 
-A project can have several releases (=versions) and each release can have
-several distributions (sdist, bdist).
+Release objects contain metadata-related information (see PEP 376);
+distribution objects contain download-related information.
+"""
 
-The release contains the metadata related informations (see PEP 384), and the
-distributions contains download related informations.
-
-"""
-import mimetypes
 import re
-import tarfile
+import hashlib
 import tempfile
 import urllib
 import urlparse
-import zipfile
-try:
-    import hashlib
-except ImportError:
-    from distutils2._backport import hashlib
+from distutils2.errors import IrrationalVersionError
+from distutils2.version import (suggest_normalized_version, NormalizedVersion,
+                               get_version_predicate)
+from distutils2.metadata import Metadata
+from distutils2.pypi.errors import (HashDoesNotMatch, UnsupportedHashName,
+                                   CantParseArchiveName)
+from distutils2.util import unpack_archive
 
-from distutils2._backport.shutil import unpack_archive
-from distutils2.errors import IrrationalVersionError
-from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName,
-                                     CantParseArchiveName)
-from distutils2.version import (suggest_normalized_version, NormalizedVersion,
-                                get_version_predicate)
-from distutils2.metadata import Metadata
-from distutils2.util import splitext
 
 __all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url']
 
@@ -94,7 +84,7 @@
     def fetch_metadata(self):
         """If the metadata is not set, use the indexes to get it"""
         if not self.metadata:
-            self._index.get_metadata(self.name, '%s' % self.version)
+            self._index.get_metadata(self.name, str(self.version))
         return self.metadata
 
     @property
@@ -104,7 +94,7 @@
 
     def fetch_distributions(self):
         if self.dists is None:
-            self._index.get_distributions(self.name, '%s' % self.version)
+            self._index.get_distributions(self.name, str(self.version))
             if self.dists is None:
                 self.dists = {}
         return self.dists
@@ -140,14 +130,14 @@
         not return one existing distribution.
         """
         if len(self.dists) == 0:
-            raise LookupError()
+            raise LookupError
         if dist_type:
             return self[dist_type]
         if prefer_source:
             if "sdist" in self.dists:
                 dist = self["sdist"]
             else:
-                dist = self.dists.values()[0]
+                dist = next(self.dists.values())
             return dist
 
     def unpack(self, path=None, prefer_source=True):
@@ -254,14 +244,14 @@
         self._url = None
         self.add_url(url, hashname, hashval, is_external)
 
-    def add_url(self, url, hashname=None, hashval=None, is_external=True):
+    def add_url(self, url=None, hashname=None, hashval=None, is_external=True):
         """Add a new url to the list of urls"""
         if hashname is not None:
             try:
                 hashlib.new(hashname)
             except ValueError:
                 raise UnsupportedHashName(hashname)
-        if not url in [u['url'] for u in self.urls]:
+        if url not in [u['url'] for u in self.urls]:
             self.urls.append({
                 'url': url,
                 'hashname': hashname,
@@ -323,20 +313,21 @@
                 path = tempfile.mkdtemp()
 
             filename = self.download(path)
-            content_type = mimetypes.guess_type(filename)[0]
-            self._unpacked_dir = unpack_archive(filename, path)
+            unpack_archive(filename, path)
+            self._unpacked_dir = path
 
-        return self._unpacked_dir
+        return path
 
     def _check_md5(self, filename):
         """Check that the md5 checksum of the given file matches the one in
         url param"""
         hashname = self.url['hashname']
         expected_hashval = self.url['hashval']
-        if not None in (expected_hashval, hashname):
-            f = open(filename)
-            hashval = hashlib.new(hashname)
-            hashval.update(f.read())
+        if None not in (expected_hashval, hashname):
+            with open(filename, 'rb') as f:
+                hashval = hashlib.new(hashname)
+                hashval.update(f.read())
+
             if hashval.hexdigest() != expected_hashval:
                 raise HashDoesNotMatch("got %s instead of %s"
                     % (hashval.hexdigest(), expected_hashval))
@@ -408,19 +399,19 @@
         """
         if release:
             if release.name.lower() != self.name.lower():
-                raise ValueError("%s is not the same project than %s" %
+                raise ValueError("%s is not the same project as %s" %
                                  (release.name, self.name))
-            version = '%s' % release.version
+            version = str(release.version)
 
-            if not version in self.get_versions():
+            if version not in self.get_versions():
                 # append only if not already exists
                 self.releases.append(release)
-            for dist in release.dists.itervalues():
+            for dist in release.dists.values():
                 for url in dist.urls:
                     self.add_release(version, dist.dist_type, **url)
         else:
-            matches = [r for r in self.releases if '%s' % r.version == version
-                                                 and r.name == self.name]
+            matches = [r for r in self.releases
+                       if str(r.version) == version and r.name == self.name]
             if not matches:
                 release = ReleaseInfo(self.name, version, index=self._index)
                 self.releases.append(release)
@@ -448,19 +439,19 @@
         sort_by.append("version")
 
         self.releases.sort(
-            key=lambda i: [getattr(i, arg) for arg in sort_by],
+            key=lambda i: tuple(getattr(i, arg) for arg in sort_by),
             reverse=reverse, *args, **kwargs)
 
     def get_release(self, version):
         """Return a release from its version."""
-        matches = [r for r in self.releases if "%s" % r.version == version]
+        matches = [r for r in self.releases if str(r.version) == version]
         if len(matches) != 1:
             raise KeyError(version)
         return matches[0]
 
     def get_versions(self):
         """Return a list of releases versions contained"""
-        return ["%s" % r.version for r in self.releases]
+        return [str(r.version) for r in self.releases]
 
     def __getitem__(self, key):
         return self.releases[key]
@@ -532,7 +523,7 @@
             # we dont get a good version number: recurse !
             return eager_split(str, maxsplit - 1)
         else:
-            return (name, version)
+            return name, version
     if probable_name is not None:
         probable_name = probable_name.lower()
     name = None
@@ -545,6 +536,6 @@
 
     version = suggest_normalized_version(version)
     if version is not None and name != "":
-        return (name.lower(), version)
+        return name.lower(), version
     else:
         raise CantParseArchiveName(archive_name)
diff --git a/distutils2/index/errors.py b/distutils2/pypi/errors.py
rename from distutils2/index/errors.py
rename to distutils2/pypi/errors.py
--- a/distutils2/index/errors.py
+++ b/distutils2/pypi/errors.py
@@ -1,27 +1,25 @@
-"""distutils2.pypi.errors
+"""Exceptions raised by distutils2.pypi code."""
 
-All errors and exceptions raised by PyPiIndex classes.
-"""
-from distutils2.errors import DistutilsIndexError
+from distutils2.errors import PackagingPyPIError
 
 
-class ProjectNotFound(DistutilsIndexError):
+class ProjectNotFound(PackagingPyPIError):
     """Project has not been found"""
 
 
-class DistributionNotFound(DistutilsIndexError):
+class DistributionNotFound(PackagingPyPIError):
     """The release has not been found"""
 
 
-class ReleaseNotFound(DistutilsIndexError):
+class ReleaseNotFound(PackagingPyPIError):
     """The release has not been found"""
 
 
-class CantParseArchiveName(DistutilsIndexError):
+class CantParseArchiveName(PackagingPyPIError):
     """An archive name can't be parsed to find distribution name and version"""
 
 
-class DownloadError(DistutilsIndexError):
+class DownloadError(PackagingPyPIError):
     """An error has occurs while downloading"""
 
 
@@ -29,13 +27,13 @@
     """Compared hashes does not match"""
 
 
-class UnsupportedHashName(DistutilsIndexError):
+class UnsupportedHashName(PackagingPyPIError):
     """A unsupported hashname has been used"""
 
 
-class UnableToDownload(DistutilsIndexError):
+class UnableToDownload(PackagingPyPIError):
     """All mirrors have been tried, without success"""
 
 
-class InvalidSearchField(DistutilsIndexError):
+class InvalidSearchField(PackagingPyPIError):
     """An invalid search field has been used"""
diff --git a/distutils2/index/mirrors.py b/distutils2/pypi/mirrors.py
rename from distutils2/index/mirrors.py
rename to distutils2/pypi/mirrors.py
--- a/distutils2/index/mirrors.py
+++ b/distutils2/pypi/mirrors.py
@@ -1,6 +1,4 @@
-"""Utilities related to the mirror infrastructure defined in PEP 381.
-See http://www.python.org/dev/peps/pep-0381/
-"""
+"""Utilities related to the mirror infrastructure defined in PEP 381."""
 
 from string import ascii_lowercase
 import socket
@@ -12,7 +10,7 @@
     """Return the list of mirrors from the last record found on the DNS
     entry::
 
-    >>> from distutils2.index.mirrors import get_mirrors
+    >>> from distutils2.pypi.mirrors import get_mirrors
     >>> get_mirrors()
     ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org',
     'd.pypi.python.org']
@@ -46,7 +44,7 @@
 
 
 def product(*args, **kwds):
-    pools = map(tuple, args) * kwds.get('repeat', 1)
+    pools = [tuple(arg) for arg in args] * kwds.get('repeat', 1)
     result = [[]]
     for pool in pools:
         result = [x + [y] for x in result for y in pool]
diff --git a/distutils2/index/simple.py b/distutils2/pypi/simple.py
rename from distutils2/index/simple.py
rename to distutils2/pypi/simple.py
--- a/distutils2/index/simple.py
+++ b/distutils2/pypi/simple.py
@@ -1,10 +1,11 @@
-"""index.simple
+"""Spider using the screen-scraping "simple" PyPI API.
 
-Contains the class "SimpleIndexCrawler", a simple spider to find and retrieve
-distributions on the Python Package Index, using its "simple" API,
-avalaible at http://pypi.python.org/simple/
+This module contains the class Crawler, a simple spider that
+can be used to find and retrieve distributions from a project index
+(like the Python Package Index), using its so-called simple API (see
+reference implementation available at http://pypi.python.org/simple/).
 """
-from fnmatch import translate
+
 import httplib
 import re
 import socket
@@ -13,17 +14,20 @@
 import urlparse
 import os
 
+from fnmatch import translate
+from functools import wraps
 from distutils2 import logger
-from distutils2.index.base import BaseClient
-from distutils2.index.dist import (ReleasesList, EXTENSIONS,
-                                   get_infos_from_url, MD5_HASH)
-from distutils2.index.errors import (DistutilsIndexError, DownloadError,
-                                     UnableToDownload, CantParseArchiveName,
-                                     ReleaseNotFound, ProjectNotFound)
-from distutils2.index.mirrors import get_mirrors
 from distutils2.metadata import Metadata
 from distutils2.version import get_version_predicate
-from distutils2 import __version__ as __distutils2_version__
+from distutils2 import __version__ as distutils2_version
+from distutils2.pypi.base import BaseClient
+from distutils2.pypi.dist import (ReleasesList, EXTENSIONS,
+                                  get_infos_from_url, MD5_HASH)
+from distutils2.pypi.errors import (PackagingPyPIError, DownloadError,
+                                    UnableToDownload, CantParseArchiveName,
+                                    ReleaseNotFound, ProjectNotFound)
+from distutils2.pypi.mirrors import get_mirrors
+from distutils2.metadata import Metadata
 
 __all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL']
 
@@ -32,7 +36,7 @@
 DEFAULT_HOSTS = ("*",)
 SOCKET_TIMEOUT = 15
 USER_AGENT = "Python-urllib/%s distutils2/%s" % (
-    sys.version[:3], __distutils2_version__)
+    sys.version[:3], distutils2_version)
 
 # -- Regexps -------------------------------------------------
 EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
@@ -48,8 +52,9 @@
 def socket_timeout(timeout=SOCKET_TIMEOUT):
     """Decorator to add a socket timeout when requesting pages on PyPI.
     """
-    def _socket_timeout(func):
-        def _socket_timeout(self, *args, **kwargs):
+    def wrapper(func):
+        @wraps(func)
+        def wrapped(self, *args, **kwargs):
             old_timeout = socket.getdefaulttimeout()
             if hasattr(self, "_timeout"):
                 timeout = self._timeout
@@ -58,13 +63,14 @@
                 return func(self, *args, **kwargs)
             finally:
                 socket.setdefaulttimeout(old_timeout)
-        return _socket_timeout
-    return _socket_timeout
+        return wrapped
+    return wrapper
 
 
 def with_mirror_support():
     """Decorator that makes the mirroring support easier"""
     def wrapper(func):
+        @wraps(func)
         def wrapped(self, *args, **kwargs):
             try:
                 return func(self, *args, **kwargs)
@@ -103,7 +109,7 @@
     :param follow_externals: tell if following external links is needed or
                              not. Default is False.
     :param mirrors_url: the url to look on for DNS records giving mirror
-                        adresses.
+                        addresses.
     :param mirrors: a list of mirrors (see PEP 381).
     :param timeout: time in seconds to consider a url has timeouted.
     :param mirrors_max_tries": number of times to try requesting informations
@@ -113,13 +119,20 @@
     def __init__(self, index_url=DEFAULT_SIMPLE_INDEX_URL, prefer_final=False,
                  prefer_source=True, hosts=DEFAULT_HOSTS,
                  follow_externals=False, mirrors_url=None, mirrors=None,
-                 timeout=SOCKET_TIMEOUT, mirrors_max_tries=0):
+                 timeout=SOCKET_TIMEOUT, mirrors_max_tries=0, verbose=False):
         super(Crawler, self).__init__(prefer_final, prefer_source)
         self.follow_externals = follow_externals
+        self.verbose = verbose
 
         # mirroring attributes.
-        if not index_url.endswith("/"):
-            index_url += "/"
+        parsed = urlparse.urlparse(index_url)
+        self.scheme = parsed[0]
+        if self.scheme == 'file':
+            ender = os.path.sep
+        else:
+            ender = '/'
+        if not index_url.endswith(ender):
+            index_url += ender
         # if no mirrors are defined, use the method described in PEP 381.
         if mirrors is None:
             mirrors = get_mirrors(mirrors_url)
@@ -145,33 +158,39 @@
 
         Return a list of names.
         """
-        index = self._open_url(self.index_url)
-        if '*' in name:
-            name.replace('*', '.*')
-        else:
-            name = "%s%s%s" % ('*.?', name, '*.?')
-        name = name.replace('*', '[^<]*')  # avoid matching of the tag's end
-        projectname = re.compile("""<a[^>]*>(%s)</a>""" % name, flags=re.I)
-        matching_projects = []
-        for match in projectname.finditer(index.read()):
+        with self._open_url(self.index_url) as index:
+            if '*' in name:
+                name.replace('*', '.*')
+            else:
+                name = "%s%s%s" % ('*.?', name, '*.?')
+            name = name.replace('*', '[^<]*')  # avoid matching end tag
+            projectname = re.compile('<a[^>]*>(%s)</a>' % name, re.I)
+            matching_projects = []
+
+            index_content = index.read()
+
+        # FIXME should use bytes I/O and regexes instead of decoding
+        index_content = index_content.decode()
+
+        for match in projectname.finditer(index_content):
             project_name = match.group(1)
             matching_projects.append(self._get_project(project_name))
         return matching_projects
 
     def get_releases(self, requirements, prefer_final=None,
                      force_update=False):
-        """Search for releases and return a ReleaseList object containing
+        """Search for releases and return a ReleasesList object containing
         the results.
         """
         predicate = get_version_predicate(requirements)
         if predicate.name.lower() in self._projects and not force_update:
             return self._projects.get(predicate.name.lower())
         prefer_final = self._get_prefer_final(prefer_final)
-        logger.info('reading info on PyPI about %s', predicate.name)
+        logger.debug('Reading info on PyPI about %s', predicate.name)
         self._process_index_page(predicate.name)
 
         if predicate.name.lower() not in self._projects:
-            raise ProjectNotFound()
+            raise ProjectNotFound
 
         releases = self._projects.get(predicate.name.lower())
         releases.sort_releases(prefer_final=prefer_final)
@@ -199,10 +218,10 @@
         Currently, download one archive, extract it and use the PKG-INFO file.
         """
         release = self.get_distributions(project_name, version)
-        if not release._metadata:
+        if not release.metadata:
             location = release.get_distribution().unpack()
             pkg_info = os.path.join(location, 'PKG-INFO')
-            release._metadata = Metadata(pkg_info)
+            release.metadata = Metadata(pkg_info)
         return release
 
     def _switch_to_next_mirror(self):
@@ -213,7 +232,8 @@
         """
         self._mirrors_used.add(self.index_url)
         index_url = self._mirrors.pop()
-        if not ("http://" or "https://" or "file://") in index_url:
+        # XXX use urlparse for a real check of missing scheme part
+        if not index_url.startswith(("http://", "https://", "file://")):
             index_url = "http://%s" % index_url
 
         if not index_url.endswith("/simple"):
@@ -264,9 +284,8 @@
             name = release.name
         else:
             name = release_info['name']
-        if not name.lower() in self._projects:
-            self._projects[name.lower()] = ReleasesList(name,
-                                                        index=self._index)
+        if name.lower() not in self._projects:
+            self._projects[name.lower()] = ReleasesList(name, index=self._index)
 
         if release:
             self._projects[name.lower()].add_release(release=release)
@@ -292,27 +311,32 @@
                              method on it)
         """
         f = self._open_url(url)
-        base_url = f.url
-        if url not in self._processed_urls:
-            self._processed_urls.append(url)
-            link_matcher = self._get_link_matcher(url)
-            for link, is_download in link_matcher(f.read(), base_url):
-                if link not in self._processed_urls:
-                    if self._is_distribution(link) or is_download:
-                        self._processed_urls.append(link)
-                        # it's a distribution, so create a dist object
-                        try:
-                            infos = get_infos_from_url(link, project_name,
-                                        is_external=not self.index_url in url)
-                        except CantParseArchiveName, e:
-                            logger.warning(
-                                "version has not been parsed: %s", e)
+        try:
+            base_url = f.url
+            if url not in self._processed_urls:
+                self._processed_urls.append(url)
+                link_matcher = self._get_link_matcher(url)
+                for link, is_download in link_matcher(f.read().decode(), base_url):
+                    if link not in self._processed_urls:
+                        if self._is_distribution(link) or is_download:
+                            self._processed_urls.append(link)
+                            # it's a distribution, so create a dist object
+                            try:
+                                infos = get_infos_from_url(link, project_name,
+                                            is_external=self.index_url not in url)
+                            except CantParseArchiveName:
+                                e = sys.exc_info()[1]
+                                if self.verbose:
+                                    logger.warning(
+                                        "version has not been parsed: %s", e)
+                            else:
+                                self._register_release(release_info=infos)
                         else:
-                            self._register_release(release_info=infos)
-                    else:
-                        if self._is_browsable(link) and follow_links:
-                            self._process_url(link, project_name,
-                                follow_links=False)
+                            if self._is_browsable(link) and follow_links:
+                                self._process_url(link, project_name,
+                                    follow_links=False)
+        finally:
+            f.close()
 
     def _get_link_matcher(self, url):
         """Returns the right link matcher function of the given url
@@ -331,6 +355,9 @@
         This matches the simple index requirements for matching links.
         If follow_externals is set to False, dont yeld the external
         urls.
+
+        :param content: the content of the page we want to parse
+        :param base_url: the url of this page.
         """
         for match in HREF.finditer(content):
             url = self._get_full_url(match.group(1), base_url)
@@ -340,7 +367,7 @@
         for match in REL.finditer(content):
             # search for rel links.
             tag, rel = match.groups()
-            rels = map(str.strip, rel.lower().split(','))
+            rels = [s.strip() for s in rel.lower().split(',')]
             if 'homepage' in rels or 'download' in rels:
                 for match in HREF.finditer(tag):
                     url = self._get_full_url(match.group(1), base_url)
@@ -363,7 +390,11 @@
         :param name: the name of the project to find the page
         """
         # Browse and index the content of the given PyPI page.
-        url = self.index_url + name + "/"
+        if self.scheme == 'file':
+            ender = os.path.sep
+        else:
+            ender = '/'
+        url = self.index_url + name + ender
         self._process_url(url, name)
 
     @socket_timeout()
@@ -376,19 +407,19 @@
 
         # authentication stuff
         if scheme in ('http', 'https'):
-            auth, host = urllib2.splituser(netloc)
+            auth, host = urlparse.splituser(netloc)
         else:
             auth = None
 
         # add index.html automatically for filesystem paths
         if scheme == 'file':
-            if url.endswith('/'):
+            if url.endswith(os.path.sep):
                 url += "index.html"
 
         # add authorization headers if auth is provided
         if auth:
             auth = "Basic " + \
-                urllib2.unquote(auth).encode('base64').strip()
+                urlparse.unquote(auth).encode('base64').strip()
             new_url = urlparse.urlunparse((
                 scheme, host, path, params, query, frag))
             request = urllib2.Request(new_url)
@@ -398,17 +429,21 @@
         request.add_header('User-Agent', USER_AGENT)
         try:
             fp = urllib2.urlopen(request)
-        except (ValueError, httplib.InvalidURL), v:
+        except (ValueError, httplib.InvalidURL):
+            v = sys.exc_info()[1]
             msg = ' '.join([str(arg) for arg in v.args])
-            raise DistutilsIndexError('%s %s' % (url, msg))
-        except urllib2.HTTPError, v:
-            return v
-        except urllib2.URLError, v:
+            raise PackagingPyPIError('%s %s' % (url, msg))
+        except urllib2.HTTPError:
+            return sys.exc_info()[1]
+        except urllib2.URLError:
+            v = sys.exc_info()[1]
             raise DownloadError("Download error for %s: %s" % (url, v.reason))
-        except httplib.BadStatusLine, v:
+        except httplib.BadStatusLine:
+            v = sys.exc_info()[1]
             raise DownloadError('%s returned a bad status line. '
                 'The server might be down, %s' % (url, v.line))
-        except httplib.HTTPException, v:
+        except httplib.HTTPException:
+            v = sys.exc_info()[1]
             raise DownloadError("Download error for %s: %s" % (url, v))
         except socket.timeout:
             raise DownloadError("The server timeouted")
@@ -430,9 +465,9 @@
         elif what.startswith('#'):
             what = int(what[1:])
         else:
-            from htmlentitydefs import name2codepoint
+            from html.entities import name2codepoint
             what = name2codepoint.get(what, match.group(0))
-        return unichr(what)
+        return chr(what)
 
     def _htmldecode(self, text):
         """Decode HTML entities in the given text."""
diff --git a/distutils2/index/wrapper.py b/distutils2/pypi/wrapper.py
rename from distutils2/index/wrapper.py
rename to distutils2/pypi/wrapper.py
--- a/distutils2/index/wrapper.py
+++ b/distutils2/pypi/wrapper.py
@@ -1,4 +1,10 @@
-from distutils2.index import simple, xmlrpc
+"""Convenient client for all PyPI APIs.
+
+This module provides a ClientWrapper class which will use the "simple"
+or XML-RPC API to request information or files from an index.
+"""
+
+from distutils2.pypi import simple, xmlrpc
 
 _WRAPPER_MAPPINGS = {'get_release': 'simple',
                      'get_releases': 'simple',
@@ -19,14 +25,14 @@
         exception = None
         methods = [func]
         for f in wrapper._indexes.values():
-            if f != func.im_self and hasattr(f, func.__name__):
+            if f != func.__self__ and hasattr(f, func.__name__):
                 methods.append(getattr(f, func.__name__))
         for method in methods:
             try:
                 response = method(*args, **kwargs)
                 retry = False
-            except Exception, e:
-                exception = e
+            except Exception:
+                exception = sys.exc_info()[1]
             if not retry:
                 break
         if retry and exception:
@@ -43,7 +49,7 @@
     mappings.
     If one of the indexes returns an error, tries to use others indexes.
 
-    :param index: tell wich index to rely on by default.
+    :param index: tell which index to rely on by default.
     :param index_classes: a dict of name:class to use as indexes.
     :param indexes: a dict of name:index already instantiated
     :param mappings: the mappings to use for this wrapper
@@ -58,7 +64,7 @@
 
         # instantiate the classes and set their _project attribute to the one
         # of the wrapper.
-        for name, cls in index_classes.iteritems():
+        for name, cls in index_classes.items():
             obj = self._indexes.setdefault(name, cls())
             obj._projects = self._projects
             obj._index = self
diff --git a/distutils2/index/xmlrpc.py b/distutils2/pypi/xmlrpc.py
rename from distutils2/index/xmlrpc.py
rename to distutils2/pypi/xmlrpc.py
--- a/distutils2/index/xmlrpc.py
+++ b/distutils2/pypi/xmlrpc.py
@@ -1,12 +1,20 @@
-import logging
-import xmlrpclib
+"""Spider using the XML-RPC PyPI API.
 
+This module contains the class Client, a spider that can be used to find
+and retrieve distributions from a project index (like the Python Package
+Index), using its XML-RPC API (see documentation of the reference
+implementation at http://wiki.python.org/moin/PyPiXmlRpc).
+"""
+
+import xmlrpclib, sys
+
+from distutils2 import logger
 from distutils2.errors import IrrationalVersionError
-from distutils2.index.base import BaseClient
-from distutils2.index.errors import (ProjectNotFound, InvalidSearchField,
-                                     ReleaseNotFound)
-from distutils2.index.dist import ReleaseInfo
 from distutils2.version import get_version_predicate
+from distutils2.pypi.base import BaseClient
+from distutils2.pypi.errors import (ProjectNotFound, InvalidSearchField,
+                                   ReleaseNotFound)
+from distutils2.pypi.dist import ReleaseInfo
 
 __all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL']
 
@@ -23,11 +31,11 @@
     If no server_url is specified, use the default PyPI XML-RPC URL,
     defined in the DEFAULT_XMLRPC_INDEX_URL constant::
 
-        >>> client = XMLRPCClient()
+        >>> client = Client()
         >>> client.server_url == DEFAULT_XMLRPC_INDEX_URL
         True
 
-        >>> client = XMLRPCClient("http://someurl/")
+        >>> client = Client("http://someurl/")
         >>> client.server_url
         'http://someurl/'
     """
@@ -46,8 +54,8 @@
         predicate = get_version_predicate(requirements)
         releases = self.get_releases(predicate.name)
         release = releases.get_last(predicate, prefer_final)
-        self.get_metadata(release.name, "%s" % release.version)
-        self.get_distributions(release.name, "%s" % release.version)
+        self.get_metadata(release.name, str(release.version))
+        self.get_distributions(release.name, str(release.version))
         return release
 
     def get_releases(self, requirements, prefer_final=None, show_hidden=True,
@@ -61,7 +69,7 @@
         informations (eg. make a new XML-RPC call).
         ::
 
-            >>> client = XMLRPCClient()
+            >>> client = Client()
             >>> client.get_releases('Foo')
             ['1.1', '1.2', '1.3']
 
@@ -84,8 +92,7 @@
                 # list of releases that does not contains hidden ones
                 all_versions = get_versions(project_name, show_hidden)
                 existing_versions = project.get_versions()
-                hidden_versions = list(set(all_versions) -
-                                       set(existing_versions))
+                hidden_versions = set(all_versions) - set(existing_versions)
                 for version in hidden_versions:
                     project.add_release(release=ReleaseInfo(project_name,
                                             version, index=self._index))
@@ -103,6 +110,7 @@
         project.sort_releases(prefer_final)
         return project
 
+
     def get_distributions(self, project_name, version):
         """Grab informations about distributions from XML-RPC.
 
@@ -163,8 +171,9 @@
                 project.add_release(release=ReleaseInfo(p['name'],
                     p['version'], metadata={'summary': p['summary']},
                     index=self._index))
-            except IrrationalVersionError, e:
-                logging.warn("Irrational version error found: %s", e)
+            except IrrationalVersionError:
+                e = sys.exc_info()[1]
+                logger.warning("Irrational version error found: %s", e)
         return [self._projects[p['name'].lower()] for p in projects]
 
     def get_all_projects(self):
@@ -181,7 +190,7 @@
 
         If no server proxy is defined yet, creates a new one::
 
-            >>> client = XmlRpcClient()
+            >>> client = Client()
             >>> client.proxy()
             <ServerProxy for python.org/pypi>
 
diff --git a/distutils2/pysetup b/distutils2/pysetup
deleted file mode 100755
--- a/distutils2/pysetup
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env python
-from distutils2.run import main
-
-if __name__ == "__main__":
-    main()
diff --git a/distutils2/resources.py b/distutils2/resources.py
deleted file mode 100644
--- a/distutils2/resources.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import os
-
-from distutils2.util import iglob
-
-
-def _rel_path(base, path):
-    assert path.startswith(base)
-    return path[len(base):].lstrip('/')
-
-
-def resources_dests(resources_root, rules):
-    """find destination of resources files"""
-    destinations = {}
-    for (base, suffix, dest) in rules:
-        prefix = os.path.join(resources_root, base)
-        for abs_base in iglob(prefix):
-            abs_glob = os.path.join(abs_base, suffix)
-            for abs_path in iglob(abs_glob):
-                resource_file = _rel_path(resources_root, abs_path)
-                if dest is None:  # remove the entry if it was here
-                    destinations.pop(resource_file, None)
-                else:
-                    rel_path = _rel_path(abs_base, abs_path)
-                    destinations[resource_file] = os.path.join(dest, rel_path)
-    return destinations
diff --git a/distutils2/run.py b/distutils2/run.py
--- a/distutils2/run.py
+++ b/distutils2/run.py
@@ -1,35 +1,26 @@
-"""distutils2.dispatcher
+"""Main command line parser.  Implements the pysetup script."""
 
-Parses the command line.
-"""
+import os
+import re
+import sys
+import getopt
 import logging
-import re
-import os
-import sys
-
-from distutils2.errors import DistutilsError, CCompilerError
-from distutils2._backport.pkgutil import get_distributions, get_distribution
-from distutils2.depgraph import generate_graph
-from distutils2.install import install, remove
-from distutils2.dist import Distribution
-
-from distutils2.command import get_command_class, STANDARD_COMMANDS
-
-from distutils2.errors import (DistutilsOptionError, DistutilsArgError,
-                               DistutilsModuleError, DistutilsClassError)
 
 from distutils2 import logger
+from distutils2.dist import Distribution
+from distutils2.util import _is_archive_file, generate_setup_py
+from distutils2.command import get_command_class, STANDARD_COMMANDS
+from distutils2.install import install, install_local_project, remove
+from distutils2.database import get_distribution, get_distributions
+from distutils2.depgraph import generate_graph
 from distutils2.fancy_getopt import FancyGetopt
+from distutils2.errors import (PackagingArgError, PackagingError,
+                              PackagingModuleError, PackagingClassError,
+                              CCompilerError)
+
 
 command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
 
-run_usage = """\
-usage: pysetup run [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
-   or: pysetup run --help
-   or: pysetup run --list-commands
-   or: pysetup run cmd --help
-"""
-
 common_usage = """\
 Actions:
 %(actions)s
@@ -39,77 +30,252 @@
     pysetup action --help
 """
 
-global_options = [('verbose', 'v', "run verbosely (default)", 1),
-                  ('quiet', 'q', "run quietly (turns verbosity off)"),
-                  ('dry-run', 'n', "don't actually do anything"),
-                  ('help', 'h', "show detailed help message"),
-                  ('no-user-cfg', None,
-                   'ignore pydistutils.cfg in your home directory'),
-                  ('version', None, 'Display the version'),
-    ]
+create_usage = """\
+Usage: pysetup create
+   or: pysetup create --help
+
+Create a new Python project.
+"""
+
+generate_usage = """\
+Usage: pysetup generate-setup
+   or: pysetup generate-setup --help
+
+Generate a setup.py script for backward-compatibility purposes.
+"""
+
+
+graph_usage = """\
+Usage: pysetup graph dist
+   or: pysetup graph --help
+
+Print dependency graph for the distribution.
+
+positional arguments:
+   dist  installed distribution name
+"""
+
+install_usage = """\
+Usage: pysetup install [dist]
+   or: pysetup install [archive]
+   or: pysetup install [src_dir]
+   or: pysetup install --help
+
+Install a Python distribution from the indexes, source directory, or sdist.
+
+positional arguments:
+   archive  path to source distribution (zip, tar.gz)
+   dist     distribution name to install from the indexes
+   scr_dir  path to source directory
+
+"""
+
+metadata_usage = """\
+Usage: pysetup metadata [dist] [-f field ...]
+   or: pysetup metadata [dist] [--all]
+   or: pysetup metadata --help
+
+Print metadata for the distribution.
+
+positional arguments:
+   dist  installed distribution name
+
+optional arguments:
+   -f     metadata field to print
+   --all  print all metadata fields
+"""
+
+remove_usage = """\
+Usage: pysetup remove dist [-y]
+   or: pysetup remove --help
+
+Uninstall a Python distribution.
+
+positional arguments:
+   dist  installed distribution name
+
+optional arguments:
+   -y  auto confirm distribution removal
+"""
+
+run_usage = """\
+Usage: pysetup run [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
+   or: pysetup run --help
+   or: pysetup run --list-commands
+   or: pysetup run cmd --help
+"""
+
+list_usage = """\
+Usage: pysetup list dist [dist ...]
+   or: pysetup list --help
+   or: pysetup list --all
+
+Print name, version and location for the matching installed distributions.
+
+positional arguments:
+   dist  installed distribution name
+
+optional arguments:
+   --all  list all installed distributions
+"""
+
+search_usage = """\
+Usage: pysetup search [project] [--simple [url]] [--xmlrpc [url] [--fieldname value ...] --operator or|and]
+   or: pysetup search --help
+
+Search the indexes for the matching projects.
+
+positional arguments:
+    project     the project pattern to search for
+
+optional arguments:
+    --xmlrpc [url]      wether to use the xmlrpc index or not. If an url is
+                        specified, it will be used rather than the default one.
+
+    --simple [url]      wether to use the simple index or not. If an url is
+                        specified, it will be used rather than the default one.
+
+    --fieldname value   Make a search on this field. Can only be used if
+                        --xmlrpc has been selected or is the default index.
+
+    --operator or|and   Defines what is the operator to use when doing xmlrpc
+                        searchs with multiple fieldnames. Can only be used if
+                        --xmlrpc has been selected or is the default index.
+"""
+
+global_options = [
+    # The fourth entry for verbose means that it can be repeated.
+    ('verbose', 'v', "run verbosely (default)", True),
+    ('quiet', 'q', "run quietly (turns verbosity off)"),
+    ('dry-run', 'n', "don't actually do anything"),
+    ('help', 'h', "show detailed help message"),
+    ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'),
+    ('version', None, 'Display the version'),
+]
+
+negative_opt = {'quiet': 'verbose'}
 
 display_options = [
-        ('help-commands', None,
-         "list all available commands"),
-        ]
-
+    ('help-commands', None, "list all available commands"),
+]
 
 display_option_names = [x[0].replace('-', '_') for x in display_options]
-negative_opt = {'quiet': 'verbose'}
 
-def _set_logger():
-    logger.setLevel(logging.INFO)
-    sth = logging.StreamHandler(sys.stderr)
-    sth.setLevel(logging.INFO)
-    logger.addHandler(sth)
-    logger.propagate = 0
 
+def _parse_args(args, options, long_options):
+    """Transform sys.argv input into a dict.
+
+    :param args: the args to parse (i.e sys.argv)
+    :param options: the list of options to pass to getopt
+    :param long_options: the list of string with the names of the long options
+                         to be passed to getopt.
+
+    The function returns a dict with options/long_options as keys and matching
+    values as values.
+    """
+    optlist, args = getopt.gnu_getopt(args, options, long_options)
+    optdict = {}
+    optdict['args'] = args
+    for k, v in optlist:
+        k = k.lstrip('-')
+        if k not in optdict:
+            optdict[k] = []
+            if v:
+                optdict[k].append(v)
+        else:
+            optdict[k].append(v)
+    return optdict
+
+
+class action_help(object):
+    """Prints a help message when the standard help flags: -h and --help
+    are used on the commandline.
+    """
+
+    def __init__(self, help_msg):
+        self.help_msg = help_msg
+
+    def __call__(self, f):
+        def wrapper(*args, **kwargs):
+            f_args = args[1]
+            if '--help' in f_args or '-h' in f_args:
+                print(self.help_msg)
+                return
+            return f(*args, **kwargs)
+        return wrapper
+
+
+ at action_help(create_usage)
+def _create(distpatcher, args, **kw):
+    from distutils2.create import main
+    return main()
+
+
+ at action_help(generate_usage)
+def _generate(distpatcher, args, **kw):
+    generate_setup_py()
+    logger.info('The setup.py was generated')
+
+
+ at action_help(graph_usage)
 def _graph(dispatcher, args, **kw):
-    # XXX
-    dists = get_distributions(use_egg_info=True)
-    graph = generate_graph(dists)
-    print(graph)
-    return 0
-
-
-    name = args[0]
+    name = args[1]
     dist = get_distribution(name, use_egg_info=True)
     if dist is None:
-        print('Distribution not found.')
+        logger.warning('Distribution not found.')
+        return 1
     else:
         dists = get_distributions(use_egg_info=True)
         graph = generate_graph(dists)
         print(graph.repr_node(dist))
 
-    return 0
 
+ at action_help(install_usage)
+def _install(dispatcher, args, **kw):
+    # first check if we are in a source directory
+    if len(args) < 2:
+        # are we inside a project dir?
+        if os.path.isfile('setup.cfg') or os.path.isfile('setup.py'):
+            args.insert(1, os.getcwd())
+        else:
+            logger.warning('No project to install.')
+            return 1
 
+    target = args[1]
+    # installing from a source dir or archive file?
+    if os.path.isdir(target) or _is_archive_file(target):
+        return not install_local_project(target)
+    else:
+        # download from PyPI
+        return not install(target)
 
-def _search(dispatcher, args, **kw):
-    search = args[0].lower()
-    for dist in get_distributions(use_egg_info=True):
-        name = dist.name.lower()
-        if search in name:
-            print('%s %s at %s' % (dist.name, dist.metadata['version'],
-                                    dist.path))
 
-    return 0
+ at action_help(metadata_usage)
+def _metadata(dispatcher, args, **kw):
+    opts = _parse_args(args[1:], 'f:', ['all'])
+    if opts['args']:
+        name = opts['args'][0]
+        dist = get_distribution(name, use_egg_info=True)
+        if dist is None:
+            logger.warning('%r not installed', name)
+            return 1
+    elif os.path.isfile('setup.cfg'):
+        logger.info('searching local dir for metadata')
+        dist = Distribution()  # XXX use config module
+        dist.parse_config_files()
+    else:
+        logger.warning('no argument given and no local setup.cfg found')
+        return 1
 
-
-def _metadata(dispatcher, args, **kw):
-    ### XXX Needs to work on any installed package as well
-    from distutils2.dist import Distribution
-    dist = Distribution()
-    dist.parse_config_files()
     metadata = dist.metadata
 
-    if 'all' in args:
+    if 'all' in opts:
         keys = metadata.keys()
     else:
-        keys = args
-        if len(keys) == 1:
-            print metadata[keys[0]]
-            return
+        if 'f' in opts:
+            keys = (k for k in opts['f'] if k in metadata)
+        else:
+            keys = ()
 
     for key in keys:
         if key in metadata:
@@ -117,26 +283,40 @@
             value = metadata[key]
             if isinstance(value, list):
                 for v in value:
-                    print('    ' + v)
+                    print('   ', v)
             else:
-                print('    ' + value.replace('\n', '\n    '))
-    return 0
+                print('   ', value.replace('\n', '\n    '))
 
+
+ at action_help(remove_usage)
+def _remove(distpatcher, args, **kw):
+    opts = _parse_args(args[1:], 'y', [])
+    if 'y' in opts:
+        auto_confirm = True
+    else:
+        auto_confirm = False
+
+    retcode = 0
+    for dist in set(opts['args']):
+        try:
+            remove(dist, auto_confirm=auto_confirm)
+        except PackagingError:
+            logger.warning('%r not installed', dist)
+            retcode = 1
+
+    return retcode
+
+
+ at action_help(run_usage)
 def _run(dispatcher, args, **kw):
     parser = dispatcher.parser
     args = args[1:]
 
     commands = STANDARD_COMMANDS  # + extra commands
 
-    # do we have a global option ?
-    if args in (['--help'], []):
-        print(run_usage)
-        return
-
     if args == ['--list-commands']:
         print('List of available commands:')
-        cmds = list(commands)
-        cmds.sort()
+        cmds = sorted(commands)
 
         for cmd in cmds:
             cls = dispatcher.cmdclass.get(cmd) or get_command_class(cmd)
@@ -160,53 +340,63 @@
     # XXX still need to be extracted from Distribution
     dist.parse_config_files()
 
-
-    try:
-        for cmd in dispatcher.commands:
-            dist.run_command(cmd, dispatcher.command_options[cmd])
-
-    except KeyboardInterrupt:
-        raise SystemExit("interrupted")
-    except (IOError, os.error, DistutilsError, CCompilerError), msg:
-        raise SystemExit("error: " + str(msg))
+    for cmd in dispatcher.commands:
+        dist.run_command(cmd, dispatcher.command_options[cmd])
 
     # XXX this is crappy
     return dist
 
-def _install(dispatcher, args, **kw):
-    install(args[0])
-    return 0
 
-def _remove(distpatcher, args, **kw):
-    remove(options.remove)
-    return 0
+ at action_help(list_usage)
+def _list(dispatcher, args, **kw):
+    opts = _parse_args(args[1:], '', ['all'])
+    dists = get_distributions(use_egg_info=True)
+    if 'all' in opts or opts['args'] == []:
+        results = dists
+        listall = True
+    else:
+        results = (d for d in dists if d.name.lower() in opts['args'])
+        listall = False
 
-def _create(distpatcher, args, **kw):
-    from distutils2.mkcfg import main
-    main()
-    return 0
+    number = 0
+    for dist in results:
+        print('%r %s (from %r)' % (dist.name, dist.version, dist.path))
+        number += 1
 
+    if number == 0:
+        if listall:
+            logger.info('Nothing seems to be installed.')
+        else:
+            logger.warning('No matching distribution found.')
+            return 1
+    else:
+        logger.info('Found %d projects installed.', number)
 
-actions = [('run', 'Run one or several commands', _run),
-           ('metadata', 'Display the metadata of a project', _metadata),
-           ('install', 'Install a project', _install),
-           ('remove', 'Remove a project', _remove),
-           ('search', 'Search for a project', _search),
-           ('graph', 'Display a graph', _graph),
-           ('create', 'Create a Project', _create),]
 
+ at action_help(search_usage)
+def _search(dispatcher, args, **kw):
+    """The search action.
 
+    It is able to search for a specific index (specified with --index), using
+    the simple or xmlrpc index types (with --type xmlrpc / --type simple)
+    """
+    #opts = _parse_args(args[1:], '', ['simple', 'xmlrpc'])
+    # 1. what kind of index is requested ? (xmlrpc / simple)
+    logger.error('not implemented')
+    return 1
 
 
-def fix_help_options(options):
-    """Convert a 4-tuple 'help_options' list as found in various command
-    classes to the 3-tuple form required by FancyGetopt.
-    """
-    new_options = []
-    for help_tuple in options:
-        new_options.append(help_tuple[0:3])
-    return new_options
-
+actions = [
+    ('run', 'Run one or several commands', _run),
+    ('metadata', 'Display the metadata of a project', _metadata),
+    ('install', 'Install a project', _install),
+    ('remove', 'Remove a project', _remove),
+    ('search', 'Search for a project in the indexes', _search),
+    ('list', 'List installed releases', _list),
+    ('graph', 'Display a graph', _graph),
+    ('create', 'Create a project', _create),
+    ('generate-setup', 'Generate a backward-comptatible setup.py', _generate),
+]
 
 
 class Dispatcher(object):
@@ -214,22 +404,21 @@
     """
     def __init__(self, args=None):
         self.verbose = 1
-        self.dry_run = 0
-        self.help = 0
-        self.script_name = 'pysetup'
+        self.dry_run = False
+        self.help = False
         self.cmdclass = {}
         self.commands = []
         self.command_options = {}
 
         for attr in display_option_names:
-            setattr(self, attr, 0)
+            setattr(self, attr, False)
 
         self.parser = FancyGetopt(global_options + display_options)
         self.parser.set_negative_aliases(negative_opt)
+        # FIXME this parses everything, including command options (e.g. "run
+        # build -i" errors with "option -i not recognized")
         args = self.parser.getopt(args=args, object=self)
 
-        #args = args[1:]
-
         # if first arg is "run", we have some commands
         if len(args) == 0:
             self.action = None
@@ -239,31 +428,44 @@
         allowed = [action[0] for action in actions] + [None]
         if self.action not in allowed:
             msg = 'Unrecognized action "%s"' % self.action
-            raise DistutilsArgError(msg)
+            raise PackagingArgError(msg)
 
-        # setting up the logger
-        handler = logging.StreamHandler()
-        logger.addHandler(handler)
-
-        if self.verbose:
-            handler.setLevel(logging.DEBUG)
-        else:
-            handler.setLevel(logging.INFO)
+        self._set_logger()
+        self.args = args
 
         # for display options we return immediately
-        option_order = self.parser.get_option_order()
-
-        self.args = args
-
         if self.help or self.action is None:
             self._show_help(self.parser, display_options_=False)
-            return
+
+    def _set_logger(self):
+        # setting up the logging level from the command-line options
+        # -q gets warning, error and critical
+        if self.verbose == 0:
+            level = logging.WARNING
+        # default level or -v gets info too
+        # XXX there's a bug somewhere: the help text says that -v is default
+        # (and verbose is set to 1 above), but when the user explicitly gives
+        # -v on the command line, self.verbose is incremented to 2!  Here we
+        # compensate for that (I tested manually).  On a related note, I think
+        # it's a good thing to use -q/nothing/-v/-vv on the command line
+        # instead of logging constants; it will be easy to add support for
+        # logging configuration in setup.cfg for advanced users. --merwok
+        elif self.verbose in (1, 2):
+            level = logging.INFO
+        else:  # -vv and more for debug
+            level = logging.DEBUG
+
+        # setting up the stream handler
+        handler = logging.StreamHandler(sys.stderr)
+        handler.setLevel(level)
+        logger.addHandler(handler)
+        logger.setLevel(level)
 
     def _parse_command_opts(self, parser, args):
         # Pull the current command from the head of the command line
         command = args[0]
         if not command_re.match(command):
-            raise SystemExit("invalid command name %r" % command)
+            raise SystemExit("invalid command name %r" % (command,))
         self.commands.append(command)
 
         # Dig up the command class that implements this command, so we
@@ -271,24 +473,24 @@
         # it takes.
         try:
             cmd_class = get_command_class(command)
-        except DistutilsModuleError, msg:
-            raise DistutilsArgError(msg)
+        except PackagingModuleError:
+            raise PackagingArgError(sys.exc_info()[1])
 
-        # XXX We want to push this in distutils.command
+        # XXX We want to push this in distutils2.command
         #
         # Require that the command class be derived from Command -- want
         # to be sure that the basic "command" interface is implemented.
         for meth in ('initialize_options', 'finalize_options', 'run'):
             if hasattr(cmd_class, meth):
                 continue
-            raise DistutilsClassError(
+            raise PackagingClassError(
                 'command %r must implement %r' % (cmd_class, meth))
 
         # Also make sure that the command object provides a list of its
         # known options.
         if not (hasattr(cmd_class, 'user_options') and
                 isinstance(cmd_class.user_options, list)):
-            raise DistutilsClassError(
+            raise PackagingClassError(
                 "command class %s must provide "
                 "'user_options' attribute (a list of tuples)" % cmd_class)
 
@@ -303,7 +505,7 @@
         # format (tuple of four) so we need to preprocess them here.
         if (hasattr(cmd_class, 'help_options') and
             isinstance(cmd_class.help_options, list)):
-            help_options = fix_help_options(cmd_class.help_options)
+            help_options = cmd_class.help_options[:]
         else:
             help_options = []
 
@@ -321,14 +523,14 @@
 
         if (hasattr(cmd_class, 'help_options') and
             isinstance(cmd_class.help_options, list)):
-            help_option_found = 0
-            for (help_option, short, desc, func) in cmd_class.help_options:
+            help_option_found = False
+            for help_option, short, desc, func in cmd_class.help_options:
                 if hasattr(opts, help_option.replace('-', '_')):
-                    help_option_found = 1
+                    help_option_found = True
                     if hasattr(func, '__call__'):
                         func()
                     else:
-                        raise DistutilsClassError(
+                        raise PackagingClassError(
                             "invalid help function %r for help option %r: "
                             "must be a callable object (function, etc.)"
                             % (func, help_option))
@@ -339,7 +541,7 @@
         # Put the options from the command line into their official
         # holding pen, the 'command_options' dictionary.
         opt_dict = self.get_option_dict(command)
-        for (name, value) in vars(opts).iteritems():
+        for name, value in vars(opts).items():
             opt_dict[name] = ("command line", value)
 
         return args
@@ -366,23 +568,23 @@
 
         parser.print_help(usage + "\nGlobal options:")
 
-    def _show_help(self, parser, global_options_=1, display_options_=1,
+    def _show_help(self, parser, global_options_=True, display_options_=True,
                    commands=[]):
         # late import because of mutual dependence between these modules
         from distutils2.command.cmd import Command
 
         print('Usage: pysetup [options] action [action_options]')
-        print('')
+        print()
         if global_options_:
             self.print_usage(self.parser)
-            print('')
+            print()
 
         if display_options_:
             parser.set_option_table(display_options)
             parser.print_help(
                 "Information display options (just display " +
                 "information, ignore any commands)")
-            print('')
+            print()
 
         for command in commands:
             if isinstance(command, type) and issubclass(command, Command):
@@ -391,35 +593,30 @@
                 cls = get_command_class(command)
             if (hasattr(cls, 'help_options') and
                 isinstance(cls.help_options, list)):
-                parser.set_option_table(cls.user_options +
-                                        fix_help_options(cls.help_options))
+                parser.set_option_table(cls.user_options + cls.help_options)
             else:
                 parser.set_option_table(cls.user_options)
 
-
             parser.print_help("Options for %r command:" % cls.__name__)
-            print('')
+            print()
 
     def _show_command_help(self, command):
-        from distutils2.command.cmd import Command
-        if isinstance(command, str):
+        if isinstance(command, basestring):
             command = get_command_class(command)
 
-        name = command.get_command_name()
-
         desc = getattr(command, 'description', '(no description available)')
-        print('Description: %s' % desc)
-        print('')
+        print('Description:', desc)
+        print()
 
         if (hasattr(command, 'help_options') and
             isinstance(command.help_options, list)):
             self.parser.set_option_table(command.user_options +
-                        fix_help_options(command.help_options))
+                                         command.help_options)
         else:
             self.parser.set_option_table(command.user_options)
 
         self.parser.print_help("Options:")
-        print('')
+        print()
 
     def _get_command_groups(self):
         """Helper function to retrieve all the command class names divided
@@ -440,21 +637,16 @@
         'description'.
         """
         std_commands, extra_commands = self._get_command_groups()
-        max_length = 0
-        for cmd in list(std_commands) + list(extra_commands):
-            if len(cmd) > max_length:
-                max_length = len(cmd)
+        max_length = max(len(command)
+                         for commands in (std_commands, extra_commands)
+                         for command in commands)
 
-        self.print_command_list(std_commands,
-                                "Standard commands",
-                                max_length)
+        self.print_command_list(std_commands, "Standard commands", max_length)
         if extra_commands:
-            print
-            self.print_command_list(extra_commands,
-                                    "Extra commands",
+            print()
+            self.print_command_list(extra_commands, "Extra commands",
                                     max_length)
 
-
     def print_command_list(self, commands, header, max_length):
         """Print a subset of the list of all commands -- used by
         'print_commands()'.
@@ -468,10 +660,10 @@
 
             print("  %-*s  %s" % (max_length, cmd, description))
 
-
     def __call__(self):
         if self.action is None:
-            return 0
+            return
+
         for action, desc, func in actions:
             if action == self.action:
                 return func(self, self.args)
@@ -479,11 +671,23 @@
 
 
 def main(args=None):
-    dispatcher = Dispatcher(args)
-    if dispatcher.action is None:
-        return 0
+    old_level = logger.level
+    old_handlers = list(logger.handlers)
+    try:
+        dispatcher = Dispatcher(args)
+        if dispatcher.action is None:
+            return
+        return dispatcher()
+    except KeyboardInterrupt:
+        logger.info('interrupted')
+        return 1
+    except (IOError, os.error, PackagingError, CCompilerError):
+        logger.exception(sys.exc_info()[1])
+        return 1
+    finally:
+        logger.setLevel(old_level)
+        logger.handlers[:] = old_handlers
 
-    return dispatcher()
 
 if __name__ == '__main__':
     sys.exit(main())
diff --git a/distutils2/tests/__init__.py b/distutils2/tests/__init__.py
--- a/distutils2/tests/__init__.py
+++ b/distutils2/tests/__init__.py
@@ -5,39 +5,23 @@
 'test' and contains a function test_suite().  The function is expected
 to return an initialized unittest.TestSuite instance.
 
-Tests for the command classes in the distutils2.command package are
-included in distutils2.tests as well, instead of using a separate
-distutils2.command.tests package, since command identification is done
-by import rather than matching pre-defined names.
+Utility code is included in distutils2.tests.support.
+"""
 
-Always import unittest from this module, it will be the right version
-(standard library unittest for 3.2 and higher, third-party unittest2
-release for older versions).
-
-Utility code is included in distutils2.tests.support.  
-"""
+# Put this text back for the backport
+#Always import unittest from this module, it will be the right version
+#(standard library unittest for 3.2 and higher, third-party unittest2
+#elease for older versions).
 
 import os
 import sys
+import unittest2 as unittest
+from .support import TESTFN
 
-if sys.version_info >= (3, 2):
-    # improved unittest package from 3.2's standard library
-    import unittest
-else:
-    try:
-        # external release of same package for older versions
-        import unittest2 as unittest
-    except ImportError:
-        sys.exit('Error: You have to install unittest2')
-
-# use TESTFN from stdlib, pull in unlink for other modules to use as well
-if sys.version_info[0] == 3:
-  from test.support import TESTFN, unlink
-else :
-  from test.test_support import TESTFN, unlink
+# XXX move helpers to support, add tests for them, remove things that
+# duplicate test.support (or keep them for the backport; needs thinking)
 
 here = os.path.dirname(__file__) or os.curdir
-
 verbose = 1
 
 def test_suite():
@@ -50,6 +34,7 @@
             suite.addTest(module.test_suite())
     return suite
 
+
 class Error(Exception):
     """Base class for regression test exceptions."""
 
@@ -88,12 +73,13 @@
 def run_unittest(classes, verbose_=1):
     """Run tests from unittest.TestCase-derived classes.
 
-    Extracted from stdlib test.test_support and modified to support unittest.
+    Originally extracted from stdlib test.test_support and modified to
+    support unittest2.
     """
     valid_types = (unittest.TestSuite, unittest.TestCase)
     suite = unittest.TestSuite()
     for cls in classes:
-        if isinstance(cls, str):
+        if isinstance(cls, basestring):
             if cls in sys.modules:
                 suite.addTest(unittest.findTestCases(sys.modules[cls]))
             else:
@@ -111,7 +97,7 @@
     stick around to hog resources and create problems when looking
     for refleaks.
 
-    Extracted from stdlib test.test_support.
+    Extracted from stdlib test.support.
     """
 
     # Reap all our dead child processes so we don't leave zombies around.
@@ -127,10 +113,11 @@
             except:
                 break
 
+
 def captured_stdout(func, *args, **kw):
-    import StringIO
+    from StringIO import StringIO
     orig_stdout = getattr(sys, 'stdout')
-    setattr(sys, 'stdout', StringIO.StringIO())
+    setattr(sys, 'stdout', StringIO())
     try:
         res = func(*args, **kw)
         sys.stdout.seek(0)
@@ -138,12 +125,9 @@
     finally:
         setattr(sys, 'stdout', orig_stdout)
 
+
 def unload(name):
     try:
         del sys.modules[name]
     except KeyError:
         pass
-
-
-if __name__ == "__main__":
-    unittest.main(defaultTest="test_suite")
diff --git a/distutils2/tests/__main__.py b/distutils2/tests/__main__.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/__main__.py
@@ -0,0 +1,23 @@
+"""Packaging test suite runner."""
+
+# Ripped from importlib tests, thanks Brett!
+
+import os
+import sys
+import unittest2
+from .support import run_unittest, reap_children, reap_threads
+
+
+ at reap_threads
+def test_main():
+    try:
+        start_dir = os.path.dirname(__file__)
+        top_dir = os.path.dirname(os.path.dirname(start_dir))
+        test_loader = unittest2.TestLoader()
+        run_unittest(test_loader.discover(start_dir, top_level_dir=top_dir))
+    finally:
+        reap_children()
+
+
+if __name__ == '__main__':
+    test_main()
diff --git a/distutils2/tests/fake_dists/babar-0.1.dist-info/INSTALLER b/distutils2/tests/fake_dists/babar-0.1.dist-info/INSTALLER
new file mode 100644
diff --git a/distutils2/tests/fake_dists/babar-0.1.dist-info/METADATA b/distutils2/tests/fake_dists/babar-0.1.dist-info/METADATA
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/babar-0.1.dist-info/METADATA
@@ -0,0 +1,4 @@
+Metadata-version: 1.2
+Name: babar
+Version: 0.1
+Author: FELD Boris
\ No newline at end of file
diff --git a/distutils2/tests/fake_dists/babar-0.1.dist-info/RECORD b/distutils2/tests/fake_dists/babar-0.1.dist-info/RECORD
new file mode 100644
diff --git a/distutils2/tests/fake_dists/babar-0.1.dist-info/REQUESTED b/distutils2/tests/fake_dists/babar-0.1.dist-info/REQUESTED
new file mode 100644
diff --git a/distutils2/tests/fake_dists/babar-0.1.dist-info/RESOURCES b/distutils2/tests/fake_dists/babar-0.1.dist-info/RESOURCES
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/babar-0.1.dist-info/RESOURCES
@@ -0,0 +1,2 @@
+babar.png,babar.png
+babar.cfg,babar.cfg
\ No newline at end of file
diff --git a/distutils2/tests/fake_dists/babar.cfg b/distutils2/tests/fake_dists/babar.cfg
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/babar.cfg
@@ -0,0 +1,1 @@
+Config
\ No newline at end of file
diff --git a/distutils2/tests/fake_dists/babar.png b/distutils2/tests/fake_dists/babar.png
new file mode 100644
diff --git a/distutils2/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/distutils2/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -0,0 +1,6 @@
+Metadata-Version: 1.2
+Name: bacon
+Version: 0.1
+Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
+Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,18 @@
+Metadata-Version: 1.0
+Name: banana
+Version: 0.4
+Summary: A yellow fruit
+Home-page: http://en.wikipedia.org/wiki/Banana
+Author: Josip Djolonga
+Author-email: foo at nbar.com
+License: BSD
+Description: A fruit
+Keywords: foo bar
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
new file mode 100644
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1,1 @@
+
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
@@ -0,0 +1,3 @@
+
+      # -*- Entry points: -*-
+      
\ No newline at end of file
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
@@ -0,0 +1,1 @@
+
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
@@ -0,0 +1,6 @@
+# this should be ignored
+
+strawberry >=0.5
+
+[section ignored]
+foo ==0.5
diff --git a/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/distutils2/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
new file mode 100644
diff --git a/distutils2/tests/fake_dists/cheese-2.0.2.egg-info b/distutils2/tests/fake_dists/cheese-2.0.2.egg-info
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/cheese-2.0.2.egg-info
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: cheese
+Version: 2.0.2
+Provides-Dist: truffles (1.0.2)
+Obsoletes-Dist: truffles (!=1.2,<=2.0)
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER b/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
new file mode 100644
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA b/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
@@ -0,0 +1,9 @@
+Metadata-Version: 1.2
+Name: choxie
+Version: 2.0.0.9
+Summary: Chocolate with a kick!
+Requires-Dist: towel-stuff (0.1)
+Requires-Dist: nut
+Provides-Dist: truffles (1.0)
+Obsoletes-Dist: truffles (<=0.8,>=0.5)
+Obsoletes-Dist: truffles (<=0.9,>=0.6)
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD b/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
new file mode 100644
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED b/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
new file mode 100644
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py b/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py b/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+from towel_stuff import Towel
+
+class Chocolate(object):
+    """A piece of chocolate."""
+
+    def wrap_with_towel(self):
+        towel = Towel()
+        towel.wrap(self)
+        return towel
diff --git a/distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py b/distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+from choxie.chocolate import Chocolate
+
+class Truffle(Chocolate):
+    """A truffle."""
diff --git a/distutils2/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO b/distutils2/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: coconuts-aster
+Version: 10.3
+Provides-Dist: strawberry (0.6)
+Provides-Dist: banana (0.4)
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER b/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
new file mode 100644
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: grammar
+Version: 1.0a4
+Requires-Dist: truffles (>=1.2)
+Author: Sherlock Holmes
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/RECORD b/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
new file mode 100644
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED b/distutils2/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
new file mode 100644
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4/grammar/__init__.py b/distutils2/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
@@ -0,0 +1,1 @@
+# -*- coding: utf-8 -*-
diff --git a/distutils2/tests/fake_dists/grammar-1.0a4/grammar/utils.py b/distutils2/tests/fake_dists/grammar-1.0a4/grammar/utils.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/grammar-1.0a4/grammar/utils.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+from random import randint
+
+def is_valid_grammar(sentence):
+    if randint(0, 10) < 2:
+        return False
+    else:
+        return True
diff --git a/distutils2/tests/fake_dists/nut-funkyversion.egg-info b/distutils2/tests/fake_dists/nut-funkyversion.egg-info
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/nut-funkyversion.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: nut
+Version: funkyversion
diff --git a/distutils2/tests/fake_dists/strawberry-0.6.egg b/distutils2/tests/fake_dists/strawberry-0.6.egg
new file mode 100644
index 0000000000000000000000000000000000000000..6d160e8b161031ae52638514843592187925b757
GIT binary patch
literal 1402
zc$`yK)KALH(=X28%1l#;R!B%nEKbc!%uQ8LF-TCbRZuEUEh#N1$<NOz)-}*GOExsr
zEvPioGuBH at Pghci<toWY%~MFNNKIBKs4U6I&jU&+=q4*DW#$&-7nLX!R~G9i<QH3m
z7<vVXB^i2|dBv$kB^m}GVTed#QZb0uP**Wf*VMFDNGr<ERX|v)mz<xQo0ylPmzr2y
z84uK6l9-dD05v};Kfk27q$sffVnb?0W{Cz|OhZ#sNkK`)L5V9hr#O{MK_N9cBOmB5
zh0J1wy!;XcpxYIa^NWg7lS>r9UQWv|0ty0Ufu2)H%gjmDgJ}xL0otCbPy`8 at OrXVy
z$=M1e`3iV~M*-+)g_5F5g~as4%sjABpm0h{1UV)xlPkcRnT3l11j?rGw_!j6Vhl12
zuI}!-o_=or`X%`V at j0nwsX2Nj6(yk|oD9qiubF*7xU_<sfsy3}GXn#dK$usBW}XPL
zdBOgnLC&thaML(|CUIalO$4ZygTbxvn9cJCa*K8Yd7pq-5ZSZ<ZwLcyl*$Bd9}q?}
z%GU3+-(d%yJ- at a8FngYAJv`50)he!AtAbSdVkX?4u~u<++)e34e{3~_X5O|jF|J;B
zC3xwx-m|X72inV6*}Y^gtiD*1wpsGu-O%40%MDdGSw1j37$$7q`bcR1wJ4|Sg?Z{f
zrybfGuv%=l^lLM>i9RnLxBqqeU~_GC)B*8Q%^m+PITr6Z&8t31F+o4>xK^{d-p7?^
zx~J#&ZkWuS9<SEFoOjj3--Yb2mtJ3TW&iZ at 1KQ^&N4;mX5B=!V_JJD_rYtj1!&DME
z6jJkm at f=@}pP5%u3=ddwV4#ZQ4p;_;F99Leli98tF#_2jEDOYNBU4ffQu9($^O7s$
zb29U?!NvlU3?q{qGp<A<0cUS%tYT!qnS at v&NeIJT2(vL05VF~)kj+L(POOmRgw<qR
zi3wsdBiLjKCI)OtixrZz at HiMVb`TC;(kREwfG at 6CA#sJpco7ENF@*?^LS_Q-7U0dw
Q22#ojgxi7o*D`~60Pqom)c^nh

diff --git a/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER b/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
new file mode 100644
diff --git a/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -0,0 +1,7 @@
+Metadata-Version: 1.2
+Name: towel-stuff
+Version: 0.1
+Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
+Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD b/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
new file mode 100644
diff --git a/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED b/distutils2/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
new file mode 100644
diff --git a/distutils2/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py b/distutils2/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+class Towel(object):
+    """A towel, that one should never be without."""
+
+    def __init__(self, color='tie-dye'):
+        self.color = color
+        self.wrapped_obj = None
+
+    def wrap(self, obj):
+        """Wrap an object up in our towel."""
+        self.wrapped_obj = obj
+
+    def unwrap(self):
+        """Unwrap whatever is in our towel and return whatever it is."""
+        obj = self.wrapped_obj
+        self.wrapped_obj = None
+        return obj
diff --git a/distutils2/tests/fake_dists/truffles-5.0.egg-info b/distutils2/tests/fake_dists/truffles-5.0.egg-info
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/fake_dists/truffles-5.0.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: truffles
+Version: 5.0
diff --git a/distutils2/tests/fixer/fix_idioms.py b/distutils2/tests/fixer/fix_idioms.py
--- a/distutils2/tests/fixer/fix_idioms.py
+++ b/distutils2/tests/fixer/fix_idioms.py
@@ -101,18 +101,18 @@
     def transform_isinstance(self, node, results):
         x = results["x"].clone() # The thing inside of type()
         T = results["T"].clone() # The type being compared against
-        x.set_prefix("")
-        T.set_prefix(" ")
+        x.prefix = ""
+        T.prefix = " "
         test = Call(Name("isinstance"), [x, Comma(), T])
         if "n" in results:
-            test.set_prefix(" ")
+            test.prefix = " "
             test = Node(syms.not_test, [Name("not"), test])
-        test.set_prefix(node.get_prefix())
+        test.prefix = getattr(node, 'prefix', ' ')
         return test
 
     def transform_while(self, node, results):
         one = results["while"]
-        one.replace(Name("True", prefix=one.get_prefix()))
+        one.replace(Name("True", prefix=one.prefix))
 
     def transform_sort(self, node, results):
         sort_stmt = results["sort"]
@@ -121,14 +121,14 @@
         simple_expr = results.get("expr")
 
         if list_call:
-            list_call.replace(Name("sorted", prefix=list_call.get_prefix()))
+            list_call.replace(Name("sorted", prefix=list_call.prefix))
         elif simple_expr:
             new = simple_expr.clone()
-            new.set_prefix("")
+            new.prefix = ""
             simple_expr.replace(Call(Name("sorted"), [new],
-                                     prefix=simple_expr.get_prefix()))
+                                     prefix=simple_expr.prefix))
         else:
             raise RuntimeError("should not have reached here")
         sort_stmt.remove()
         if next_stmt:
-            next_stmt[0].set_prefix(sort_stmt.get_prefix())
+            next_stmt[0].prefix = sort_stmt._prefix
diff --git a/distutils2/tests/pypi_server.py b/distutils2/tests/pypi_server.py
--- a/distutils2/tests/pypi_server.py
+++ b/distutils2/tests/pypi_server.py
@@ -22,48 +22,44 @@
 
 Then, the server must have only one port to rely on, eg.
 
-    >>> server.fulladress()
+    >>> server.fulladdress()
     "http://ip:port/"
 
 It could be simple to have one HTTP server, relaying the requests to the two
 implementations (static HTTP and XMLRPC over HTTP).
 """
 
-import os.path
+import os
+import queue
 import select
-import socket
 import threading
-
-# several packages had different names in Python 2.x
-try:
-    import queue
-    import socketserver
-    from http.server import HTTPServer, SimpleHTTPRequestHandler
-    from xmlrpc.server import SimpleXMLRPCServer
-except ImportError:
-    import Queue as queue
-    import SocketServer as socketserver
-    from BaseHTTPServer import HTTPServer    
-    from SimpleHTTPServer import SimpleHTTPRequestHandler
-    from SimpleXMLRPCServer import SimpleXMLRPCServer
+import socketserver
+from functools import wraps
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer
 
 from distutils2.tests import unittest
 
-PYPI_DEFAULT_STATIC_PATH = os.path.dirname(os.path.abspath(__file__)) + "/pypiserver"
+PYPI_DEFAULT_STATIC_PATH = os.path.join(
+    os.path.dirname(os.path.abspath(__file__)), 'pypiserver')
+
 
 def use_xmlrpc_server(*server_args, **server_kwargs):
     server_kwargs['serve_xmlrpc'] = True
     return use_pypi_server(*server_args, **server_kwargs)
 
+
 def use_http_server(*server_args, **server_kwargs):
     server_kwargs['serve_xmlrpc'] = False
     return use_pypi_server(*server_args, **server_kwargs)
 
+
 def use_pypi_server(*server_args, **server_kwargs):
     """Decorator to make use of the PyPIServer for test methods,
     just when needed, and not for the entire duration of the testcase.
     """
     def wrapper(func):
+        @wraps(func)
         def wrapped(*args, **kwargs):
             server = PyPIServer(*server_args, **server_kwargs)
             server.start()
@@ -74,16 +70,15 @@
         return wrapped
     return wrapper
 
+
 class PyPIServerTestCase(unittest.TestCase):
 
     def setUp(self):
         super(PyPIServerTestCase, self).setUp()
         self.pypi = PyPIServer()
         self.pypi.start()
+        self.addCleanup(self.pypi.stop)
 
-    def tearDown(self):
-        super(PyPIServerTestCase, self).tearDown()
-        self.pypi.stop()
 
 class PyPIServer(threading.Thread):
     """PyPI Mocked server.
@@ -93,8 +88,8 @@
     """
 
     def __init__(self, test_static_path=None,
-                 static_filesystem_paths=["default"],
-                 static_uri_paths=["simple"], serve_xmlrpc=False) :
+                 static_filesystem_paths=None,
+                 static_uri_paths=["simple", "packages"], serve_xmlrpc=False):
         """Initialize the server.
 
         Default behavior is to start the HTTP server. You can either start the
@@ -110,6 +105,8 @@
         threading.Thread.__init__(self)
         self._run = True
         self._serve_xmlrpc = serve_xmlrpc
+        if static_filesystem_paths is None:
+            static_filesystem_paths = ["default"]
 
         #TODO allow to serve XMLRPC and HTTP static files at the same time.
         if not self._serve_xmlrpc:
@@ -118,15 +115,18 @@
 
             self.request_queue = queue.Queue()
             self._requests = []
-            self.default_response_status = 200
+            self.default_response_status = 404
             self.default_response_headers = [('Content-type', 'text/plain')]
-            self.default_response_data = "hello"
+            self.default_response_data = "The page does not exists"
 
             # initialize static paths / filesystems
             self.static_uri_paths = static_uri_paths
+
+            # append the static paths defined locally
             if test_static_path is not None:
                 static_filesystem_paths.append(test_static_path)
-            self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path
+            self.static_filesystem_paths = [
+                PYPI_DEFAULT_STATIC_PATH + "/" + path
                 for path in static_filesystem_paths]
         else:
             # XMLRPC server
@@ -136,7 +136,7 @@
             self.server.register_introspection_functions()
             self.server.register_instance(self.xmlrpc)
 
-        self.address = (self.server.server_name, self.server.server_port)
+        self.address = ('127.0.0.1', self.server.server_port)
         # to not have unwanted outputs.
         self.server.RequestHandlerClass.log_request = lambda *_: None
 
@@ -150,6 +150,9 @@
     def stop(self):
         """self shutdown is not supported for python < 2.6"""
         self._run = False
+        if self.is_alive():
+            self.join()
+        self.server.server_close()
 
     def get_next_response(self):
         return (self.default_response_status,
@@ -177,29 +180,23 @@
     # we need to access the pypi server while serving the content
     pypi_server = None
 
-    def do_POST(self):
-        return self.serve_request()
-    def do_GET(self):
-        return self.serve_request()
-    def do_DELETE(self):
-        return self.serve_request()
-    def do_PUT(self):
-        return self.serve_request()
-
     def serve_request(self):
         """Serve the content.
 
         Also record the requests to be accessed later. If trying to access an
         url matching a static uri, serve static content, otherwise serve
         what is provided by the `get_next_response` method.
+
+        If nothing is defined there, return a 404 header.
         """
         # record the request. Read the input only on PUT or POST requests
         if self.command in ("PUT", "POST"):
-            if 'content-length' in self.headers.dict:
+            if 'content-length' in self.headers:
                 request_data = self.rfile.read(
                     int(self.headers['content-length']))
             else:
                 request_data = self.rfile.read()
+
         elif self.command in ("GET", "DELETE"):
             request_data = ''
 
@@ -220,13 +217,19 @@
                 try:
                     if self.path.endswith("/"):
                         relative_path += "index.html"
-                    file = open(fs_path + relative_path)
-                    data = file.read()
+
                     if relative_path.endswith('.tar.gz'):
-                        headers=[('Content-type', 'application/x-gtar')]
+                        with open(fs_path + relative_path, 'br') as file:
+                            data = file.read()
+                        headers = [('Content-type', 'application/x-gtar')]
                     else:
-                        headers=[('Content-type', 'text/html')]
+                        with open(fs_path + relative_path) as file:
+                            data = file.read().encode()
+                        headers = [('Content-type', 'text/html')]
+
+                    headers.append(('Content-Length', len(data)))
                     self.make_response(data, headers=headers)
+
                 except IOError:
                     pass
 
@@ -239,6 +242,8 @@
             status, headers, data = self.pypi_server.get_next_response()
             self.make_response(data, status, headers)
 
+    do_POST = do_GET = do_DELETE = do_PUT = serve_request
+
     def make_response(self, data, status=200,
                       headers=[('Content-type', 'text/html')]):
         """Send the response to the HTTP client"""
@@ -254,18 +259,24 @@
         for header, value in headers:
             self.send_header(header, value)
         self.end_headers()
+
+        if type(data) is str:
+            data = data.encode()
+
         self.wfile.write(data)
 
+
 class PyPIXMLRPCServer(SimpleXMLRPCServer):
     def server_bind(self):
         """Override server_bind to store the server name."""
         socketserver.TCPServer.server_bind(self)
         host, port = self.socket.getsockname()[:2]
-        self.server_name = socket.getfqdn(host)
         self.server_port = port
 
+
 class MockDist(object):
     """Fake distribution, used in the Mock PyPI Server"""
+
     def __init__(self, name, version="1.0", hidden=False, url="http://url/",
              type="sdist", filename="", size=10000,
              digest="123456", downloads=7, has_sig=False,
@@ -377,6 +388,7 @@
             'summary': self.summary,
         }
 
+
 class XMLRPCMockIndex(object):
     """Mock XMLRPC server"""
 
@@ -386,7 +398,7 @@
 
     def add_distributions(self, dists):
         for dist in dists:
-           self._dists.append(MockDist(**dist))
+            self._dists.append(MockDist(**dist))
 
     def set_distributions(self, dists):
         self._dists = []
diff --git a/distutils2/tests/pypi_test_server.py b/distutils2/tests/pypi_test_server.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/pypi_test_server.py
@@ -0,0 +1,59 @@
+"""Test PyPI Server implementation at testpypi.python.org, to use in tests.
+
+This is a drop-in replacement for the mock pypi server for testing against a
+real pypi server hosted by python.org especially for testing against.
+"""
+
+import unittest
+
+PYPI_DEFAULT_STATIC_PATH = None
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = True
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+    server_kwargs['serve_xmlrpc'] = False
+    return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+    """Decorator to make use of the PyPIServer for test methods,
+    just when needed, and not for the entire duration of the testcase.
+    """
+    def wrapper(func):
+        def wrapped(*args, **kwargs):
+            server = PyPIServer(*server_args, **server_kwargs)
+            func(server=server, *args, **kwargs)
+        return wrapped
+    return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+    def setUp(self):
+        super(PyPIServerTestCase, self).setUp()
+        self.pypi = PyPIServer()
+        self.pypi.start()
+        self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer(object):
+    """Shim to access testpypi.python.org, for testing a real server."""
+
+    def __init__(self, test_static_path=None,
+                 static_filesystem_paths=["default"],
+                 static_uri_paths=["simple"], serve_xmlrpc=False):
+        self.address = ('testpypi.python.org', '80')
+
+    def start(self):
+        pass
+
+    def stop(self):
+        pass
+
+    @property
+    def full_address(self):
+        return "http://%s:%s" % self.address
diff --git a/distutils2/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz b/distutils2/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..333961eb18a6e7db80fefd41c339ab218d5180c4
GIT binary patch
literal 110
zc$|~(=3uy!>FUeC{PvtR-ysJc)&sVu<PP%fK3N&$;N1M<j{R(=POen}XGQA#H*w#;
z=4~0pQ=DD>?9yZ7`(A1Di)P(6s!I71JWZ;--fWND`LA)=lAmk-7Jbj=XMlnFEsQ#U
Kd|Vkc7#IK&xGYxy

diff --git a/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz b/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz
deleted file mode 100644
Binary file distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz has changed
diff --git a/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
--- a/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
+++ b/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
@@ -1,3 +1,3 @@
 <html><body>
-<a href="foobar-0.1.tar.gz#md5=d41d8cd98f00b204e9800998ecf8427e" rel="download">foobar-0.1.tar.gz</a><br/>
+<a href="foobar-0.1.tar.gz#md5=fe18804c5b722ff024cabdf514924fc4" rel="download">foobar-0.1.tar.gz</a><br/>
 </body></html>
diff --git a/distutils2/tests/support.py b/distutils2/tests/support.py
--- a/distutils2/tests/support.py
+++ b/distutils2/tests/support.py
@@ -1,8 +1,7 @@
 """Support code for distutils2 test cases.
 
-Four helper classes are provided: LoggingCatcher, TempdirManager,
-EnvironGuard and WarningsCatcher. They are written to be used as mixins,
-e.g. ::
+A few helper classes are provided: LoggingCatcher, TempdirManager and
+EnvironRestorer. They are written to be used as mixins::
 
     from distutils2.tests import unittest
     from distutils2.tests.support import LoggingCatcher
@@ -21,83 +20,112 @@
 tests of another command that needs them, a create_distribution function
 and a skip_unless_symlink decorator.
 
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, a create_distribution function
+and a skip_unless_symlink decorator.
+
 Each class or function has a docstring to explain its purpose and usage.
 """
 
+import codecs
 import os
 import shutil
+import logging
+import logging.handlers
+import subprocess
+import sys
+import weakref
 import tempfile
-import warnings
-from copy import deepcopy
-import logging
+try:
+    import _thread, threading
+except ImportError:
+    _thread = None
+    threading = None
+try:
+    import zlib
+except ImportError:
+    zlib = None
 
-from distutils2 import logger
 from distutils2.dist import Distribution
 from distutils2.tests import unittest
 
-__all__ = ['LoggingCatcher', 'WarningsCatcher', 'TempdirManager',
-           'EnvironGuard', 'DummyCommand', 'unittest', 'create_distribution',
-           'skip_unless_symlink']
+__all__ = ['LoggingCatcher', 'TempdirManager', 'EnvironRestorer',
+           'DummyCommand', 'unittest', 'create_distribution',
+           'skip_unless_symlink', 'requires_zlib']
+
+
+logger = logging.getLogger('distutils2')
+logger2to3 = logging.getLogger('RefactoringTool')
+
+
+class _TestHandler(logging.handlers.BufferingHandler):
+    # stolen and adapted from test.support
+
+    def __init__(self):
+        logging.handlers.BufferingHandler.__init__(self, 0)
+        self.setLevel(logging.DEBUG)
+
+    def shouldFlush(self):
+        return False
+
+    def emit(self, record):
+        self.buffer.append(record)
 
 
 class LoggingCatcher(object):
-    """TestCase-compatible mixin to catch logging calls.
+    """TestCase-compatible mixin to receive logging calls.
 
-    Every log message that goes through distutils2.log will get appended to
-    self.logs instead of being printed. You can check that your code logs
-    warnings and errors as documented by inspecting that list; helper methods
-    get_logs and clear_logs are also provided.
+    Upon setUp, instances of this classes get a BufferingHandler that's
+    configured to record all messages logged to the 'distutils2' logger.
+
+    Use get_logs to retrieve messages and self.loghandler.flush to discard
+    them.  get_logs automatically flushes the logs; if you test code that
+    generates logging messages but don't use get_logs, you have to flush
+    manually before doing other checks on logging message, otherwise you
+    will get irrelevant results.  See example in test_command_check.
     """
 
     def setUp(self):
         super(LoggingCatcher, self).setUp()
-        # TODO read the new logging docs and/or the python-dev posts about
-        # logging and tests to properly use a handler instead of
-        # monkey-patching
-        self.old_log = logger._log
-        logger._log = self._log
-        logger.setLevel(logging.INFO)
-        self.logs = []
-
-    def _log(self, *args, **kw):
-        self.logs.append(args)
+        self.loghandler = handler = _TestHandler()
+        self._old_levels = logger.level, logger2to3.level
+        logger.addHandler(handler)
+        logger.setLevel(logging.DEBUG)  # we want all messages
+        logger2to3.setLevel(logging.CRITICAL)  # we don't want 2to3 messages
 
     def tearDown(self):
-        logger._log = self.old_log
+        handler = self.loghandler
+        # All this is necessary to properly shut down the logging system and
+        # avoid a regrtest complaint.  Thanks to Vinay Sajip for the help.
+        handler.close()
+        logger.removeHandler(handler)
+        for ref in weakref.getweakrefs(handler):
+            logging._removeHandlerRef(ref)
+        del self.loghandler
+        logger.setLevel(self._old_levels[0])
+        logger2to3.setLevel(self._old_levels[1])
         super(LoggingCatcher, self).tearDown()
 
     def get_logs(self, *levels):
-        """Return a list of caught messages with level in `levels`.
+        """Return all log messages with level in *levels*.
 
-        Example: self.get_logs(log.WARN, log.DEBUG) -> list
+        Without explicit levels given, returns all messages.  *levels* defaults
+        to all levels.  For log calls with arguments (i.e.
+        logger.info('bla bla %r', arg)), the messages will be formatted before
+        being returned (e.g. "bla bla 'thing'").
+
+        Returns a list.  Automatically flushes the loghandler after being
+        called.
+
+        Example: self.get_logs(logging.WARN, logging.DEBUG).
         """
-        def _format(msg, args):
-            if len(args) == 0:
-                return msg
-            return msg % args
-        return [_format(msg, args) for level, msg, args
-                in self.logs if level in levels]
-
-    def clear_logs(self):
-        """Empty the internal list of caught messages."""
-        del self.logs[:]
-
-
-class WarningsCatcher(object):
-
-    def setUp(self):
-        self._orig_showwarning = warnings.showwarning
-        warnings.showwarning = self._record_showwarning
-        self.warnings = []
-
-    def _record_showwarning(self, message, category, filename, lineno,
-                            file=None, line=None):
-        self.warnings.append({"message": message, "category": category,
-                              "filename": filename, "lineno": lineno,
-                              "file": file, "line": line})
-
-    def tearDown(self):
-        warnings.showwarning = self._orig_showwarning
+        if not levels:
+            messages = [log.getMessage() for log in self.loghandler.buffer]
+        else:
+            messages = [log.getMessage() for log in self.loghandler.buffer
+                        if log.levelno in levels]
+        self.loghandler.flush()
+        return messages
 
 
 class TempdirManager(object):
@@ -109,24 +137,33 @@
 
     def setUp(self):
         super(TempdirManager, self).setUp()
+        self._olddir = os.getcwd()
         self._basetempdir = tempfile.mkdtemp()
+        self._files = []
 
     def tearDown(self):
+        for handle, name in self._files:
+            handle.close()
+            unlink(name)
+
+        os.chdir(self._olddir)
+        shutil.rmtree(self._basetempdir)
         super(TempdirManager, self).tearDown()
-        shutil.rmtree(self._basetempdir, os.name in ('nt', 'cygwin'))
 
     def mktempfile(self):
         """Create a read-write temporary file and return it."""
         fd, fn = tempfile.mkstemp(dir=self._basetempdir)
         os.close(fd)
-        return open(fn, 'w+')
+        fp = open(fn, 'w+')
+        self._files.append((fp, fn))
+        return fp
 
     def mkdtemp(self):
         """Create a temporary directory and return its path."""
         d = tempfile.mkdtemp(dir=self._basetempdir)
         return d
 
-    def write_file(self, path, content='xxx'):
+    def write_file(self, path, content='xxx', encoding=None):
         """Write a file at the given path.
 
         path can be a string, a tuple or a list; if it's a tuple or list,
@@ -134,11 +171,8 @@
         """
         if isinstance(path, (list, tuple)):
             path = os.path.join(*path)
-        f = open(path, 'w')
-        try:
+        with codecs.open(path, 'w', encoding=encoding) as f:
             f.write(content)
-        finally:
-            f.close()
 
     def create_dist(self, **kw):
         """Create a stub distribution object and files.
@@ -151,9 +185,6 @@
         You can use self.write_file to write any file in that
         directory, e.g. setup scripts or Python modules.
         """
-        # Late import so that third parties can import support without
-        # loading a ton of distutils2 modules in memory.
-        from distutils2.dist import Distribution
         if 'name' not in kw:
             kw['name'] = 'foo'
         tmp_dir = self.mkdtemp()
@@ -176,25 +207,34 @@
 
     def assertIsNotFile(self, *args):
         path = os.path.join(*args)
-        assert not os.path.isfile(path), "%s exist" % path
+        self.assertFalse(os.path.isfile(path), "%r exists" % path)
 
-class EnvironGuard(object):
-    """TestCase-compatible mixin to save and restore the environment."""
+
+class EnvironRestorer(object):
+    """TestCase-compatible mixin to restore or delete environment variables.
+
+    The variables to restore (or delete if they were not originally present)
+    must be explicitly listed in self.restore_environ.  It's better to be
+    aware of what we're modifying instead of saving and restoring the whole
+    environment.
+    """
 
     def setUp(self):
-        super(EnvironGuard, self).setUp()
-        self.old_environ = deepcopy(os.environ)
+        super(EnvironRestorer, self).setUp()
+        self._saved = []
+        self._added = []
+        for key in self.restore_environ:
+            if key in os.environ:
+                self._saved.append((key, os.environ[key]))
+            else:
+                self._added.append(key)
 
     def tearDown(self):
-        for key, value in self.old_environ.iteritems():
-            if os.environ.get(key) != value:
-                os.environ[key] = value
-
-        for key in os.environ.keys():
-            if key not in self.old_environ:
-                del os.environ[key]
-
-        super(EnvironGuard, self).tearDown()
+        for key, value in self._saved:
+            os.environ[key] = value
+        for key in self._added:
+            os.environ.pop(key, None)
+        super(EnvironRestorer, self).tearDown()
 
 
 class DummyCommand(object):
@@ -205,7 +245,7 @@
     """
 
     def __init__(self, **kwargs):
-        for kw, val in kwargs.iteritems():
+        for kw, val in kwargs.items():
             setattr(self, kw, val)
 
     def ensure_finalized(self):
@@ -234,8 +274,231 @@
     return d
 
 
-try:
-    from test.test_support import skip_unless_symlink
-except ImportError:
-    skip_unless_symlink = unittest.skip(
-        'requires test.test_support.skip_unless_symlink')
+def fake_dec(*args, **kw):
+    """Fake decorator"""
+    def _wrap(func):
+        def __wrap(*args, **kw):
+            return func(*args, **kw)
+        return __wrap
+    return _wrap
+
+
+#try:
+#    from test.support import skip_unless_symlink
+#except ImportError:
+#    skip_unless_symlink = unittest.skip(
+#        'requires test.support.skip_unless_symlink')
+
+if os.name == 'java':
+    # Jython disallows @ in module names
+    TESTFN = '$test'
+else:
+    TESTFN = '@test'
+
+# Disambiguate TESTFN for parallel testing, while letting it remain a valid
+# module name.
+TESTFN = "{0}_{1}_tmp".format(TESTFN, os.getpid())
+
+
+# TESTFN_UNICODE is a non-ascii filename
+TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f"
+if sys.platform == 'darwin':
+    # In Mac OS X's VFS API file names are, by definition, canonically
+    # decomposed Unicode, encoded using UTF-8. See QA1173:
+    # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html
+    import unicodedata
+    TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE)
+TESTFN_ENCODING = sys.getfilesystemencoding()
+
+# TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be
+# encoded by the filesystem encoding (in strict mode). It can be None if we
+# cannot generate such filename.
+TESTFN_UNENCODABLE = None
+if os.name in ('nt', 'ce'):
+    # skip win32s (0) or Windows 9x/ME (1)
+    if sys.getwindowsversion().platform >= 2:
+        # Different kinds of characters from various languages to minimize the
+        # probability that the whole name is encodable to MBCS (issue #9819)
+        TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80"
+        try:
+            TESTFN_UNENCODABLE.encode(TESTFN_ENCODING)
+        except UnicodeEncodeError:
+            pass
+        else:
+            print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). '
+                  'Unicode filename tests may not be effective'
+                  % (TESTFN_UNENCODABLE, TESTFN_ENCODING))
+            TESTFN_UNENCODABLE = None
+# Mac OS X denies unencodable filenames (invalid utf-8)
+elif sys.platform != 'darwin':
+    try:
+        # ascii and utf-8 cannot encode the byte 0xff
+        b'\xff'.decode(TESTFN_ENCODING)
+    except UnicodeDecodeError:
+        # 0xff will be encoded using the surrogate character u+DCFF
+        try:
+            TESTFN_UNENCODABLE = TESTFN \
+                + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape')
+        except LookupError:
+            pass
+    else:
+        # File system encoding (eg. ISO-8859-* encodings) can encode
+        # the byte 0xff. Skip some unicode filename tests.
+        pass
+
+def unlink(filename):
+    try:
+        os.unlink(filename)
+    except OSError:
+        error = sys.exc_info()[1]
+        # The filename need not exist.
+        if error.errno not in (errno.ENOENT, errno.ENOTDIR):
+            raise
+
+def _filter_suite(suite, pred):
+    """Recursively filter test cases in a suite based on a predicate."""
+    newtests = []
+    for test in suite._tests:
+        if isinstance(test, unittest.TestSuite):
+            _filter_suite(test, pred)
+            newtests.append(test)
+        else:
+            if pred(test):
+                newtests.append(test)
+    suite._tests = newtests
+
+class Error(Exception):
+    """Base class for regression test exceptions."""
+
+class TestFailed(Error):
+    """Test failed."""
+
+
+verbose = True
+failfast = False
+
+def _run_suite(suite):
+    """Run tests from a unittest.TestSuite-derived class."""
+    if verbose:
+        runner = unittest.TextTestRunner(sys.stdout, verbosity=2,
+                                         failfast=failfast)
+    else:
+        runner = BasicTestRunner()
+
+    result = runner.run(suite)
+    if not result.wasSuccessful():
+        if len(result.errors) == 1 and not result.failures:
+            err = result.errors[0][1]
+        elif len(result.failures) == 1 and not result.errors:
+            err = result.failures[0][1]
+        else:
+            err = "multiple errors occurred"
+            if not verbose: err += "; run in verbose mode for details"
+        raise TestFailed(err)
+
+match_tests = None
+
+def run_unittest(*classes):
+    """Run tests from unittest.TestCase-derived classes."""
+    valid_types = (unittest.TestSuite, unittest.TestCase)
+    suite = unittest.TestSuite()
+    for cls in classes:
+        if isinstance(cls, basestring):
+            if cls in sys.modules:
+                suite.addTest(unittest.findTestCases(sys.modules[cls]))
+            else:
+                raise ValueError("str arguments must be keys in sys.modules")
+        elif isinstance(cls, valid_types):
+            suite.addTest(cls)
+        else:
+            suite.addTest(unittest.makeSuite(cls))
+    def case_pred(test):
+        if match_tests is None:
+            return True
+        for name in test.id().split("."):
+            if fnmatch.fnmatchcase(name, match_tests):
+                return True
+        return False
+    _filter_suite(suite, case_pred)
+    _run_suite(suite)
+
+
+def reap_threads(func):
+    """Use this function when threads are being used.  This will
+    ensure that the threads are cleaned up even when the test fails.
+    If threading is unavailable this function does nothing.
+    """
+    if not _thread:
+        return func
+
+    @functools.wraps(func)
+    def decorator(*args):
+        key = threading_setup()
+        try:
+            return func(*args)
+        finally:
+            threading_cleanup(*key)
+    return decorator
+
+def reap_children():
+    """Use this function at the end of test_main() whenever sub-processes
+    are started.  This will help ensure that no extra children (zombies)
+    stick around to hog resources and create problems when looking
+    for refleaks.
+    """
+
+    # Reap all our dead child processes so we don't leave zombies around.
+    # These hog resources and might be causing some of the buildbots to die.
+    if hasattr(os, 'waitpid'):
+        any_process = -1
+        while True:
+            try:
+                # This will raise an exception on Windows.  That's ok.
+                pid, status = os.waitpid(any_process, os.WNOHANG)
+                if pid == 0:
+                    break
+            except:
+                break
+
+requires_zlib = unittest.skipUnless(zlib, 'requires zlib')
+
+# Executing the interpreter in a subprocess
+def _assert_python(expected_success, *args, **env_vars):
+    cmd_line = [sys.executable]
+    if not env_vars:
+        cmd_line.append('-E')
+    cmd_line.extend(args)
+    # Need to preserve the original environment, for in-place testing of
+    # shared library builds.
+    env = os.environ.copy()
+    env.update(env_vars)
+    p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                         env=env)
+    try:
+        out, err = p.communicate()
+    finally:
+        subprocess._cleanup()
+        p.stdout.close()
+        p.stderr.close()
+    rc = p.returncode
+    err =  strip_python_stderr(err)
+    if (rc and expected_success) or (not rc and not expected_success):
+        raise AssertionError(
+            "Process return code is %d, "
+            "stderr follows:\n%s" % (rc, err.decode('ascii', 'ignore')))
+    return rc, out, err
+
+def assert_python_ok(*args, **env_vars):
+    """
+    Assert that running the interpreter with `args` and optional environment
+    variables `env_vars` is ok and return a (return code, stdout, stderr) tuple.
+    """
+    return _assert_python(True, *args, **env_vars)
+
+def unload(name):
+    try:
+        del sys.modules[name]
+    except KeyError:
+        pass
+
diff --git a/distutils2/tests/test_ccompiler.py b/distutils2/tests/test_ccompiler.py
--- a/distutils2/tests/test_ccompiler.py
+++ b/distutils2/tests/test_ccompiler.py
@@ -1,10 +1,10 @@
-"""Tests for distutils.ccompiler."""
-from distutils2.compiler.ccompiler import CCompiler
+"""Tests for distutils.compiler.ccompiler."""
+
+from distutils2.compiler import ccompiler
 from distutils2.tests import unittest, support
 
 
-class CCompilerTestCase(support.EnvironGuard, unittest.TestCase):
-
+class CCompilerTestCase(unittest.TestCase):
     pass  # XXX need some tests on CCompiler
 
 
diff --git a/distutils2/tests/test_command_bdist.py b/distutils2/tests/test_command_bdist.py
--- a/distutils2/tests/test_command_bdist.py
+++ b/distutils2/tests/test_command_bdist.py
@@ -1,13 +1,13 @@
 """Tests for distutils.command.bdist."""
 
 from distutils2 import util
-from distutils2.tests import run_unittest
+from distutils2.command.bdist import bdist, show_formats
 
-from distutils2.command.bdist import bdist, show_formats
 from distutils2.tests import unittest, support, captured_stdout
 
 
 class BuildTestCase(support.TempdirManager,
+                    support.LoggingCatcher,
                     unittest.TestCase):
 
     def _mock_get_platform(self):
@@ -74,4 +74,4 @@
     return unittest.makeSuite(BuildTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_bdist_dumb.py b/distutils2/tests/test_command_bdist_dumb.py
--- a/distutils2/tests/test_command_bdist_dumb.py
+++ b/distutils2/tests/test_command_bdist_dumb.py
@@ -1,54 +1,34 @@
 """Tests for distutils.command.bdist_dumb."""
 
-import sys
 import os
-
-# zlib is not used here, but if it's not available
-# test_simple_built will fail
-try:
-    import zlib
-except ImportError:
-    zlib = None
-
-from distutils2.tests import run_unittest, unittest
+import distutils2.util
 
 from distutils2.dist import Distribution
 from distutils2.command.bdist_dumb import bdist_dumb
-from distutils2.tests import support
+from distutils2.tests import unittest, support
+from distutils2.tests.support import requires_zlib
 
-SETUP_PY = """\
-from distutils.run import setup
-import foo
-
-setup(name='foo', version='0.1', py_modules=['foo'],
-      url='xxx', author='xxx', author_email='xxx')
-
-"""
 
 class BuildDumbTestCase(support.TempdirManager,
                         support.LoggingCatcher,
-                        support.EnvironGuard,
                         unittest.TestCase):
 
     def setUp(self):
         super(BuildDumbTestCase, self).setUp()
         self.old_location = os.getcwd()
-        self.old_sys_argv = sys.argv, sys.argv[:]
 
     def tearDown(self):
         os.chdir(self.old_location)
-        sys.argv = self.old_sys_argv[0]
-        sys.argv[:] = self.old_sys_argv[1]
+        distutils2.util._path_created.clear()
         super(BuildDumbTestCase, self).tearDown()
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
     def test_simple_built(self):
 
         # let's create a simple package
         tmp_dir = self.mkdtemp()
         pkg_dir = os.path.join(tmp_dir, 'foo')
         os.mkdir(pkg_dir)
-        self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
         self.write_file((pkg_dir, 'foo.py'), '#')
         self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
         self.write_file((pkg_dir, 'README'), '')
@@ -57,10 +37,7 @@
                              'py_modules': ['foo'],
                              'url': 'xxx', 'author': 'xxx',
                              'author_email': 'xxx'})
-        dist.script_name = 'setup.py'
         os.chdir(pkg_dir)
-
-        sys.argv = ['setup.py']
         cmd = bdist_dumb(dist)
 
         # so the output is the same no matter
@@ -97,8 +74,9 @@
         default = cmd.default_format[os.name]
         self.assertEqual(cmd.format, default)
 
+
 def test_suite():
     return unittest.makeSuite(BuildDumbTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_bdist_msi.py b/distutils2/tests/test_command_bdist_msi.py
--- a/distutils2/tests/test_command_bdist_msi.py
+++ b/distutils2/tests/test_command_bdist_msi.py
@@ -1,9 +1,8 @@
 """Tests for distutils.command.bdist_msi."""
 import sys
 
-from distutils2.tests import run_unittest
+from distutils2.tests import unittest, support
 
-from distutils2.tests import unittest, support
 
 class BDistMSITestCase(support.TempdirManager,
                        support.LoggingCatcher,
@@ -17,8 +16,10 @@
         cmd = bdist_msi(dist)
         cmd.ensure_finalized()
 
+
 def test_suite():
     return unittest.makeSuite(BDistMSITestCase)
 
+
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_bdist_wininst.py b/distutils2/tests/test_command_bdist_wininst.py
--- a/distutils2/tests/test_command_bdist_wininst.py
+++ b/distutils2/tests/test_command_bdist_wininst.py
@@ -1,7 +1,8 @@
 """Tests for distutils.command.bdist_wininst."""
 
-from distutils2.tests import unittest, support, run_unittest
 from distutils2.command.bdist_wininst import bdist_wininst
+from distutils2.tests import unittest, support
+
 
 class BuildWinInstTestCase(support.TempdirManager,
                            support.LoggingCatcher,
@@ -20,10 +21,12 @@
         # and make sure it finds it and returns its content
         # no matter what platform we have
         exe_file = cmd.get_exe_bytes()
-        self.assertTrue(len(exe_file) > 10)
+        self.assertGreater(len(exe_file), 10)
+
 
 def test_suite():
     return unittest.makeSuite(BuildWinInstTestCase)
 
+
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_build.py b/distutils2/tests/test_command_build.py
--- a/distutils2/tests/test_command_build.py
+++ b/distutils2/tests/test_command_build.py
@@ -3,8 +3,9 @@
 import sys
 
 from distutils2.command.build import build
+from sysconfig import get_platform
 from distutils2.tests import unittest, support
-from distutils2._backport.sysconfig import get_platform
+
 
 class BuildTestCase(support.TempdirManager,
                     support.LoggingCatcher,
@@ -40,12 +41,13 @@
         self.assertEqual(cmd.build_temp, wanted)
 
         # build_scripts is build/scripts-x.x
-        wanted = os.path.join(cmd.build_base, 'scripts-' +  sys.version[0:3])
+        wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3])
         self.assertEqual(cmd.build_scripts, wanted)
 
         # executable is os.path.normpath(sys.executable)
         self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
 
+
 def test_suite():
     return unittest.makeSuite(BuildTestCase)
 
diff --git a/distutils2/tests/test_command_build_clib.py b/distutils2/tests/test_command_build_clib.py
--- a/distutils2/tests/test_command_build_clib.py
+++ b/distutils2/tests/test_command_build_clib.py
@@ -2,10 +2,11 @@
 import os
 import sys
 
+from distutils2.util import find_executable
 from distutils2.command.build_clib import build_clib
-from distutils2.errors import DistutilsSetupError
+from distutils2.errors import PackagingSetupError
 from distutils2.tests import unittest, support
-from distutils2.util import find_executable
+
 
 class BuildCLibTestCase(support.TempdirManager,
                         support.LoggingCatcher,
@@ -16,24 +17,24 @@
         cmd = build_clib(dist)
 
         # 'libraries' option must be a list
-        self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo')
+        self.assertRaises(PackagingSetupError, cmd.check_library_list, 'foo')
 
         # each element of 'libraries' must a 2-tuple
-        self.assertRaises(DistutilsSetupError, cmd.check_library_list,
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
                           ['foo1', 'foo2'])
 
         # first element of each tuple in 'libraries'
         # must be a string (the library name)
-        self.assertRaises(DistutilsSetupError, cmd.check_library_list,
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
                           [(1, 'foo1'), ('name', 'foo2')])
 
         # library name may not contain directory separators
-        self.assertRaises(DistutilsSetupError, cmd.check_library_list,
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
                           [('name', 'foo1'),
                            ('another/name', 'foo2')])
 
         # second element of each tuple must be a dictionary (build info)
-        self.assertRaises(DistutilsSetupError, cmd.check_library_list,
+        self.assertRaises(PackagingSetupError, cmd.check_library_list,
                           [('name', {}),
                            ('another', 'foo2')])
 
@@ -48,10 +49,10 @@
         # "in 'libraries' option 'sources' must be present and must be
         # a list of source filenames
         cmd.libraries = [('name', {})]
-        self.assertRaises(DistutilsSetupError, cmd.get_source_files)
+        self.assertRaises(PackagingSetupError, cmd.get_source_files)
 
         cmd.libraries = [('name', {'sources': 1})]
-        self.assertRaises(DistutilsSetupError, cmd.get_source_files)
+        self.assertRaises(PackagingSetupError, cmd.get_source_files)
 
         cmd.libraries = [('name', {'sources': ['a', 'b']})]
         self.assertEqual(cmd.get_source_files(), ['a', 'b'])
@@ -64,24 +65,24 @@
         self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
 
     def test_build_libraries(self):
-
         pkg_dir, dist = self.create_dist()
         cmd = build_clib(dist)
-        class FakeCompiler:
+
+        class FakeCompiler(object):
             def compile(*args, **kw):
                 pass
             create_static_lib = compile
 
         cmd.compiler = FakeCompiler()
 
-        # build_libraries is also doing a bit of typoe checking
+        # build_libraries is also doing a bit of type checking
         lib = [('name', {'sources': 'notvalid'})]
-        self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib)
+        self.assertRaises(PackagingSetupError, cmd.build_libraries, lib)
 
-        lib = [('name', {'sources': list()})]
+        lib = [('name', {'sources': []})]
         cmd.build_libraries(lib)
 
-        lib = [('name', {'sources': tuple()})]
+        lib = [('name', {'sources': ()})]
         cmd.build_libraries(lib)
 
     def test_finalize_options(self):
@@ -97,13 +98,10 @@
         self.assertEqual(cmd.include_dirs, [])
 
         cmd.distribution.libraries = 'WONTWORK'
-        self.assertRaises(DistutilsSetupError, cmd.finalize_options)
+        self.assertRaises(PackagingSetupError, cmd.finalize_options)
 
+    @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
     def test_run(self):
-        # can't test on windows
-        if sys.platform == 'win32':
-            return
-
         pkg_dir, dist = self.create_dist()
         cmd = build_clib(dist)
 
@@ -127,13 +125,14 @@
             if ccmd is None:
                 continue
             if find_executable(ccmd[0]) is None:
-                return # can't test
+                raise unittest.SkipTest("can't test")
 
         # this should work
         cmd.run()
 
         # let's check the result
-        self.assertTrue('libfoo.a' in os.listdir(build_temp))
+        self.assertIn('libfoo.a', os.listdir(build_temp))
+
 
 def test_suite():
     return unittest.makeSuite(BuildCLibTestCase)
diff --git a/distutils2/tests/test_command_build_ext.py b/distutils2/tests/test_command_build_ext.py
--- a/distutils2/tests/test_command_build_ext.py
+++ b/distutils2/tests/test_command_build_ext.py
@@ -1,26 +1,29 @@
+import os
 import sys
-import os
+import site
 import shutil
+import sysconfig
+import textwrap
 from StringIO import StringIO
+from distutils2.dist import Distribution
+from distutils2.errors import (UnknownFileError, CompileError,
+                              PackagingPlatformError)
+from distutils2.command.build_ext import build_ext
+from distutils2.compiler.extension import Extension
+from .support import assert_python_ok
 
-import distutils2.tests
-from distutils2.tests import unittest
-from distutils2.compiler.extension import Extension
-from distutils2.dist import Distribution
-from distutils2.command.build_ext import build_ext
-from distutils2.tests import support
-from distutils2.errors import (UnknownFileError, DistutilsSetupError,
-                               CompileError)
-from distutils2._backport import sysconfig
+from distutils2.tests import support, unittest, verbose, unload
 
 
-# http://bugs.python.org/issue4373
-# Don't load the xx module more than once.
-ALREADY_TESTED = False
-CURDIR = os.path.abspath(os.path.dirname(__file__))
-
 def _get_source_filename():
-    return os.path.join(CURDIR, 'xxmodule.c')
+    # use installed copy if available
+    tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c')
+    if os.path.exists(tests_f):
+        return tests_f
+    # otherwise try using copy from build directory
+    srcdir = sysconfig.get_config_var('srcdir')
+    return os.path.join(srcdir, 'Modules', 'xxmodule.c')
+
 
 class BuildExtTestCase(support.TempdirManager,
                        support.LoggingCatcher,
@@ -30,25 +33,51 @@
         # Note that we're making changes to sys.path
         super(BuildExtTestCase, self).setUp()
         self.tmp_dir = self.mkdtemp()
-        self.sys_path = sys.path, sys.path[:]
-        sys.path.append(self.tmp_dir)
-        shutil.copy(_get_source_filename(), self.tmp_dir)
+        filename = _get_source_filename()
+        if os.path.exists(filename):
+            shutil.copy(filename, self.tmp_dir)
+        self.old_user_base = site.USER_BASE
+        site.USER_BASE = self.mkdtemp()
+        build_ext.USER_BASE = site.USER_BASE
+
+    def tearDown(self):
+        # Get everything back to normal
         if sys.version > "2.6":
-            import site
-            self.old_user_base = site.USER_BASE
-            site.USER_BASE = self.mkdtemp()
-            from distutils2.command import build_ext
-            build_ext.USER_BASE = site.USER_BASE
+            site.USER_BASE = self.old_user_base
+            build_ext.USER_BASE = self.old_user_base
 
-    # XXX only works with 2.6 > -- dunno why yet
-    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+        super(BuildExtTestCase, self).tearDown()
+
+    def _fixup_command(self, cmd):
+        # When Python was build with --enable-shared, -L. is not good enough
+        # to find the libpython<blah>.so.  This is because regrtest runs it
+        # under a tempdir, not in the top level where the .so lives.  By the
+        # time we've gotten here, Python's already been chdir'd to the
+        # tempdir.
+        #
+        # To further add to the fun, we can't just add library_dirs to the
+        # Extension() instance because that doesn't get plumbed through to the
+        # final compiler command.
+        if (sysconfig.get_config_var('Py_ENABLE_SHARED') and
+            not sys.platform.startswith('win')):
+            runshared = sysconfig.get_config_var('RUNSHARED')
+            if runshared is None:
+                cmd.library_dirs = ['.']
+            else:
+                name, equals, value = runshared.partition('=')
+                cmd.library_dirs = value.split(os.pathsep)
+
     def test_build_ext(self):
-        global ALREADY_TESTED
         xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
+        if not os.path.exists(xx_c):
+            # skipping if we cannot find it
+            return
         xx_ext = Extension('xx', [xx_c])
         dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
         dist.package_dir = self.tmp_dir
         cmd = build_ext(dist)
+        self._fixup_command(cmd)
+
         if os.name == "nt":
             # On Windows, we must build a debug version iff running
             # a debug build of Python
@@ -57,7 +86,7 @@
         cmd.build_temp = self.tmp_dir
 
         old_stdout = sys.stdout
-        if not distutils2.tests.verbose:
+        if not verbose:
             # silence compiler output
             sys.stdout = StringIO()
         try:
@@ -66,44 +95,32 @@
         finally:
             sys.stdout = old_stdout
 
-        if ALREADY_TESTED:
-            return
-        else:
-            ALREADY_TESTED = True
+        code = """if 1:
+            import sys
+            sys.path.insert(0, %r)
 
-        import xx
+            import xx
 
-        for attr in ('error', 'foo', 'new', 'roj'):
-            self.assertTrue(hasattr(xx, attr))
+            for attr in ('error', 'foo', 'new', 'roj'):
+                assert hasattr(xx, attr)
 
-        self.assertEqual(xx.foo(2, 5), 7)
-        self.assertEqual(xx.foo(13,15), 28)
-        self.assertEqual(xx.new().demo(), None)
-        doc = 'This is a template module just for instruction.'
-        self.assertEqual(xx.__doc__, doc)
-        self.assertTrue(isinstance(xx.Null(), xx.Null))
-        self.assertTrue(isinstance(xx.Str(), xx.Str))
-
-    def tearDown(self):
-        # Get everything back to normal
-        distutils2.tests.unload('xx')
-        sys.path = self.sys_path[0]
-        sys.path[:] = self.sys_path[1]
-        if sys.version > "2.6":
-            import site
-            site.USER_BASE = self.old_user_base
-            from distutils2.command import build_ext
-            build_ext.USER_BASE = self.old_user_base
-
-        super(BuildExtTestCase, self).tearDown()
+            assert xx.foo(2, 5) == 7
+            assert xx.foo(13, 15) == 28
+            assert xx.new().demo() is None
+            doc = 'This is a template module just for instruction.'
+            assert xx.__doc__ == doc
+            assert isinstance(xx.Null(), xx.Null)
+            assert isinstance(xx.Str(), xx.Str)"""
+        code = code % self.tmp_dir
+        assert_python_ok('-c', code)
 
     def test_solaris_enable_shared(self):
         dist = Distribution({'name': 'xx'})
         cmd = build_ext(dist)
         old = sys.platform
 
-        sys.platform = 'sunos' # fooling finalize_options
-        from distutils2._backport.sysconfig import _CONFIG_VARS
+        sys.platform = 'sunos'  # fooling finalize_options
+        from sysconfig import _CONFIG_VARS
 
         old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED')
         _CONFIG_VARS['Py_ENABLE_SHARED'] = 1
@@ -117,21 +134,20 @@
                 _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var
 
         # make sure we get some library dirs under solaris
-        self.assertTrue(len(cmd.library_dirs) > 0)
+        self.assertGreater(len(cmd.library_dirs), 0)
 
     @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
     def test_user_site(self):
-        import site
         dist = Distribution({'name': 'xx'})
         cmd = build_ext(dist)
 
         # making sure the user option is there
-        options = [name for name, short, lable in
+        options = [name for name, short, label in
                    cmd.user_options]
-        self.assertTrue('user' in options)
+        self.assertIn('user', options)
 
         # setting a value
-        cmd.user = 1
+        cmd.user = True
 
         # setting user based lib and include
         lib = os.path.join(site.USER_BASE, 'lib')
@@ -144,9 +160,9 @@
 
         # see if include_dirs and library_dirs
         # were set
-        self.assertTrue(lib in cmd.library_dirs)
-        self.assertTrue(lib in cmd.rpath)
-        self.assertTrue(incl in cmd.include_dirs)
+        self.assertIn(lib, cmd.library_dirs)
+        self.assertIn(lib, cmd.rpath)
+        self.assertIn(incl, cmd.include_dirs)
 
     def test_optional_extension(self):
 
@@ -174,10 +190,10 @@
         cmd.finalize_options()
 
         py_include = sysconfig.get_path('include')
-        self.assertTrue(py_include in cmd.include_dirs)
+        self.assertIn(py_include, cmd.include_dirs)
 
         plat_py_include = sysconfig.get_path('platinclude')
-        self.assertTrue(plat_py_include in cmd.include_dirs)
+        self.assertIn(plat_py_include, cmd.include_dirs)
 
         # make sure cmd.libraries is turned into a list
         # if it's a string
@@ -191,7 +207,7 @@
         cmd = build_ext(dist)
         cmd.library_dirs = 'my_lib_dir'
         cmd.finalize_options()
-        self.assertTrue('my_lib_dir' in cmd.library_dirs)
+        self.assertIn('my_lib_dir', cmd.library_dirs)
 
         # make sure rpath is turned into a list
         # if it's a list of os.pathsep's paths
@@ -248,11 +264,12 @@
     def test_get_outputs(self):
         tmp_dir = self.mkdtemp()
         c_file = os.path.join(tmp_dir, 'foo.c')
-        self.write_file(c_file, 'void initfoo(void) {};\n')
+        self.write_file(c_file, 'void PyInit_foo(void) {}\n')
         ext = Extension('foo', [c_file], optional=False)
         dist = Distribution({'name': 'xx',
                              'ext_modules': [ext]})
         cmd = build_ext(dist)
+        self._fixup_command(cmd)
         cmd.ensure_finalized()
         self.assertEqual(len(cmd.get_outputs()), 1)
 
@@ -268,7 +285,7 @@
         old_wd = os.getcwd()
         os.chdir(other_tmp_dir)
         try:
-            cmd.inplace = 1
+            cmd.inplace = True
             cmd.run()
             so_file = cmd.get_outputs()[0]
         finally:
@@ -279,7 +296,7 @@
         so_dir = os.path.dirname(so_file)
         self.assertEqual(so_dir, other_tmp_dir)
 
-        cmd.inplace = 0
+        cmd.inplace = False
         cmd.run()
         so_file = cmd.get_outputs()[0]
         self.assertTrue(os.path.exists(so_file))
@@ -287,7 +304,7 @@
         so_dir = os.path.dirname(so_file)
         self.assertEqual(so_dir, cmd.build_lib)
 
-        # inplace = 0, cmd.package = 'bar'
+        # inplace = False, cmd.package = 'bar'
         build_py = cmd.get_finalized_command('build_py')
         build_py.package_dir = 'bar'
         path = cmd.get_ext_fullpath('foo')
@@ -295,8 +312,8 @@
         path = os.path.split(path)[0]
         self.assertEqual(path, cmd.build_lib)
 
-        # inplace = 1, cmd.package = 'bar'
-        cmd.inplace = 1
+        # inplace = True, cmd.package = 'bar'
+        cmd.inplace = True
         other_tmp_dir = os.path.realpath(self.mkdtemp())
         old_wd = os.getcwd()
         os.chdir(other_tmp_dir)
@@ -317,7 +334,7 @@
         #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
         dist = Distribution()
         cmd = build_ext(dist)
-        cmd.inplace = 1
+        cmd.inplace = True
         cmd.distribution.package_dir = 'src'
         cmd.distribution.packages = ['lxml', 'lxml.html']
         curdir = os.getcwd()
@@ -326,7 +343,7 @@
         self.assertEqual(wanted, path)
 
         # building lxml.etree not inplace
-        cmd.inplace = 0
+        cmd.inplace = False
         cmd.build_lib = os.path.join(curdir, 'tmpdir')
         wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
         path = cmd.get_ext_fullpath('lxml.etree')
@@ -342,19 +359,110 @@
         self.assertEqual(wanted, path)
 
         # building twisted.runner.portmap inplace
-        cmd.inplace = 1
+        cmd.inplace = True
         path = cmd.get_ext_fullpath('twisted.runner.portmap')
         wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
         self.assertEqual(wanted, path)
 
+    @unittest.skipUnless(sys.platform == 'darwin',
+                         'test only relevant for Mac OS X')
+    def test_deployment_target_default(self):
+        # Issue 9516: Test that, in the absence of the environment variable,
+        # an extension module is compiled with the same deployment target as
+        #  the interpreter.
+        self._try_compile_deployment_target('==', None)
+
+    @unittest.skipUnless(sys.platform == 'darwin',
+                         'test only relevant for Mac OS X')
+    def test_deployment_target_too_low(self):
+        # Issue 9516: Test that an extension module is not allowed to be
+        # compiled with a deployment target less than that of the interpreter.
+        self.assertRaises(PackagingPlatformError,
+            self._try_compile_deployment_target, '>', '10.1')
+
+    @unittest.skipUnless(sys.platform == 'darwin',
+                         'test only relevant for Mac OS X')
+    def test_deployment_target_higher_ok(self):
+        # Issue 9516: Test that an extension module can be compiled with a
+        # deployment target higher than that of the interpreter: the ext
+        # module may depend on some newer OS feature.
+        deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+        if deptarget:
+            # increment the minor version number (i.e. 10.6 -> 10.7)
+            deptarget = [int(x) for x in deptarget.split('.')]
+            deptarget[-1] += 1
+            deptarget = '.'.join(str(i) for i in deptarget)
+            self._try_compile_deployment_target('<', deptarget)
+
+    def _try_compile_deployment_target(self, operator, target):
+        orig_environ = os.environ
+        os.environ = orig_environ.copy()
+        self.addCleanup(setattr, os, 'environ', orig_environ)
+
+        if target is None:
+            if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
+                del os.environ['MACOSX_DEPLOYMENT_TARGET']
+        else:
+            os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
+
+        deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c')
+
+        with open(deptarget_c, 'w') as fp:
+            fp.write(textwrap.dedent('''\
+                #include <AvailabilityMacros.h>
+
+                int dummy;
+
+                #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED
+                #else
+                #error "Unexpected target"
+                #endif
+
+            ''' % operator))
+
+        # get the deployment target that the interpreter was built with
+        target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
+        target = tuple(map(int, target.split('.')))
+        target = '%02d%01d0' % target
+
+        deptarget_ext = Extension(
+            'deptarget',
+            [deptarget_c],
+            extra_compile_args=['-DTARGET=%s' % (target,)],
+        )
+        dist = Distribution({
+            'name': 'deptarget',
+            'ext_modules': [deptarget_ext],
+        })
+        dist.package_dir = self.tmp_dir
+        cmd = build_ext(dist)
+        cmd.build_lib = self.tmp_dir
+        cmd.build_temp = self.tmp_dir
+
+        try:
+            old_stdout = sys.stdout
+            if not verbose:
+                # silence compiler output
+                sys.stdout = StringIO()
+            try:
+                cmd.ensure_finalized()
+                cmd.run()
+            finally:
+                sys.stdout = old_stdout
+
+        except CompileError:
+            self.fail("Wrong deployment target during compilation")
+
+
 def test_suite():
     src = _get_source_filename()
     if not os.path.exists(src):
-        if distutils2.tests.verbose:
-            print ('test_build_ext: Cannot find source code (test'
-                   ' must run in python build dir)')
+        if verbose:
+            print('test_command_build_ext: Cannot find source code (test'
+                  ' must run in python build dir)')
         return unittest.TestSuite()
-    else: return unittest.makeSuite(BuildExtTestCase)
+    else:
+        return unittest.makeSuite(BuildExtTestCase)
 
 if __name__ == '__main__':
-    distutils2.tests.run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_build_py.py b/distutils2/tests/test_command_build_py.py
--- a/distutils2/tests/test_command_build_py.py
+++ b/distutils2/tests/test_command_build_py.py
@@ -2,11 +2,10 @@
 
 import os
 import sys
-import StringIO
 
 from distutils2.command.build_py import build_py
 from distutils2.dist import Distribution
-from distutils2.errors import DistutilsFileError
+from distutils2.errors import PackagingFileError
 
 from distutils2.tests import unittest, support
 
@@ -34,11 +33,9 @@
 
         dist = Distribution({"packages": ["pkg"],
                              "package_dir": sources})
-        # script_name need not exist, it just need to be initialized
 
-        dist.script_name = os.path.join(sources, "setup.py")
         dist.command_obj["build"] = support.DummyCommand(
-            force=0,
+            force=False,
             build_lib=destination,
             use_2to3_fixers=None,
             convert_2to3_doctests=None,
@@ -48,7 +45,7 @@
         dist.package_dir = sources
 
         cmd = build_py(dist)
-        cmd.compile = 1
+        cmd.compile = True
         cmd.ensure_finalized()
         self.assertEqual(cmd.package_data, dist.package_data)
 
@@ -61,11 +58,15 @@
         self.assertEqual(len(cmd.get_outputs()), 3)
         pkgdest = os.path.join(destination, "pkg")
         files = os.listdir(pkgdest)
-        self.assertTrue("__init__.py" in files)
-        self.assertTrue("__init__.pyc" in files)
-        self.assertTrue("README.txt" in files)
+        self.assertIn("__init__.py", files)
+        self.assertIn("README.txt", files)
+        # XXX even with -O, distutils writes pyc, not pyo; bug?
+        if sys.dont_write_bytecode:
+            self.assertNotIn("__init__.pyc", files)
+        else:
+            self.assertIn("__init__.pyc", files)
 
-    def test_empty_package_dir (self):
+    def test_empty_package_dir(self):
         # See SF 1668596/1720897.
         cwd = os.getcwd()
 
@@ -73,27 +74,24 @@
         sources = self.mkdtemp()
         pkg = os.path.join(sources, 'pkg')
         os.mkdir(pkg)
-        open(os.path.join(pkg, "__init__.py"), "w").close()
+        open(os.path.join(pkg, "__init__.py"), "wb").close()
         testdir = os.path.join(pkg, "doc")
         os.mkdir(testdir)
-        open(os.path.join(testdir, "testfile"), "w").close()
+        open(os.path.join(testdir, "testfile"), "wb").close()
 
         os.chdir(sources)
         old_stdout = sys.stdout
-        #sys.stdout = StringIO.StringIO()
 
         try:
             dist = Distribution({"packages": ["pkg"],
                                  "package_dir": sources,
                                  "package_data": {"pkg": ["doc/*"]}})
-            # script_name need not exist, it just need to be initialized
-            dist.script_name = os.path.join(sources, "setup.py")
             dist.script_args = ["build"]
             dist.parse_command_line()
 
             try:
                 dist.run_commands()
-            except DistutilsFileError, e:
+            except PackagingFileError:
                 self.fail("failed package_data test when package_dir is ''")
         finally:
             # Restore state.
@@ -106,7 +104,7 @@
         # makes sure byte_compile is not used
         pkg_dir, dist = self.create_dist()
         cmd = build_py(dist)
-        cmd.compile = 1
+        cmd.compile = True
         cmd.optimize = 1
 
         old_dont_write_bytecode = sys.dont_write_bytecode
@@ -116,7 +114,7 @@
         finally:
             sys.dont_write_bytecode = old_dont_write_bytecode
 
-        self.assertIn('byte-compiling is disabled', self.logs[0][2][1])
+        self.assertIn('byte-compiling is disabled', self.get_logs()[0])
 
 def test_suite():
     return unittest.makeSuite(BuildPyTestCase)
diff --git a/distutils2/tests/test_command_build_scripts.py b/distutils2/tests/test_command_build_scripts.py
--- a/distutils2/tests/test_command_build_scripts.py
+++ b/distutils2/tests/test_command_build_scripts.py
@@ -1,11 +1,10 @@
 """Tests for distutils.command.build_scripts."""
 
 import os
-
+import sys
+import sysconfig
+from distutils2.dist import Distribution
 from distutils2.command.build_scripts import build_scripts
-from distutils2.dist import Distribution
-from distutils2._backport import sysconfig
-
 from distutils2.tests import unittest, support
 
 
@@ -15,8 +14,8 @@
 
     def test_default_settings(self):
         cmd = self.get_build_scripts_cmd("/foo/bar", [])
-        self.assertTrue(not cmd.force)
-        self.assertTrue(cmd.build_dir is None)
+        self.assertFalse(cmd.force)
+        self.assertIs(cmd.build_dir, None)
 
         cmd.finalize_options()
 
@@ -36,15 +35,14 @@
 
         built = os.listdir(target)
         for name in expected:
-            self.assertTrue(name in built)
+            self.assertIn(name, built)
 
     def get_build_scripts_cmd(self, target, scripts):
-        import sys
         dist = Distribution()
         dist.scripts = scripts
         dist.command_obj["build"] = support.DummyCommand(
             build_scripts=target,
-            force=1,
+            force=True,
             executable=sys.executable,
             use_2to3=False,
             use_2to3_fixers=None,
@@ -72,11 +70,8 @@
         return expected
 
     def write_script(self, dir, name, text):
-        f = open(os.path.join(dir, name), "w")
-        try:
+        with open(os.path.join(dir, name), "w") as f:
             f.write(text)
-        finally:
-            f.close()
 
     def test_version_int(self):
         source = self.mkdtemp()
@@ -104,7 +99,7 @@
 
         built = os.listdir(target)
         for name in expected:
-            self.assertTrue(name in built)
+            self.assertIn(name, built)
 
 def test_suite():
     return unittest.makeSuite(BuildScriptsTestCase)
diff --git a/distutils2/tests/test_command_check.py b/distutils2/tests/test_command_check.py
--- a/distutils2/tests/test_command_check.py
+++ b/distutils2/tests/test_command_check.py
@@ -1,10 +1,10 @@
 """Tests for distutils.command.check."""
 
+import logging
 from distutils2.command.check import check
 from distutils2.metadata import _HAS_DOCUTILS
+from distutils2.errors import PackagingSetupError, MetadataMissingError
 from distutils2.tests import unittest, support
-from distutils2.errors import DistutilsSetupError
-from distutils2.errors import MetadataMissingError
 
 
 class CheckTestCase(support.LoggingCatcher,
@@ -13,7 +13,7 @@
 
     def _run(self, metadata=None, **options):
         if metadata is None:
-            metadata = {'name': 'xxx', 'version': 'xxx'}
+            metadata = {'name': 'xxx', 'version': '1.2'}
         pkg_info, dist = self.create_dist(**metadata)
         cmd = check(dist)
         cmd.initialize_options()
@@ -28,66 +28,70 @@
         # by default, check is checking the metadata
         # should have some warnings
         cmd = self._run()
-        self.assertTrue(len(cmd._warnings) > 0)
+        # trick: using assertNotEqual with an empty list will give us a more
+        # useful error message than assertGreater(.., 0) when the code change
+        # and the test fails
+        self.assertNotEqual([], self.get_logs(logging.WARNING))
 
         # now let's add the required fields
         # and run it again, to make sure we don't get
         # any warning anymore
         metadata = {'home_page': 'xxx', 'author': 'xxx',
                     'author_email': 'xxx',
-                    'name': 'xxx', 'version': 'xxx',
+                    'name': 'xxx', 'version': '4.2',
                     }
         cmd = self._run(metadata)
-        self.assertEqual(len(cmd._warnings), 0)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
         # now with the strict mode, we should
         # get an error if there are missing metadata
         self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
-        self.assertRaises(DistutilsSetupError, self._run,
+        self.assertRaises(PackagingSetupError, self._run,
             {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
 
+        # clear warnings from the previous calls
+        self.loghandler.flush()
+
         # and of course, no error when all metadata fields are present
-        cmd = self._run(metadata, strict=1)
-        self.assertEqual(len(cmd._warnings), 0)
+        cmd = self._run(metadata, strict=True)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
     def test_check_metadata_1_2(self):
         # let's run the command with no metadata at all
         # by default, check is checking the metadata
         # should have some warnings
         cmd = self._run()
-        self.assertTrue(len(cmd._warnings) > 0)
+        self.assertNotEqual([], self.get_logs(logging.WARNING))
 
-        # now let's add the required fields
-        # and run it again, to make sure we don't get
-        # any warning anymore
-        # let's use requires_python as a marker to enforce
-        # Metadata-Version 1.2
+        # now let's add the required fields and run it again, to make sure we
+        # don't get any warning anymore let's use requires_python as a marker
+        # to enforce Metadata-Version 1.2
         metadata = {'home_page': 'xxx', 'author': 'xxx',
                     'author_email': 'xxx',
-                    'name': 'xxx', 'version': 'xxx',
+                    'name': 'xxx', 'version': '4.2',
                     'requires_python': '2.4',
                     }
         cmd = self._run(metadata)
-        self.assertEqual(len(cmd._warnings), 1)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
         # now with the strict mode, we should
         # get an error if there are missing metadata
         self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
-        self.assertRaises(DistutilsSetupError, self._run,
+        self.assertRaises(PackagingSetupError, self._run,
             {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
 
         # complain about version format
-        self.assertRaises(DistutilsSetupError, self._run, metadata,
+        metadata['version'] = 'xxx'
+        self.assertRaises(PackagingSetupError, self._run, metadata,
             **{'strict': 1})
 
-        # now with correct version format
-        metadata = {'home_page': 'xxx', 'author': 'xxx',
-                    'author_email': 'xxx',
-                    'name': 'xxx', 'version': '1.2',
-                    'requires_python': '2.4',
-                    }
-        cmd = self._run(metadata, strict=1)
-        self.assertEqual(len(cmd._warnings), 0)
+        # clear warnings from the previous calls
+        self.loghandler.flush()
+
+        # now with correct version format again
+        metadata['version'] = '4.2'
+        cmd = self._run(metadata, strict=True)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
     @unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
     def test_check_restructuredtext(self):
@@ -96,16 +100,15 @@
         pkg_info, dist = self.create_dist(description=broken_rest)
         cmd = check(dist)
         cmd.check_restructuredtext()
-        self.assertEqual(len(cmd._warnings), 1)
+        self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
 
         pkg_info, dist = self.create_dist(description='title\n=====\n\ntest')
         cmd = check(dist)
         cmd.check_restructuredtext()
-        self.assertEqual(len(cmd._warnings), 0)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
     def test_check_all(self):
-
-        self.assertRaises(DistutilsSetupError, self._run,
+        self.assertRaises(PackagingSetupError, self._run,
                           {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1,
                                  'all': 1})
         self.assertRaises(MetadataMissingError, self._run,
@@ -119,7 +122,18 @@
         }
         cmd = check(dist)
         cmd.check_hooks_resolvable()
-        self.assertEqual(len(cmd._warnings), 1)
+        self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+
+    def test_warn(self):
+        _, dist = self.create_dist()
+        cmd = check(dist)
+        self.assertEqual([], self.get_logs())
+        cmd.warn('hello')
+        self.assertEqual(['check: hello'], self.get_logs())
+        cmd.warn('hello %s', 'world')
+        self.assertEqual(['check: hello world'], self.get_logs())
+        cmd.warn('hello %s %s', 'beautiful', 'world')
+        self.assertEqual(['check: hello beautiful world'], self.get_logs())
 
 
 def test_suite():
diff --git a/distutils2/tests/test_command_clean.py b/distutils2/tests/test_command_clean.py
--- a/distutils2/tests/test_command_clean.py
+++ b/distutils2/tests/test_command_clean.py
@@ -4,8 +4,8 @@
 from distutils2.command.clean import clean
 from distutils2.tests import unittest, support
 
-class cleanTestCase(support.TempdirManager,
-                    support.LoggingCatcher,
+
+class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
                     unittest.TestCase):
 
     def test_simple_run(self):
@@ -26,20 +26,21 @@
                 self.write_file(os.path.join(path, f))
 
         # let's run the command
-        cmd.all = 1
+        cmd.all = True
         cmd.ensure_finalized()
         cmd.run()
 
         # make sure the files where removed
         for name, path in dirs:
-            self.assertTrue(not os.path.exists(path),
-                         '%s was not removed' % path)
+            self.assertFalse(os.path.exists(path),
+                             '%r was not removed' % path)
 
-        # let's run the command again (should spit warnings but suceed)
-        cmd.all = 1
+        # let's run the command again (should spit warnings but succeed)
+        cmd.all = True
         cmd.ensure_finalized()
         cmd.run()
 
+
 def test_suite():
     return unittest.makeSuite(cleanTestCase)
 
diff --git a/distutils2/tests/test_command_cmd.py b/distutils2/tests/test_command_cmd.py
--- a/distutils2/tests/test_command_cmd.py
+++ b/distutils2/tests/test_command_cmd.py
@@ -1,24 +1,26 @@
 """Tests for distutils.cmd."""
 import os
-from distutils2.tests import run_unittest
 
 from distutils2.command.cmd import Command
 from distutils2.dist import Distribution
-from distutils2.errors import DistutilsOptionError
-from distutils2.tests import unittest
+from distutils2.errors import PackagingOptionError
+from distutils2.tests import support, unittest
+
 
 class MyCmd(Command):
     def initialize_options(self):
         pass
 
-class CommandTestCase(unittest.TestCase):
+
+class CommandTestCase(support.LoggingCatcher,
+                      unittest.TestCase):
 
     def setUp(self):
+        super(CommandTestCase, self).setUp()
         dist = Distribution()
         self.cmd = MyCmd(dist)
 
     def test_make_file(self):
-
         cmd = self.cmd
 
         # making sure it raises when infiles is not a string or a list/tuple
@@ -33,12 +35,7 @@
         cmd.make_file(infiles='in', outfile='out', func='func', args=())
 
     def test_dump_options(self):
-
-        msgs = []
-        def _announce(msg, level):
-            msgs.append(msg)
         cmd = self.cmd
-        cmd.announce = _announce
         cmd.option1 = 1
         cmd.option2 = 1
         cmd.user_options = [('option1', '', ''), ('option2', '', '')]
@@ -46,6 +43,7 @@
 
         wanted = ["command options for 'MyCmd':", '  option1 = 1',
                   '  option2 = 1']
+        msgs = self.get_logs()
         self.assertEqual(msgs, wanted)
 
     def test_ensure_string(self):
@@ -58,7 +56,7 @@
         self.assertTrue(hasattr(cmd, 'option2'))
 
         cmd.option3 = 1
-        self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3')
+        self.assertRaises(PackagingOptionError, cmd.ensure_string, 'option3')
 
     def test_ensure_string_list(self):
         cmd = self.cmd
@@ -75,10 +73,10 @@
 
         cmd.not_string_list = ['one', 2, 'three']
         cmd.not_string_list2 = object()
-        self.assertRaises(DistutilsOptionError,
+        self.assertRaises(PackagingOptionError,
                           cmd.ensure_string_list, 'not_string_list')
 
-        self.assertRaises(DistutilsOptionError,
+        self.assertRaises(PackagingOptionError,
                           cmd.ensure_string_list, 'not_string_list2')
 
     def test_ensure_filename(self):
@@ -86,17 +84,18 @@
         cmd.option1 = __file__
         cmd.ensure_filename('option1')
         cmd.option2 = 'xxx'
-        self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2')
+        self.assertRaises(PackagingOptionError, cmd.ensure_filename, 'option2')
 
     def test_ensure_dirname(self):
         cmd = self.cmd
         cmd.option1 = os.path.dirname(__file__) or os.curdir
         cmd.ensure_dirname('option1')
         cmd.option2 = 'xxx'
-        self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2')
+        self.assertRaises(PackagingOptionError, cmd.ensure_dirname, 'option2')
+
 
 def test_suite():
     return unittest.makeSuite(CommandTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_command_config.py b/distutils2/tests/test_command_config.py
--- a/distutils2/tests/test_command_config.py
+++ b/distutils2/tests/test_command_config.py
@@ -1,32 +1,30 @@
 """Tests for distutils.command.config."""
 import os
 import sys
+import logging
 
 from distutils2.command.config import dump_file, config
 from distutils2.tests import unittest, support
 
+
 class ConfigTestCase(support.LoggingCatcher,
                      support.TempdirManager,
                      unittest.TestCase):
 
     def test_dump_file(self):
-        this_file = os.path.splitext(__file__)[0] + '.py'
-        f = open(this_file)
-        try:
+        this_file = __file__.rstrip('co')
+        with open(this_file) as f:
             numlines = len(f.readlines())
-        finally:
-            f.close()
 
         dump_file(this_file, 'I am the header')
+
         logs = []
-        for log in self.logs:
-            log = log[1]
-            logs.extend([log for log in log.split('\n')])
-        self.assertEqual(len(logs), numlines+2)
+        for log in self.get_logs(logging.INFO):
+            logs.extend(line for line in log.split('\n'))
+        self.assertEqual(len(logs), numlines + 2)
 
+    @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
     def test_search_cpp(self):
-        if sys.platform == 'win32':
-            return
         pkg_dir, dist = self.create_dist()
         cmd = config(dist)
 
@@ -68,7 +66,8 @@
         cmd._clean(f1, f2)
 
         for f in (f1, f2):
-            self.assertTrue(not os.path.exists(f))
+            self.assertFalse(os.path.exists(f))
+
 
 def test_suite():
     return unittest.makeSuite(ConfigTestCase)
diff --git a/distutils2/tests/test_command_install_data.py b/distutils2/tests/test_command_install_data.py
--- a/distutils2/tests/test_command_install_data.py
+++ b/distutils2/tests/test_command_install_data.py
@@ -1,30 +1,33 @@
-"""Tests for distutils.command.install_data."""
-import cmd
+"""Tests for distutils2.command.install_data."""
 import os
+import sysconfig
+from sysconfig import _get_default_scheme
+from distutils2.tests import unittest, support
+from distutils2.command.install_data import install_data
 
-from distutils2.command.install_data import install_data
-from distutils2.tests import unittest, support
 
 class InstallDataTestCase(support.TempdirManager,
                           support.LoggingCatcher,
-                          support.EnvironGuard,
                           unittest.TestCase):
 
     def test_simple_run(self):
-        from distutils2._backport.sysconfig import _SCHEMES as sysconfig_SCHEMES
-        from distutils2._backport.sysconfig import _get_default_scheme
-            #dirty but hit marmoute
-
-        old_scheme = sysconfig_SCHEMES
+        scheme = _get_default_scheme()
+        old_items = sysconfig._SCHEMES.items(scheme)
+        def restore():
+            sysconfig._SCHEMES.remove_section(scheme)
+            sysconfig._SCHEMES.add_section(scheme)
+            for option, value in old_items:
+                sysconfig._SCHEMES.set(scheme, option, value)
+        self.addCleanup(restore)
 
         pkg_dir, dist = self.create_dist()
         cmd = install_data(dist)
         cmd.install_dir = inst = os.path.join(pkg_dir, 'inst')
 
-        sysconfig_SCHEMES.set(_get_default_scheme(), 'inst',
-            os.path.join(pkg_dir, 'inst'))
-        sysconfig_SCHEMES.set(_get_default_scheme(), 'inst2',
-            os.path.join(pkg_dir, 'inst2'))
+        sysconfig._SCHEMES.set(scheme, 'inst',
+                               os.path.join(pkg_dir, 'inst'))
+        sysconfig._SCHEMES.set(scheme, 'inst2',
+                               os.path.join(pkg_dir, 'inst2'))
 
         one = os.path.join(pkg_dir, 'one')
         self.write_file(one, 'xxx')
@@ -32,7 +35,7 @@
         two = os.path.join(pkg_dir, 'two')
         self.write_file(two, 'xxx')
 
-        cmd.data_files = {one : '{inst}/one', two : '{inst2}/two'}
+        cmd.data_files = {one: '{inst}/one', two: '{inst2}/two'}
         self.assertItemsEqual(cmd.get_inputs(), [one, two])
 
         # let's run the command
@@ -48,7 +51,7 @@
         cmd.outfiles = []
 
         # let's try with warn_dir one
-        cmd.warn_dir = 1
+        cmd.warn_dir = True
         cmd.ensure_finalized()
         cmd.run()
 
@@ -60,15 +63,14 @@
 
         # now using root and empty dir
         cmd.root = os.path.join(pkg_dir, 'root')
-        inst4 = os.path.join(pkg_dir, 'inst4')
         three = os.path.join(cmd.install_dir, 'three')
         self.write_file(three, 'xx')
 
-        sysconfig_SCHEMES.set(_get_default_scheme(), 'inst3', cmd.install_dir)
+        sysconfig._SCHEMES.set(scheme, 'inst3',
+                               cmd.install_dir)
 
-        cmd.data_files = {one : '{inst}/one',
-                          two : '{inst2}/two',
-                          three : '{inst3}/three'}
+        cmd.data_files = {one: '{inst}/one', two: '{inst2}/two',
+                          three: '{inst3}/three'}
         cmd.ensure_finalized()
         cmd.run()
 
@@ -77,7 +79,6 @@
         self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
         self.assertTrue(os.path.exists(os.path.join(inst, rone)))
 
-        sysconfig_SCHEMES = old_scheme
 
 def test_suite():
     return unittest.makeSuite(InstallDataTestCase)
diff --git a/distutils2/tests/test_command_install_dist.py b/distutils2/tests/test_command_install_dist.py
--- a/distutils2/tests/test_command_install_dist.py
+++ b/distutils2/tests/test_command_install_dist.py
@@ -1,12 +1,10 @@
-"""Tests for distutils.command.install."""
+"""Tests for distutils2.command.install."""
 
 import os
 import sys
 
-from distutils2._backport.sysconfig import (get_scheme_names,
-                                            get_config_vars,
-                                            _SCHEMES,
-                                            get_config_var, get_path)
+from sysconfig import (get_scheme_names, get_config_vars,
+                       _SCHEMES, get_config_var, get_path)
 
 _CONFIG_VARS = get_config_vars()
 
@@ -15,13 +13,12 @@
 from distutils2.command.install_dist import install_dist
 from distutils2.command import install_dist as install_module
 from distutils2.dist import Distribution
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 
 from distutils2.tests import unittest, support
 
 
 class InstallTestCase(support.TempdirManager,
-                      support.EnvironGuard,
                       support.LoggingCatcher,
                       unittest.TestCase):
 
@@ -33,12 +30,10 @@
         destination = os.path.join(builddir, "installation")
 
         dist = Distribution({"name": "foopkg"})
-        # script_name need not exist, it just need to be initialized
-        dist.script_name = os.path.join(builddir, "setup.py")
         dist.command_obj["build"] = support.DummyCommand(
             build_base=builddir,
             build_lib=os.path.join(builddir, "lib"),
-            )
+        )
 
         old_posix_prefix = _SCHEMES.get('posix_prefix', 'platinclude')
         old_posix_home = _SCHEMES.get('posix_home', 'platinclude')
@@ -104,21 +99,21 @@
     def _test_user_site(self):
         schemes = get_scheme_names()
         for key in ('nt_user', 'posix_user', 'os2_home'):
-            self.assertTrue(key in schemes)
+            self.assertIn(key, schemes)
 
         dist = Distribution({'name': 'xx'})
         cmd = install_dist(dist)
         # making sure the user option is there
         options = [name for name, short, lable in
                    cmd.user_options]
-        self.assertTrue('user' in options)
+        self.assertIn('user', options)
 
         # setting a value
-        cmd.user = 1
+        cmd.user = True
 
         # user base and site shouldn't be created yet
-        self.assertTrue(not os.path.exists(self.user_base))
-        self.assertTrue(not os.path.exists(self.user_site))
+        self.assertFalse(os.path.exists(self.user_base))
+        self.assertFalse(os.path.exists(self.user_site))
 
         # let's run finalize
         cmd.ensure_finalized()
@@ -127,8 +122,8 @@
         self.assertTrue(os.path.exists(self.user_base))
         self.assertTrue(os.path.exists(self.user_site))
 
-        self.assertTrue('userbase' in cmd.config_vars)
-        self.assertTrue('usersite' in cmd.config_vars)
+        self.assertIn('userbase', cmd.config_vars)
+        self.assertIn('usersite', cmd.config_vars)
 
     def test_handle_extra_path(self):
         dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
@@ -156,7 +151,7 @@
 
         # three elements (no way !)
         cmd.extra_path = 'path,dirs,again'
-        self.assertRaises(DistutilsOptionError, cmd.handle_extra_path)
+        self.assertRaises(PackagingOptionError, cmd.handle_extra_path)
 
     def test_finalize_options(self):
         dist = Distribution({'name': 'xx'})
@@ -166,19 +161,19 @@
         # install-base/install-platbase -- not both
         cmd.prefix = 'prefix'
         cmd.install_base = 'base'
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
         # must supply either home or prefix/exec-prefix -- not both
         cmd.install_base = None
         cmd.home = 'home'
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
         if sys.version >= '2.6':
             # can't combine user with with prefix/exec_prefix/home or
             # install_(plat)base
             cmd.prefix = None
             cmd.user = 'user'
-            self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+            self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
     def test_old_record(self):
         # test pre-PEP 376 --record option (outside dist-info dir)
@@ -196,25 +191,12 @@
         # let's check the record file was created with four
         # lines, one for each .dist-info entry: METADATA,
         # INSTALLER, REQUSTED, RECORD
-        f = open(cmd.record)
-        try:
+        with open(cmd.record) as f:
             self.assertEqual(len(f.readlines()), 4)
-        finally:
-            f.close()
 
         # XXX test that fancy_getopt is okay with options named
         # record and no-record but unrelated
 
-    def _test_debug_mode(self):
-        # this covers the code called when DEBUG is set
-        old_logs_len = len(self.logs)
-        install_module.DEBUG = True
-        try:
-            __, stdout = captured_stdout(self.test_record)
-        finally:
-            install_module.DEBUG = False
-        self.assertTrue(len(self.logs) > old_logs_len)
-
 
 def test_suite():
     return unittest.makeSuite(InstallTestCase)
diff --git a/distutils2/tests/test_command_install_distinfo.py b/distutils2/tests/test_command_install_distinfo.py
--- a/distutils2/tests/test_command_install_distinfo.py
+++ b/distutils2/tests/test_command_install_distinfo.py
@@ -2,17 +2,14 @@
 
 import os
 import csv
+import hashlib
+import sys
 
 from distutils2.command.install_distinfo import install_distinfo
 from distutils2.command.cmd import Command
 from distutils2.metadata import Metadata
 from distutils2.tests import unittest, support
 
-try:
-    import hashlib
-except ImportError:
-    from distutils2._backport import hashlib
-
 
 class DummyInstallCmd(Command):
 
@@ -21,19 +18,18 @@
         self.distribution = dist
 
     def __getattr__(self, name):
-            return None
+        return None
 
     def ensure_finalized(self):
         pass
 
     def get_outputs(self):
-        return self.outputs + \
-               self.get_finalized_command('install_distinfo').get_outputs()
+        return (self.outputs +
+                self.get_finalized_command('install_distinfo').get_outputs())
 
 
 class InstallDistinfoTestCase(support.TempdirManager,
                               support.LoggingCatcher,
-                              support.EnvironGuard,
                               unittest.TestCase):
 
     checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y))
@@ -59,10 +55,10 @@
         dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
         self.checkLists(os.listdir(dist_info),
                         ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER'])
-        self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(),
-                         'distutils')
-        self.assertEqual(open(os.path.join(dist_info, 'REQUESTED')).read(),
-                         '')
+        with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+            self.assertEqual(fp.read(), 'distutils')
+        with open(os.path.join(dist_info, 'REQUESTED')) as fp:
+            self.assertEqual(fp.read(), '')
         meta_path = os.path.join(dist_info, 'METADATA')
         self.assertTrue(Metadata(path=meta_path).check())
 
@@ -84,8 +80,8 @@
         cmd.run()
 
         dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
-        self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(),
-                         'bacon-python')
+        with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+            self.assertEqual(fp.read(), 'bacon-python')
 
     def test_requested(self):
         pkg_dir, dist = self.create_dist(name='foo',
@@ -137,25 +133,23 @@
         install = DummyInstallCmd(dist)
         dist.command_obj['install_dist'] = install
 
-        fake_dists = os.path.join(os.path.dirname(__file__), '..',
-                                  '_backport', 'tests', 'fake_dists')
+        fake_dists = os.path.join(os.path.dirname(__file__), 'fake_dists')
         fake_dists = os.path.realpath(fake_dists)
 
         # for testing, we simply add all files from _backport's fake_dists
         dirs = []
         for dir in os.listdir(fake_dists):
-                full_path = os.path.join(fake_dists, dir)
-                if (not dir.endswith('.egg') or dir.endswith('.egg-info') or
-                    dir.endswith('.dist-info')) and os.path.isdir(full_path):
-                    dirs.append(full_path)
+            full_path = os.path.join(fake_dists, dir)
+            if (not dir.endswith('.egg') or dir.endswith('.egg-info') or
+                dir.endswith('.dist-info')) and os.path.isdir(full_path):
+                dirs.append(full_path)
 
         for dir in dirs:
-            for (path, subdirs, files) in os.walk(dir):
+            for path, subdirs, files in os.walk(dir):
                 install.outputs += [os.path.join(path, f) for f in files]
                 install.outputs += [os.path.join('path', f + 'c')
                                     for f in files if f.endswith('.py')]
 
-
         cmd = install_distinfo(dist)
         dist.command_obj['install_distinfo'] = cmd
 
@@ -168,25 +162,23 @@
 
         expected = []
         for f in install.get_outputs():
-            if f.endswith('.pyc') or \
-               f == os.path.join(install_dir, 'foo-1.0.dist-info', 'RECORD'):
+            if (f.endswith(('.pyc', '.pyo')) or f == os.path.join(
+                install_dir, 'foo-1.0.dist-info', 'RECORD')):
                 expected.append([f, '', ''])
             else:
                 size = os.path.getsize(f)
                 md5 = hashlib.md5()
-                md5.update(open(f).read())
+                with open(f, 'rb') as fp:
+                    md5.update(fp.read())
                 hash = md5.hexdigest()
                 expected.append([f, hash, str(size)])
 
         parsed = []
-        f = open(os.path.join(dist_info, 'RECORD'), 'rb')
-        try:
+        with open(os.path.join(dist_info, 'RECORD'), 'r') as f:
             reader = csv.reader(f, delimiter=',',
                                    lineterminator=os.linesep,
                                    quotechar='"')
             parsed = list(reader)
-        finally:
-            f.close()
 
         self.maxDiff = None
         self.checkLists(parsed, expected)
diff --git a/distutils2/tests/test_command_install_headers.py b/distutils2/tests/test_command_install_headers.py
--- a/distutils2/tests/test_command_install_headers.py
+++ b/distutils2/tests/test_command_install_headers.py
@@ -1,12 +1,12 @@
-"""Tests for distutils.command.install_headers."""
+"""Tests for distutils2.command.install_headers."""
 import os
 
 from distutils2.command.install_headers import install_headers
 from distutils2.tests import unittest, support
 
+
 class InstallHeadersTestCase(support.TempdirManager,
                              support.LoggingCatcher,
-                             support.EnvironGuard,
                              unittest.TestCase):
 
     def test_simple_run(self):
@@ -30,6 +30,7 @@
         # let's check the results
         self.assertEqual(len(cmd.get_outputs()), 2)
 
+
 def test_suite():
     return unittest.makeSuite(InstallHeadersTestCase)
 
diff --git a/distutils2/tests/test_command_install_lib.py b/distutils2/tests/test_command_install_lib.py
--- a/distutils2/tests/test_command_install_lib.py
+++ b/distutils2/tests/test_command_install_lib.py
@@ -1,11 +1,11 @@
-"""Tests for distutils.command.install_data."""
+"""Tests for distutils2.command.install_data."""
 import sys
 import os
 
+from distutils2.tests import unittest, support
 from distutils2.command.install_lib import install_lib
 from distutils2.compiler.extension import Extension
-from distutils2.tests import unittest, support
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 
 try:
     no_bytecode = sys.dont_write_bytecode
@@ -14,24 +14,27 @@
     no_bytecode = False
     bytecode_support = False
 
+
 class InstallLibTestCase(support.TempdirManager,
                          support.LoggingCatcher,
-                         support.EnvironGuard,
+                         support.EnvironRestorer,
                          unittest.TestCase):
 
+    restore_environ = ['PYTHONPATH']
+
     def test_finalize_options(self):
         pkg_dir, dist = self.create_dist()
         cmd = install_lib(dist)
 
         cmd.finalize_options()
-        self.assertEqual(cmd.compile, 1)
+        self.assertTrue(cmd.compile)
         self.assertEqual(cmd.optimize, 0)
 
         # optimize must be 0, 1, or 2
         cmd.optimize = 'foo'
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
         cmd.optimize = '4'
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
         cmd.optimize = '2'
         cmd.finalize_options()
@@ -41,7 +44,8 @@
     def test_byte_compile(self):
         pkg_dir, dist = self.create_dist()
         cmd = install_lib(dist)
-        cmd.compile = cmd.optimize = 1
+        cmd.compile = True
+        cmd.optimize = 1
 
         f = os.path.join(pkg_dir, 'foo.py')
         self.write_file(f, '# python file')
@@ -54,29 +58,33 @@
         cmd = install_lib(dist)
 
         # setting up a dist environment
-        cmd.compile = cmd.optimize = 1
+        cmd.compile = True
+        cmd.optimize = 1
         cmd.install_dir = pkg_dir
         f = os.path.join(pkg_dir, '__init__.py')
         self.write_file(f, '# python package')
         cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
         cmd.distribution.packages = [pkg_dir]
-        cmd.distribution.script_name = 'setup.py'
+
+        # make sure the build_lib is set the temp dir
+        build_dir = os.path.split(pkg_dir)[0]
+        cmd.get_finalized_command('build_py').build_lib = build_dir
 
         # get_output should return 4 elements
-        self.assertTrue(len(cmd.get_outputs()) >= 2)
+        self.assertEqual(len(cmd.get_outputs()), 4)
 
     def test_get_inputs(self):
         pkg_dir, dist = self.create_dist()
         cmd = install_lib(dist)
 
         # setting up a dist environment
-        cmd.compile = cmd.optimize = 1
+        cmd.compile = True
+        cmd.optimize = 1
         cmd.install_dir = pkg_dir
         f = os.path.join(pkg_dir, '__init__.py')
         self.write_file(f, '# python package')
         cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
         cmd.distribution.packages = [pkg_dir]
-        cmd.distribution.script_name = 'setup.py'
 
         # get_input should return 2 elements
         self.assertEqual(len(cmd.get_inputs()), 2)
@@ -87,17 +95,16 @@
         # makes sure byte_compile is not used
         pkg_dir, dist = self.create_dist()
         cmd = install_lib(dist)
-        cmd.compile = 1
+        cmd.compile = True
         cmd.optimize = 1
 
-        old_dont_write_bytecode = sys.dont_write_bytecode
+        self.addCleanup(setattr, sys, 'dont_write_bytecode',
+                        sys.dont_write_bytecode)
         sys.dont_write_bytecode = True
-        try:
-            cmd.byte_compile([])
-        finally:
-            sys.dont_write_bytecode = old_dont_write_bytecode
+        cmd.byte_compile([])
 
-        self.assertIn('byte-compiling is disabled', self.logs[0][2][1])
+        self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
 
 def test_suite():
     return unittest.makeSuite(InstallLibTestCase)
diff --git a/distutils2/tests/test_command_install_scripts.py b/distutils2/tests/test_command_install_scripts.py
--- a/distutils2/tests/test_command_install_scripts.py
+++ b/distutils2/tests/test_command_install_scripts.py
@@ -1,12 +1,10 @@
-"""Tests for distutils.command.install_scripts."""
-
+"""Tests for distutils2.command.install_scripts."""
 import os
 
+from distutils2.tests import unittest, support
 from distutils2.command.install_scripts import install_scripts
 from distutils2.dist import Distribution
 
-from distutils2.tests import unittest, support
-
 
 class InstallScriptsTestCase(support.TempdirManager,
                              support.LoggingCatcher,
@@ -18,14 +16,14 @@
             build_scripts="/foo/bar")
         dist.command_obj["install_dist"] = support.DummyCommand(
             install_scripts="/splat/funk",
-            force=1,
-            skip_build=1,
+            force=True,
+            skip_build=True,
             )
         cmd = install_scripts(dist)
-        self.assertTrue(not cmd.force)
-        self.assertTrue(not cmd.skip_build)
-        self.assertTrue(cmd.build_dir is None)
-        self.assertTrue(cmd.install_dir is None)
+        self.assertFalse(cmd.force)
+        self.assertFalse(cmd.skip_build)
+        self.assertIs(cmd.build_dir, None)
+        self.assertIs(cmd.install_dir, None)
 
         cmd.finalize_options()
 
@@ -40,11 +38,8 @@
 
         def write_script(name, text):
             expected.append(name)
-            f = open(os.path.join(source, name), "w")
-            try:
+            with open(os.path.join(source, name), "w") as f:
                 f.write(text)
-            finally:
-                f.close()
 
         write_script("script1.py", ("#! /usr/bin/env python2.3\n"
                                     "# bogus script w/ Python sh-bang\n"
@@ -61,8 +56,8 @@
         dist.command_obj["build"] = support.DummyCommand(build_scripts=source)
         dist.command_obj["install_dist"] = support.DummyCommand(
             install_scripts=target,
-            force=1,
-            skip_build=1,
+            force=True,
+            skip_build=True,
             )
         cmd = install_scripts(dist)
         cmd.finalize_options()
@@ -70,7 +65,7 @@
 
         installed = os.listdir(target)
         for name in expected:
-            self.assertTrue(name in installed)
+            self.assertIn(name, installed)
 
 
 def test_suite():
diff --git a/distutils2/tests/test_command_register.py b/distutils2/tests/test_command_register.py
--- a/distutils2/tests/test_command_register.py
+++ b/distutils2/tests/test_command_register.py
@@ -1,21 +1,18 @@
-# -*- encoding: utf-8 -*-
-"""Tests for distutils.command.register."""
+"""Tests for distutils2.command.register."""
 import os
 import getpass
 import urllib2
-
 try:
     import docutils
     DOCUTILS_SUPPORT = True
 except ImportError:
     DOCUTILS_SUPPORT = False
 
+from distutils2.tests import unittest, support
 from distutils2.command import register as register_module
 from distutils2.command.register import register
-from distutils2.dist import Distribution
-from distutils2.errors import DistutilsSetupError
+from distutils2.errors import PackagingSetupError
 
-from distutils2.tests import unittest, support
 
 PYPIRC_NOPASSWORD = """\
 [distutils]
@@ -37,7 +34,8 @@
 password:password
 """
 
-class RawInputs(object):
+
+class Inputs(object):
     """Fakes user inputs."""
     def __init__(self, *answers):
         self.answers = answers
@@ -49,6 +47,7 @@
         finally:
             self.index += 1
 
+
 class FakeOpener(object):
     """Fakes a PyPI server"""
     def __init__(self):
@@ -64,11 +63,14 @@
     def read(self):
         return 'xxx'
 
+
 class RegisterTestCase(support.TempdirManager,
-                       support.EnvironGuard,
+                       support.EnvironRestorer,
                        support.LoggingCatcher,
                        unittest.TestCase):
 
+    restore_environ = ['HOME']
+
     def setUp(self):
         super(RegisterTestCase, self).setUp()
         self.tmp_dir = self.mkdtemp()
@@ -77,8 +79,10 @@
 
         # patching the password prompt
         self._old_getpass = getpass.getpass
+
         def _getpass(prompt):
             return 'password'
+
         getpass.getpass = _getpass
         self.old_opener = urllib2.build_opener
         self.conn = urllib2.build_opener = FakeOpener()
@@ -86,8 +90,8 @@
     def tearDown(self):
         getpass.getpass = self._old_getpass
         urllib2.build_opener = self.old_opener
-        if hasattr(register_module, 'raw_input'):
-            del register_module.raw_input
+        if hasattr(register_module, 'input'):
+            del register_module.input
         super(RegisterTestCase, self).tearDown()
 
     def _get_cmd(self, metadata=None):
@@ -106,24 +110,26 @@
         cmd = self._get_cmd()
 
         # we shouldn't have a .pypirc file yet
-        self.assertTrue(not os.path.exists(self.rc))
+        self.assertFalse(os.path.exists(self.rc))
 
-        # patching raw_input and getpass.getpass
+        # patching input and getpass.getpass
         # so register gets happy
         # Here's what we are faking :
         # use your existing login (choice 1.)
         # Username : 'tarek'
         # Password : 'password'
         # Save your login (y/N)? : 'y'
-        inputs = RawInputs('1', 'tarek', 'y')
-        register_module.raw_input = inputs.__call__
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
         cmd.run()
 
         # we should have a brand new .pypirc file
         self.assertTrue(os.path.exists(self.rc))
 
         # with the content similar to WANTED_PYPIRC
-        content = open(self.rc).read()
+        with open(self.rc) as fp:
+            content = fp.read()
         self.assertEqual(content, WANTED_PYPIRC)
 
         # now let's make sure the .pypirc file generated
@@ -132,17 +138,18 @@
         def _no_way(prompt=''):
             raise AssertionError(prompt)
 
-        register_module.raw_input = _no_way
-        cmd.show_response = 1
+        register_module.input = _no_way
+        cmd.show_response = True
+        cmd.ensure_finalized()
         cmd.run()
 
         # let's see what the server received : we should
         # have 2 similar requests
-        self.assertTrue(self.conn.reqs, 2)
+        self.assertEqual(len(self.conn.reqs), 2)
         req1 = dict(self.conn.reqs[0].headers)
         req2 = dict(self.conn.reqs[1].headers)
         self.assertEqual(req2['Content-length'], req1['Content-length'])
-        self.assertTrue('xxx' in self.conn.reqs[1].data)
+        self.assertIn(b'xxx', self.conn.reqs[1].data)
 
     def test_password_not_in_file(self):
 
@@ -159,33 +166,34 @@
     def test_registration(self):
         # this test runs choice 2
         cmd = self._get_cmd()
-        inputs = RawInputs('2', 'tarek', 'tarek at ziade.org')
-        register_module.raw_input = inputs.__call__
+        inputs = Inputs('2', 'tarek', 'tarek at ziade.org')
+        register_module.input = inputs
         # let's run the command
         # FIXME does this send a real request? use a mock server
-        # also, silence self.announce (with LoggingCatcher)
+        cmd.ensure_finalized()
         cmd.run()
 
         # we should have send a request
-        self.assertTrue(self.conn.reqs, 1)
+        self.assertEqual(len(self.conn.reqs), 1)
         req = self.conn.reqs[0]
         headers = dict(req.headers)
-        self.assertEqual(headers['Content-length'], '608')
-        self.assertTrue('tarek' in req.data)
+        self.assertEqual(headers['Content-length'], '628')
+        self.assertIn(b'tarek', req.data)
 
     def test_password_reset(self):
         # this test runs choice 3
         cmd = self._get_cmd()
-        inputs = RawInputs('3', 'tarek at ziade.org')
-        register_module.raw_input = inputs.__call__
+        inputs = Inputs('3', 'tarek at ziade.org')
+        register_module.input = inputs
+        cmd.ensure_finalized()
         cmd.run()
 
         # we should have send a request
-        self.assertTrue(self.conn.reqs, 1)
+        self.assertEqual(len(self.conn.reqs), 1)
         req = self.conn.reqs[0]
         headers = dict(req.headers)
-        self.assertEqual(headers['Content-length'], '290')
-        self.assertTrue('tarek' in req.data)
+        self.assertEqual(headers['Content-length'], '298')
+        self.assertIn(b'tarek', req.data)
 
     @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
     def test_strict(self):
@@ -197,37 +205,39 @@
         # empty metadata
         cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
         cmd.ensure_finalized()
-        cmd.strict = 1
-        inputs = RawInputs('1', 'tarek', 'y')
-        register_module.raw_input = inputs.__call__
-        self.assertRaises(DistutilsSetupError, cmd.run)
+        cmd.strict = True
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        self.assertRaises(PackagingSetupError, cmd.run)
 
         # metadata is OK but long_description is broken
         metadata = {'home_page': 'xxx', 'author': 'xxx',
-                    'author_email': u'éxéxé',
+                    'author_email': '\xc3x\xc3x\xc3',
                     'name': 'xxx', 'version': 'xxx',
                     'description': 'title\n==\n\ntext'}
 
         cmd = self._get_cmd(metadata)
         cmd.ensure_finalized()
-        cmd.strict = 1
+        cmd.strict = True
 
-        self.assertRaises(DistutilsSetupError, cmd.run)
+        self.assertRaises(PackagingSetupError, cmd.run)
 
         # now something that works
         metadata['description'] = 'title\n=====\n\ntext'
         cmd = self._get_cmd(metadata)
         cmd.ensure_finalized()
-        cmd.strict = 1
-        inputs = RawInputs('1', 'tarek', 'y')
-        register_module.raw_input = inputs.__call__
+        cmd.strict = True
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
         cmd.run()
 
         # strict is not by default
         cmd = self._get_cmd()
         cmd.ensure_finalized()
-        inputs = RawInputs('1', 'tarek', 'y')
-        register_module.raw_input = inputs.__call__
+        inputs = Inputs('1', 'tarek', 'y')
+        register_module.input = inputs
+        cmd.ensure_finalized()
         cmd.run()
 
     def test_register_pep345(self):
@@ -238,6 +248,7 @@
         self.assertEqual(data['metadata_version'], '1.2')
         self.assertEqual(data['requires_dist'], ['lxml'])
 
+
 def test_suite():
     return unittest.makeSuite(RegisterTestCase)
 
diff --git a/distutils2/tests/test_command_sdist.py b/distutils2/tests/test_command_sdist.py
--- a/distutils2/tests/test_command_sdist.py
+++ b/distutils2/tests/test_command_sdist.py
@@ -1,16 +1,10 @@
-"""Tests for distutils.command.sdist."""
+"""Tests for distutils2.command.sdist."""
 import os
-import shutil
 import zipfile
 import tarfile
 import logging
 
-# zlib is not used here, but if it's not available
-# the tests that use zipfile may fail
-try:
-    import zlib
-except ImportError:
-    zlib = None
+from distutils2.tests.support import requires_zlib
 
 try:
     import grp
@@ -20,32 +14,20 @@
     UID_GID_SUPPORT = False
 
 from os.path import join
-import sys
-
 from distutils2.tests import captured_stdout
-
 from distutils2.command.sdist import sdist
 from distutils2.command.sdist import show_formats
 from distutils2.dist import Distribution
 from distutils2.tests import unittest
-from distutils2.errors import DistutilsExecError, DistutilsOptionError
-from distutils2.util import find_executable
+from distutils2.errors import PackagingOptionError
+from distutils2.util import find_executable, get_archive_formats
 from distutils2.tests import support
-try:
-    from shutil import get_archive_formats
-except ImportError:
-    from distutils2._backport.shutil import get_archive_formats
 
-SETUP_PY = """
-from distutils.core import setup
-import somecode
-
-setup(name='fake')
-"""
 
 MANIFEST = """\
-# file GENERATED by distutils, do NOT edit
+# file GENERATED by distutils2, do NOT edit
 inroot.txt
+setup.cfg
 data%(sep)sdata.dt
 scripts%(sep)sscript.py
 some%(sep)sfile.txt
@@ -55,16 +37,19 @@
 somecode%(sep)sdoc.txt
 """
 
+
 def builder(dist, filelist):
     filelist.append('bah')
 
 
-class SDistTestCase(support.TempdirManager, support.LoggingCatcher,
-                    support.EnvironGuard, unittest.TestCase):
+class SDistTestCase(support.TempdirManager,
+                    support.LoggingCatcher,
+                    support.EnvironRestorer,
+                    unittest.TestCase):
+
+    restore_environ = ['HOME']
 
     def setUp(self):
-        # PyPIRCCommandTestCase creates a temp dir already
-        # and put it in self.tmp_dir
         super(SDistTestCase, self).setUp()
         self.tmp_dir = self.mkdtemp()
         os.environ['HOME'] = self.tmp_dir
@@ -75,7 +60,6 @@
         # a package, and a README
         self.write_file((self.tmp_dir, 'README'), 'xxx')
         self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
-        self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
         os.chdir(self.tmp_dir)
 
     def tearDown(self):
@@ -90,14 +74,13 @@
                         'url': 'xxx', 'author': 'xxx',
                         'author_email': 'xxx'}
         dist = Distribution(metadata)
-        dist.script_name = 'setup.py'
         dist.packages = ['somecode']
         dist.include_package_data = True
         cmd = sdist(dist)
         cmd.dist_dir = 'dist'
         return dist, cmd
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
     def test_prune_file_list(self):
         # this test creates a package with some vcs dirs in it
         # and launch sdist to make sure they get pruned
@@ -105,6 +88,7 @@
 
         # creating VCS directories with some files in them
         os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
+
         self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
 
         os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
@@ -139,15 +123,12 @@
         # making sure everything has been pruned correctly
         self.assertEqual(len(content), 2)
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
+    @unittest.skipIf(find_executable('tar') is None or
+                     find_executable('gzip') is None,
+                     'requires tar and gzip programs')
     def test_make_distribution(self):
-
-        # check if tar and gzip are installed
-        if (find_executable('tar') is None or
-            find_executable('gzip') is None):
-            return
-
-        # now building a sdist
+        # building a sdist
         dist, cmd = self.get_cmd()
 
         # creating a gztar then a tar
@@ -157,10 +138,8 @@
 
         # making sure we have two files
         dist_folder = join(self.tmp_dir, 'dist')
-        result = os.listdir(dist_folder)
-        result.sort()
-        self.assertEqual(result,
-                          ['fake-1.0.tar', 'fake-1.0.tar.gz'] )
+        result = sorted(os.listdir(dist_folder))
+        self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
 
         os.remove(join(dist_folder, 'fake-1.0.tar'))
         os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
@@ -171,12 +150,10 @@
         cmd.ensure_finalized()
         cmd.run()
 
-        result = os.listdir(dist_folder)
-        result.sort()
-        self.assertEqual(result,
-                ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+        result = sorted(os.listdir(dist_folder))
+        self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
     def test_add_defaults(self):
 
         # http://bugs.python.org/issue2279
@@ -189,6 +166,7 @@
         # in package_data
         dist.package_data = {'': ['*.cfg', '*.dat'],
                              'somecode': ['*.txt']}
+        self.write_file((self.tmp_dir, 'setup.cfg'), '#')
         self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
         self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
 
@@ -202,10 +180,10 @@
         self.write_file((some_dir, 'file.txt'), '#')
         self.write_file((some_dir, 'other_file.txt'), '#')
 
-        dist.data_files = {'data/data.dt' : '{appdata}/data.dt',
-                           'inroot.txt' : '{appdata}/inroot.txt',
-                           'some/file.txt' : '{appdata}/file.txt',
-                           'some/other_file.txt' : '{appdata}/other_file.txt'}
+        dist.data_files = {'data/data.dt': '{appdata}/data.dt',
+                           'inroot.txt': '{appdata}/inroot.txt',
+                           'some/file.txt': '{appdata}/file.txt',
+                           'some/other_file.txt': '{appdata}/other_file.txt'}
 
         # adding a script
         script_dir = join(self.tmp_dir, 'scripts')
@@ -230,38 +208,40 @@
         finally:
             zip_file.close()
 
-        # making sure everything was added
-        self.assertEqual(len(content), 9)
+        # Making sure everything was added. This includes 8 code and data
+        # files in addition to PKG-INFO and setup.cfg
+        self.assertEqual(len(content), 10)
 
-        # checking the MANIFEST
-        manifest = open(join(self.tmp_dir, 'MANIFEST')).read()
+        # Checking the MANIFEST
+        with open(join(self.tmp_dir, 'MANIFEST')) as fp:
+            manifest = fp.read()
         self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
     def test_metadata_check_option(self):
         # testing the `check-metadata` option
-        dist, cmd = self.get_cmd(metadata={'name':'xxx', 'version':'xxx'})
+        dist, cmd = self.get_cmd(metadata={'name': 'xxx', 'version': 'xxx'})
 
-        # this should raise some warnings !
-        # with the `check` subcommand
+        # this should raise some warnings
+        # with the check subcommand
         cmd.ensure_finalized()
         cmd.run()
         warnings = self.get_logs(logging.WARN)
-        self.assertEqual(len(warnings), 2)
+        self.assertEqual(len(warnings), 4)
 
         # trying with a complete set of metadata
-        self.clear_logs()
+        self.loghandler.flush()
         dist, cmd = self.get_cmd()
         cmd.ensure_finalized()
-        cmd.metadata_check = 0
+        cmd.metadata_check = False
         cmd.run()
         warnings = self.get_logs(logging.WARN)
         # removing manifest generated warnings
         warnings = [warn for warn in warnings if
                     not warn.endswith('-- skipping')]
-        # the remaining warning is about the use of the default file list
-        self.assertEqual(len(warnings), 1)
-
+        # the remaining warnings are about the use of the default file list and
+        # the absence of setup.cfg
+        self.assertEqual(len(warnings), 2)
 
     def test_show_formats(self):
         __, stdout = captured_stdout(show_formats)
@@ -284,24 +264,21 @@
         # formats has to be a string splitable on (' ', ',') or
         # a stringlist
         cmd.formats = 1
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
         cmd.formats = ['zip']
         cmd.finalize_options()
 
         # formats has to be known
         cmd.formats = 'supazipa'
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
-    @unittest.skipUnless(zlib, "requires zlib")
+    @requires_zlib
     @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support")
+    @unittest.skipIf(find_executable('tar') is None or
+                     find_executable('gzip') is None,
+                     'requires tar and gzip programs')
     def test_make_distribution_owner_group(self):
-
-        # check if tar and gzip are installed
-        if (find_executable('tar') is None or
-            find_executable('gzip') is None):
-            return
-
-        # now building a sdist
+        # building a sdist
         dist, cmd = self.get_cmd()
 
         # creating a gztar and specifying the owner+group
@@ -332,71 +309,65 @@
         # making sure we have the good rights
         archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
         archive = tarfile.open(archive_name)
-
-        # note that we are not testing the group ownership here
-        # because, depending on the platforms and the container
-        # rights (see #7408)
         try:
+            # note that we are not testing the group ownership here
+            # because, depending on the platforms and the container
+            # rights (see #7408)
             for member in archive.getmembers():
                 self.assertEqual(member.uid, os.getuid())
         finally:
             archive.close()
 
+    @requires_zlib
     def test_get_file_list(self):
         # make sure MANIFEST is recalculated
         dist, cmd = self.get_cmd()
-
         # filling data_files by pointing files in package_data
         dist.package_data = {'somecode': ['*.txt']}
         self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
         cmd.ensure_finalized()
         cmd.run()
 
-        f = open(cmd.manifest)
-        try:
+        # Should produce four lines. Those lines are one comment, one default
+        # (README) and two package files.
+        with open(cmd.manifest) as f:
             manifest = [line.strip() for line in f.read().split('\n')
                         if line.strip() != '']
-        finally:
-            f.close()
         self.assertEqual(len(manifest), 3)
 
-        # adding a file
+        # Adding a file
         self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
 
-        # make sure build_py is reinitinialized, like a fresh run
+        # make sure build_py is reinitialized, like a fresh run
         build_py = dist.get_command_obj('build_py')
         build_py.finalized = False
         build_py.ensure_finalized()
 
         cmd.run()
 
-        f = open(cmd.manifest)
-        try:
+        with open(cmd.manifest) as f:
             manifest2 = [line.strip() for line in f.read().split('\n')
                          if line.strip() != '']
-        finally:
-            f.close()
 
-        # do we have the new file in MANIFEST ?
+        # Do we have the new file in MANIFEST?
         self.assertEqual(len(manifest2), 4)
         self.assertIn('doc2.txt', manifest2[-1])
 
+    @requires_zlib
     def test_manifest_marker(self):
         # check that autogenerated MANIFESTs have a marker
         dist, cmd = self.get_cmd()
         cmd.ensure_finalized()
         cmd.run()
 
-        f = open(cmd.manifest)
-        try:
+        with open(cmd.manifest) as f:
             manifest = [line.strip() for line in f.read().split('\n')
                         if line.strip() != '']
-        finally:
-            f.close()
 
         self.assertEqual(manifest[0],
-                         '# file GENERATED by distutils, do NOT edit')
+                         '# file GENERATED by distutils2, do NOT edit')
 
+    @requires_zlib
     def test_manual_manifest(self):
         # check that a MANIFEST without a marker is left alone
         dist, cmd = self.get_cmd()
@@ -404,29 +375,25 @@
         self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
         cmd.run()
 
-        f = open(cmd.manifest)
-        try:
+        with open(cmd.manifest) as f:
             manifest = [line.strip() for line in f.read().split('\n')
                         if line.strip() != '']
-        finally:
-            f.close()
 
         self.assertEqual(manifest, ['README.manual'])
 
+    @requires_zlib
     def test_template(self):
         dist, cmd = self.get_cmd()
         dist.extra_files = ['include yeah']
         cmd.ensure_finalized()
         self.write_file((self.tmp_dir, 'yeah'), 'xxx')
         cmd.run()
-        f = open(cmd.manifest)
-        try:
+        with open(cmd.manifest) as f:
             content = f.read()
-        finally:
-            f.close()
 
         self.assertIn('yeah', content)
 
+    @requires_zlib
     def test_manifest_builder(self):
         dist, cmd = self.get_cmd()
         cmd.manifest_builders = 'distutils2.tests.test_command_sdist.builder'
diff --git a/distutils2/tests/test_command_test.py b/distutils2/tests/test_command_test.py
--- a/distutils2/tests/test_command_test.py
+++ b/distutils2/tests/test_command_test.py
@@ -2,20 +2,19 @@
 import re
 import sys
 import shutil
+import logging
 import unittest as ut1
+import distutils2.database
 
-from copy import copy
 from os.path import join
 from operator import getitem, setitem, delitem
-from StringIO import StringIO
-
 from distutils2.command.build import build
 from distutils2.tests import unittest
-from distutils2.tests.support import TempdirManager, LoggingCatcher
+from distutils2.tests.support import (TempdirManager, EnvironRestorer,
+                                     LoggingCatcher)
 from distutils2.command.test import test
 from distutils2.command import set_command
 from distutils2.dist import Distribution
-from distutils2._backport import pkgutil
 
 
 EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\)
@@ -28,32 +27,37 @@
 
 here = os.path.dirname(os.path.abspath(__file__))
 
-_RECORD = []
 
 class MockBuildCmd(build):
     build_lib = "mock build lib"
     command_name = 'build'
     plat_name = 'whatever'
-    def initialize_options(self): pass
-    def finalize_options(self): pass
-    def run(self): _RECORD.append("build run")
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def run(self):
+        self._record.append("build has run")
 
 
 class TestTest(TempdirManager,
+               EnvironRestorer,
                LoggingCatcher,
                unittest.TestCase):
 
+    restore_environ = ['PYTHONPATH']
+
     def setUp(self):
         super(TestTest, self).setUp()
-
-        distutils2path = os.path.dirname(os.path.dirname(here))
-        self.old_pythonpath = os.environ.get('PYTHONPATH', '')
-        os.environ['PYTHONPATH'] = distutils2path + os.pathsep + self.old_pythonpath
-
-    def tearDown(self):
-        pkgutil.clear_cache()
-        os.environ['PYTHONPATH'] = self.old_pythonpath
-        super(TestTest, self).tearDown()
+        self.addCleanup(distutils2.database.clear_cache)
+        new_pythonpath = os.path.dirname(os.path.dirname(here))
+        pythonpath = os.environ.get('PYTHONPATH')
+        if pythonpath is not None:
+            new_pythonpath = os.pathsep.join((new_pythonpath, pythonpath))
+        os.environ['PYTHONPATH'] = new_pythonpath
 
     def assert_re_match(self, pattern, string):
         def quote(s):
@@ -71,7 +75,8 @@
         shutil.copytree(pkg_dir, temp_pkg_dir)
         return temp_pkg_dir
 
-    def safely_replace(self, obj, attr, new_val=None, delete=False, dictionary=False):
+    def safely_replace(self, obj, attr,
+                       new_val=None, delete=False, dictionary=False):
         """Replace a object's attribute returning to its original state at the
         end of the test run. Creates the attribute if not present before
         (deleting afterwards). When delete=True, makes sure the value is del'd
@@ -79,10 +84,12 @@
         rather than attributes."""
         if dictionary:
             _setattr, _getattr, _delattr = setitem, getitem, delitem
+
             def _hasattr(_dict, value):
                 return value in _dict
         else:
-            _setattr, _getattr, _delattr, _hasattr = setattr, getattr, delattr, hasattr
+            _setattr, _getattr, _delattr, _hasattr = (setattr, getattr,
+                                                      delattr, hasattr)
 
         orig_has_attr = _hasattr(obj, attr)
         if orig_has_attr:
@@ -107,9 +114,12 @@
         a_module.recorder = lambda *args: record.append("suite")
 
         class MockTextTestRunner(object):
-            def __init__(*_, **__): pass
+            def __init__(*_, **__):
+                pass
+
             def run(_self, suite):
                 record.append("run")
+
         self.safely_replace(ut1, "TextTestRunner", MockTextTestRunner)
 
         dist = Distribution()
@@ -119,17 +129,16 @@
         self.assertEqual(record, ["suite", "run"])
 
     def test_builds_before_running_tests(self):
+        self.addCleanup(set_command, 'distutils2.command.build.build')
         set_command('distutils2.tests.test_command_test.MockBuildCmd')
-        try:
-            dist = Distribution()
-            cmd = test(dist)
-            cmd.runner = self.prepare_named_function(lambda: None)
-            _RECORD[:] = []
-            cmd.ensure_finalized()
-            cmd.run()
-            self.assertEqual(_RECORD, ['build run'])
-        finally:
-            set_command('distutils2.command.build.build')
+
+        dist = Distribution()
+        dist.get_command_obj('build')._record = record = []
+        cmd = test(dist)
+        cmd.runner = self.prepare_named_function(lambda: None)
+        cmd.ensure_finalized()
+        cmd.run()
+        self.assertEqual(['build has run'], record)
 
     def _test_works_with_2to3(self):
         pass
@@ -139,16 +148,14 @@
         cmd = test(dist)
         phony_project = 'ohno_ohno-impossible_1234-name_stop-that!'
         cmd.tests_require = [phony_project]
-        record = []
-        cmd.announce = lambda *args: record.append(args)
         cmd.ensure_finalized()
-        self.assertEqual(1, len(record))
-        self.assertIn(phony_project, record[0][0])
+        logs = self.get_logs(logging.WARNING)
+        self.assertIn(phony_project, logs[-1])
 
     def prepare_a_module(self):
         tmp_dir = self.mkdtemp()
         sys.path.append(tmp_dir)
-        self.addCleanup(lambda: sys.path.remove(tmp_dir))
+        self.addCleanup(sys.path.remove, tmp_dir)
 
         self.write_file((tmp_dir, 'distutils2_tests_a.py'), '')
         import distutils2_tests_a as a_module
@@ -163,21 +170,30 @@
         dist = Distribution()
         cmd = test(dist)
         record = []
-        cmd.runner = self.prepare_named_function(lambda: record.append("runner called"))
+        cmd.runner = self.prepare_named_function(
+            lambda: record.append("runner called"))
         cmd.ensure_finalized()
         cmd.run()
         self.assertEqual(["runner called"], record)
 
     def prepare_mock_ut2(self):
         class MockUTClass(object):
-            def __init__(*_, **__): pass
-            def discover(self): pass
-            def run(self, _): pass
+            def __init__(*_, **__):
+                pass
+
+            def discover(self):
+                pass
+
+            def run(self, _):
+                pass
+
         class MockUTModule(object):
             TestLoader = MockUTClass
             TextTestRunner = MockUTClass
+
         mock_ut2 = MockUTModule()
-        self.safely_replace(sys.modules, "unittest2", mock_ut2, dictionary=True)
+        self.safely_replace(sys.modules, "unittest2",
+                            mock_ut2, dictionary=True)
         return mock_ut2
 
     def test_gets_unittest_discovery(self):
@@ -193,12 +209,13 @@
     def test_calls_discover(self):
         self.safely_replace(ut1.TestLoader, "discover", delete=True)
         mock_ut2 = self.prepare_mock_ut2()
-        _RECORD[:] = []
-        mock_ut2.TestLoader.discover = lambda self, path: _RECORD.append(path)
+        record = []
+        mock_ut2.TestLoader.discover = lambda self, path: record.append(path)
         dist = Distribution()
         cmd = test(dist)
         cmd.run()
-        self.assertEqual(_RECORD, [os.curdir])
+        self.assertEqual([os.curdir], record)
+
 
 def test_suite():
     return unittest.makeSuite(TestTest)
diff --git a/distutils2/tests/test_command_upload.py b/distutils2/tests/test_command_upload.py
--- a/distutils2/tests/test_command_upload.py
+++ b/distutils2/tests/test_command_upload.py
@@ -1,14 +1,18 @@
-# -*- encoding: utf-8 -*-
-"""Tests for distutils.command.upload."""
+"""Tests for distutils2.command.upload."""
 import os
 import sys
 
 from distutils2.command.upload import upload
 from distutils2.dist import Distribution
-from distutils2.errors import DistutilsOptionError
+from distutils2.errors import PackagingOptionError
 
 from distutils2.tests import unittest, support
-from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase
+try:
+    import threading
+    from distutils2.tests.pypi_server import PyPIServerTestCase
+except ImportError:
+    threading = None
+    PyPIServerTestCase = unittest.TestCase
 
 
 PYPIRC_NOPASSWORD = """\
@@ -40,9 +44,12 @@
 """
 
 
-class UploadTestCase(support.TempdirManager, support.EnvironGuard,
+ at unittest.skipIf(threading is None, 'needs threading')
+class UploadTestCase(support.TempdirManager, support.EnvironRestorer,
                      support.LoggingCatcher, PyPIServerTestCase):
 
+    restore_environ = ['HOME']
+
     def setUp(self):
         super(UploadTestCase, self).setUp()
         self.tmp_dir = self.mkdtemp()
@@ -60,13 +67,13 @@
                                ('repository', 'http://pypi.python.org/pypi')):
             self.assertEqual(getattr(cmd, attr), expected)
 
-    def test_finalize_options_unsigned_identity_yields_exception(self):
+    def test_finalize_options_unsigned_identity_raises_exception(self):
         self.write_file(self.rc, PYPIRC)
         dist = Distribution()
         cmd = upload(dist)
         cmd.identity = True
         cmd.sign = False
-        self.assertRaises(DistutilsOptionError, cmd.finalize_options) 
+        self.assertRaises(PackagingOptionError, cmd.finalize_options)
 
     def test_saved_password(self):
         # file with no password
@@ -85,19 +92,19 @@
         cmd.finalize_options()
         self.assertEqual(cmd.password, 'xxx')
 
-    def test_upload_without_files_yields_exception(self):
+    def test_upload_without_files_raises_exception(self):
         dist = Distribution()
         cmd = upload(dist)
-        self.assertRaises(DistutilsOptionError, cmd.run)
+        self.assertRaises(PackagingOptionError, cmd.run)
 
     def test_upload(self):
         path = os.path.join(self.tmp_dir, 'xxx')
         self.write_file(path)
-        command, pyversion, filename = 'xxx', '2.6', path
+        command, pyversion, filename = 'xxx', '3.3', path
         dist_files = [(command, pyversion, filename)]
 
         # lets run it
-        pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'dédé')
+        pkg_dir, dist = self.create_dist(dist_files=dist_files, author='d\xc3d\xc3')
         cmd = upload(dist)
         cmd.ensure_finalized()
         cmd.repository = self.pypi.full_address
@@ -105,11 +112,12 @@
 
         # what did we send ?
         handler, request_data = self.pypi.requests[-1]
-        headers = handler.headers.dict
-        self.assertIn('dédé', request_data)
-        self.assertIn('xxx', request_data)
+        headers = handler.headers
+        #self.assertIn('d\xc3d\xc3', str(request_data))
+        self.assertIn(b'xxx', request_data)
+
         self.assertEqual(int(headers['content-length']), len(request_data))
-        self.assertTrue(int(headers['content-length']) < 2000)
+        self.assertLess(int(headers['content-length']), 2500)
         self.assertTrue(headers['content-type'].startswith('multipart/form-data'))
         self.assertEqual(handler.command, 'POST')
         self.assertNotIn('\n', headers['authorization'])
@@ -117,7 +125,7 @@
     def test_upload_docs(self):
         path = os.path.join(self.tmp_dir, 'xxx')
         self.write_file(path)
-        command, pyversion, filename = 'xxx', '2.6', path
+        command, pyversion, filename = 'xxx', '3.3', path
         dist_files = [(command, pyversion, filename)]
         docs_path = os.path.join(self.tmp_dir, "build", "docs")
         os.makedirs(docs_path)
@@ -125,26 +133,28 @@
         self.write_file(self.rc, PYPIRC)
 
         # lets run it
-        pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'dédé')
+        pkg_dir, dist = self.create_dist(dist_files=dist_files, author='d\xc3d\xc3')
 
         cmd = upload(dist)
         cmd.get_finalized_command("build").run()
         cmd.upload_docs = True
         cmd.ensure_finalized()
         cmd.repository = self.pypi.full_address
+        prev_dir = os.getcwd()
         try:
-            prev_dir = os.getcwd()
             os.chdir(self.tmp_dir)
             cmd.run()
         finally:
             os.chdir(prev_dir)
 
         handler, request_data = self.pypi.requests[-1]
-        action, name, content =\
-            request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4]
+        action, name, content = request_data.split(
+            "----------------GHSKFJDLGDS7543FJKLFHRE75642756743254"
+            .encode())[1:4]
 
-        self.assertIn('name=":action"', action)
-        self.assertIn("doc_upload", action)
+        self.assertIn(b'name=":action"', action)
+        self.assertIn(b'doc_upload', action)
+
 
 def test_suite():
     return unittest.makeSuite(UploadTestCase)
diff --git a/distutils2/tests/test_command_upload_docs.py b/distutils2/tests/test_command_upload_docs.py
--- a/distutils2/tests/test_command_upload_docs.py
+++ b/distutils2/tests/test_command_upload_docs.py
@@ -1,46 +1,27 @@
-# -*- encoding: utf-8 -*-
-"""Tests for distutils.command.upload_docs."""
+"""Tests for distutils2.command.upload_docs."""
 import os
 import sys
-import httplib
 import shutil
 import zipfile
 try:
-    from cStringIO import StringIO
+    import _ssl
 except ImportError:
-    from StringIO import StringIO
+    _ssl = None
 
 from distutils2.command import upload_docs as upload_docs_mod
-from distutils2.command.upload_docs import (upload_docs, zip_dir,
-                                            encode_multipart)
+from distutils2.command.upload_docs import upload_docs, zip_dir
 from distutils2.dist import Distribution
-from distutils2.errors import DistutilsFileError, DistutilsOptionError
+from distutils2.errors import PackagingFileError, PackagingOptionError
 
 from distutils2.tests import unittest, support
-from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase
+try:
+    import threading
+    from distutils2.tests.pypi_server import PyPIServerTestCase
+except ImportError:
+    threading = None
+    PyPIServerTestCase = object
 
 
-EXPECTED_MULTIPART_OUTPUT = "\r\n".join([
-'---x',
-'Content-Disposition: form-data; name="a"',
-'',
-'b',
-'---x',
-'Content-Disposition: form-data; name="c"',
-'',
-'d',
-'---x',
-'Content-Disposition: form-data; name="e"; filename="f"',
-'',
-'g',
-'---x',
-'Content-Disposition: form-data; name="h"; filename="i"',
-'',
-'j',
-'---x--',
-'',
-])
-
 PYPIRC = """\
 [distutils]
 index-servers = server1
@@ -51,8 +32,14 @@
 password = long_island
 """
 
-class UploadDocsTestCase(support.TempdirManager, support.EnvironGuard,
-                         support.LoggingCatcher, PyPIServerTestCase):
+
+ at unittest.skipIf(threading is None, "Needs threading")
+class UploadDocsTestCase(support.TempdirManager,
+                         support.EnvironRestorer,
+                         support.LoggingCatcher,
+                         PyPIServerTestCase):
+
+    restore_environ = ['HOME']
 
     def setUp(self):
         super(UploadDocsTestCase, self).setUp()
@@ -103,13 +90,6 @@
         zip_f = zipfile.ZipFile(compressed)
         self.assertEqual(zip_f.namelist(), ['index.html', 'docs/index.html'])
 
-    def test_encode_multipart(self):
-        fields = [("a", "b"), ("c", "d")]
-        files = [("e", "f", "g"), ("h", "i", "j")]
-        content_type, body = encode_multipart(fields, files, "-x")
-        self.assertEqual(content_type, "multipart/form-data; boundary=-x")
-        self.assertEqual(body, EXPECTED_MULTIPART_OUTPUT)
-
     def prepare_command(self):
         self.cmd.upload_dir = self.prepare_sample_dir()
         self.cmd.ensure_finalized()
@@ -123,61 +103,60 @@
 
         self.assertEqual(len(self.pypi.requests), 1)
         handler, request_data = self.pypi.requests[-1]
-        self.assertIn("content", request_data)
-        self.assertIn("Basic", handler.headers.dict['authorization'])
-        self.assertTrue(handler.headers.dict['content-type']
+        self.assertIn(b"content", request_data)
+        self.assertIn("Basic", handler.headers['authorization'])
+        self.assertTrue(handler.headers['content-type']
             .startswith('multipart/form-data;'))
 
         action, name, version, content =\
-            request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:5]
+            request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254".encode())[1:5]
+
 
         # check that we picked the right chunks
-        self.assertIn('name=":action"', action)
-        self.assertIn('name="name"', name)
-        self.assertIn('name="version"', version)
-        self.assertIn('name="content"', content)
+        self.assertIn(b'name=":action"', action)
+        self.assertIn(b'name="name"', name)
+        self.assertIn(b'name="version"', version)
+        self.assertIn(b'name="content"', content)
 
         # check their contents
-        self.assertIn("doc_upload", action)
-        self.assertIn("distr-name", name)
-        self.assertIn("docs/index.html", content)
-        self.assertIn("Ce mortel ennui", content)
+        self.assertIn(b'doc_upload', action)
+        self.assertIn(b'distr-name', name)
+        self.assertIn(b'docs/index.html', content)
+        self.assertIn(b'Ce mortel ennui', content)
 
+    @unittest.skipIf(_ssl is None, 'Needs SSL support')
     def test_https_connection(self):
-        https_called = False
+        self.https_called = False
+
         orig_https = upload_docs_mod.httplib.HTTPSConnection
+
         def https_conn_wrapper(*args):
-            https_called = True
-            return upload_docs_mod.httplib.HTTPConnection(*args) # the testing server is http
+            self.https_called = True
+            # the testing server is http
+            return upload_docs_mod.httplib.HTTPConnection(*args)
+
         upload_docs_mod.httplib.HTTPSConnection = https_conn_wrapper
         try:
             self.prepare_command()
             self.cmd.run()
-            self.assertFalse(https_called)
+            self.assertFalse(self.https_called)
 
             self.cmd.repository = self.cmd.repository.replace("http", "https")
             self.cmd.run()
-            self.assertFalse(https_called)
+            self.assertTrue(self.https_called)
         finally:
             upload_docs_mod.httplib.HTTPSConnection = orig_https
 
     def test_handling_response(self):
-        calls = []
-        def aggr(*args):
-            calls.append(args)
         self.pypi.default_response_status = '403 Forbidden'
         self.prepare_command()
-        self.cmd.announce = aggr
         self.cmd.run()
-        message, _ = calls[-1]
-        self.assertIn('Upload failed (403): Forbidden', message)
+        self.assertIn('Upload failed (403): Forbidden', self.get_logs()[-1])
 
-        calls = []
         self.pypi.default_response_status = '301 Moved Permanently'
         self.pypi.default_response_headers.append(("Location", "brand_new_location"))
         self.cmd.run()
-        message = calls[-1][0]
-        self.assertIn('brand_new_location', message)
+        self.assertIn('brand_new_location', self.get_logs()[-1])
 
     def test_reads_pypirc_data(self):
         self.write_file(self.rc, PYPIRC % self.pypi.full_address)
@@ -190,19 +169,18 @@
     def test_checks_index_html_presence(self):
         self.cmd.upload_dir = self.prepare_sample_dir()
         os.remove(os.path.join(self.cmd.upload_dir, "index.html"))
-        self.assertRaises(DistutilsFileError, self.cmd.ensure_finalized)
+        self.assertRaises(PackagingFileError, self.cmd.ensure_finalized)
 
     def test_checks_upload_dir(self):
         self.cmd.upload_dir = self.prepare_sample_dir()
         shutil.rmtree(os.path.join(self.cmd.upload_dir))
-        self.assertRaises(DistutilsOptionError, self.cmd.ensure_finalized)
+        self.assertRaises(PackagingOptionError, self.cmd.ensure_finalized)
 
     def test_show_response(self):
         self.prepare_command()
         self.cmd.show_response = True
         self.cmd.run()
-        record = self.logs[-1][1]
-
+        record = self.get_logs()[-1]
         self.assertTrue(record, "should report the response")
         self.assertIn(self.pypi.default_response_data, record)
 
diff --git a/distutils2/tests/test_compiler.py b/distutils2/tests/test_compiler.py
--- a/distutils2/tests/test_compiler.py
+++ b/distutils2/tests/test_compiler.py
@@ -2,7 +2,7 @@
 import os
 
 from distutils2.compiler import (get_default_compiler, customize_compiler,
-                                 gen_lib_options)
+                                gen_lib_options)
 from distutils2.tests import unittest, support
 
 
@@ -17,26 +17,26 @@
     def runtime_library_dir_option(self, dir):
         return ["-cool", "-R" + dir]
 
-    def find_library_file(self, dirs, lib, debug=0):
+    def find_library_file(self, dirs, lib, debug=False):
         return 'found'
 
     def library_option(self, lib):
         return "-l" + lib
 
 
-class CompilerTestCase(support.EnvironGuard, unittest.TestCase):
+class CompilerTestCase(support.EnvironRestorer, unittest.TestCase):
 
+    restore_environ = ['AR', 'ARFLAGS']
+
+    @unittest.skipUnless(get_default_compiler() == 'unix',
+                        'irrelevant if default compiler is not unix')
     def test_customize_compiler(self):
 
-        # not testing if default compiler is not unix
-        if get_default_compiler() != 'unix':
-            return
-
         os.environ['AR'] = 'my_ar'
         os.environ['ARFLAGS'] = '-arflags'
 
         # make sure AR gets caught
-        class compiler:
+        class compiler(object):
             name = 'unix'
 
             def set_executables(self, **kw):
diff --git a/distutils2/tests/test_config.py b/distutils2/tests/test_config.py
--- a/distutils2/tests/test_config.py
+++ b/distutils2/tests/test_config.py
@@ -1,25 +1,30 @@
-# -*- encoding: utf-8 -*-
-"""Tests for distutils.config."""
+"""Tests for distutils2.config."""
 import os
 import sys
+import logging
 from StringIO import StringIO
 
-from distutils2.tests import unittest, support, run_unittest
+from distutils2 import command
+from distutils2.dist import Distribution
+from distutils2.errors import PackagingFileError
+from distutils2.compiler import new_compiler, _COMPILERS
 from distutils2.command.sdist import sdist
-from distutils2.errors import DistutilsFileError
 
+from distutils2.tests import unittest, support
+from distutils2.tests.support import requires_zlib
 
-SETUP_CFG = """
+
+SETUP_CFG = u"""
 [metadata]
 name = RestingParrot
 version = 0.6.4
 author = Carl Meyer
 author_email = carl at oddbird.net
-maintainer = Éric Araujo
+maintainer = \xc3ric Araujo
 maintainer_email = merwok at netwok.org
-summary = A sample project demonstrating distutils2 packaging
+summary = A sample project demonstrating distutils2
 description-file = %(description-file)s
-keywords = distutils2, packaging, sample project
+keywords = distutils2, sample project
 
 classifier =
   Development Status :: 4 - Beta
@@ -85,7 +90,7 @@
 compilers =
     distutils2.tests.test_config.DCompiler
 
-setup_hook = distutils2.tests.test_config.hook
+setup_hooks = %(setup-hooks)s
 
 
 
@@ -109,7 +114,7 @@
     GecodeInt GecodeKernel -- sys.platform == 'win32'
 
 [extension=fast_taunt]
-name = three.fast_taunt
+name = two.fast_taunt
 sources = cxx_src/utils_taunt.cxx
           cxx_src/python_module.cxx
 include_dirs = /usr/include/gecode
@@ -121,6 +126,15 @@
 
 """
 
+HOOKS_MODULE = """
+import logging
+
+logger = logging.getLogger('distutils2')
+
+def logging_hook(config):
+    logger.warning('logging_hook called')
+"""
+
 
 class DCompiler(object):
     name = 'd'
@@ -129,8 +143,17 @@
     def __init__(self, *args):
         pass
 
-def hook(content):
-    content['metadata']['version'] += '.dev1'
+
+def version_hook(config):
+    config['metadata']['version'] += '.dev1'
+
+
+def first_hook(config):
+    config['files']['modules'] += '\n first'
+
+
+def third_hook(config):
+    config['files']['modules'] += '\n third'
 
 
 class FooBarBazTest(object):
@@ -139,11 +162,11 @@
         self.distribution = dist
 
     @classmethod
-    def get_command_name(self):
+    def get_command_name(cls):
         return 'foo'
 
     def run(self):
-        self.distribution.foo_was_here = 1
+        self.distribution.foo_was_here = True
 
     def nothing(self):
         pass
@@ -155,57 +178,53 @@
 
 
 class ConfigTestCase(support.TempdirManager,
+                     support.EnvironRestorer,
                      support.LoggingCatcher,
                      unittest.TestCase):
 
+    restore_environ = ['PLAT']
+
     def setUp(self):
         super(ConfigTestCase, self).setUp()
         self.addCleanup(setattr, sys, 'stdout', sys.stdout)
         self.addCleanup(setattr, sys, 'stderr', sys.stderr)
-        #sys.stdout = sys.stderr = StringIO()
+        sys.stdout = StringIO()
+        sys.stderr = StringIO()
 
         self.addCleanup(os.chdir, os.getcwd())
         tempdir = self.mkdtemp()
+        self.working_dir = os.getcwd()
         os.chdir(tempdir)
         self.tempdir = tempdir
 
-        self.addCleanup(setattr, sys, 'argv', sys.argv)
+    def tearDown(self):
+        os.chdir(self.working_dir)
+        super(ConfigTestCase, self).tearDown()
 
     def write_setup(self, kwargs=None):
-        opts = {'description-file': 'README', 'extra-files':''}
+        opts = {'description-file': 'README', 'extra-files': '',
+                'setup-hooks': 'distutils2.tests.test_config.version_hook'}
         if kwargs:
             opts.update(kwargs)
-        self.write_file('setup.cfg', SETUP_CFG % opts)
+        self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8')
 
-
-    def run_setup(self, *args):
-        # run setup with args
-        args = ['run'] + list(args)
-        from distutils2.run import main
-        dist = main(args)
-        return dist
-
-    def _get_metadata(self, name='version'):
-        from distutils2.dist import Distribution
+    def get_dist(self):
         dist = Distribution()
         dist.parse_config_files()
-        return dist, dist.metadata[name]
+        return dist
 
     def test_config(self):
         self.write_setup()
         self.write_file('README', 'yeah')
         os.mkdir('bm')
-        self.write_file(os.path.join('bm', 'b1.gif'), '')
-        self.write_file(os.path.join('bm', 'b2.gif'), '')
+        self.write_file(('bm', 'b1.gif'), '')
+        self.write_file(('bm', 'b2.gif'), '')
         os.mkdir('Cfg')
-        self.write_file(os.path.join('Cfg', 'data.CFG'), '')
+        self.write_file(('Cfg', 'data.CFG'), '')
         self.write_file('init_script', '')
 
         # try to load the metadata now
-        dist, version = self._get_metadata()
-
-        # sanity check
-        self.assertEqual(version, '0.6.4.dev1')
+        dist = self.get_dist()
 
         # check what was done
         self.assertEqual(dist.metadata['Author'], 'Carl Meyer')
@@ -214,16 +233,17 @@
         # the hook adds .dev1
         self.assertEqual(dist.metadata['Version'], '0.6.4.dev1')
 
-        wanted = ['Development Status :: 4 - Beta',
-                'Environment :: Console (Text Based)',
-                "Environment :: X11 Applications :: GTK; python_version < '3'",
-                'License :: OSI Approved :: MIT License',
-                'Programming Language :: Python',
-                'Programming Language :: Python :: 2',
-                'Programming Language :: Python :: 3']
+        wanted = [
+            'Development Status :: 4 - Beta',
+            'Environment :: Console (Text Based)',
+            "Environment :: X11 Applications :: GTK; python_version < '3'",
+            'License :: OSI Approved :: MIT License',
+            'Programming Language :: Python',
+            'Programming Language :: Python :: 2',
+            'Programming Language :: Python :: 3']
         self.assertEqual(dist.metadata['Classifier'], wanted)
 
-        wanted = ['distutils2', 'packaging', 'sample project']
+        wanted = ['distutils2', 'sample project']
         self.assertEqual(dist.metadata['Keywords'], wanted)
 
         self.assertEqual(dist.metadata['Requires-Python'], '>=2.4, <3.2')
@@ -245,10 +265,10 @@
         self.assertEqual(dist.py_modules, ['haven'])
         self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'})
         self.assertEqual(
-            {'bm/b1.gif' : '{icon}/b1.gif',
-             'bm/b2.gif' : '{icon}/b2.gif',
-             'Cfg/data.CFG' : '{config}/baBar/data.CFG',
-             'init_script' : '{script}/JunGle/init_script'},
+            {'bm/b1.gif': '{icon}/b1.gif',
+             'bm/b2.gif': '{icon}/b2.gif',
+             'Cfg/data.CFG': '{config}/baBar/data.CFG',
+             'init_script': '{script}/JunGle/init_script'},
              dist.data_files)
 
         self.assertEqual(dist.package_dir, 'src')
@@ -260,37 +280,36 @@
         # this file would be __main__.Foo when run as "python test_config.py".
         # The name FooBarBazTest should be unique enough to prevent
         # collisions.
-        self.assertEqual(dist.get_command_obj('foo').__class__.__name__,
-                         'FooBarBazTest')
+        self.assertEqual('FooBarBazTest',
+                         dist.get_command_obj('foo').__class__.__name__)
 
         # did the README got loaded ?
         self.assertEqual(dist.metadata['description'], 'yeah')
 
         # do we have the D Compiler enabled ?
-        from distutils2.compiler import new_compiler, _COMPILERS
         self.assertIn('d', _COMPILERS)
         d = new_compiler(compiler='d')
         self.assertEqual(d.description, 'D Compiler')
 
-
     def test_multiple_description_file(self):
         self.write_setup({'description-file': 'README  CHANGES'})
         self.write_file('README', 'yeah')
         self.write_file('CHANGES', 'changelog2')
-        dist, version = self._get_metadata()
+        dist = self.get_dist()
         self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
 
     def test_multiline_description_file(self):
         self.write_setup({'description-file': 'README\n  CHANGES'})
         self.write_file('README', 'yeah')
         self.write_file('CHANGES', 'changelog')
-        dist, desc = self._get_metadata('description')
-        self.assertEqual(desc, 'yeah\nchangelog')
+        dist = self.get_dist()
+        self.assertEqual(dist.metadata['description'], 'yeah\nchangelog')
         self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
 
     def test_parse_extensions_in_config(self):
         self.write_file('setup.cfg', EXT_SETUP_CFG)
-        dist, version = self._get_metadata()
+        dist = self.get_dist()
+
         ext_modules = dict((mod.name, mod) for mod in dist.ext_modules)
         self.assertEqual(len(ext_modules), 2)
         ext = ext_modules.get('one.speed_coconuts')
@@ -303,105 +322,152 @@
         self.assertEqual(ext.extra_link_args,
             ['`gcc -print-file-name=libgcc.a`', '-shared'])
 
-        ext = ext_modules.get('three.fast_taunt')
+        ext = ext_modules.get('two.fast_taunt')
         self.assertEqual(ext.sources,
             ['cxx_src/utils_taunt.cxx', 'cxx_src/python_module.cxx'])
         self.assertEqual(ext.include_dirs,
             ['/usr/include/gecode', '/usr/include/blitz'])
         cargs = ['-fPIC', '-O2']
         if sys.platform == 'win32':
-            cargs.append("/DGECODE_VERSION='win32'")
+            cargs.append("/DGECODE_VERSION=win32")
         else:
             cargs.append('-DGECODE_VERSION=$(./gecode_version)')
         self.assertEqual(ext.extra_compile_args, cargs)
         self.assertEqual(ext.language, 'cxx')
 
+    def test_project_setup_hook_works(self):
+        # Bug #11637: ensure the project directory is on sys.path to allow
+        # project-specific hooks
+        self.write_setup({'setup-hooks': 'hooks.logging_hook'})
+        self.write_file('README', 'yeah')
+        self.write_file('hooks.py', HOOKS_MODULE)
+        self.get_dist()
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(['logging_hook called'], logs)
+        self.assertIn('hooks', sys.modules)
+
+    def test_missing_setup_hook_warns(self):
+        self.write_setup({'setup-hooks': 'this.does._not.exist'})
+        self.write_file('README', 'yeah')
+        self.get_dist()
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('cannot find setup hook', logs[0])
+
+    def test_multiple_setup_hooks(self):
+        self.write_setup({
+            'setup-hooks': '\n  distutils2.tests.test_config.first_hook'
+                           '\n  distutils2.tests.test_config.missing_hook'
+                           '\n  distutils2.tests.test_config.third_hook'
+        })
+        self.write_file('README', 'yeah')
+        dist = self.get_dist()
+
+        self.assertEqual(['haven', 'first', 'third'], dist.py_modules)
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('cannot find setup hook', logs[0])
 
     def test_metadata_requires_description_files_missing(self):
-        self.write_setup({'description-file': 'README\n  README2'})
+        self.write_setup({'description-file': 'README README2'})
         self.write_file('README', 'yeah')
         self.write_file('README2', 'yeah')
-        self.write_file('haven.py', '#')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
         self.write_file('script1.py', '#')
         os.mkdir('scripts')
-        self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
+        self.write_file(('scripts', 'find-coconuts'), '#')
         os.mkdir('bin')
-        self.write_file(os.path.join('bin', 'taunt'), '#')
+        self.write_file(('bin', 'taunt'), '#')
 
-        os.mkdir('src')
         for pkg in ('one', 'two', 'three'):
             pkg = os.path.join('src', pkg)
             os.mkdir(pkg)
-            self.write_file(os.path.join(pkg, '__init__.py'), '#')
+            self.write_file((pkg, '__init__.py'), '#')
 
-        dist, version = self._get_metadata()
+        dist = self.get_dist()
         cmd = sdist(dist)
         cmd.finalize_options()
         cmd.get_file_list()
-        self.assertRaises(DistutilsFileError, cmd.make_distribution)
+        self.assertRaises(PackagingFileError, cmd.make_distribution)
 
+    @requires_zlib
     def test_metadata_requires_description_files(self):
+        # Create the following file structure:
+        #   README
+        #   README2
+        #   script1.py
+        #   scripts/
+        #       find-coconuts
+        #   bin/
+        #       taunt
+        #   src/
+        #       haven.py
+        #       one/__init__.py
+        #       two/__init__.py
+        #       three/__init__.py
+
         self.write_setup({'description-file': 'README\n  README2',
-                          'extra-files':'\n  README2'})
-        self.write_file('README', 'yeah')
-        self.write_file('README2', 'yeah')
-        self.write_file('haven.py', '#')
+                          'extra-files': '\n  README3'})
+        self.write_file('README', 'yeah 1')
+        self.write_file('README2', 'yeah 2')
+        self.write_file('README3', 'yeah 3')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
         self.write_file('script1.py', '#')
         os.mkdir('scripts')
-        self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
+        self.write_file(('scripts', 'find-coconuts'), '#')
         os.mkdir('bin')
-        self.write_file(os.path.join('bin', 'taunt'), '#')
+        self.write_file(('bin', 'taunt'), '#')
 
-        os.mkdir('src')
         for pkg in ('one', 'two', 'three'):
             pkg = os.path.join('src', pkg)
             os.mkdir(pkg)
-            self.write_file(os.path.join(pkg, '__init__.py'), '#')
+            self.write_file((pkg, '__init__.py'), '#')
 
-        dist, desc = self._get_metadata('description')
-        self.assertIn('yeah\nyeah', desc)
+        dist = self.get_dist()
+        self.assertIn('yeah 1\nyeah 2', dist.metadata['description'])
 
         cmd = sdist(dist)
         cmd.finalize_options()
         cmd.get_file_list()
-        self.assertRaises(DistutilsFileError, cmd.make_distribution)
+        self.assertRaises(PackagingFileError, cmd.make_distribution)
 
         self.write_setup({'description-file': 'README\n  README2',
                           'extra-files': '\n  README2\n    README'})
-        dist, desc = self._get_metadata('description')
-
+        dist = self.get_dist()
         cmd = sdist(dist)
         cmd.finalize_options()
         cmd.get_file_list()
         cmd.make_distribution()
-        self.assertIn('README\nREADME2\n', open('MANIFEST').read())
+        with open('MANIFEST') as fp:
+            self.assertIn('README\nREADME2\n', fp.read())
 
     def test_sub_commands(self):
         self.write_setup()
         self.write_file('README', 'yeah')
-        self.write_file('haven.py', '#')
+        os.mkdir('src')
+        self.write_file(('src', 'haven.py'), '#')
         self.write_file('script1.py', '#')
         os.mkdir('scripts')
-        self.write_file(os.path.join('scripts', 'find-coconuts'), '#')
+        self.write_file(('scripts', 'find-coconuts'), '#')
         os.mkdir('bin')
-        self.write_file(os.path.join('bin', 'taunt'), '#')
-        os.mkdir('src')
+        self.write_file(('bin', 'taunt'), '#')
 
         for pkg in ('one', 'two', 'three'):
             pkg = os.path.join('src', pkg)
             os.mkdir(pkg)
-            self.write_file(os.path.join(pkg, '__init__.py'), '#')
+            self.write_file((pkg, '__init__.py'), '#')
 
         # try to run the install command to see if foo is called
-        from distutils2.dist import Distribution
-        dist = Distribution()
-        dist.parse_config_files()
-        dist.run_command('install_dist')
-        self.assertEqual(dist.foo_was_here, 1)
+        dist = self.get_dist()
+        self.assertIn('foo', command.get_command_names())
+        self.assertEqual('FooBarBazTest',
+                         dist.get_command_obj('foo').__class__.__name__)
 
 
 def test_suite():
     return unittest.makeSuite(ConfigTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_create.py b/distutils2/tests/test_create.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_create.py
@@ -0,0 +1,243 @@
+"""Tests for distutils2.create."""
+from StringIO import StringIO
+import os
+import sys
+import sysconfig
+from textwrap import dedent
+from distutils2.create import MainProgram, ask_yn, ask, main
+
+from distutils2.tests import support, unittest
+
+
+class CreateTestCase(support.TempdirManager,
+                     support.EnvironRestorer,
+                     unittest.TestCase):
+
+    maxDiff = None
+    restore_environ = ['PLAT']
+
+    def setUp(self):
+        super(CreateTestCase, self).setUp()
+        self._stdin = sys.stdin  # TODO use Inputs
+        self._stdout = sys.stdout
+        sys.stdin = StringIO()
+        sys.stdout = StringIO()
+        self._cwd = os.getcwd()
+        self.wdir = self.mkdtemp()
+        os.chdir(self.wdir)
+        # patch sysconfig
+        self._old_get_paths = sysconfig.get_paths
+        sysconfig.get_paths = lambda *args, **kwargs: {
+            'man': sys.prefix + '/share/man',
+            'doc': sys.prefix + '/share/doc/pyxfoil', }
+
+    def tearDown(self):
+        sys.stdin = self._stdin
+        sys.stdout = self._stdout
+        os.chdir(self._cwd)
+        sysconfig.get_paths = self._old_get_paths
+        super(CreateTestCase, self).tearDown()
+
+    def test_ask_yn(self):
+        sys.stdin.write(u'y\n')
+        sys.stdin.seek(0)
+        self.assertEqual('y', ask_yn('is this a test'))
+
+    def test_ask(self):
+        sys.stdin.write(u'a\n')
+        sys.stdin.write(u'b\n')
+        sys.stdin.seek(0)
+        self.assertEqual('a', ask('is this a test'))
+        self.assertEqual('b', ask(str(list(range(0, 70))), default='c',
+                                  lengthy=True))
+
+    def test_set_multi(self):
+        mainprogram = MainProgram()
+        sys.stdin.write(u'aaaaa\n')
+        sys.stdin.seek(0)
+        mainprogram.data['author'] = []
+        mainprogram._set_multi('_set_multi test', 'author')
+        self.assertEqual(['aaaaa'], mainprogram.data['author'])
+
+    def test_find_files(self):
+        # making sure we scan a project dir correctly
+        mainprogram = MainProgram()
+
+        # building the structure
+        tempdir = self.wdir
+        dirs = ['pkg1', 'data', 'pkg2', 'pkg2/sub']
+        files = [
+            'README',
+            'data/data1',
+            'foo.py',
+            'pkg1/__init__.py',
+            'pkg1/bar.py',
+            'pkg2/__init__.py',
+            'pkg2/sub/__init__.py',
+        ]
+
+        for dir_ in dirs:
+            os.mkdir(os.path.join(tempdir, dir_))
+
+        for file_ in files:
+            path = os.path.join(tempdir, file_)
+            self.write_file(path, 'xxx')
+
+        mainprogram._find_files()
+        mainprogram.data['packages'].sort()
+
+        # do we have what we want?
+        self.assertEqual(mainprogram.data['packages'],
+                         ['pkg1', 'pkg2', 'pkg2.sub'])
+        self.assertEqual(mainprogram.data['modules'], ['foo'])
+        data_fn = os.path.join('data', 'data1')
+        self.assertEqual(mainprogram.data['extra_files'],
+                         ['README', data_fn])
+
+    def test_convert_setup_py_to_cfg(self):
+        self.write_file((self.wdir, 'setup.py'),
+                        dedent(u"""
+        # coding: utf-8
+        from distutils.core import setup
+
+        long_description = '''My super Death-scription
+        barbar is now on the public domain,
+        ho, baby !'''
+
+        setup(name='pyxfoil',
+              version='0.2',
+              description='Python bindings for the Xfoil engine',
+              long_description=long_description,
+              maintainer='Andr\xc3 Espaze',
+              maintainer_email='andre.espaze at logilab.fr',
+              url='http://www.python-science.org/project/pyxfoil',
+              license='GPLv2',
+              packages=['pyxfoil', 'babar', 'me'],
+              data_files=[
+                  ('share/doc/pyxfoil', ['README.rst']),
+                  ('share/man', ['pyxfoil.1']),
+                         ],
+              py_modules=['my_lib', 'mymodule'],
+              package_dir={
+                  'babar': '',
+                  'me': 'Martinique/Lamentin',
+                          },
+              package_data={
+                  'babar': ['Pom', 'Flora', 'Alexander'],
+                  'me': ['dady', 'mumy', 'sys', 'bro'],
+                  '':  ['setup.py', 'README'],
+                  'pyxfoil': ['fengine.so'],
+                           },
+              scripts=['my_script', 'bin/run'],
+              )
+        """), encoding='utf-8')
+        sys.stdin.write(u'y\n')
+        sys.stdin.seek(0)
+        main()
+
+        with open(os.path.join(self.wdir, 'setup.cfg'), encoding='utf-8') as fp:
+            contents = fp.read()
+
+        self.assertEqual(contents, dedent(u"""\
+            [metadata]
+            name = pyxfoil
+            version = 0.2
+            summary = Python bindings for the Xfoil engine
+            download_url = UNKNOWN
+            home_page = http://www.python-science.org/project/pyxfoil
+            maintainer = Andr\xc3 Espaze
+            maintainer_email = andre.espaze at logilab.fr
+            description = My super Death-scription
+                   |barbar is now on the public domain,
+                   |ho, baby !
+
+            [files]
+            packages = pyxfoil
+                babar
+                me
+            modules = my_lib
+                mymodule
+            scripts = my_script
+                bin/run
+            extra_files = Martinique/Lamentin/dady
+                Martinique/Lamentin/mumy
+                Martinique/Lamentin/sys
+                Martinique/Lamentin/bro
+                setup.py
+                README
+                Pom
+                Flora
+                Alexander
+                pyxfoil/fengine.so
+
+            resources =
+                README.rst = {doc}
+                pyxfoil.1 = {man}
+
+            """))
+
+    def test_convert_setup_py_to_cfg_with_description_in_readme(self):
+        self.write_file((self.wdir, 'setup.py'),
+                        dedent(u"""
+        # coding: utf-8
+        from distutils.core import setup
+        with open('README.txt') as fp:
+            long_description = fp.read()
+
+        setup(name='pyxfoil',
+              version='0.2',
+              description='Python bindings for the Xfoil engine',
+              long_description=long_description,
+              maintainer='Andr\xc3 Espaze',
+              maintainer_email='andre.espaze at logilab.fr',
+              url='http://www.python-science.org/project/pyxfoil',
+              license='GPLv2',
+              packages=['pyxfoil'],
+              package_data={'pyxfoil': ['fengine.so', 'babar.so']},
+              data_files=[
+                ('share/doc/pyxfoil', ['README.rst']),
+                ('share/man', ['pyxfoil.1']),
+              ],
+        )
+        """), encoding='utf-8')
+        self.write_file((self.wdir, 'README.txt'),
+                        dedent('''
+My super Death-scription
+barbar is now in the public domain,
+ho, baby!
+                        '''))
+        sys.stdin.write(u'y\n')
+        sys.stdin.seek(0)
+        # FIXME Out of memory error.
+        main()
+        with open(os.path.join(self.wdir, 'setup.cfg'), encoding='utf-8') as fp:
+            contents = fp.read()
+
+        self.assertEqual(contents, dedent(u"""\
+            [metadata]
+            name = pyxfoil
+            version = 0.2
+            summary = Python bindings for the Xfoil engine
+            download_url = UNKNOWN
+            home_page = http://www.python-science.org/project/pyxfoil
+            maintainer = Andr\xc3 Espaze
+            maintainer_email = andre.espaze at logilab.fr
+            description-file = README.txt
+
+            [files]
+            packages = pyxfoil
+            extra_files = pyxfoil/fengine.so
+                pyxfoil/babar.so
+
+            resources =
+                README.rst = {doc}
+                pyxfoil.1 = {man}
+
+            """))
+
+
+def test_suite():
+    return unittest.makeSuite(CreateTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_cygwinccompiler.py b/distutils2/tests/test_cygwinccompiler.py
--- a/distutils2/tests/test_cygwinccompiler.py
+++ b/distutils2/tests/test_cygwinccompiler.py
@@ -1,18 +1,13 @@
-"""Tests for distutils.cygwinccompiler."""
+"""Tests for distutils2.cygwinccompiler."""
+import os
 import sys
-import os
+import sysconfig
+from distutils2.compiler.cygwinccompiler import (
+    check_config_h, get_msvcr,
+    CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN)
 
-from distutils2._backport import sysconfig
+from distutils2.tests import unittest, support
 
-from distutils2.tests import run_unittest
-from distutils2.tests import captured_stdout
-
-from distutils2.compiler import cygwinccompiler
-from distutils2.compiler.cygwinccompiler import (
-    CygwinCCompiler, check_config_h, get_msvcr,
-    CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN)
-from distutils2.util import get_compiler_versions
-from distutils2.tests import unittest, support
 
 class CygwinCCompilerTestCase(support.TempdirManager,
                               unittest.TestCase):
@@ -33,7 +28,6 @@
         return self.python_h
 
     def test_check_config_h(self):
-
         # check_config_h looks for "GCC" in sys.version first
         # returns CONFIG_H_OK if found
         sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) \n[GCC '
@@ -56,10 +50,9 @@
         self.assertEqual(check_config_h()[0], CONFIG_H_OK)
 
     def test_get_msvcr(self):
-
         # none
-        sys.version  = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) '
-                        '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]')
+        sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) '
+                       '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]')
         self.assertEqual(get_msvcr(), None)
 
         # MSVC 7.0
@@ -92,4 +85,4 @@
     return unittest.makeSuite(CygwinCCompilerTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_database.py b/distutils2/tests/test_database.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_database.py
@@ -0,0 +1,674 @@
+import os
+import csv
+import sys
+import shutil
+import tempfile
+from os.path import relpath  # separate import for backport concerns
+from hashlib import md5
+from textwrap import dedent
+
+from distutils2.tests.test_util import GlobTestCaseBase
+from distutils2.tests.support import requires_zlib
+
+from distutils2.config import get_resources_dests
+from distutils2.errors import PackagingError
+from distutils2.metadata import Metadata
+from distutils2.tests import unittest, support
+from distutils2.database import (
+    Distribution, EggInfoDistribution, get_distribution, get_distributions,
+    provides_distribution, obsoletes_distribution, get_file_users,
+    enable_cache, disable_cache, distinfo_dirname, _yield_distributions,
+    get_file, get_file_path)
+
+# TODO Add a test for getting a distribution provided by another distribution
+# TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini)
+# TODO Add tests from the former pep376 project (zipped site-packages, etc.)
+
+
+def get_hexdigest(filename):
+    with open(filename, 'rb') as file:
+        checksum = md5(file.read())
+    return checksum.hexdigest()
+
+
+def record_pieces(file):
+    path = relpath(file, sys.prefix)
+    digest = get_hexdigest(file)
+    size = os.path.getsize(file)
+    return [path, digest, size]
+
+
+class FakeDistsMixin(object):
+
+    def setUp(self):
+        super(FakeDistsMixin, self).setUp()
+        self.addCleanup(enable_cache)
+        disable_cache()
+
+        # make a copy that we can write into for our fake installed
+        # distributions
+        tmpdir = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, tmpdir)
+        self.fake_dists_path = os.path.join(tmpdir, 'fake_dists')
+        fake_dists_src = os.path.abspath(
+            os.path.join(os.path.dirname(__file__), 'fake_dists'))
+        shutil.copytree(fake_dists_src, self.fake_dists_path)
+        # XXX ugly workaround: revert copystat calls done by shutil behind our
+        # back (to avoid getting a read-only copy of a read-only file).  we
+        # could pass a custom copy_function to change the mode of files, but
+        # shutil gives no control over the mode of directories :(
+        for root, dirs, files in os.walk(self.fake_dists_path):
+            os.chmod(root, 0o755)
+            for f in files:
+                os.chmod(os.path.join(root, f), 0o644)
+            for d in dirs:
+                os.chmod(os.path.join(root, d), 0o755)
+
+
+class CommonDistributionTests(FakeDistsMixin):
+    """Mixin used to test the interface common to both Distribution classes.
+
+    Derived classes define cls, sample_dist, dirs and records.  These
+    attributes are used in test methods.  See source code for details.
+    """
+
+    def test_instantiation(self):
+        # check that useful attributes are here
+        name, version, distdir = self.sample_dist
+        here = os.path.abspath(os.path.dirname(__file__))
+        dist_path = os.path.join(here, 'fake_dists', distdir)
+
+        dist = self.dist = self.cls(dist_path)
+        self.assertEqual(dist.path, dist_path)
+        self.assertEqual(dist.name, name)
+        self.assertEqual(dist.metadata['Name'], name)
+        self.assertIsInstance(dist.metadata, Metadata)
+        self.assertEqual(dist.version, version)
+        self.assertEqual(dist.metadata['Version'], version)
+
+    @requires_zlib
+    def test_repr(self):
+        dist = self.cls(self.dirs[0])
+        # just check that the class name is in the repr
+        self.assertIn(self.cls.__name__, repr(dist))
+
+    @requires_zlib
+    def test_comparison(self):
+        # tests for __eq__ and __hash__
+        dist = self.cls(self.dirs[0])
+        dist2 = self.cls(self.dirs[0])
+        dist3 = self.cls(self.dirs[1])
+        self.assertIn(dist, {dist: True})
+        self.assertEqual(dist, dist)
+
+        self.assertIsNot(dist, dist2)
+        self.assertEqual(dist, dist2)
+        self.assertNotEqual(dist, dist3)
+        self.assertNotEqual(dist, ())
+
+    def test_list_installed_files(self):
+        for dir_ in self.dirs:
+            dist = self.cls(dir_)
+            for path, md5_, size in dist.list_installed_files():
+                record_data = self.records[dist.path]
+                self.assertIn(path, record_data)
+                self.assertEqual(md5_, record_data[path][0])
+                self.assertEqual(size, record_data[path][1])
+
+
+class TestDistribution(CommonDistributionTests, unittest.TestCase):
+
+    cls = Distribution
+    sample_dist = 'choxie', '2.0.0.9', 'choxie-2.0.0.9.dist-info'
+
+    def setUp(self):
+        super(TestDistribution, self).setUp()
+        self.dirs = [os.path.join(self.fake_dists_path, f)
+                     for f in os.listdir(self.fake_dists_path)
+                     if f.endswith('.dist-info')]
+
+        self.records = {}
+        for distinfo_dir in self.dirs:
+
+            record_file = os.path.join(distinfo_dir, 'RECORD')
+            with open(record_file, 'w') as file:
+                record_writer = csv.writer(
+                    file, delimiter=',', quoting=csv.QUOTE_NONE,
+                    lineterminator='\n')
+
+                dist_location = distinfo_dir.replace('.dist-info', '')
+
+                for path, dirs, files in os.walk(dist_location):
+                    for f in files:
+                        record_writer.writerow(record_pieces(
+                                               os.path.join(path, f)))
+                for file in ('INSTALLER', 'METADATA', 'REQUESTED'):
+                    record_writer.writerow(record_pieces(
+                                           os.path.join(distinfo_dir, file)))
+                record_writer.writerow([relpath(record_file, sys.prefix)])
+
+            with open(record_file) as file:
+                record_reader = csv.reader(file, lineterminator='\n')
+                record_data = {}
+                for row in record_reader:
+                    if row == []:
+                        continue
+                    path, md5_, size = (row[:] +
+                                        [None for i in range(len(row), 3)])
+                    record_data[path] = md5_, size
+            self.records[distinfo_dir] = record_data
+
+    def test_instantiation(self):
+        super(TestDistribution, self).test_instantiation()
+        self.assertIsInstance(self.dist.requested, bool)
+
+    def test_uses(self):
+        # Test to determine if a distribution uses a specified file.
+        # Criteria to test against
+        distinfo_name = 'grammar-1.0a4'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        true_path = [self.fake_dists_path, distinfo_name,
+                     'grammar', 'utils.py']
+        true_path = relpath(os.path.join(*true_path), sys.prefix)
+        false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff',
+                      '__init__.py']
+        false_path = relpath(os.path.join(*false_path), sys.prefix)
+
+        # Test if the distribution uses the file in question
+        dist = Distribution(distinfo_dir)
+        self.assertTrue(dist.uses(true_path))
+        self.assertFalse(dist.uses(false_path))
+
+    def test_get_distinfo_file(self):
+        # Test the retrieval of dist-info file objects.
+        distinfo_name = 'choxie-2.0.0.9'
+        other_distinfo_name = 'grammar-1.0a4'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        # Test for known good file matches
+        distinfo_files = [
+            # Relative paths
+            'INSTALLER', 'METADATA',
+            # Absolute paths
+            os.path.join(distinfo_dir, 'RECORD'),
+            os.path.join(distinfo_dir, 'REQUESTED'),
+        ]
+
+        for distfile in distinfo_files:
+            with dist.get_distinfo_file(distfile) as value:
+                self.assertIsInstance(value, file)
+                # Is it the correct file?
+                self.assertEqual(value.name,
+                                 os.path.join(distinfo_dir, distfile))
+
+        # Test an absolute path that is part of another distributions dist-info
+        other_distinfo_file = os.path.join(
+            self.fake_dists_path, other_distinfo_name + '.dist-info',
+            'REQUESTED')
+        self.assertRaises(PackagingError, dist.get_distinfo_file,
+                          other_distinfo_file)
+        # Test for a file that should not exist
+        self.assertRaises(PackagingError, dist.get_distinfo_file,
+                          'MAGICFILE')
+
+    def test_list_distinfo_files(self):
+        # Test for the iteration of RECORD path entries.
+        distinfo_name = 'towel_stuff-0.1'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        # Test for the iteration of the raw path
+        distinfo_record_paths = self.records[distinfo_dir].keys()
+        found = dist.list_distinfo_files()
+        self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+        # Test for the iteration of local absolute paths
+        distinfo_record_paths = [os.path.join(sys.prefix, path)
+            for path in self.records[distinfo_dir]]
+        found = dist.list_distinfo_files(local=True)
+        self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+
+    def test_get_resources_path(self):
+        distinfo_name = 'babar-0.1'
+        distinfo_dir = os.path.join(self.fake_dists_path,
+                                    distinfo_name + '.dist-info')
+        dist = Distribution(distinfo_dir)
+        resource_path = dist.get_resource_path('babar.png')
+        self.assertEqual(resource_path, 'babar.png')
+        self.assertRaises(KeyError, dist.get_resource_path, 'notexist')
+
+
+class TestEggInfoDistribution(CommonDistributionTests,
+                              support.LoggingCatcher,
+                              unittest.TestCase):
+
+    cls = EggInfoDistribution
+    sample_dist = 'bacon', '0.1', 'bacon-0.1.egg-info'
+
+    def setUp(self):
+        super(TestEggInfoDistribution, self).setUp()
+
+        self.dirs = [os.path.join(self.fake_dists_path, f)
+                     for f in os.listdir(self.fake_dists_path)
+                     if f.endswith('.egg') or f.endswith('.egg-info')]
+
+        self.records = {}
+
+    @unittest.skip('not implemented yet')
+    def test_list_installed_files(self):
+        # EggInfoDistribution defines list_installed_files but there is no
+        # test for it yet; someone with setuptools expertise needs to add a
+        # file with the list of installed files for one of the egg fake dists
+        # and write the support code to populate self.records (and then delete
+        # this method)
+        pass
+
+
+class TestDatabase(support.LoggingCatcher,
+                   FakeDistsMixin,
+                   unittest.TestCase):
+
+    def setUp(self):
+        super(TestDatabase, self).setUp()
+        sys.path.insert(0, self.fake_dists_path)
+        self.addCleanup(sys.path.remove, self.fake_dists_path)
+
+    def test_distinfo_dirname(self):
+        # Given a name and a version, we expect the distinfo_dirname function
+        # to return a standard distribution information directory name.
+
+        items = [
+            # (name, version, standard_dirname)
+            # Test for a very simple single word name and decimal version
+            # number
+            ('docutils', '0.5', 'docutils-0.5.dist-info'),
+            # Test for another except this time with a '-' in the name, which
+            # needs to be transformed during the name lookup
+            ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'),
+            # Test for both '-' in the name and a funky version number
+            ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'),
+            ]
+
+        # Loop through the items to validate the results
+        for name, version, standard_dirname in items:
+            dirname = distinfo_dirname(name, version)
+            self.assertEqual(dirname, standard_dirname)
+
+    @requires_zlib
+    def test_get_distributions(self):
+        # Lookup all distributions found in the ``sys.path``.
+        # This test could potentially pick up other installed distributions
+        fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'),
+                      ('towel-stuff', '0.1'), ('babar', '0.1')]
+        found_dists = []
+
+        # Verify the fake dists have been found.
+        dists = [dist for dist in get_distributions()]
+        for dist in dists:
+            self.assertIsInstance(dist, Distribution)
+            if (dist.name in dict(fake_dists) and
+                dist.path.startswith(self.fake_dists_path)):
+                found_dists.append((dist.name, dist.version))
+            else:
+                # check that it doesn't find anything more than this
+                self.assertFalse(dist.path.startswith(self.fake_dists_path))
+            # otherwise we don't care what other distributions are found
+
+        # Finally, test that we found all that we were looking for
+        self.assertEqual(sorted(found_dists), sorted(fake_dists))
+
+        # Now, test if the egg-info distributions are found correctly as well
+        fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'),
+                       ('coconuts-aster', '10.3'),
+                       ('banana', '0.4'), ('strawberry', '0.6'),
+                       ('truffles', '5.0'), ('nut', 'funkyversion')]
+        found_dists = []
+
+        dists = [dist for dist in get_distributions(use_egg_info=True)]
+        for dist in dists:
+            self.assertIsInstance(dist, (Distribution, EggInfoDistribution))
+            if (dist.name in dict(fake_dists) and
+                dist.path.startswith(self.fake_dists_path)):
+                found_dists.append((dist.name, dist.version))
+            else:
+                self.assertFalse(dist.path.startswith(self.fake_dists_path))
+
+        self.assertEqual(sorted(fake_dists), sorted(found_dists))
+
+    @requires_zlib
+    def test_get_distribution(self):
+        # Test for looking up a distribution by name.
+        # Test the lookup of the towel-stuff distribution
+        name = 'towel-stuff'  # Note: This is different from the directory name
+
+        # Lookup the distribution
+        dist = get_distribution(name)
+        self.assertIsInstance(dist, Distribution)
+        self.assertEqual(dist.name, name)
+
+        # Verify that an unknown distribution returns None
+        self.assertIsNone(get_distribution('bogus'))
+
+        # Verify partial name matching doesn't work
+        self.assertIsNone(get_distribution('towel'))
+
+        # Verify that it does not find egg-info distributions, when not
+        # instructed to
+        self.assertIsNone(get_distribution('bacon'))
+        self.assertIsNone(get_distribution('cheese'))
+        self.assertIsNone(get_distribution('strawberry'))
+        self.assertIsNone(get_distribution('banana'))
+
+        # Now check that it works well in both situations, when egg-info
+        # is a file and directory respectively.
+        dist = get_distribution('cheese', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'cheese')
+
+        dist = get_distribution('bacon', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'bacon')
+
+        dist = get_distribution('banana', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'banana')
+
+        dist = get_distribution('strawberry', use_egg_info=True)
+        self.assertIsInstance(dist, EggInfoDistribution)
+        self.assertEqual(dist.name, 'strawberry')
+
+    def test_get_file_users(self):
+        # Test the iteration of distributions that use a file.
+        name = 'towel_stuff-0.1'
+        path = os.path.join(self.fake_dists_path, name,
+                            'towel_stuff', '__init__.py')
+        for dist in get_file_users(path):
+            self.assertIsInstance(dist, Distribution)
+            self.assertEqual(dist.name, name)
+
+    @requires_zlib
+    def test_provides(self):
+        # Test for looking up distributions by what they provide
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        l = [dist.name for dist in provides_distribution('truffles')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.0')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.0',
+                                                         use_egg_info=True)]
+        checkLists(l, ['choxie', 'cheese'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.1.2')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '1.1')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles',
+                                                         '!=1.1,<=2.0')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in provides_distribution('truffles',
+                                                         '!=1.1,<=2.0',
+                                                          use_egg_info=True)]
+        checkLists(l, ['choxie', 'bacon', 'cheese'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.0')]
+        checkLists(l, ['towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.5')]
+        checkLists(l, [])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>1.5',
+                                                         use_egg_info=True)]
+        checkLists(l, ['bacon'])
+
+        l = [dist.name for dist in provides_distribution('truffles', '>=1.0')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>=0.5',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('strawberry', '>0.6',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
+        l = [dist.name for dist in provides_distribution('banana', '0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('banana', '>=0.3',
+                                                         use_egg_info=True)]
+        checkLists(l, ['coconuts-aster'])
+
+        l = [dist.name for dist in provides_distribution('banana', '!=0.4',
+                                                         use_egg_info=True)]
+        checkLists(l, [])
+
+    @requires_zlib
+    def test_obsoletes(self):
+        # Test looking for distributions based on what they obsolete
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '1.0')]
+        checkLists(l, [])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '1.0',
+                                                          use_egg_info=True)]
+        checkLists(l, ['cheese', 'bacon'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.8')]
+        checkLists(l, ['choxie'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.8',
+                                                          use_egg_info=True)]
+        checkLists(l, ['choxie', 'cheese'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.9.6')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles',
+                                                          '0.5.2.3')]
+        checkLists(l, ['choxie', 'towel-stuff'])
+
+        l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')]
+        checkLists(l, ['towel-stuff'])
+
+    @requires_zlib
+    def test_yield_distribution(self):
+        # tests the internal function _yield_distributions
+        checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+        eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'),
+                ('truffles', '5.0'), ('cheese', '2.0.2'),
+                ('coconuts-aster', '10.3'), ('nut', 'funkyversion')]
+        dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'),
+                 ('towel-stuff', '0.1'), ('babar', '0.1')]
+
+        checkLists([], _yield_distributions(False, False, sys.path))
+
+        found = [(dist.name, dist.version)
+                 for dist in _yield_distributions(False, True, sys.path)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(eggs, found)
+
+        found = [(dist.name, dist.version)
+                 for dist in _yield_distributions(True, False, sys.path)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(dists, found)
+
+        found = [(dist.name, dist.version)
+                 for dist in _yield_distributions(True, True, sys.path)
+                 if dist.path.startswith(self.fake_dists_path)]
+        checkLists(dists + eggs, found)
+
+
+class DataFilesTestCase(GlobTestCaseBase):
+
+    def assertRulesMatch(self, rules, spec):
+        tempdir = self.build_files_tree(spec)
+        expected = self.clean_tree(spec)
+        result = get_resources_dests(tempdir, rules)
+        self.assertEqual(expected, result)
+
+    def clean_tree(self, spec):
+        files = {}
+        for path, value in spec.items():
+            if value is not None:
+                files[path] = value
+        return files
+
+    def test_simple_glob(self):
+        rules = [('', '*.tpl', '{data}')]
+        spec = {'coucou.tpl': '{data}/coucou.tpl',
+                'Donotwant': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_multiple_match(self):
+        rules = [('scripts', '*.bin', '{appdata}'),
+                 ('scripts', '*', '{appscript}')]
+        spec = {'scripts/script.bin': '{appscript}/script.bin',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match(self):
+        rules = [('scripts', '*.{bin,sh}', '{appscript}')]
+        spec = {'scripts/script.bin': '{appscript}/script.bin',
+                'scripts/babar.sh':  '{appscript}/babar.sh',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match_multiple(self):
+        rules = [('scripts', 'script{s,}.{bin,sh}', '{appscript}')]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripts/script.sh':  '{appscript}/script.sh',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_set_match_exclude(self):
+        rules = [('scripts', '*', '{appscript}'),
+                 ('', os.path.join('**', '*.sh'), None)]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripts/script.sh':  None,
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_glob_in_base(self):
+        rules = [('scrip*', '*.bin', '{appscript}')]
+        spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+                'scripouille/babar.bin': '{appscript}/babar.bin',
+                'scriptortu/lotus.bin': '{appscript}/lotus.bin',
+                'Babarlikestrawberry': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_recursive_glob(self):
+        rules = [('', os.path.join('**', '*.bin'), '{binary}')]
+        spec = {'binary0.bin': '{binary}/binary0.bin',
+                'scripts/binary1.bin': '{binary}/scripts/binary1.bin',
+                'scripts/bin/binary2.bin': '{binary}/scripts/bin/binary2.bin',
+                'you/kill/pandabear.guy': None}
+        self.assertRulesMatch(rules, spec)
+
+    def test_final_exemple_glob(self):
+        rules = [
+            ('mailman/database/schemas/', '*', '{appdata}/schemas'),
+            ('', os.path.join('**', '*.tpl'), '{appdata}/templates'),
+            ('', os.path.join('developer-docs', '**', '*.txt'), '{doc}'),
+            ('', 'README', '{doc}'),
+            ('mailman/etc/', '*', '{config}'),
+            ('mailman/foo/', os.path.join('**', 'bar', '*.cfg'),
+             '{config}/baz'),
+            ('mailman/foo/', os.path.join('**', '*.cfg'), '{config}/hmm'),
+            ('', 'some-new-semantic.sns', '{funky-crazy-category}'),
+        ]
+        spec = {
+            'README': '{doc}/README',
+            'some.tpl': '{appdata}/templates/some.tpl',
+            'some-new-semantic.sns':
+                '{funky-crazy-category}/some-new-semantic.sns',
+            'mailman/database/mailman.db': None,
+            'mailman/database/schemas/blah.schema':
+                '{appdata}/schemas/blah.schema',
+            'mailman/etc/my.cnf': '{config}/my.cnf',
+            'mailman/foo/some/path/bar/my.cfg':
+                '{config}/hmm/some/path/bar/my.cfg',
+            'mailman/foo/some/path/other.cfg':
+                '{config}/hmm/some/path/other.cfg',
+            'developer-docs/index.txt': '{doc}/developer-docs/index.txt',
+            'developer-docs/api/toc.txt': '{doc}/developer-docs/api/toc.txt',
+        }
+        self.maxDiff = None
+        self.assertRulesMatch(rules, spec)
+
+    def test_get_file(self):
+        # Create a fake dist
+        temp_site_packages = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, temp_site_packages)
+
+        dist_name = 'test'
+        dist_info = os.path.join(temp_site_packages, 'test-0.1.dist-info')
+        os.mkdir(dist_info)
+
+        metadata_path = os.path.join(dist_info, 'METADATA')
+        resources_path = os.path.join(dist_info, 'RESOURCES')
+
+        with open(metadata_path, 'w') as fp:
+            fp.write(dedent("""\
+                Metadata-Version: 1.2
+                Name: test
+                Version: 0.1
+                Summary: test
+                Author: me
+                """))
+
+        test_path = 'test.cfg'
+
+        fd, test_resource_path = tempfile.mkstemp()
+        os.close(fd)
+        self.addCleanup(os.remove, test_resource_path)
+
+        with open(test_resource_path, 'w') as fp:
+            fp.write('Config')
+
+        with open(resources_path, 'w') as fp:
+            fp.write('%s,%s' % (test_path, test_resource_path))
+
+        # Add fake site-packages to sys.path to retrieve fake dist
+        self.addCleanup(sys.path.remove, temp_site_packages)
+        sys.path.insert(0, temp_site_packages)
+
+        # Force distutils2.database to rescan the sys.path
+        self.addCleanup(enable_cache)
+        disable_cache()
+
+        # Try to retrieve resources paths and files
+        self.assertEqual(get_file_path(dist_name, test_path),
+                         test_resource_path)
+        self.assertRaises(KeyError, get_file_path, dist_name, 'i-dont-exist')
+
+        with get_file(dist_name, test_path) as fp:
+            self.assertEqual(fp.read(), 'Config')
+        self.assertRaises(KeyError, get_file, dist_name, 'i-dont-exist')
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    load = unittest.defaultTestLoader.loadTestsFromTestCase
+    suite.addTest(load(TestDistribution))
+    suite.addTest(load(TestEggInfoDistribution))
+    suite.addTest(load(TestDatabase))
+    suite.addTest(load(DataFilesTestCase))
+    return suite
+
+
+if __name__ == "__main__":
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_depgraph.py b/distutils2/tests/test_depgraph.py
--- a/distutils2/tests/test_depgraph.py
+++ b/distutils2/tests/test_depgraph.py
@@ -1,33 +1,25 @@
-"""Tests for distutils.depgraph """
+"""Tests for distutils2.depgraph """
+import os
+import re
+import sys
+from StringIO import StringIO
+
+import distutils2.database
+from distutils2 import depgraph
 
 from distutils2.tests import unittest, support
-from distutils2 import depgraph
-from distutils2._backport import pkgutil
+from distutils2.tests.support import requires_zlib
 
-import os
-import sys
-import re
-try:
-    import cStringIO as StringIO
-except ImportError:
-    import StringIO
 
 class DepGraphTestCase(support.LoggingCatcher,
-                       support.WarningsCatcher,
                        unittest.TestCase):
 
     DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
-    DISTROS_EGG  = ('bacon', 'banana', 'strawberry', 'cheese')
+    DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese')
     BAD_EGGS = ('nut',)
 
     EDGE = re.compile(
-           r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]'
-           )
-
-    def tearDown(self):
-        super(DepGraphTestCase, self).tearDown()
-        pkgutil.enable_cache()
-        sys.path = self.sys_path
+           r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]')
 
     def checkLists(self, l1, l2):
         """ Compare two lists without taking the order into consideration """
@@ -35,17 +27,17 @@
 
     def setUp(self):
         super(DepGraphTestCase, self).setUp()
-        path = os.path.join(os.path.dirname(__file__), '..', '_backport',
-                            'tests', 'fake_dists')
+        path = os.path.join(os.path.dirname(__file__), 'fake_dists')
         path = os.path.abspath(path)
-        self.sys_path = sys.path[:]
-        sys.path[0:0] = [path]
-        pkgutil.disable_cache()
+        sys.path.insert(0, path)
+        self.addCleanup(sys.path.remove, path)
+        self.addCleanup(distutils2.database.enable_cache)
+        distutils2.database.disable_cache()
 
     def test_generate_graph(self):
         dists = []
         for name in self.DISTROS_DIST:
-            dist = pkgutil.get_distribution(name)
+            dist = distutils2.database.get_distribution(name)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
@@ -53,23 +45,24 @@
 
         graph = depgraph.generate_graph(dists)
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[choxie]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
         self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
-        self.assertTrue(choxie in graph.reverse_list[towel])
+        self.assertIn(choxie, graph.reverse_list[towel])
         self.checkLists(graph.missing[choxie], ['nut'])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[grammar]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
         self.checkLists([], deps)
         self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[towel]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
         self.checkLists([], deps)
         self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
 
+    @requires_zlib
     def test_generate_graph_egg(self):
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
@@ -77,42 +70,42 @@
 
         graph = depgraph.generate_graph(dists)
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[choxie]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
         self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
-        self.assertTrue(choxie in graph.reverse_list[towel])
+        self.assertIn(choxie, graph.reverse_list[towel])
         self.checkLists(graph.missing[choxie], ['nut'])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[grammar]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
         self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
         self.checkLists(graph.missing[grammar], [])
-        self.assertTrue(grammar in graph.reverse_list[bacon])
+        self.assertIn(grammar, graph.reverse_list[bacon])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[towel]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
         self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
         self.checkLists(graph.missing[towel], [])
-        self.assertTrue(towel in graph.reverse_list[bacon])
+        self.assertIn(towel, graph.reverse_list[bacon])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[bacon]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[bacon]]
         self.checkLists([], deps)
         self.checkLists(graph.missing[bacon], [])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[banana]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[banana]]
         self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
         self.checkLists(graph.missing[banana], [])
-        self.assertTrue(banana in graph.reverse_list[strawberry])
+        self.assertIn(banana, graph.reverse_list[strawberry])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[strawberry]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[strawberry]]
         self.checkLists([], deps)
         self.checkLists(graph.missing[strawberry], [])
 
-        deps = [(x.name, y) for (x,y) in graph.adjacency_list[cheese]]
+        deps = [(x.name, y) for x, y in graph.adjacency_list[cheese]]
         self.checkLists([], deps)
         self.checkLists(graph.missing[cheese], [])
 
     def test_dependent_dists(self):
         dists = []
         for name in self.DISTROS_DIST:
-            dist = pkgutil.get_distribution(name)
+            dist = distutils2.database.get_distribution(name)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
@@ -127,11 +120,11 @@
         deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
         self.checkLists(['choxie'], deps)
 
-
+    @requires_zlib
     def test_dependent_dists_egg(self):
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
@@ -155,52 +148,54 @@
         deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
         self.checkLists([], deps)
 
+    @requires_zlib
     def test_graph_to_dot(self):
         expected = (
             ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
             ('grammar', 'bacon', 'truffles (>=1.2)'),
             ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
-            ('banana', 'strawberry', 'strawberry (>=0.5)')
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
         )
 
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
         graph = depgraph.generate_graph(dists)
-        buf = StringIO.StringIO()
+        buf = StringIO()
         depgraph.graph_to_dot(graph, buf)
         buf.seek(0)
         matches = []
         lines = buf.readlines()
-        for line in lines[1:-1]: # skip the first and the last lines
+        for line in lines[1:-1]:  # skip the first and the last lines
             if line[-1] == '\n':
                 line = line[:-1]
             match = self.EDGE.match(line.strip())
-            self.assertTrue(match is not None)
+            self.assertIsNot(match, None)
             matches.append(match.groups())
 
         self.checkLists(matches, expected)
 
+    @requires_zlib
     def test_graph_disconnected_to_dot(self):
         dependencies_expected = (
             ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
             ('grammar', 'bacon', 'truffles (>=1.2)'),
             ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
-            ('banana', 'strawberry', 'strawberry (>=0.5)')
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
         )
         disconnected_expected = ('cheese', 'bacon', 'strawberry')
 
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
         graph = depgraph.generate_graph(dists)
-        buf = StringIO.StringIO()
+        buf = StringIO()
         depgraph.graph_to_dot(graph, buf, skip_disconnected=False)
         buf.seek(0)
         lines = buf.readlines()
@@ -212,7 +207,7 @@
         # We also skip the attribute lines, and don't include the "{" and "}"
         # lines.
         disconnected_active = False
-        for line in lines[1:-1]: # Skip first and last line
+        for line in lines[1:-1]:  # Skip first and last line
             if line.startswith('subgraph disconnected'):
                 disconnected_active = True
                 continue
@@ -232,7 +227,7 @@
             if line[-1] == '\n':
                 line = line[:-1]
             match = self.EDGE.match(line.strip())
-            self.assertTrue(match is not None)
+            self.assertIsNot(match, None)
             dependencies_matches.append(match.groups())
 
         disconnected_matches = []
@@ -245,47 +240,50 @@
         self.checkLists(dependencies_matches, dependencies_expected)
         self.checkLists(disconnected_matches, disconnected_expected)
 
+    @requires_zlib
     def test_graph_bad_version_to_dot(self):
         expected = (
             ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
             ('grammar', 'bacon', 'truffles (>=1.2)'),
             ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
-            ('banana', 'strawberry', 'strawberry (>=0.5)')
+            ('banana', 'strawberry', 'strawberry (>=0.5)'),
         )
 
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
         graph = depgraph.generate_graph(dists)
-        buf = StringIO.StringIO()
+        buf = StringIO()
         depgraph.graph_to_dot(graph, buf)
         buf.seek(0)
         matches = []
         lines = buf.readlines()
-        for line in lines[1:-1]: # skip the first and the last lines
+        for line in lines[1:-1]:  # skip the first and the last lines
             if line[-1] == '\n':
                 line = line[:-1]
             match = self.EDGE.match(line.strip())
-            self.assertTrue(match is not None)
+            self.assertIsNot(match, None)
             matches.append(match.groups())
 
         self.checkLists(matches, expected)
 
+    @requires_zlib
     def test_repr(self):
         dists = []
         for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
-            dist = pkgutil.get_distribution(name, use_egg_info=True)
+            dist = distutils2.database.get_distribution(name, use_egg_info=True)
             self.assertNotEqual(dist, None)
             dists.append(dist)
 
         graph = depgraph.generate_graph(dists)
-        assert repr(graph)
+        self.assertTrue(repr(graph))
 
+    @requires_zlib
     def test_main(self):
-        tempout = StringIO.StringIO()
+        tempout = StringIO()
         old = sys.stdout
         sys.stdout = tempout
         oldargv = sys.argv[:]
@@ -302,7 +300,7 @@
         # checks what main did XXX could do more here
         tempout.seek(0)
         res = tempout.read()
-        self.assertTrue('towel' in res)
+        self.assertIn('towel', res)
 
 
 def test_suite():
diff --git a/distutils2/tests/test_dist.py b/distutils2/tests/test_dist.py
--- a/distutils2/tests/test_dist.py
+++ b/distutils2/tests/test_dist.py
@@ -1,19 +1,22 @@
-# -*- coding: utf-8 -*-
+"""Tests for distutils2.dist."""
+import codecs
+import os
+import sys
+import logging
+import textwrap
+import sysconfig
+import distutils2.dist
+from StringIO import StringIO
 
-"""Tests for distutils2.dist."""
-import os
-import StringIO
-import sys
-import textwrap
-
-import distutils2.dist
-from distutils2.dist import Distribution, fix_help_options
+from distutils2.dist import Distribution
 from distutils2.command import set_command
 from distutils2.command.cmd import Command
-from distutils2.errors import DistutilsModuleError, DistutilsOptionError
+from distutils2.metadata import Metadata
+from distutils2.errors import PackagingModuleError, PackagingOptionError
 from distutils2.tests import TESTFN, captured_stdout
 from distutils2.tests import support, unittest
 from distutils2.tests.support import create_distribution
+from .support import unload
 
 
 class test_dist(Command):
@@ -32,10 +35,11 @@
 
 class DistributionTestCase(support.TempdirManager,
                            support.LoggingCatcher,
-                           support.WarningsCatcher,
-                           support.EnvironGuard,
+                           support.EnvironRestorer,
                            unittest.TestCase):
 
+    restore_environ = ['HOME', 'PLAT']
+
     def setUp(self):
         super(DistributionTestCase, self).setUp()
         self.argv = sys.argv, sys.argv[:]
@@ -48,16 +52,12 @@
 
     def test_debug_mode(self):
         self.addCleanup(os.unlink, TESTFN)
-        f = open(TESTFN, "w")
-        try:
+        with open(TESTFN, "w") as f:
             f.write("[global]\n")
             f.write("command_packages = foo.bar, splat")
-        finally:
-            f.close()
 
         files = [TESTFN]
         sys.argv.append("build")
-
         __, stdout = captured_stdout(create_distribution, files)
         self.assertEqual(stdout, '')
         distutils2.dist.DEBUG = True
@@ -73,16 +73,17 @@
         my_file = os.path.join(tmp_dir, 'f')
         cls = Distribution
 
-        dist = cls(attrs={'author': u'Mister Café',
+        dist = cls(attrs={'author': u'Mister Caf\xe9',
                           'name': 'my.package',
-                          'maintainer': u'Café Junior',
-                          'summary': u'Café torréfié',
-                          'description': u'Héhéhé'})
+                          'maintainer': u'Caf\xe9 Junior',
+                          'summary': u'Caf\xe9 torr\xe9fi\xe9',
+                          'description': u'H\xe9h\xe9h\xe9'})
 
         # let's make sure the file can be written
         # with Unicode fields. they are encoded with
         # PKG_INFO_ENCODING
-        dist.metadata.write_file(open(my_file, 'w'))
+        with codecs.open(my_file, 'w', encoding='utf-8') as fp:
+            dist.metadata.write_file(fp)
 
         # regular ascii is of course always usable
         dist = cls(attrs={'author': 'Mister Cafe',
@@ -91,29 +92,38 @@
                           'summary': 'Cafe torrefie',
                           'description': 'Hehehe'})
 
-        dist.metadata.write_file(open(my_file, 'w'))
+        with open(my_file, 'w') as fp:
+            dist.metadata.write_file(fp)
 
     def test_bad_attr(self):
         Distribution(attrs={'author': 'xxx',
                             'name': 'xxx',
-                            'version': 'xxx',
+                            'version': '1.2',
                             'url': 'xxxx',
                             'badoptname': 'xxx'})
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('unknown argument', logs[0])
 
-        self.assertEqual(len(self.warnings), 1)
-        self.assertIn("Unknown distribution",
-                      self.warnings[0]['message'].args[0])
+    def test_bad_version(self):
+        Distribution(attrs={'author': 'xxx',
+                            'name': 'xxx',
+                            'version': 'xxx',
+                            'url': 'xxxx'})
+        logs = self.get_logs(logging.WARNING)
+        self.assertEqual(1, len(logs))
+        self.assertIn('not a valid version', logs[0])
 
     def test_empty_options(self):
         # an empty options dictionary should not stay in the
         # list of attributes
         Distribution(attrs={'author': 'xxx',
                             'name': 'xxx',
-                            'version': 'xxx',
+                            'version': '1.2',
                             'url': 'xxxx',
                             'options': {}})
 
-        self.assertEqual(len(self.warnings), 0)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
     def test_non_empty_options(self):
         # TODO: how to actually use options is not documented except
@@ -143,13 +153,6 @@
         self.assertEqual(dist.metadata['platform'], ['one', 'two'])
         self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
 
-    def test_announce(self):
-        # make sure the level is known
-        dist = Distribution()
-        args = ('ok',)
-        kwargs = {'level': 'ok2'}
-        self.assertRaises(TypeError, dist.announce, args, kwargs)
-
     def test_find_config_files_disable(self):
         # Bug #1180: Allow users to disable their own config file.
         temp_home = self.mkdtemp()
@@ -158,11 +161,8 @@
         else:
             user_filename = os.path.join(temp_home, "pydistutils.cfg")
 
-        f = open(user_filename, 'w')
-        try:
+        with open(user_filename, 'w') as f:
             f.write('[distutils2]\n')
-        finally:
-            f.close()
 
         def _expander(path):
             return temp_home
@@ -174,13 +174,13 @@
             all_files = d.find_config_files()
 
             d = distutils2.dist.Distribution(attrs={'script_args':
-                                             ['--no-user-cfg']})
+                                                   ['--no-user-cfg']})
             files = d.find_config_files()
         finally:
             os.path.expanduser = old_expander
 
         # make sure --no-user-cfg disables the user cfg file
-        self.assertEqual(len(all_files) - 1, len(files))
+        self.assertEqual((len(all_files) - 1), len(files))
 
     def test_special_hooks_parsing(self):
         temp_home = self.mkdtemp()
@@ -200,23 +200,26 @@
 
     def test_hooks_get_run(self):
         temp_home = self.mkdtemp()
+        module_name = os.path.split(temp_home)[-1]
+        pyname = '%s.py' % module_name
         config_file = os.path.join(temp_home, "config1.cfg")
-        hooks_module = os.path.join(temp_home, "testhooks.py")
+        hooks_module = os.path.join(temp_home, pyname)
 
-        self.write_file(config_file, textwrap.dedent('''
+        self.write_file(config_file, textwrap.dedent('''\
             [test_dist]
-            pre-hook.test = testhooks.log_pre_call
-            post-hook.test = testhooks.log_post_call'''))
+            pre-hook.test = %(modname)s.log_pre_call
+            post-hook.test = %(modname)s.log_post_call'''
+            % {'modname': module_name}))
 
-        self.write_file(hooks_module, textwrap.dedent('''
-        record = []
+        self.write_file(hooks_module, textwrap.dedent('''\
+            record = []
 
-        def log_pre_call(cmd):
-            record.append('pre-%s' % cmd.get_command_name())
+            def log_pre_call(cmd):
+                record.append('pre-%s' % cmd.get_command_name())
 
-        def log_post_call(cmd):
-            record.append('post-%s' % cmd.get_command_name())
-        '''))
+            def log_post_call(cmd):
+                record.append('post-%s' % cmd.get_command_name())
+            '''))
 
         set_command('distutils2.tests.test_dist.test_dist')
         d = create_distribution([config_file])
@@ -224,15 +227,12 @@
 
         # prepare the call recorders
         sys.path.append(temp_home)
-        from testhooks import record
-
-        self.addCleanup(setattr, cmd, 'run', cmd.run)
-        self.addCleanup(setattr, cmd, 'finalize_options',
-                        cmd.finalize_options)
+        self.addCleanup(sys.path.remove, temp_home)
+        self.addCleanup(unload, module_name)
+        record = __import__(module_name).record
 
         cmd.run = lambda: record.append('run')
         cmd.finalize_options = lambda: record.append('finalize')
-
         d.run_command('test_dist')
 
         self.assertEqual(record, ['finalize',
@@ -244,7 +244,7 @@
         temp_home = self.mkdtemp()
         config_file = os.path.join(temp_home, "config1.cfg")
 
-        self.write_file(config_file, textwrap.dedent('''
+        self.write_file(config_file, textwrap.dedent('''\
             [test_dist]
             pre-hook.test = nonexistent.dotted.name'''))
 
@@ -253,13 +253,13 @@
         cmd = d.get_command_obj("test_dist")
         cmd.ensure_finalized()
 
-        self.assertRaises(DistutilsModuleError, d.run_command, 'test_dist')
+        self.assertRaises(PackagingModuleError, d.run_command, 'test_dist')
 
     def test_hooks_callable(self):
         temp_home = self.mkdtemp()
         config_file = os.path.join(temp_home, "config1.cfg")
 
-        self.write_file(config_file, textwrap.dedent('''
+        self.write_file(config_file, textwrap.dedent('''\
             [test_dist]
             pre-hook.test = distutils2.tests.test_dist.__doc__'''))
 
@@ -268,11 +268,12 @@
         cmd = d.get_command_obj("test_dist")
         cmd.ensure_finalized()
 
-        self.assertRaises(DistutilsOptionError, d.run_command, 'test_dist')
+        self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
 
 
-class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
-                       support.LoggingCatcher, unittest.TestCase):
+class MetadataTestCase(support.TempdirManager,
+                       support.LoggingCatcher,
+                       unittest.TestCase):
 
     def setUp(self):
         super(MetadataTestCase, self).setUp()
@@ -288,10 +289,10 @@
                  "version": "1.0"}
         dist = Distribution(attrs)
         meta = self.format_metadata(dist)
-        self.assertTrue("Metadata-Version: 1.0" in meta)
-        self.assertTrue("provides:" not in meta.lower())
-        self.assertTrue("requires:" not in meta.lower())
-        self.assertTrue("obsoletes:" not in meta.lower())
+        self.assertIn("Metadata-Version: 1.0", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertNotIn("requires:", meta.lower())
+        self.assertNotIn("obsoletes:", meta.lower())
 
     def test_provides_dist(self):
         attrs = {"name": "package",
@@ -301,9 +302,9 @@
         self.assertEqual(dist.metadata['Provides-Dist'],
                          ["package", "package.sub"])
         meta = self.format_metadata(dist)
-        self.assertTrue("Metadata-Version: 1.2" in meta)
-        self.assertTrue("requires:" not in meta.lower())
-        self.assertTrue("obsoletes:" not in meta.lower())
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("requires:", meta.lower())
+        self.assertNotIn("obsoletes:", meta.lower())
 
     def _test_provides_illegal(self):
         # XXX to do: check the versions
@@ -320,11 +321,11 @@
         self.assertEqual(dist.metadata['Requires-Dist'],
                          ["other", "another (==1.0)"])
         meta = self.format_metadata(dist)
-        self.assertTrue("Metadata-Version: 1.2" in meta)
-        self.assertTrue("provides:" not in meta.lower())
-        self.assertTrue("Requires-Dist: other" in meta)
-        self.assertTrue("Requires-Dist: another (==1.0)" in meta)
-        self.assertTrue("obsoletes:" not in meta.lower())
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertIn("Requires-Dist: other", meta)
+        self.assertIn("Requires-Dist: another (==1.0)", meta)
+        self.assertNotIn("obsoletes:", meta.lower())
 
     def _test_requires_illegal(self):
         # XXX
@@ -341,11 +342,11 @@
         self.assertEqual(dist.metadata['Obsoletes-Dist'],
                          ["other", "another (<1.0)"])
         meta = self.format_metadata(dist)
-        self.assertTrue("Metadata-Version: 1.2" in meta)
-        self.assertTrue("provides:" not in meta.lower())
-        self.assertTrue("requires:" not in meta.lower())
-        self.assertTrue("Obsoletes-Dist: other" in meta)
-        self.assertTrue("Obsoletes-Dist: another (<1.0)" in meta)
+        self.assertIn("Metadata-Version: 1.2", meta)
+        self.assertNotIn("provides:", meta.lower())
+        self.assertNotIn("requires:", meta.lower())
+        self.assertIn("Obsoletes-Dist: other", meta)
+        self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
 
     def _test_obsoletes_illegal(self):
         # XXX
@@ -355,7 +356,7 @@
                            "obsoletes": ["my.pkg (splat)"]})
 
     def format_metadata(self, dist):
-        sio = StringIO.StringIO()
+        sio = StringIO()
         dist.metadata.write_file(sio)
         return sio.getvalue()
 
@@ -369,11 +370,8 @@
 
         temp_dir = self.mkdtemp()
         user_filename = os.path.join(temp_dir, user_filename)
-        f = open(user_filename, 'w')
-        try:
+        with open(user_filename, 'w') as f:
             f.write('.')
-        finally:
-            f.close()
 
         dist = Distribution()
 
@@ -381,7 +379,7 @@
         if sys.platform in ('linux', 'darwin'):
             os.environ['HOME'] = temp_dir
             files = dist.find_config_files()
-            self.assertTrue(user_filename in files)
+            self.assertIn(user_filename, files)
 
         # win32-style
         if sys.platform == 'win32':
@@ -390,22 +388,17 @@
             files = dist.find_config_files()
             self.assertIn(user_filename, files)
 
-    def test_fix_help_options(self):
-        help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
-        fancy_options = fix_help_options(help_tuples)
-        self.assertEqual(fancy_options[0], ('a', 'b', 'c'))
-        self.assertEqual(fancy_options[1], (1, 2, 3))
-
     def test_show_help(self):
         # smoke test, just makes sure some help is displayed
         dist = Distribution()
         sys.argv = []
-        dist.help = 1
-        dist.script_name = 'setup.py'
+        dist.help = True
+        dist.script_name = os.path.join(sysconfig.get_path('scripts'),
+                                        'pysetup')
         __, stdout = captured_stdout(dist.parse_command_line)
         output = [line for line in stdout.split('\n')
                   if line.strip() != '']
-        self.assertTrue(len(output) > 0)
+        self.assertGreater(len(output), 0)
 
     def test_description(self):
         desc = textwrap.dedent("""\
@@ -420,7 +413,7 @@
         dist = distutils2.dist.Distribution(attrs)
         meta = self.format_metadata(dist)
         meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
-        self.assertTrue(desc in meta)
+        self.assertIn(desc, meta)
 
     def test_read_metadata(self):
         attrs = {"name": "package",
@@ -432,14 +425,13 @@
                  "requires_dist": ['foo']}
 
         dist = Distribution(attrs)
-        metadata = dist.metadata
-
-        # write it then reloads it
-        PKG_INFO = StringIO.StringIO()
-        metadata.write_file(PKG_INFO)
+        PKG_INFO = StringIO()
+        dist.metadata.write_file(PKG_INFO)
         PKG_INFO.seek(0)
 
+        metadata = Metadata()
         metadata.read_file(PKG_INFO)
+
         self.assertEqual(metadata['name'], "package")
         self.assertEqual(metadata['version'], "1.0")
         self.assertEqual(metadata['summary'], "xxx")
diff --git a/distutils2/tests/test_extension.py b/distutils2/tests/test_extension.py
--- a/distutils2/tests/test_extension.py
+++ b/distutils2/tests/test_extension.py
@@ -1,4 +1,4 @@
-"""Tests for distutils.extension."""
+"""Tests for distutils2.extension."""
 import os
 
 from distutils2.compiler.extension import Extension
diff --git a/distutils2/tests/test_index_dist.py b/distutils2/tests/test_index_dist.py
deleted file mode 100644
--- a/distutils2/tests/test_index_dist.py
+++ /dev/null
@@ -1,282 +0,0 @@
-"""Tests for the distutils2.index.dist module."""
-
-import os
-
-from distutils2.tests.pypi_server import use_pypi_server
-from distutils2.tests import run_unittest
-from distutils2.tests import unittest
-from distutils2.tests.support import TempdirManager
-from distutils2.version import VersionPredicate
-from distutils2.index.errors import HashDoesNotMatch, UnsupportedHashName
-from distutils2.index.dist import (ReleaseInfo, ReleasesList, DistInfo,
-                                   split_archive_name, get_infos_from_url)
-
-
-def Dist(*args, **kwargs):
-    # DistInfo takes a release as a first parameter, avoid this in tests.
-    return DistInfo(None, *args, **kwargs)
-
-
-class TestReleaseInfo(unittest.TestCase):
-
-    def test_instantiation(self):
-        # Test the DistInfo class provides us the good attributes when
-        # given on construction
-        release = ReleaseInfo("FooBar", "1.1")
-        self.assertEqual("FooBar", release.name)
-        self.assertEqual("1.1", "%s" % release.version)
-
-    def test_add_dist(self):
-        # empty distribution type should assume "sdist"
-        release = ReleaseInfo("FooBar", "1.1")
-        release.add_distribution(url="http://example.org/")
-        # should not fail
-        release['sdist']
-
-    def test_get_unknown_distribution(self):
-        # should raise a KeyError
-        pass
-
-    def test_get_infos_from_url(self):
-        # Test that the the URLs are parsed the right way
-        url_list = {
-            'FooBar-1.1.0.tar.gz': {
-                'name': 'foobar',  # lowercase the name
-                'version': '1.1.0',
-            },
-            'Foo-Bar-1.1.0.zip': {
-                'name': 'foo-bar',  # keep the dash
-                'version': '1.1.0',
-            },
-            'foobar-1.1b2.tar.gz#md5=123123123123123': {
-                'name': 'foobar',
-                'version': '1.1b2',
-                'url': 'http://example.org/foobar-1.1b2.tar.gz',  # no hash
-                'hashval': '123123123123123',
-                'hashname': 'md5',
-            },
-            'foobar-1.1-rc2.tar.gz': {  # use suggested name
-                'name': 'foobar',
-                'version': '1.1c2',
-                'url': 'http://example.org/foobar-1.1-rc2.tar.gz',
-            }
-        }
-
-        for url, attributes in url_list.items():
-            # for each url
-            infos = get_infos_from_url("http://example.org/" + url)
-            for attribute, expected in attributes.items():
-                got = infos.get(attribute)
-                if attribute == "version":
-                    self.assertEqual("%s" % got, expected)
-                else:
-                    self.assertEqual(got, expected)
-
-    def test_split_archive_name(self):
-        # Test we can split the archive names
-        names = {
-            'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
-            'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
-            'foobarbaz-1.0': ('foobarbaz', '1.0'),
-        }
-        for name, results in names.items():
-            self.assertEqual(results, split_archive_name(name))
-
-
-class TestDistInfo(TempdirManager, unittest.TestCase):
-
-    def test_get_url(self):
-        # Test that the url property works well
-
-        d = Dist(url="test_url")
-        self.assertDictEqual(d.url, {
-            "url": "test_url",
-            "is_external": True,
-            "hashname": None,
-            "hashval": None,
-        })
-
-        # add a new url
-        d.add_url(url="internal_url", is_external=False)
-        self.assertEqual(d._url, None)
-        self.assertDictEqual(d.url, {
-            "url": "internal_url",
-            "is_external": False,
-            "hashname": None,
-            "hashval": None,
-        })
-        self.assertEqual(2, len(d.urls))
-
-    def test_comparison(self):
-        # Test that we can compare DistInfoributionInfoList
-        foo1 = ReleaseInfo("foo", "1.0")
-        foo2 = ReleaseInfo("foo", "2.0")
-        bar = ReleaseInfo("bar", "2.0")
-        # assert we use the version to compare
-        self.assertTrue(foo1 < foo2)
-        self.assertFalse(foo1 > foo2)
-        self.assertFalse(foo1 == foo2)
-
-        # assert we can't compare dists with different names
-        self.assertRaises(TypeError, foo1.__eq__, bar)
-
-    @use_pypi_server("downloads_with_md5")
-    def test_download(self, server):
-        # Download is possible, and the md5 is checked if given
-
-        url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address
-        # check md5 if given
-        dist = Dist(url=url, hashname="md5",
-                    hashval="fe18804c5b722ff024cabdf514924fc4")
-        dist.download(self.mkdtemp())
-
-        # a wrong md5 fails
-        dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5")
-
-        self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp())
-
-        # we can omit the md5 hash
-        dist3 = Dist(url=url)
-        dist3.download(self.mkdtemp())
-
-        # and specify a temporary location
-        # for an already downloaded dist
-        path1 = self.mkdtemp()
-        dist3.download(path=path1)
-        # and for a new one
-        path2_base = self.mkdtemp()
-        dist4 = Dist(url=url)
-        path2 = dist4.download(path=path2_base)
-        self.assertTrue(path2_base in path2)
-
-    def test_hashname(self):
-        # Invalid hashnames raises an exception on assignation
-        Dist(hashname="md5", hashval="value")
-
-        self.assertRaises(UnsupportedHashName, Dist,
-                          hashname="invalid_hashname",
-                          hashval="value")
-
-    @use_pypi_server('downloads_with_md5')
-    def test_unpack(self, server):
-        url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address
-        dist1 = Dist(url=url)
-        # doing an unpack
-        dist1_here = self.mkdtemp()
-        dist1_there = dist1.unpack(path=dist1_here)
-        # assert we unpack to the path provided
-        self.assertEqual(dist1_here, dist1_there)
-        dist1_result = os.listdir(dist1_there)
-        self.assertIn('paf', dist1_result)
-        os.remove(os.path.join(dist1_there, 'paf'))
-
-        # Test unpack works without a path argument
-        dist2 = Dist(url=url)
-        # doing an unpack
-        dist2_there = dist2.unpack()
-        dist2_result = os.listdir(dist2_there)
-        self.assertIn('paf', dist2_result)
-        os.remove(os.path.join(dist2_there, 'paf'))
-
-    def test_hashname(self):
-        # Invalid hashnames raises an exception on assignation
-        Dist(hashname="md5", hashval="value")
-
-        self.assertRaises(UnsupportedHashName, Dist,
-                          hashname="invalid_hashname",
-                          hashval="value")
-
-
-class TestReleasesList(unittest.TestCase):
-
-    def test_filter(self):
-        # Test we filter the distributions the right way, using version
-        # predicate match method
-        releases = ReleasesList('FooBar', (
-            ReleaseInfo("FooBar", "1.1"),
-            ReleaseInfo("FooBar", "1.1.1"),
-            ReleaseInfo("FooBar", "1.2"),
-            ReleaseInfo("FooBar", "1.2.1"),
-        ))
-        filtered = releases.filter(VersionPredicate("FooBar (<1.2)"))
-        self.assertNotIn(releases[2], filtered)
-        self.assertNotIn(releases[3], filtered)
-        self.assertIn(releases[0], filtered)
-        self.assertIn(releases[1], filtered)
-
-    def test_append(self):
-        # When adding a new item to the list, the behavior is to test if
-        # a release with the same name and version number already exists,
-        # and if so, to add a new distribution for it. If the distribution type
-        # is already defined too, add url informations to the existing DistInfo
-        # object.
-
-        releases = ReleasesList("FooBar", [
-            ReleaseInfo("FooBar", "1.1", url="external_url",
-                        dist_type="sdist"),
-        ])
-        self.assertEqual(1, len(releases))
-        releases.add_release(release=ReleaseInfo("FooBar", "1.1",
-                                                 url="internal_url",
-                                                 is_external=False,
-                                                 dist_type="sdist"))
-        self.assertEqual(1, len(releases))
-        self.assertEqual(2, len(releases[0]['sdist'].urls))
-
-        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
-                                                 dist_type="sdist"))
-        self.assertEqual(2, len(releases))
-
-        # when adding a distribution whith a different type, a new distribution
-        # has to be added.
-        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
-                                                 dist_type="bdist"))
-        self.assertEqual(2, len(releases))
-        self.assertEqual(2, len(releases[1].dists))
-
-    def test_prefer_final(self):
-        # Can order the distributions using prefer_final
-
-        fb10 = ReleaseInfo("FooBar", "1.0")  # final distribution
-        fb11a = ReleaseInfo("FooBar", "1.1a1")  # alpha
-        fb12a = ReleaseInfo("FooBar", "1.2a1")  # alpha
-        fb12b = ReleaseInfo("FooBar", "1.2b1")  # beta
-        dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b])
-
-        dists.sort_releases(prefer_final=True)
-        self.assertEqual(fb10, dists[0])
-
-        dists.sort_releases(prefer_final=False)
-        self.assertEqual(fb12b, dists[0])
-
-#    def test_prefer_source(self):
-#        # Ordering support prefer_source
-#        fb_source = Dist("FooBar", "1.0", type="source")
-#        fb_binary = Dist("FooBar", "1.0", type="binary")
-#        fb2_binary = Dist("FooBar", "2.0", type="binary")
-#        dists = ReleasesList([fb_binary, fb_source])
-#
-#        dists.sort_distributions(prefer_source=True)
-#        self.assertEqual(fb_source, dists[0])
-#
-#        dists.sort_distributions(prefer_source=False)
-#        self.assertEqual(fb_binary, dists[0])
-#
-#        dists.append(fb2_binary)
-#        dists.sort_distributions(prefer_source=True)
-#        self.assertEqual(fb2_binary, dists[0])
-
-    def test_get_last(self):
-        dists = ReleasesList('Foo')
-        self.assertEqual(dists.get_last('Foo 1.0'), None)
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestDistInfo))
-    suite.addTest(unittest.makeSuite(TestReleaseInfo))
-    suite.addTest(unittest.makeSuite(TestReleasesList))
-    return suite
-
-if __name__ == '__main__':
-    run_unittest(test_suite())
diff --git a/distutils2/tests/test_index_simple.py b/distutils2/tests/test_index_simple.py
deleted file mode 100644
--- a/distutils2/tests/test_index_simple.py
+++ /dev/null
@@ -1,322 +0,0 @@
-"""Tests for the pypi.simple module.
-
-"""
-import sys
-import os
-import urllib2
-
-from distutils2.index.simple import Crawler
-from distutils2.tests import unittest
-from distutils2.tests.support import TempdirManager, LoggingCatcher
-from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer,
-                                          PYPI_DEFAULT_STATIC_PATH)
-
-
-class SimpleCrawlerTestCase(TempdirManager,
-                            LoggingCatcher,
-                            unittest.TestCase):
-
-    def _get_simple_crawler(self, server, base_url="/simple/", hosts=None,
-                          *args, **kwargs):
-        """Build and return a SimpleIndex instance, with the test server
-        urls
-        """
-        if hosts is None:
-            hosts = (server.full_address.replace("http://", ""),)
-        kwargs['hosts'] = hosts
-        return Crawler(server.full_address + base_url, *args,
-            **kwargs)
-
-    @use_pypi_server()
-    def test_bad_urls(self, server):
-        crawler = Crawler()
-        url = 'http://127.0.0.1:0/nonesuch/test_simple'
-        try:
-            v = crawler._open_url(url)
-        except Exception, v:
-            self.assertTrue(url in str(v))
-        else:
-            self.assertTrue(isinstance(v, urllib2.HTTPError))
-
-        # issue 16
-        # easy_install inquant.contentmirror.plone breaks because of a typo
-        # in its home URL
-        crawler = Crawler(hosts=('example.org',))
-        url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk'
-        try:
-            v = crawler._open_url(url)
-        except Exception, v:
-            self.assertTrue(url in str(v))
-        else:
-            self.assertTrue(isinstance(v, urllib2.HTTPError))
-
-        def _urlopen(*args):
-            import httplib
-            raise httplib.BadStatusLine('line')
-
-        old_urlopen = urllib2.urlopen
-        urllib2.urlopen = _urlopen
-        url = 'http://example.org'
-        try:
-            try:
-                v = crawler._open_url(url)
-            except Exception, v:
-                self.assertTrue('line' in str(v))
-            else:
-                raise AssertionError('Should have raise here!')
-        finally:
-            urllib2.urlopen = old_urlopen
-
-        # issue 20
-        url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
-        try:
-            crawler._open_url(url)
-        except Exception, v:
-            self.assertTrue('nonnumeric port' in str(v))
-
-        # issue #160
-        if sys.version_info[0] == 2 and sys.version_info[1] == 7:
-            # this should not fail
-            url = server.full_address
-            page = ('<a href="http://www.famfamfam.com]('
-                    'http://www.famfamfam.com/">')
-            crawler._process_url(url, page)
-
-    @use_pypi_server("test_found_links")
-    def test_found_links(self, server):
-        # Browse the index, asking for a specified release version
-        # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
-        crawler = self._get_simple_crawler(server)
-        last_release = crawler.get_release("foobar")
-
-        # we have scanned the index page
-        self.assertIn(server.full_address + "/simple/foobar/",
-            crawler._processed_urls)
-
-        # we have found 4 releases in this page
-        self.assertEqual(len(crawler._projects["foobar"]), 4)
-
-        # and returned the most recent one
-        self.assertEqual("%s" % last_release.version, '2.0.1')
-
-    def test_is_browsable(self):
-        crawler = Crawler(follow_externals=False)
-        self.assertTrue(crawler._is_browsable(crawler.index_url + "test"))
-
-        # Now, when following externals, we can have a list of hosts to trust.
-        # and don't follow other external links than the one described here.
-        crawler = Crawler(hosts=["pypi.python.org", "example.org"],
-                                   follow_externals=True)
-        good_urls = (
-            "http://pypi.python.org/foo/bar",
-            "http://pypi.python.org/simple/foobar",
-            "http://example.org",
-            "http://example.org/",
-            "http://example.org/simple/",
-        )
-        bad_urls = (
-            "http://python.org",
-            "http://example.tld",
-        )
-
-        for url in good_urls:
-            self.assertTrue(crawler._is_browsable(url))
-
-        for url in bad_urls:
-            self.assertFalse(crawler._is_browsable(url))
-
-        # allow all hosts
-        crawler = Crawler(follow_externals=True, hosts=("*",))
-        self.assertTrue(crawler._is_browsable("http://an-external.link/path"))
-        self.assertTrue(crawler._is_browsable("pypi.example.org/a/path"))
-
-        # specify a list of hosts we want to allow
-        crawler = Crawler(follow_externals=True,
-                                   hosts=("*.example.org",))
-        self.assertFalse(crawler._is_browsable("http://an-external.link/path"))
-        self.assertTrue(crawler._is_browsable("http://pypi.example.org/a/path"))
-
-    @use_pypi_server("with_externals")
-    def test_follow_externals(self, server):
-        # Include external pages
-        # Try to request the package index, wich contains links to "externals"
-        # resources. They have to  be scanned too.
-        crawler = self._get_simple_crawler(server, follow_externals=True)
-        crawler.get_release("foobar")
-        self.assertIn(server.full_address + "/external/external.html",
-            crawler._processed_urls)
-
-    @use_pypi_server("with_real_externals")
-    def test_restrict_hosts(self, server):
-        # Only use a list of allowed hosts is possible
-        # Test that telling the simple pyPI client to not retrieve external
-        # works
-        crawler = self._get_simple_crawler(server, follow_externals=False)
-        crawler.get_release("foobar")
-        self.assertNotIn(server.full_address + "/external/external.html",
-            crawler._processed_urls)
-
-    @use_pypi_server(static_filesystem_paths=["with_externals"],
-        static_uri_paths=["simple", "external"])
-    def test_links_priority(self, server):
-        # Download links from the pypi simple index should be used before
-        # external download links.
-        # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
-        #
-        # Usecase :
-        # - someone uploads a package on pypi, a md5 is generated
-        # - someone manually coindexes this link (with the md5 in the url) onto
-        #   an external page accessible from the package page.
-        # - someone reuploads the package (with a different md5)
-        # - while easy_installing, an MD5 error occurs because the external
-        # link is used
-        # -> The index should use the link from pypi, not the external one.
-
-        # start an index server
-        index_url = server.full_address + '/simple/'
-
-        # scan a test index
-        crawler = Crawler(index_url, follow_externals=True)
-        releases = crawler.get_releases("foobar")
-        server.stop()
-
-        # we have only one link, because links are compared without md5
-        self.assertEqual(1, len(releases))
-        self.assertEqual(1, len(releases[0].dists))
-        # the link should be from the index
-        self.assertEqual(2, len(releases[0].dists['sdist'].urls))
-        self.assertEqual('12345678901234567',
-                         releases[0].dists['sdist'].url['hashval'])
-        self.assertEqual('md5', releases[0].dists['sdist'].url['hashname'])
-
-    @use_pypi_server(static_filesystem_paths=["with_norel_links"],
-        static_uri_paths=["simple", "external"])
-    def test_not_scan_all_links(self, server):
-        # Do not follow all index page links.
-        # The links not tagged with rel="download" and rel="homepage" have
-        # to not be processed by the package index, while processing "pages".
-
-        # process the pages
-        crawler = self._get_simple_crawler(server, follow_externals=True)
-        crawler.get_releases("foobar")
-        # now it should have processed only pages with links rel="download"
-        # and rel="homepage"
-        self.assertIn("%s/simple/foobar/" % server.full_address,
-            crawler._processed_urls)  # it's the simple index page
-        self.assertIn("%s/external/homepage.html" % server.full_address,
-            crawler._processed_urls)  # the external homepage is rel="homepage"
-        self.assertNotIn("%s/external/nonrel.html" % server.full_address,
-            crawler._processed_urls)  # this link contains no rel=*
-        self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address,
-            crawler._processed_urls)  # linked from simple index (no rel)
-        self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address,
-            crawler._processed_urls)  # linked from simple index (rel)
-        self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address,
-            crawler._processed_urls)  # linked from external homepage (rel)
-
-    def test_uses_mirrors(self):
-        # When the main repository seems down, try using the given mirrors"""
-        server = PyPIServer("foo_bar_baz")
-        mirror = PyPIServer("foo_bar_baz")
-        mirror.start()  # we dont start the server here
-
-        try:
-            # create the index using both servers
-            crawler = Crawler(server.full_address + "/simple/",
-                hosts=('*',), timeout=1,  # set the timeout to 1s for the tests
-                mirrors=[mirror.full_address])
-
-            # this should not raise a timeout
-            self.assertEqual(4, len(crawler.get_releases("foo")))
-        finally:
-            mirror.stop()
-
-    def test_simple_link_matcher(self):
-        # Test that the simple link matcher yields the right links"""
-        crawler = Crawler(follow_externals=False)
-
-        # Here, we define:
-        #   1. one link that must be followed, cause it's a download one
-        #   2. one link that must *not* be followed, cause the is_browsable
-        #      returns false for it.
-        #   3. one link that must be followed cause it's a homepage that is
-        #      browsable
-        #   4. one link that must be followed, because it contain a md5 hash
-        self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url))
-        self.assertFalse(crawler._is_browsable("http://dl-link2"))
-        content = """
-        <a href="http://dl-link1" rel="download">download_link1</a>
-        <a href="http://dl-link2" rel="homepage">homepage_link1</a>
-        <a href="%(index_url)stest" rel="homepage">homepage_link2</a>
-        <a href="%(index_url)stest/foobar-1.tar.gz#md5=abcdef>download_link2</a>
-        """ % {'index_url': crawler.index_url }
-
-        # Test that the simple link matcher yield the good links.
-        generator = crawler._simple_link_matcher(content, crawler.index_url)
-        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' % crawler.index_url,
-                         True), generator.next())
-        self.assertEqual(('http://dl-link1', True), generator.next())
-        self.assertEqual(('%stest' % crawler.index_url, False),
-                         generator.next())
-        self.assertRaises(StopIteration, generator.next)
-
-        # Follow the external links is possible (eg. homepages)
-        crawler.follow_externals = True
-        generator = crawler._simple_link_matcher(content, crawler.index_url)
-        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' % crawler.index_url,
-                         True), generator.next())
-        self.assertEqual(('http://dl-link1', True), generator.next())
-        self.assertEqual(('http://dl-link2', False), generator.next())
-        self.assertEqual(('%stest' % crawler.index_url, False),
-                         generator.next())
-        self.assertRaises(StopIteration, generator.next)
-
-    def test_browse_local_files(self):
-        # Test that we can browse local files"""
-        index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH,
-                                  "test_found_links", "simple"])
-        crawler = Crawler(index_path)
-        dists = crawler.get_releases("foobar")
-        self.assertEqual(4, len(dists))
-
-    def test_get_link_matcher(self):
-        crawler = Crawler("http://example.org")
-        self.assertEqual('_simple_link_matcher', crawler._get_link_matcher(
-                         "http://example.org/some/file").__name__)
-        self.assertEqual('_default_link_matcher', crawler._get_link_matcher(
-                         "http://other-url").__name__)
-
-    def test_default_link_matcher(self):
-        crawler = Crawler("http://example.org", mirrors=[])
-        crawler.follow_externals = True
-        crawler._is_browsable = lambda *args:True
-        base_url = "http://example.org/some/file/"
-        content = """
-<a href="../homepage" rel="homepage">link</a>
-<a href="../download" rel="download">link2</a>
-<a href="../simpleurl">link2</a>
-        """
-        found_links = set(dict(crawler._default_link_matcher(content,
-                                                             base_url)))
-        self.assertIn('http://example.org/some/homepage', found_links)
-        self.assertIn('http://example.org/some/simpleurl', found_links)
-        self.assertIn('http://example.org/some/download', found_links)
-
-    @use_pypi_server("project_list")
-    def test_search_projects(self, server):
-        # we can search the index for some projects, on their names
-        # the case used no matters here
-        crawler = self._get_simple_crawler(server)
-        tests = (('Foobar', ['FooBar-bar', 'Foobar-baz', 'Baz-FooBar']),
-                 ('foobar*', ['FooBar-bar', 'Foobar-baz']),
-                 ('*foobar', ['Baz-FooBar',]))
-
-        for search, expected in tests:
-            projects = [p.name for p in crawler.search_projects(search)]
-            self.assertListEqual(expected, projects)
-
-def test_suite():
-    return unittest.makeSuite(SimpleCrawlerTestCase)
-
-if __name__ == '__main__':
-    unittest.main(defaultTest="test_suite")
diff --git a/distutils2/tests/test_index_xmlrpc.py b/distutils2/tests/test_index_xmlrpc.py
deleted file mode 100644
--- a/distutils2/tests/test_index_xmlrpc.py
+++ /dev/null
@@ -1,109 +0,0 @@
-"""Tests for the distutils2.index.xmlrpc module."""
-
-from distutils2.tests.pypi_server import use_xmlrpc_server
-from distutils2.tests import unittest, run_unittest
-from distutils2.index.xmlrpc import Client, InvalidSearchField, ProjectNotFound
-
-
-class TestXMLRPCClient(unittest.TestCase):
-    def _get_client(self, server, *args, **kwargs):
-        return Client(server.full_address, *args, **kwargs)
-
-    @use_xmlrpc_server()
-    def test_search_projects(self, server):
-        client = self._get_client(server)
-        server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo'])
-        results = [r.name for r in client.search_projects(name='Foo')]
-        self.assertEqual(3, len(results))
-        self.assertIn('FooBar', results)
-        self.assertIn('Foo', results)
-        self.assertIn('FooFoo', results)
-
-    def test_search_projects_bad_fields(self):
-        client = Client()
-        self.assertRaises(InvalidSearchField, client.search_projects, 
-                          invalid="test")
-
-    @use_xmlrpc_server()
-    def test_get_all_projects(self, server):
-        client = self._get_client(server)
-        server.xmlrpc.set_distributions([
-            {'name': 'FooBar', 'version': '1.1'},
-            {'name': 'FooBar', 'version': '1.2'},
-            {'name': 'Foo', 'version': '1.1'},
-        ])
-        results = client.get_all_projects()
-        self.assertEqual(2, len(results))
-
-        # check we do have two releases for Foobar's project
-        self.assertEqual(2, len(results[0].releases))
-
-        names = [r.name for r in results]
-        self.assertIn('FooBar', names)
-        self.assertIn('Foo', names)
-
-    @use_xmlrpc_server()
-    def test_get_releases(self, server):
-        client = self._get_client(server)
-        server.xmlrpc.set_distributions([
-            {'name': 'FooBar', 'version': '1.1'},
-            {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'},
-            {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'},
-        ])
-
-        # use a lambda here to avoid an useless mock call
-        server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3']
-
-        releases = client.get_releases('FooBar (<=1.2)')
-        # dont call release_data and release_url; just return name and version.
-        self.assertEqual(2, len(releases))
-        versions = releases.get_versions()
-        self.assertIn('1.1', versions)
-        self.assertIn('1.2', versions)
-        self.assertNotIn('1.3', versions)
-
-        self.assertRaises(ProjectNotFound, client.get_releases,'Foo')
-
-    @use_xmlrpc_server()
-    def test_get_distributions(self, server):
-        client = self._get_client(server)
-        server.xmlrpc.set_distributions([
-            {'name':'FooBar', 'version': '1.1', 'url':
-             'http://example.org/foobar-1.1-sdist.tar.gz',
-             'digest': '1234567', 'type': 'sdist', 'python_version':'source'},
-            {'name':'FooBar', 'version': '1.1', 'url':
-             'http://example.org/foobar-1.1-bdist.tar.gz',
-             'digest': '8912345', 'type': 'bdist'},
-        ])
-
-        releases = client.get_releases('FooBar', '1.1')
-        client.get_distributions('FooBar', '1.1')
-        release = releases.get_release('1.1')
-        self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz',
-                        release['sdist'].url['url'])
-        self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz',
-                release['bdist'].url['url'])
-        self.assertEqual(release['sdist'].python_version, 'source')
-    
-    @use_xmlrpc_server()
-    def test_get_metadata(self, server):
-        client = self._get_client(server)
-        server.xmlrpc.set_distributions([
-            {'name':'FooBar', 
-             'version': '1.1',
-             'keywords': '',
-             'obsoletes_dist': ['FooFoo'],
-             'requires_external': ['Foo'],
-            }])
-        release = client.get_metadata('FooBar', '1.1')
-        self.assertEqual(['Foo'], release.metadata['requires_external'])
-        self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist'])
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestXMLRPCClient))
-    return suite
-
-if __name__ == '__main__':
-    run_unittest(test_suite())
diff --git a/distutils2/tests/test_install.py b/distutils2/tests/test_install.py
--- a/distutils2/tests/test_install.py
+++ b/distutils2/tests/test_install.py
@@ -1,28 +1,35 @@
 """Tests for the distutils2.install module."""
+import os
+import logging
+from sysconfig import is_python_build
+from tempfile import mkstemp
 
-import os
-
-from tempfile import mkstemp
 from distutils2 import install
-from distutils2.index.xmlrpc import Client
+from distutils2.pypi.xmlrpc import Client
 from distutils2.metadata import Metadata
-from distutils2.tests import run_unittest
-from distutils2.tests.support import LoggingCatcher, TempdirManager, unittest
-from distutils2.tests.pypi_server import use_xmlrpc_server
+from distutils2.tests.support import (LoggingCatcher, TempdirManager, unittest,
+                                     fake_dec)
+try:
+    import threading
+    from distutils2.tests.pypi_server import use_xmlrpc_server
+except ImportError:
+    threading = None
+    use_xmlrpc_server = fake_dec
 
 
 class InstalledDist(object):
     """Distribution object, represent distributions currently installed on the
     system"""
     def __init__(self, name, version, deps):
+        self.metadata = Metadata()
         self.name = name
         self.version = version
-        self.metadata = Metadata()
+        self.metadata['Name'] = name
+        self.metadata['Version'] = version
         self.metadata['Requires-Dist'] = deps
-        self.metadata['Provides-Dist'] = ['%s (%s)' % (name, version)]
 
     def __repr__(self):
-        return '<InstalledDist %s>' % self.name
+        return '<InstalledDist %r>' % self.metadata['Name']
 
 
 class ToInstallDist(object):
@@ -30,25 +37,29 @@
 
     def __init__(self, files=False):
         self._files = files
+        self.install_called = False
+        self.install_called_with = {}
         self.uninstall_called = False
         self._real_files = []
         self.name = "fake"
         self.version = "fake"
         if files:
-            for f in range(0,3):
-               self._real_files.append(mkstemp())
+            for f in range(0, 3):
+                fp, fn = mkstemp()
+                os.close(fp)
+                self._real_files.append(fn)
 
     def _unlink_installed_files(self):
         if self._files:
-            for f in self._real_files:
-                os.unlink(f[1])
+            for fn in self._real_files:
+                os.unlink(fn)
 
-    def get_installed_files(self, **args):
+    def list_installed_files(self, **args):
         if self._files:
-            return [f[1] for f in self._real_files]
+            return self._real_files
 
     def get_install(self, **args):
-        return self.get_installed_files()
+        return self.list_installed_files()
 
 
 class MagicMock(object):
@@ -78,7 +89,7 @@
     """Return a list of fake installed dists.
     The list is name, version, deps"""
     objects = []
-    for (name, version, deps) in dists:
+    for name, version, deps in dists:
         objects.append(InstalledDist(name, version, deps))
     return objects
 
@@ -89,11 +100,12 @@
 
     def _get_results(self, output):
         """return a list of results"""
-        installed = [(o.name, '%s' % o.version) for o in output['install']]
-        remove = [(o.name, '%s' % o.version) for o in output['remove']]
-        conflict = [(o.name, '%s' % o.version) for o in output['conflict']]
-        return (installed, remove, conflict)
+        installed = [(o.name, str(o.version)) for o in output['install']]
+        remove = [(o.name, str(o.version)) for o in output['remove']]
+        conflict = [(o.name, str(o.version)) for o in output['conflict']]
+        return installed, remove, conflict
 
+    @unittest.skipIf(threading is None, 'needs threading')
     @use_xmlrpc_server()
     def test_existing_deps(self, server):
         # Test that the installer get the dependencies from the metadatas
@@ -105,64 +117,65 @@
         client = self._get_client(server)
         archive_path = '%s/distribution.tar.gz' % server.full_address
         server.xmlrpc.set_distributions([
-            {'name':'choxie',
+            {'name': 'choxie',
              'version': '2.0.0.9',
-             'requires_dist': ['towel-stuff (0.1)',],
+             'requires_dist': ['towel-stuff (0.1)'],
              'url': archive_path},
-            {'name':'towel-stuff',
+            {'name': 'towel-stuff',
              'version': '0.1',
-             'requires_dist': ['bacon (<= 0.2)',],
+             'requires_dist': ['bacon (<= 0.2)'],
              'url': archive_path},
-            {'name':'bacon',
+            {'name': 'bacon',
              'version': '0.1',
              'requires_dist': [],
              'url': archive_path},
             ])
-        installed = get_installed_dists([('bacon', '0.1', []),])
+        installed = get_installed_dists([('bacon', '0.1', [])])
         output = install.get_infos("choxie", index=client,
-                           installed=installed)
+                                   installed=installed)
 
-        # we dont have installed bacon as it's already installed on the system.
+        # we don't have installed bacon as it's already installed system-wide
         self.assertEqual(0, len(output['remove']))
         self.assertEqual(2, len(output['install']))
-        readable_output = [(o.name, '%s' % o.version)
+        readable_output = [(o.name, str(o.version))
                            for o in output['install']]
         self.assertIn(('towel-stuff', '0.1'), readable_output)
         self.assertIn(('choxie', '2.0.0.9'), readable_output)
 
+    @unittest.skipIf(threading is None, 'needs threading')
     @use_xmlrpc_server()
     def test_upgrade_existing_deps(self, server):
-        # Tests that the existing distributions can be upgraded if needed.
         client = self._get_client(server)
         archive_path = '%s/distribution.tar.gz' % server.full_address
         server.xmlrpc.set_distributions([
-            {'name':'choxie',
+            {'name': 'choxie',
              'version': '2.0.0.9',
-             'requires_dist': ['towel-stuff (0.1)',],
+             'requires_dist': ['towel-stuff (0.1)'],
              'url': archive_path},
-            {'name':'towel-stuff',
+            {'name': 'towel-stuff',
              'version': '0.1',
-             'requires_dist': ['bacon (>= 0.2)',],
+             'requires_dist': ['bacon (>= 0.2)'],
              'url': archive_path},
-            {'name':'bacon',
+            {'name': 'bacon',
              'version': '0.2',
              'requires_dist': [],
              'url': archive_path},
             ])
 
-        output = install.get_infos("choxie", index=client, installed=
-                           get_installed_dists([('bacon', '0.1', []),]))
-        installed = [(o.name, '%s' % o.version) for o in output['install']]
+        output = install.get_infos("choxie", index=client,
+                     installed=get_installed_dists([('bacon', '0.1', [])]))
+        installed = [(o.name, str(o.version)) for o in output['install']]
 
         # we need bacon 0.2, but 0.1 is installed.
         # So we expect to remove 0.1 and to install 0.2 instead.
-        remove = [(o.name, '%s' % o.version) for o in output['remove']]
+        remove = [(o.name, str(o.version)) for o in output['remove']]
         self.assertIn(('choxie', '2.0.0.9'), installed)
         self.assertIn(('towel-stuff', '0.1'), installed)
         self.assertIn(('bacon', '0.2'), installed)
         self.assertIn(('bacon', '0.1'), remove)
         self.assertEqual(0, len(output['conflict']))
 
+    @unittest.skipIf(threading is None, 'needs threading')
     @use_xmlrpc_server()
     def test_conflicts(self, server):
         # Tests that conflicts are detected
@@ -171,15 +184,15 @@
 
         # choxie depends on towel-stuff, which depends on bacon.
         server.xmlrpc.set_distributions([
-            {'name':'choxie',
+            {'name': 'choxie',
              'version': '2.0.0.9',
-             'requires_dist': ['towel-stuff (0.1)',],
+             'requires_dist': ['towel-stuff (0.1)'],
              'url': archive_path},
-            {'name':'towel-stuff',
+            {'name': 'towel-stuff',
              'version': '0.1',
-             'requires_dist': ['bacon (>= 0.2)',],
+             'requires_dist': ['bacon (>= 0.2)'],
              'url': archive_path},
-            {'name':'bacon',
+            {'name': 'bacon',
              'version': '0.2',
              'requires_dist': [],
              'url': archive_path},
@@ -188,8 +201,9 @@
         # name, version, deps.
         already_installed = [('bacon', '0.1', []),
                              ('chicken', '1.1', ['bacon (0.1)'])]
-        output = install.get_infos("choxie", index=client, installed=
-                           get_installed_dists(already_installed))
+        output = install.get_infos(
+            'choxie', index=client,
+            installed=get_installed_dists(already_installed))
 
         # we need bacon 0.2, but 0.1 is installed.
         # So we expect to remove 0.1 and to install 0.2 instead.
@@ -200,6 +214,7 @@
         self.assertIn(('bacon', '0.1'), remove)
         self.assertIn(('chicken', '1.1'), conflict)
 
+    @unittest.skipIf(threading is None, 'needs threading')
     @use_xmlrpc_server()
     def test_installation_unexisting_project(self, server):
         # Test that the isntalled raises an exception if the project does not
@@ -207,32 +222,35 @@
         client = self._get_client(server)
         self.assertRaises(install.InstallationException,
                           install.get_infos,
-                          'unexistant project', index=client)
+                          'unexisting project', index=client)
 
     def test_move_files(self):
         # test that the files are really moved, and that the new path is
         # returned.
         path = self.mkdtemp()
         newpath = self.mkdtemp()
-        files = [os.path.join(path, '%s' % x) for x in range(1, 20)]
+        files = [os.path.join(path, str(x)) for x in range(1, 20)]
         for f in files:
-            file(f, 'a+')
+            open(f, 'ab+').close()
         output = [o for o in install._move_files(files, newpath)]
 
         # check that output return the list of old/new places
-        for f in files:
-            self.assertIn((f, '%s%s' % (newpath, f)), output)
+        for file_ in files:
+            name = os.path.split(file_)[-1]
+            newloc = os.path.join(newpath, name)
+            self.assertIn((file_, newloc), output)
 
         # remove the files
         for f in [o[1] for o in output]:  # o[1] is the new place
             os.remove(f)
 
     def test_update_infos(self):
-        tests = [[{'foo': ['foobar', 'foo', 'baz'], 'baz': ['foo', 'foo']},
-                  {'foo': ['additional_content', 'yeah'],
-                   'baz': ['test', 'foo']},
-                  {'foo': ['foobar', 'foo', 'baz', 'additional_content', 'yeah'],
-                   'baz': ['foo', 'foo', 'test', 'foo']}],]
+        tests = [[
+            {'foo': ['foobar', 'foo', 'baz'], 'baz': ['foo', 'foo']},
+            {'foo': ['additional_content', 'yeah'], 'baz': ['test', 'foo']},
+            {'foo': ['foobar', 'foo', 'baz', 'additional_content', 'yeah'],
+             'baz': ['foo', 'foo', 'test', 'foo']},
+        ]]
 
         for dict1, dict2, expect in tests:
             install._update_infos(dict1, dict2)
@@ -247,7 +265,7 @@
         old_uninstall = getattr(install, 'uninstall', None)
 
         install._install_dist = MagicMock(return_value=[],
-                raise_exception=(False, True))
+                                          raise_exception=(False, True))
         install.remove = MagicMock()
         try:
             d1 = ToInstallDist()
@@ -260,12 +278,11 @@
             install._install_dist = old_install_dist
             install.remove = old_uninstall
 
-
     def test_install_dists_success(self):
         old_install_dist = install._install_dist
         install._install_dist = MagicMock(return_value=[])
         try:
-            # test that the install method is called on each of the distributions.
+            # test that the install method is called on each distributions
             d1 = ToInstallDist()
             d2 = ToInstallDist()
 
@@ -285,16 +302,16 @@
 
     def test_install_from_infos_remove_success(self):
         old_install_dists = install.install_dists
-        install.install_dists = lambda x,y=None: None
+        install.install_dists = lambda x, y=None: None
         try:
             dists = []
-            for i in range(0,2):
+            for i in range(2):
                 dists.append(ToInstallDist(files=True))
             install.install_from_infos(remove=dists)
 
             # assert that the files have been removed
             for dist in dists:
-                for f in dist.get_installed_files():
+                for f in dist.list_installed_files():
                     self.assertFalse(os.path.exists(f))
         finally:
             install.install_dists = old_install_dists
@@ -309,7 +326,7 @@
         try:
             # assert that if an error occurs, the removed files are restored.
             remove = []
-            for i in range(0,2):
+            for i in range(2):
                 remove.append(ToInstallDist(files=True))
             to_install = [ToInstallDist(), ToInstallDist()]
             temp_dir = self.mkdtemp()
@@ -320,14 +337,13 @@
             # assert that the files are in the same place
             # assert that the files have been removed
             for dist in remove:
-                for f in dist.get_installed_files():
+                for f in dist.list_installed_files():
                     self.assertTrue(os.path.exists(f))
                 dist._unlink_installed_files()
         finally:
-            install.install_dist = old_install_dist
+            install._install_dist = old_install_dist
             install.uninstall = old_uninstall
 
-
     def test_install_from_infos_install_succes(self):
         old_install_dist = install._install_dist
         install._install_dist = MagicMock([])
@@ -342,6 +358,29 @@
         finally:
             install._install_dist = old_install_dist
 
+    def test_install_permission_denied(self):
+        # if we don't have access to the installation path, we should abort
+        # immediately
+        project = os.path.join(os.path.dirname(__file__), 'package.tgz')
+
+        # when running from an uninstalled build, a warning is emitted and the
+        # installation is not attempted
+        if is_python_build():
+            self.assertFalse(install.install(project))
+            self.assertEqual(1, len(self.get_logs(logging.ERROR)))
+            return
+
+        install_path = self.mkdtemp()
+        old_get_path = install.get_path
+        install.get_path = lambda path: install_path
+        old_mod = os.stat(install_path).st_mode
+        os.chmod(install_path, 0)
+        try:
+            self.assertFalse(install.install(project))
+        finally:
+            os.chmod(install_path, old_mod)
+            install.get_path = old_get_path
+
 
 def test_suite():
     suite = unittest.TestSuite()
@@ -349,4 +388,4 @@
     return suite
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_manifest.py b/distutils2/tests/test_manifest.py
--- a/distutils2/tests/test_manifest.py
+++ b/distutils2/tests/test_manifest.py
@@ -1,11 +1,10 @@
-"""Tests for distutils.manifest."""
+"""Tests for distutils2.manifest."""
 import os
 import logging
 from StringIO import StringIO
+from distutils2.manifest import Manifest
 
-from distutils2.tests import run_unittest
 from distutils2.tests import unittest, support
-from distutils2.manifest import Manifest
 
 _MANIFEST = """\
 recursive-include foo *.py   # ok
@@ -24,66 +23,55 @@
 
 
 class ManifestTestCase(support.TempdirManager,
-                       # enable this after LoggingCatcher is fixed
-                       #support.LoggingCatcher,
+                       support.LoggingCatcher,
                        unittest.TestCase):
 
+    def setUp(self):
+        super(ManifestTestCase, self).setUp()
+        self.cwd = os.getcwd()
+
+    def tearDown(self):
+        os.chdir(self.cwd)
+        super(ManifestTestCase, self).tearDown()
+
     def test_manifest_reader(self):
         tmpdir = self.mkdtemp()
         MANIFEST = os.path.join(tmpdir, 'MANIFEST.in')
-        f = open(MANIFEST, 'w')
-        try:
+        with open(MANIFEST, 'w') as f:
             f.write(_MANIFEST)
-        finally:
-            f.close()
+
         manifest = Manifest()
+        manifest.read_template(MANIFEST)
 
-        # remove this when LoggingCatcher is fixed
-        warns = []
-        def _warn(*args):
-            warns.append(args[0])
-
-        old_warn = logging.warning
-        logging.warning = _warn
-        try:
-            manifest.read_template(MANIFEST)
-        finally:
-            logging.warning = old_warn
-
-        # the manifest should have been read
-        # and 3 warnings issued (we ddidn't provided the files)
-        self.assertEqual(len(warns), 3)
-        for warn in warns:
-            self.assertIn('no files found matching', warn)
+        warnings = self.get_logs(logging.WARNING)
+        # the manifest should have been read and 3 warnings issued
+        # (we didn't provide the files)
+        self.assertEqual(3, len(warnings))
+        for warning in warnings:
+            self.assertIn('no files found matching', warning)
 
         # manifest also accepts file-like objects
-        old_warn = logging.warning
-        logging.warning = _warn
-        try:
-            manifest.read_template(open(MANIFEST))
-        finally:
-            logging.warning = old_warn
+        with open(MANIFEST) as f:
+            manifest.read_template(f)
 
-        # the manifest should have been read
-        # and 3 warnings issued (we ddidn't provided the files)
-        self.assertEqual(len(warns), 6)
+        # the manifest should have been read and 3 warnings issued
+        # (we didn't provide the files)
+        self.assertEqual(3, len(warnings))
 
     def test_default_actions(self):
         tmpdir = self.mkdtemp()
-        old_dir = os.getcwd()
+        self.addCleanup(os.chdir, os.getcwd())
         os.chdir(tmpdir)
-        try:
-            self.write_file('README', 'xxx')
-            self.write_file('file1', 'xxx')
-            content = StringIO(_MANIFEST2)
-            manifest = Manifest()
-            manifest.read_template(content)
-            self.assertEqual(manifest.files, ['README', 'file1'])
-        finally:
-            os.chdir(old_dir)
+        self.write_file('README', 'xxx')
+        self.write_file('file1', 'xxx')
+        content = StringIO(_MANIFEST2)
+        manifest = Manifest()
+        manifest.read_template(content)
+        self.assertEqual(['README', 'file1'], manifest.files)
+
 
 def test_suite():
     return unittest.makeSuite(ManifestTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_markers.py b/distutils2/tests/test_markers.py
--- a/distutils2/tests/test_markers.py
+++ b/distutils2/tests/test_markers.py
@@ -1,15 +1,14 @@
-"""Tests for distutils.metadata."""
+"""Tests for distutils2.markers."""
 import os
 import sys
 import platform
-from StringIO import StringIO
+from distutils2.markers import interpret
 
-from distutils2.markers import interpret
-from distutils2.tests import run_unittest, unittest
-from distutils2.tests.support import LoggingCatcher, WarningsCatcher
+from distutils2.tests import unittest
+from distutils2.tests.support import LoggingCatcher
 
 
-class MarkersTestCase(LoggingCatcher, WarningsCatcher,
+class MarkersTestCase(LoggingCatcher,
                       unittest.TestCase):
 
     def test_interpret(self):
@@ -18,6 +17,7 @@
         os_name = os.name
         platform_version = platform.version()
         platform_machine = platform.machine()
+        platform_python_implementation = platform.python_implementation()
 
         self.assertTrue(interpret("sys.platform == '%s'" % sys_platform))
         self.assertTrue(interpret(
@@ -30,6 +30,8 @@
         self.assertTrue(interpret(
             'platform.version == "%s" and platform.machine == "%s"' %
             (platform_version, platform_machine)))
+        self.assertTrue(interpret('platform.python_implementation == "%s"' %
+            platform_python_implementation))
 
         # stuff that need to raise a syntax error
         ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
@@ -66,4 +68,4 @@
     return unittest.makeSuite(MarkersTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_metadata.py b/distutils2/tests/test_metadata.py
--- a/distutils2/tests/test_metadata.py
+++ b/distutils2/tests/test_metadata.py
@@ -1,27 +1,25 @@
-"""Tests for distutils.metadata."""
+"""Tests for distutils2.metadata."""
+import codecs
 import os
 import sys
-import platform
+import logging
 from StringIO import StringIO
 
-from distutils2.metadata import (Metadata, get_metadata_version,
-                                 PKG_INFO_PREFERRED_VERSION)
-from distutils2.tests import run_unittest, unittest
-from distutils2.tests.support import LoggingCatcher, WarningsCatcher
-from distutils2.errors import (MetadataConflictError,
-                               MetadataUnrecognizedVersionError)
+from distutils2.errors import (MetadataConflictError, MetadataMissingError,
+                              MetadataUnrecognizedVersionError)
+from distutils2.metadata import Metadata, PKG_INFO_PREFERRED_VERSION
 
+from distutils2.tests import unittest
+from distutils2.tests.support import LoggingCatcher
 
-class MetadataTestCase(LoggingCatcher, WarningsCatcher,
-                                   unittest.TestCase):
+
+class MetadataTestCase(LoggingCatcher,
+                       unittest.TestCase):
 
     def test_instantiation(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
-        fp = open(PKG_INFO)
-        try:
-            contents = fp.read()
-        finally:
-            fp.close()
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            contents = f.read()
         fp = StringIO(contents)
 
         m = Metadata()
@@ -54,15 +52,16 @@
         out.seek(0)
         res = Metadata()
         res.read_file(out)
-        for k in metadata.keys():
-            self.assertTrue(metadata[k] == res[k])
+        for k in metadata:
+            self.assertEqual(metadata[k], res[k])
 
     def test_metadata_markers(self):
         # see if we can be platform-aware
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
-        content = open(PKG_INFO).read()
-        content = content % sys.platform
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            content = f.read() % sys.platform
         metadata = Metadata(platform_dependent=True)
+
         metadata.read_file(StringIO(content))
         self.assertEqual(metadata['Requires-Dist'], ['bar'])
         metadata['Name'] = "baz; sys.platform == 'blah'"
@@ -79,14 +78,15 @@
 
     def test_description(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
-        content = open(PKG_INFO).read()
-        content = content % sys.platform
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            content = f.read() % sys.platform
         metadata = Metadata()
         metadata.read_file(StringIO(content))
 
         # see if we can read the description now
         DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
-        wanted = open(DESC).read()
+        with open(DESC) as f:
+            wanted = f.read()
         self.assertEqual(wanted, metadata['Description'])
 
         # save the file somewhere and make sure we can read it back
@@ -98,8 +98,8 @@
 
     def test_mapping_api(self):
         PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
-        content = open(PKG_INFO).read()
-        content = content % sys.platform
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            content = f.read() % sys.platform
         metadata = Metadata(fileobj=StringIO(content))
         self.assertIn('Version', metadata.keys())
         self.assertIn('0.5', metadata.values())
@@ -110,6 +110,8 @@
         metadata.update([('version', '0.7')])
         self.assertEqual(metadata['Version'], '0.7')
 
+        self.assertEqual(list(metadata), list(metadata.keys()))
+
     def test_versions(self):
         metadata = Metadata()
         metadata['Obsoletes'] = 'ok'
@@ -126,27 +128,27 @@
         del metadata['Obsoletes-Dist']
         metadata['Version'] = '1'
         self.assertEqual(metadata['Metadata-Version'], '1.0')
-        self.assertEqual(get_metadata_version(metadata), '1.0')
 
         PKG_INFO = os.path.join(os.path.dirname(__file__),
                                 'SETUPTOOLS-PKG-INFO')
-        metadata.read_file(StringIO(open(PKG_INFO).read()))
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            content = f.read()
+        metadata.read_file(StringIO(content))
         self.assertEqual(metadata['Metadata-Version'], '1.0')
-        self.assertEqual(get_metadata_version(metadata), '1.0')
 
         PKG_INFO = os.path.join(os.path.dirname(__file__),
                                 'SETUPTOOLS-PKG-INFO2')
-        metadata.read_file(StringIO(open(PKG_INFO).read()))
+        with codecs.open(PKG_INFO, 'r', encoding='utf-8') as f:
+            content = f.read()
+        metadata.read_file(StringIO(content))
         self.assertEqual(metadata['Metadata-Version'], '1.1')
-        self.assertEqual(get_metadata_version(metadata), '1.1')
 
         # Update the _fields dict directly to prevent 'Metadata-Version'
         # from being updated by the _set_best_version() method.
         metadata._fields['Metadata-Version'] = '1.618'
         self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
 
-    # XXX Spurious Warnings were disabled
-    def XXXtest_warnings(self):
+    def test_warnings(self):
         metadata = Metadata()
 
         # these should raise a warning
@@ -157,7 +159,7 @@
             metadata.set(name, value)
 
         # we should have a certain amount of warnings
-        self.assertEqual(len(self.logs), 2)
+        self.assertEqual(len(self.get_logs()), 2)
 
     def test_multiple_predicates(self):
         metadata = Metadata()
@@ -167,7 +169,7 @@
         metadata['Requires-Python'] = '>=2.6, <3.0'
         metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
 
-        self.assertEqual(len(self.warnings), 0)
+        self.assertEqual([], self.get_logs(logging.WARNING))
 
     def test_project_url(self):
         metadata = Metadata()
@@ -191,7 +193,6 @@
         metadata['Home-page'] = 'http://pypi.python.org'
         metadata['Author'] = 'Monty Python'
         metadata.docutils_support = False
-        from distutils2.errors import MetadataMissingError
         self.assertRaises(MetadataMissingError, metadata.check, strict=True)
 
     def test_check_name(self):
@@ -209,7 +210,6 @@
         metadata['Home-page'] = 'http://pypi.python.org'
         metadata['Author'] = 'Monty Python'
         metadata.docutils_support = False
-        from distutils2.errors import MetadataMissingError
         self.assertRaises(MetadataMissingError, metadata.check, strict=True)
 
     def test_check_author(self):
@@ -251,11 +251,8 @@
         metadata['Version'] = '1.0'
         self.assertEqual(metadata['Metadata-Version'],
                          PKG_INFO_PREFERRED_VERSION)
-        self.assertEqual(get_metadata_version(metadata),
-                         PKG_INFO_PREFERRED_VERSION)
         metadata['Classifier'] = ['ok']
         self.assertEqual(metadata['Metadata-Version'], '1.2')
-        self.assertEqual(get_metadata_version(metadata), '1.2')
 
     def test_project_urls(self):
         # project-url is a bit specific, make sure we write it
@@ -280,4 +277,4 @@
     return unittest.makeSuite(MetadataTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_mixin2to3.py b/distutils2/tests/test_mixin2to3.py
--- a/distutils2/tests/test_mixin2to3.py
+++ b/distutils2/tests/test_mixin2to3.py
@@ -1,70 +1,84 @@
-"""Tests for distutils.command.build_py."""
 import sys
-import logging
+import textwrap
 
-import distutils2
 from distutils2.tests import unittest, support
 from distutils2.compat import Mixin2to3
 
 
-class Mixin2to3TestCase(support.TempdirManager, support.WarningsCatcher,
+class Mixin2to3TestCase(support.TempdirManager,
+                        support.LoggingCatcher,
                         unittest.TestCase):
 
-    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    #@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    @unittest.skipIf(True, 'Not needed for backport')
     def test_convert_code_only(self):
         # used to check if code gets converted properly.
-        code_content = "print 'test'\n"
-        code_handle = self.mktempfile()
-        code_name = code_handle.name
+        code = "print 'test'"
 
-        code_handle.write(code_content)
-        code_handle.flush()
+        with self.mktempfile() as fp:
+            fp.write(code)
 
         mixin2to3 = Mixin2to3()
-        mixin2to3._run_2to3([code_name])
-        converted_code_content = "print('test')\n"
-        new_code_content = "".join(open(code_name).readlines())
+        mixin2to3._run_2to3([fp.name])
+        expected = "print('test')"
 
-        self.assertEqual(new_code_content, converted_code_content)
+        with open(fp.name) as fp:
+            converted = fp.read()
 
-    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+        self.assertEqual(expected, converted)
+
+    #@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    @unittest.skipIf(True, 'Not needed for backport')
     def test_doctests_only(self):
         # used to check if doctests gets converted properly.
-        doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n'
-        doctest_handle = self.mktempfile()
-        doctest_name = doctest_handle.name
+        doctest = textwrap.dedent('''\
+            """Example docstring.
 
-        doctest_handle.write(doctest_content)
-        doctest_handle.flush()
+            >>> print test
+            test
+
+            It works.
+            """''')
+
+        with self.mktempfile() as fp:
+            fp.write(doctest)
 
         mixin2to3 = Mixin2to3()
-        mixin2to3._run_2to3([doctest_name])
+        mixin2to3._run_2to3([fp.name])
+        expected = textwrap.dedent('''\
+            """Example docstring.
 
-        converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""',
-                                     'print(test)', '', '', '']
-        converted_doctest_content = '\n'.join(converted_doctest_content)
-        new_doctest_content = "".join(open(doctest_name).readlines())
+            >>> print(test)
+            test
 
-        self.assertEqual(new_doctest_content, converted_doctest_content)
+            It works.
+            """\n''')
 
-    @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+        with open(fp.name) as fp:
+            converted = fp.read()
+
+        self.assertEqual(expected, converted)
+
+    #@unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+    @unittest.skipIf(True, 'Not needed for backport')
     def test_additional_fixers(self):
         # used to check if use_2to3_fixers works
-        from distutils2.tests import fixer
-        code_content = "type(x) is T"
-        code_handle = self.mktempfile()
-        code_name = code_handle.name
+        code = 'type(x) is not T'
 
-        code_handle.write(code_content)
-        code_handle.flush()
+        with self.mktempfile() as fp:
+            fp.write(code)
 
         mixin2to3 = Mixin2to3()
+        mixin2to3._run_2to3(files=[fp.name], doctests=[fp.name],
+                            fixers=['distutils2.tests.fixer'])
 
-        mixin2to3._run_2to3(files=[code_name], doctests=[code_name],
-                            fixers=['distutils2.tests.fixer'])
-        converted_code_content = "isinstance(x, T)"
-        new_code_content = "".join(open(code_name).readlines())
-        self.assertEqual(new_code_content, converted_code_content)
+        expected = 'not isinstance(x, T)'
+
+        with open(fp.name) as fp:
+            converted = fp.read()
+
+        self.assertEqual(expected, converted)
+
 
 def test_suite():
     return unittest.makeSuite(Mixin2to3TestCase)
diff --git a/distutils2/tests/test_mkcfg.py b/distutils2/tests/test_mkcfg.py
deleted file mode 100644
--- a/distutils2/tests/test_mkcfg.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# -*- coding: utf-8 -*-
-"""Tests for distutils.mkcfg."""
-import os
-import sys
-import StringIO
-from textwrap import dedent
-
-from distutils2.tests import run_unittest, support, unittest
-from distutils2.mkcfg import MainProgram
-from distutils2.mkcfg import ask_yn, ask, main
-from distutils2._backport import sysconfig
-
-
-class MkcfgTestCase(support.TempdirManager,
-                    unittest.TestCase):
-
-    def setUp(self):
-        super(MkcfgTestCase, self).setUp()
-        self._stdin = sys.stdin
-        self._stdout = sys.stdout
-        sys.stdin = StringIO.StringIO()
-        sys.stdout = StringIO.StringIO()
-        self._cwd = os.getcwd()
-        self.wdir = self.mkdtemp()
-        os.chdir(self.wdir)
-        # patch sysconfig
-        self._old_get_paths = sysconfig.get_paths
-        sysconfig.get_paths = lambda *args, **kwargs: {
-                'man': sys.prefix + '/share/man', 
-                'doc': sys.prefix + '/share/doc/pyxfoil',}
-
-    def tearDown(self):
-        super(MkcfgTestCase, self).tearDown()
-        sys.stdin = self._stdin
-        sys.stdout = self._stdout
-        os.chdir(self._cwd)
-        sysconfig.get_paths = self._old_get_paths
-
-    def test_ask_yn(self):
-        sys.stdin.write('y\n')
-        sys.stdin.seek(0)
-        self.assertEqual('y', ask_yn('is this a test'))
-
-    def test_ask(self):
-        sys.stdin.write('a\n')
-        sys.stdin.write('b\n')
-        sys.stdin.seek(0)
-        self.assertEqual('a', ask('is this a test'))
-        self.assertEqual('b', ask(str(range(0,70)), default='c', lengthy=True))
-
-    def test_set_multi(self):
-        main = MainProgram()
-        sys.stdin.write('aaaaa\n')
-        sys.stdin.seek(0)
-        main.data['author'] = []
-        main._set_multi('_set_multi test', 'author')
-        self.assertEqual(['aaaaa'], main.data['author'])
-
-    def test_find_files(self):
-        # making sure we scan a project dir correctly
-        main = MainProgram()
-
-        # building the structure
-        tempdir = self.wdir
-        dirs = ['pkg1', 'data', 'pkg2', 'pkg2/sub']
-        files = ['README', 'setup.cfg', 'foo.py',
-                 'pkg1/__init__.py', 'pkg1/bar.py',
-                 'data/data1', 'pkg2/__init__.py',
-                 'pkg2/sub/__init__.py']
-
-        for dir_ in dirs:
-            os.mkdir(os.path.join(tempdir, dir_))
-
-        for file_ in files:
-            path = os.path.join(tempdir, file_)
-            self.write_file(path, 'xxx')
-
-        main._find_files()
-
-        # do we have what we want ?
-        self.assertEqual(main.data['packages'], ['pkg1', 'pkg2', 'pkg2.sub'])
-        self.assertEqual(main.data['modules'], ['foo'])
-        self.assertEqual(set(main.data['extra_files']),
-                         set(['setup.cfg', 'README', 'data/data1']))
-
-    def test_convert_setup_py_to_cfg(self):
-        self.write_file((self.wdir, 'setup.py'),
-                        dedent("""
-        # -*- coding: utf-8 -*-
-        from distutils.core import setup
-        lg_dsc = '''My super Death-scription
-        barbar is now on the public domain,
-        ho, baby !'''
-        setup(name='pyxfoil',
-              version='0.2',
-              description='Python bindings for the Xfoil engine',
-              long_description = lg_dsc,
-              maintainer='André Espaze',
-              maintainer_email='andre.espaze at logilab.fr',
-              url='http://www.python-science.org/project/pyxfoil',
-              license='GPLv2',
-              packages=['pyxfoil', 'babar', 'me'],
-              data_files=[('share/doc/pyxfoil', ['README.rst']),
-                          ('share/man', ['pyxfoil.1']),
-                         ],
-              py_modules = ['my_lib', 'mymodule'],
-              package_dir = {'babar' : '',
-                             'me' : 'Martinique/Lamentin',
-                            },
-              package_data = {'babar': ['Pom', 'Flora', 'Alexander'],
-                              'me': ['dady', 'mumy', 'sys', 'bro'],
-                              '':  ['setup.py', 'README'],
-                              'pyxfoil' : ['fengine.so'],
-                             },
-              scripts = ['my_script', 'bin/run'],
-              )
-        """))
-        sys.stdin.write('y\n')
-        sys.stdin.seek(0)
-        main()
-        fp = open(os.path.join(self.wdir, 'setup.cfg'))
-        try:
-            lines = set([line.rstrip() for line in fp])
-        finally:
-            fp.close()
-        self.assertEqual(lines, set(['',
-            '[metadata]',
-            'version = 0.2',
-            'name = pyxfoil',
-            'maintainer = André Espaze',
-            'description = My super Death-scription',
-            '       |barbar is now on the public domain,',
-            '       |ho, baby !',
-            'maintainer_email = andre.espaze at logilab.fr',
-            'home_page = http://www.python-science.org/project/pyxfoil',
-            'download_url = UNKNOWN',
-            'summary = Python bindings for the Xfoil engine',
-            '[files]',
-            'modules = my_lib',
-            '    mymodule',
-            'packages = pyxfoil',
-            '    babar',
-            '    me',
-            'extra_files = Martinique/Lamentin/dady',
-            '    Martinique/Lamentin/mumy',
-            '    Martinique/Lamentin/sys',
-            '    Martinique/Lamentin/bro',
-            '    Pom',
-            '    Flora',
-            '    Alexander',
-            '    setup.py',
-            '    README',
-            '    pyxfoil/fengine.so',
-            'scripts = my_script',
-            '    bin/run',
-            'resources =',
-            '    README.rst = {doc}',
-            '    pyxfoil.1 = {man}',
-        ]))
-
-    def test_convert_setup_py_to_cfg_with_description_in_readme(self):
-        self.write_file((self.wdir, 'setup.py'),
-                        dedent("""
-        # -*- coding: utf-8 -*-
-        from distutils.core import setup
-        lg_dsc = open('README.txt').read()
-        setup(name='pyxfoil',
-              version='0.2',
-              description='Python bindings for the Xfoil engine',
-              long_description=lg_dsc,
-              maintainer='André Espaze',
-              maintainer_email='andre.espaze at logilab.fr',
-              url='http://www.python-science.org/project/pyxfoil',
-              license='GPLv2',
-              packages=['pyxfoil'],
-              package_data={'pyxfoil' : ['fengine.so', 'babar.so']},
-              data_files=[
-                ('share/doc/pyxfoil', ['README.rst']),
-                ('share/man', ['pyxfoil.1']),
-              ],
-        )
-        """))
-        self.write_file((self.wdir, 'README.txt'),
-                        dedent('''
-My super Death-scription
-barbar is now on the public domain,
-ho, baby !
-                        '''))
-        sys.stdin.write('y\n')
-        sys.stdin.seek(0)
-        main()
-        fp = open(os.path.join(self.wdir, 'setup.cfg'))
-        try:
-            lines = set([line.rstrip() for line in fp])
-        finally:
-            fp.close()
-        self.assertEqual(lines, set(['',
-            '[metadata]',
-            'version = 0.2',
-            'name = pyxfoil',
-            'maintainer = André Espaze',
-            'maintainer_email = andre.espaze at logilab.fr',
-            'home_page = http://www.python-science.org/project/pyxfoil',
-            'download_url = UNKNOWN',
-            'summary = Python bindings for the Xfoil engine',
-            'description-file = README.txt',
-            '[files]',
-            'packages = pyxfoil',
-            'extra_files = pyxfoil/fengine.so',
-            '    pyxfoil/babar.so',
-            'resources =',
-            '    README.rst = {doc}',
-            '    pyxfoil.1 = {man}',
-        ]))
-
-
-def test_suite():
-    return unittest.makeSuite(MkcfgTestCase)
-
-if __name__ == '__main__':
-    run_unittest(test_suite())
diff --git a/distutils2/tests/test_msvc9compiler.py b/distutils2/tests/test_msvc9compiler.py
--- a/distutils2/tests/test_msvc9compiler.py
+++ b/distutils2/tests/test_msvc9compiler.py
@@ -1,8 +1,9 @@
-"""Tests for distutils.msvc9compiler."""
+"""Tests for distutils2.compiler.msvc9compiler."""
+import os
 import sys
-import os
 
-from distutils2.errors import DistutilsPlatformError
+from distutils2.errors import PackagingPlatformError
+
 from distutils2.tests import unittest, support
 
 _MANIFEST = """\
@@ -65,22 +66,22 @@
 
     @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
     def test_no_compiler(self):
-        # makes sure query_vcvarsall throws
-        # a DistutilsPlatformError if the compiler
-        # is not found
+        # make sure query_vcvarsall raises a PackagingPlatformError if
+        # the compiler is not found
         from distutils2.compiler.msvccompiler import get_build_version
         if get_build_version() < 8.0:
-            # this test is only for MSVC8.0 or above
-            return
+            raise unittest.SkipTest('only for MSVC8.0 or above')
+
+        from distutils2.compiler import msvc9compiler
         from distutils2.compiler.msvc9compiler import query_vcvarsall
+
         def _find_vcvarsall(version):
             return None
 
-        from distutils2 import msvc9compiler
         old_find_vcvarsall = msvc9compiler.find_vcvarsall
         msvc9compiler.find_vcvarsall = _find_vcvarsall
         try:
-            self.assertRaises(DistutilsPlatformError, query_vcvarsall,
+            self.assertRaises(PackagingPlatformError, query_vcvarsall,
                              'wont find this version')
         finally:
             msvc9compiler.find_vcvarsall = old_find_vcvarsall
@@ -97,16 +98,16 @@
         # looking for values that should exist on all
         # windows registeries versions.
         path = r'Control Panel\Desktop'
-        v = Reg.get_value(path, u'dragfullwindows')
-        self.assertTrue(v in (u'0', u'1', u'2'))
+        v = Reg.get_value(path, 'dragfullwindows')
+        self.assertIn(v, ('0', '1', '2'))
 
-        import _winreg
-        HKCU = _winreg.HKEY_CURRENT_USER
+        import winreg
+        HKCU = winreg.HKEY_CURRENT_USER
         keys = Reg.read_keys(HKCU, 'xxxx')
         self.assertEqual(keys, None)
 
         keys = Reg.read_keys(HKCU, r'Control Panel')
-        self.assertTrue('Desktop' in keys)
+        self.assertIn('Desktop', keys)
 
     @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
     def test_remove_visual_c_ref(self):
@@ -117,22 +118,16 @@
         from distutils2.compiler.msvc9compiler import MSVCCompiler
         tempdir = self.mkdtemp()
         manifest = os.path.join(tempdir, 'manifest')
-        f = open(manifest, 'w')
-        try:
+        with open(manifest, 'w') as f:
             f.write(_MANIFEST)
-        finally:
-            f.close()
 
         compiler = MSVCCompiler()
         compiler._remove_visual_c_ref(manifest)
 
         # see what we got
-        f = open(manifest)
-        try:
+        with open(manifest) as f:
             # removing trailing spaces
-            content = '\n'.join([line.rstrip() for line in f.readlines()])
-        finally:
-            f.close()
+            content = '\n'.join(line.rstrip() for line in f.readlines())
 
         # makes sure the manifest was properly cleaned
         self.assertEqual(content, _CLEANED_MANIFEST)
diff --git a/distutils2/tests/test_pypi_dist.py b/distutils2/tests/test_pypi_dist.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_pypi_dist.py
@@ -0,0 +1,285 @@
+"""Tests for the distutils2.pypi.dist module."""
+
+import os
+from distutils2.version import VersionPredicate
+from distutils2.pypi.dist import (ReleaseInfo, ReleasesList, DistInfo,
+                                 split_archive_name, get_infos_from_url)
+from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName
+
+from distutils2.tests import unittest
+from distutils2.tests.support import TempdirManager, requires_zlib, fake_dec
+try:
+    import threading
+    from distutils2.tests.pypi_server import use_pypi_server
+except ImportError:
+    threading = None
+    use_pypi_server = fake_dec
+
+
+def Dist(*args, **kwargs):
+    # DistInfo takes a release as a first parameter, avoid this in tests.
+    return DistInfo(None, *args, **kwargs)
+
+
+class TestReleaseInfo(unittest.TestCase):
+
+    def test_instantiation(self):
+        # Test the DistInfo class provides us the good attributes when
+        # given on construction
+        release = ReleaseInfo("FooBar", "1.1")
+        self.assertEqual("FooBar", release.name)
+        self.assertEqual("1.1", "%s" % release.version)
+
+    def test_add_dist(self):
+        # empty distribution type should assume "sdist"
+        release = ReleaseInfo("FooBar", "1.1")
+        release.add_distribution(url="http://example.org/")
+        # should not fail
+        release['sdist']
+
+    def test_get_unknown_distribution(self):
+        # should raise a KeyError
+        pass
+
+    def test_get_infos_from_url(self):
+        # Test that the the URLs are parsed the right way
+        url_list = {
+            'FooBar-1.1.0.tar.gz': {
+                'name': 'foobar',  # lowercase the name
+                'version': '1.1.0',
+            },
+            'Foo-Bar-1.1.0.zip': {
+                'name': 'foo-bar',  # keep the dash
+                'version': '1.1.0',
+            },
+            'foobar-1.1b2.tar.gz#md5=123123123123123': {
+                'name': 'foobar',
+                'version': '1.1b2',
+                'url': 'http://example.org/foobar-1.1b2.tar.gz',  # no hash
+                'hashval': '123123123123123',
+                'hashname': 'md5',
+            },
+            'foobar-1.1-rc2.tar.gz': {  # use suggested name
+                'name': 'foobar',
+                'version': '1.1c2',
+                'url': 'http://example.org/foobar-1.1-rc2.tar.gz',
+            }
+        }
+
+        for url, attributes in url_list.items():
+            # for each url
+            infos = get_infos_from_url("http://example.org/" + url)
+            for attribute, expected in attributes.items():
+                got = infos.get(attribute)
+                if attribute == "version":
+                    self.assertEqual("%s" % got, expected)
+                else:
+                    self.assertEqual(got, expected)
+
+    def test_split_archive_name(self):
+        # Test we can split the archive names
+        names = {
+            'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
+            'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
+            'foobarbaz-1.0': ('foobarbaz', '1.0'),
+        }
+        for name, results in names.items():
+            self.assertEqual(results, split_archive_name(name))
+
+
+class TestDistInfo(TempdirManager, unittest.TestCase):
+    srcpath = "/packages/source/f/foobar/foobar-0.1.tar.gz"
+
+    def test_get_url(self):
+        # Test that the url property works well
+
+        d = Dist(url="test_url")
+        self.assertDictEqual(d.url, {
+            "url": "test_url",
+            "is_external": True,
+            "hashname": None,
+            "hashval": None,
+        })
+
+        # add a new url
+        d.add_url(url="internal_url", is_external=False)
+        self.assertEqual(d._url, None)
+        self.assertDictEqual(d.url, {
+            "url": "internal_url",
+            "is_external": False,
+            "hashname": None,
+            "hashval": None,
+        })
+        self.assertEqual(2, len(d.urls))
+
+    def test_comparison(self):
+        # Test that we can compare DistInfoributionInfoList
+        foo1 = ReleaseInfo("foo", "1.0")
+        foo2 = ReleaseInfo("foo", "2.0")
+        bar = ReleaseInfo("bar", "2.0")
+        # assert we use the version to compare
+        self.assertTrue(foo1 < foo2)
+        self.assertFalse(foo1 > foo2)
+        self.assertFalse(foo1 == foo2)
+
+        # assert we can't compare dists with different names
+        self.assertRaises(TypeError, foo1.__eq__, bar)
+
+    @unittest.skipIf(threading is None, 'needs threading')
+    @use_pypi_server("downloads_with_md5")
+    def test_download(self, server):
+        # Download is possible, and the md5 is checked if given
+
+        url = server.full_address + self.srcpath
+
+        # check that a md5 if given
+        dist = Dist(url=url, hashname="md5",
+                    hashval="fe18804c5b722ff024cabdf514924fc4")
+        dist.download(self.mkdtemp())
+
+        # a wrong md5 fails
+        dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5")
+
+        self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp())
+
+        # we can omit the md5 hash
+        dist3 = Dist(url=url)
+        dist3.download(self.mkdtemp())
+
+        # and specify a temporary location
+        # for an already downloaded dist
+        path1 = self.mkdtemp()
+        dist3.download(path=path1)
+        # and for a new one
+        path2_base = self.mkdtemp()
+        dist4 = Dist(url=url)
+        path2 = dist4.download(path=path2_base)
+        self.assertIn(path2_base, path2)
+
+    def test_hashname(self):
+        # Invalid hashnames raises an exception on assignation
+        Dist(hashname="md5", hashval="value")
+
+        self.assertRaises(UnsupportedHashName, Dist,
+                          hashname="invalid_hashname",
+                          hashval="value")
+
+    @unittest.skipIf(threading is None, 'needs threading')
+    @requires_zlib
+    @use_pypi_server('downloads_with_md5')
+    def test_unpack(self, server):
+        url = server.full_address + self.srcpath
+        dist1 = Dist(url=url)
+
+        # unpack the distribution in a specfied folder
+        dist1_here = self.mkdtemp()
+        dist1_there = dist1.unpack(path=dist1_here)
+
+        # assert we unpack to the path provided
+        self.assertEqual(dist1_here, dist1_there)
+        dist1_result = os.listdir(dist1_there)
+        self.assertIn('paf', dist1_result)
+        os.remove(os.path.join(dist1_there, 'paf'))
+
+        # Test unpack works without a path argument
+        dist2 = Dist(url=url)
+        # doing an unpack
+        dist2_there = dist2.unpack()
+        dist2_result = os.listdir(dist2_there)
+        self.assertIn('paf', dist2_result)
+        os.remove(os.path.join(dist2_there, 'paf'))
+
+
+class TestReleasesList(unittest.TestCase):
+
+    def test_filter(self):
+        # Test we filter the distributions the right way, using version
+        # predicate match method
+        releases = ReleasesList('FooBar', (
+            ReleaseInfo("FooBar", "1.1"),
+            ReleaseInfo("FooBar", "1.1.1"),
+            ReleaseInfo("FooBar", "1.2"),
+            ReleaseInfo("FooBar", "1.2.1"),
+        ))
+        filtered = releases.filter(VersionPredicate("FooBar (<1.2)"))
+        self.assertNotIn(releases[2], filtered)
+        self.assertNotIn(releases[3], filtered)
+        self.assertIn(releases[0], filtered)
+        self.assertIn(releases[1], filtered)
+
+    def test_append(self):
+        # When adding a new item to the list, the behavior is to test if
+        # a release with the same name and version number already exists,
+        # and if so, to add a new distribution for it. If the distribution type
+        # is already defined too, add url informations to the existing DistInfo
+        # object.
+
+        releases = ReleasesList("FooBar", [
+            ReleaseInfo("FooBar", "1.1", url="external_url",
+                        dist_type="sdist"),
+        ])
+        self.assertEqual(1, len(releases))
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1",
+                                                 url="internal_url",
+                                                 is_external=False,
+                                                 dist_type="sdist"))
+        self.assertEqual(1, len(releases))
+        self.assertEqual(2, len(releases[0]['sdist'].urls))
+
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+                                                 dist_type="sdist"))
+        self.assertEqual(2, len(releases))
+
+        # when adding a distribution whith a different type, a new distribution
+        # has to be added.
+        releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+                                                 dist_type="bdist"))
+        self.assertEqual(2, len(releases))
+        self.assertEqual(2, len(releases[1].dists))
+
+    def test_prefer_final(self):
+        # Can order the distributions using prefer_final
+        fb10 = ReleaseInfo("FooBar", "1.0")  # final distribution
+        fb11a = ReleaseInfo("FooBar", "1.1a1")  # alpha
+        fb12a = ReleaseInfo("FooBar", "1.2a1")  # alpha
+        fb12b = ReleaseInfo("FooBar", "1.2b1")  # beta
+        dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b])
+
+        dists.sort_releases(prefer_final=True)
+        self.assertEqual(fb10, dists[0])
+
+        dists.sort_releases(prefer_final=False)
+        self.assertEqual(fb12b, dists[0])
+
+    @unittest.skip('method not implemented yet')
+    def test_prefer_source(self):
+        # Ordering supports prefer_source
+        fb_source = Dist("FooBar", "1.0", type="source")
+        fb_binary = Dist("FooBar", "1.0", type="binary")
+        fb2_binary = Dist("FooBar", "2.0", type="binary")
+        dists = ReleasesList([fb_binary, fb_source])
+
+        dists.sort_distributions(prefer_source=True)
+        self.assertEqual(fb_source, dists[0])
+
+        dists.sort_distributions(prefer_source=False)
+        self.assertEqual(fb_binary, dists[0])
+
+        dists.append(fb2_binary)
+        dists.sort_distributions(prefer_source=True)
+        self.assertEqual(fb2_binary, dists[0])
+
+    def test_get_last(self):
+        dists = ReleasesList('Foo')
+        self.assertEqual(dists.get_last('Foo 1.0'), None)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestDistInfo))
+    suite.addTest(unittest.makeSuite(TestReleaseInfo))
+    suite.addTest(unittest.makeSuite(TestReleasesList))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_pypi_server.py b/distutils2/tests/test_pypi_server.py
--- a/distutils2/tests/test_pypi_server.py
+++ b/distutils2/tests/test_pypi_server.py
@@ -1,32 +1,46 @@
-"""Tests for distutils.command.bdist."""
-import urllib
+"""Tests for distutils2.command.bdist."""
+import sys
+
 import urllib2
-import os.path
 
-from distutils2.tests.pypi_server import PyPIServer, PYPI_DEFAULT_STATIC_PATH
+try:
+    import threading
+    from distutils2.tests.pypi_server import PyPIServer, PYPI_DEFAULT_STATIC_PATH
+except ImportError:
+    threading = None
+    PyPIServer = None
+    PYPI_DEFAULT_STATIC_PATH = None
+
 from distutils2.tests import unittest
 
 
+ at unittest.skipIf(threading is None, "Needs threading")
 class PyPIServerTest(unittest.TestCase):
 
     def test_records_requests(self):
         # We expect that PyPIServer can log our requests
         server = PyPIServer()
-        server.start()
-        self.assertEqual(len(server.requests), 0)
+        server.default_response_status = 200
 
-        data = "Rock Around The Bunker"
-        headers = {"X-test-header": "Mister Iceberg"}
+        try:
+            server.start()
+            self.assertEqual(len(server.requests), 0)
 
-        request = urllib2.Request(server.full_address, data, headers)
-        urllib2.urlopen(request)
-        self.assertEqual(len(server.requests), 1)
-        handler, request_data = server.requests[-1]
-        self.assertIn("Rock Around The Bunker", request_data)
-        self.assertIn("x-test-header", handler.headers.dict)
-        self.assertEqual(handler.headers.dict["x-test-header"],
-                         "Mister Iceberg")
-        server.stop()
+            data = b'Rock Around The Bunker'
+
+            headers = {"X-test-header": "Mister Iceberg"}
+
+            request = urllib2.Request(server.full_address, data, headers)
+            urllib2.urlopen(request)
+            self.assertEqual(len(server.requests), 1)
+            handler, request_data = server.requests[-1]
+            self.assertIn(data, request_data)
+            self.assertIn("x-test-header", handler.headers)
+            self.assertEqual(handler.headers["x-test-header"], "Mister Iceberg")
+
+        finally:
+            server.stop()
+
 
     def test_serve_static_content(self):
         # PYPI Mocked server can serve static content from disk.
@@ -38,28 +52,30 @@
             url = server.full_address + url_path
             request = urllib2.Request(url)
             response = urllib2.urlopen(request)
-            file = open(PYPI_DEFAULT_STATIC_PATH + "/test_pypi_server" +
-               url_path)
-            return response.read() == file.read()
+            with open(PYPI_DEFAULT_STATIC_PATH + "/test_pypi_server"
+                      + url_path) as file:
+                return response.read().decode() == file.read()
 
         server = PyPIServer(static_uri_paths=["simple", "external"],
             static_filesystem_paths=["test_pypi_server"])
         server.start()
+        try:
+            # the file does not exists on the disc, so it might not be served
+            url = server.full_address + "/simple/unexisting_page"
+            request = urllib2.Request(url)
+            try:
+                urllib2.urlopen(request)
+            except urllib2.HTTPError:
+                self.assertEqual(sys.exc_info()[1].code, 404)
 
-        # the file does not exists on the disc, so it might not be served
-        url = server.full_address + "/simple/unexisting_page"
-        request = urllib2.Request(url)
-        try:
-            urllib2.urlopen(request)
-        except urllib2.HTTPError,e:
-            self.assertEqual(e.code, 404)
+            # now try serving a content that do exists
+            self.assertTrue(uses_local_files_for(server, "/simple/index.html"))
 
-        # now try serving a content that do exists
-        self.assertTrue(uses_local_files_for(server, "/simple/index.html"))
+            # and another one in another root path
+            self.assertTrue(uses_local_files_for(server, "/external/index.html"))
 
-        # and another one in another root path
-        self.assertTrue(uses_local_files_for(server, "/external/index.html"))
-        server.stop()
+        finally:
+            server.stop()
 
 
 def test_suite():
diff --git a/distutils2/tests/test_pypi_simple.py b/distutils2/tests/test_pypi_simple.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_pypi_simple.py
@@ -0,0 +1,351 @@
+"""Tests for the distutils2.pypi.simple module."""
+import re
+import os
+import sys
+import httplib
+import urllib2
+
+from distutils2.pypi.simple import Crawler
+
+from distutils2.tests import unittest
+from distutils2.tests.support import (TempdirManager, LoggingCatcher,
+                                     fake_dec)
+
+try:
+    import _thread
+    from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer,
+                                             PYPI_DEFAULT_STATIC_PATH)
+except ImportError:
+    _thread = None
+    use_pypi_server = fake_dec
+    PYPI_DEFAULT_STATIC_PATH = os.path.join(
+        os.path.dirname(os.path.abspath(__file__)), 'pypiserver')
+
+
+
+class SimpleCrawlerTestCase(TempdirManager,
+                            LoggingCatcher,
+                            unittest.TestCase):
+
+    def _get_simple_crawler(self, server, base_url="/simple/", hosts=None,
+                            *args, **kwargs):
+        """Build and return a SimpleIndex with the test server urls"""
+        if hosts is None:
+            hosts = (server.full_address.replace("http://", ""),)
+        kwargs['hosts'] = hosts
+        return Crawler(server.full_address + base_url, *args,
+                       **kwargs)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server()
+    def test_bad_urls(self, server):
+        crawler = Crawler()
+        url = 'http://127.0.0.1:0/nonesuch/test_simple'
+        try:
+            v = crawler._open_url(url)
+        except Exception:
+            self.assertIn(url, str(sys.exc_info()[1]))
+        else:
+            v.close()
+            self.assertIsInstance(v, urllib2.HTTPError)
+
+        # issue 16
+        # easy_install inquant.contentmirror.plone breaks because of a typo
+        # in its home URL
+        crawler = Crawler(hosts=('example.org',))
+        url = ('url:%20https://svn.plone.org/svn/collective/'
+               'inquant.contentmirror.plone/trunk')
+        try:
+            v = crawler._open_url(url)
+        except Exception:
+            self.assertIn(url, str(sys.exc_info()[1]))
+        else:
+            v.close()
+            self.assertIsInstance(v, urllib2.HTTPError)
+
+        def _urlopen(*args):
+            raise httplib.BadStatusLine('line')
+
+        old_urlopen = urllib2.urlopen
+        urllib2.urlopen = _urlopen
+        url = 'http://example.org'
+        try:
+            v = crawler._open_url(url)
+        except Exception:
+            self.assertIn('line', str(sys.exc_info()[1]))
+        else:
+            v.close()
+            # TODO use self.assertRaises
+            raise AssertionError('Should have raise here!')
+        finally:
+            urllib2.urlopen = old_urlopen
+
+        # issue 20
+        url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
+        try:
+            crawler._open_url(url)
+        except Exception:
+            self.assertIn('nonnumeric port', str(sys.exc_info()[1]))
+
+        # issue #160
+        url = server.full_address
+        page = ('<a href="http://www.famfamfam.com]('
+                'http://www.famfamfam.com/">')
+        crawler._process_url(url, page)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server("test_found_links")
+    def test_found_links(self, server):
+        # Browse the index, asking for a specified release version
+        # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
+        crawler = self._get_simple_crawler(server)
+        last_release = crawler.get_release("foobar")
+
+        # we have scanned the index page
+        self.assertIn(server.full_address + "/simple/foobar/",
+            crawler._processed_urls)
+
+        # we have found 4 releases in this page
+        self.assertEqual(len(crawler._projects["foobar"]), 4)
+
+        # and returned the most recent one
+        self.assertEqual("%s" % last_release.version, '2.0.1')
+
+    def test_is_browsable(self):
+        crawler = Crawler(follow_externals=False)
+        self.assertTrue(crawler._is_browsable(crawler.index_url + "test"))
+
+        # Now, when following externals, we can have a list of hosts to trust.
+        # and don't follow other external links than the one described here.
+        crawler = Crawler(hosts=["pypi.python.org", "example.org"],
+                          follow_externals=True)
+        good_urls = (
+            "http://pypi.python.org/foo/bar",
+            "http://pypi.python.org/simple/foobar",
+            "http://example.org",
+            "http://example.org/",
+            "http://example.org/simple/",
+        )
+        bad_urls = (
+            "http://python.org",
+            "http://example.tld",
+        )
+
+        for url in good_urls:
+            self.assertTrue(crawler._is_browsable(url))
+
+        for url in bad_urls:
+            self.assertFalse(crawler._is_browsable(url))
+
+        # allow all hosts
+        crawler = Crawler(follow_externals=True, hosts=("*",))
+        self.assertTrue(crawler._is_browsable("http://an-external.link/path"))
+        self.assertTrue(crawler._is_browsable("pypi.example.org/a/path"))
+
+        # specify a list of hosts we want to allow
+        crawler = Crawler(follow_externals=True,
+                          hosts=("*.example.org",))
+        self.assertFalse(crawler._is_browsable("http://an-external.link/path"))
+        self.assertTrue(
+            crawler._is_browsable("http://pypi.example.org/a/path"))
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server("with_externals")
+    def test_follow_externals(self, server):
+        # Include external pages
+        # Try to request the package index, wich contains links to "externals"
+        # resources. They have to  be scanned too.
+        crawler = self._get_simple_crawler(server, follow_externals=True)
+        crawler.get_release("foobar")
+        self.assertIn(server.full_address + "/external/external.html",
+            crawler._processed_urls)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server("with_real_externals")
+    def test_restrict_hosts(self, server):
+        # Only use a list of allowed hosts is possible
+        # Test that telling the simple pyPI client to not retrieve external
+        # works
+        crawler = self._get_simple_crawler(server, follow_externals=False)
+        crawler.get_release("foobar")
+        self.assertNotIn(server.full_address + "/external/external.html",
+            crawler._processed_urls)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server(static_filesystem_paths=["with_externals"],
+        static_uri_paths=["simple", "external"])
+    def test_links_priority(self, server):
+        # Download links from the pypi simple index should be used before
+        # external download links.
+        # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+        #
+        # Usecase :
+        # - someone uploads a package on pypi, a md5 is generated
+        # - someone manually coindexes this link (with the md5 in the url) onto
+        #   an external page accessible from the package page.
+        # - someone reuploads the package (with a different md5)
+        # - while easy_installing, an MD5 error occurs because the external
+        # link is used
+        # -> The index should use the link from pypi, not the external one.
+
+        # start an index server
+        index_url = server.full_address + '/simple/'
+
+        # scan a test index
+        crawler = Crawler(index_url, follow_externals=True)
+        releases = crawler.get_releases("foobar")
+        server.stop()
+
+        # we have only one link, because links are compared without md5
+        self.assertEqual(1, len(releases))
+        self.assertEqual(1, len(releases[0].dists))
+        # the link should be from the index
+        self.assertEqual(2, len(releases[0].dists['sdist'].urls))
+        self.assertEqual('12345678901234567',
+                         releases[0].dists['sdist'].url['hashval'])
+        self.assertEqual('md5', releases[0].dists['sdist'].url['hashname'])
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server(static_filesystem_paths=["with_norel_links"],
+        static_uri_paths=["simple", "external"])
+    def test_not_scan_all_links(self, server):
+        # Do not follow all index page links.
+        # The links not tagged with rel="download" and rel="homepage" have
+        # to not be processed by the package index, while processing "pages".
+
+        # process the pages
+        crawler = self._get_simple_crawler(server, follow_externals=True)
+        crawler.get_releases("foobar")
+        # now it should have processed only pages with links rel="download"
+        # and rel="homepage"
+        self.assertIn("%s/simple/foobar/" % server.full_address,
+            crawler._processed_urls)  # it's the simple index page
+        self.assertIn("%s/external/homepage.html" % server.full_address,
+            crawler._processed_urls)  # the external homepage is rel="homepage"
+        self.assertNotIn("%s/external/nonrel.html" % server.full_address,
+            crawler._processed_urls)  # this link contains no rel=*
+        self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from simple index (no rel)
+        self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from simple index (rel)
+        self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address,
+            crawler._processed_urls)  # linked from external homepage (rel)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    def test_uses_mirrors(self):
+        # When the main repository seems down, try using the given mirrors"""
+        server = PyPIServer("foo_bar_baz")
+        mirror = PyPIServer("foo_bar_baz")
+        mirror.start()  # we dont start the server here
+
+        try:
+            # create the index using both servers
+            crawler = Crawler(server.full_address + "/simple/", hosts=('*',),
+                              # set the timeout to 1s for the tests
+                              timeout=1, mirrors=[mirror.full_address])
+
+            # this should not raise a timeout
+            self.assertEqual(4, len(crawler.get_releases("foo")))
+        finally:
+            mirror.stop()
+            server.stop()
+
+    def test_simple_link_matcher(self):
+        # Test that the simple link matcher finds the right links"""
+        crawler = Crawler(follow_externals=False)
+
+        # Here, we define:
+        #   1. one link that must be followed, cause it's a download one
+        #   2. one link that must *not* be followed, cause the is_browsable
+        #      returns false for it.
+        #   3. one link that must be followed cause it's a homepage that is
+        #      browsable
+        #   4. one link that must be followed, because it contain a md5 hash
+        self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url))
+        self.assertFalse(crawler._is_browsable("http://dl-link2"))
+        content = """
+        <a href="http://dl-link1" rel="download">download_link1</a>
+        <a href="http://dl-link2" rel="homepage">homepage_link1</a>
+        <a href="%(index_url)stest" rel="homepage">homepage_link2</a>
+        <a href="%(index_url)stest/foobar-1.tar.gz#md5=abcdef>download_link2</a>
+        """ % {'index_url': crawler.index_url}
+
+        # Test that the simple link matcher yield the good links.
+        generator = crawler._simple_link_matcher(content, crawler.index_url)
+        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+                          crawler.index_url, True), next(generator))
+        self.assertEqual(('http://dl-link1', True), next(generator))
+        self.assertEqual(('%stest' % crawler.index_url, False),
+                         next(generator))
+        self.assertRaises(StopIteration, generator.next)
+
+        # Follow the external links is possible (eg. homepages)
+        crawler.follow_externals = True
+        generator = crawler._simple_link_matcher(content, crawler.index_url)
+        self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+                          crawler.index_url, True), next(generator))
+        self.assertEqual(('http://dl-link1', True), next(generator))
+        self.assertEqual(('http://dl-link2', False), next(generator))
+        self.assertEqual(('%stest' % crawler.index_url, False),
+                         next(generator))
+        self.assertRaises(StopIteration, generator.next)
+
+    def test_browse_local_files(self):
+        # Test that we can browse local files"""
+        index_url = "file://" + PYPI_DEFAULT_STATIC_PATH
+        if sys.platform == 'win32':
+            # under windows the correct syntax is:
+            #   file:///C|\the\path\here
+            # instead of
+            #   file://C:\the\path\here
+            fix = re.compile(r'^(file://)([A-Za-z])(:)')
+            index_url = fix.sub('\\1/\\2|', index_url)
+
+        index_path = os.sep.join([index_url, "test_found_links", "simple"])
+        crawler = Crawler(index_path)
+        dists = crawler.get_releases("foobar")
+        self.assertEqual(4, len(dists))
+
+    def test_get_link_matcher(self):
+        crawler = Crawler("http://example.org")
+        self.assertEqual('_simple_link_matcher', crawler._get_link_matcher(
+                         "http://example.org/some/file").__name__)
+        self.assertEqual('_default_link_matcher', crawler._get_link_matcher(
+                         "http://other-url").__name__)
+
+    def test_default_link_matcher(self):
+        crawler = Crawler("http://example.org", mirrors=[])
+        crawler.follow_externals = True
+        crawler._is_browsable = lambda *args: True
+        base_url = "http://example.org/some/file/"
+        content = """
+<a href="../homepage" rel="homepage">link</a>
+<a href="../download" rel="download">link2</a>
+<a href="../simpleurl">link2</a>
+        """
+        found_links = set(uri for uri, _ in
+                          crawler._default_link_matcher(content, base_url))
+        self.assertIn('http://example.org/some/homepage', found_links)
+        self.assertIn('http://example.org/some/simpleurl', found_links)
+        self.assertIn('http://example.org/some/download', found_links)
+
+    @unittest.skipIf(_thread is None, 'needs threads')
+    @use_pypi_server("project_list")
+    def test_search_projects(self, server):
+        # we can search the index for some projects, on their names
+        # the case used no matters here
+        crawler = self._get_simple_crawler(server)
+        tests = (('Foobar', ['FooBar-bar', 'Foobar-baz', 'Baz-FooBar']),
+                 ('foobar*', ['FooBar-bar', 'Foobar-baz']),
+                 ('*foobar', ['Baz-FooBar']))
+
+        for search, expected in tests:
+            projects = [p.name for p in crawler.search_projects(search)]
+            self.assertListEqual(expected, projects)
+
+
+def test_suite():
+    return unittest.makeSuite(SimpleCrawlerTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest="test_suite")
diff --git a/distutils2/tests/test_pypi_xmlrpc.py b/distutils2/tests/test_pypi_xmlrpc.py
new file mode 100644
--- /dev/null
+++ b/distutils2/tests/test_pypi_xmlrpc.py
@@ -0,0 +1,101 @@
+"""Tests for the distutils2.pypi.xmlrpc module."""
+
+from distutils2.pypi.xmlrpc import Client, InvalidSearchField, ProjectNotFound
+
+from distutils2.tests import unittest
+from distutils2.tests.support import fake_dec
+
+try:
+    import threading
+    from distutils2.tests.pypi_server import use_xmlrpc_server
+except ImportError:
+    threading = None
+    use_xmlrpc_server = fake_dec
+
+
+ at unittest.skipIf(threading is None, "Needs threading")
+class TestXMLRPCClient(unittest.TestCase):
+    def _get_client(self, server, *args, **kwargs):
+        return Client(server.full_address, *args, **kwargs)
+
+    @use_xmlrpc_server()
+    def test_search_projects(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo'])
+        results = [r.name for r in client.search_projects(name='Foo')]
+        self.assertEqual(3, len(results))
+        self.assertIn('FooBar', results)
+        self.assertIn('Foo', results)
+        self.assertIn('FooFoo', results)
+
+    def test_search_projects_bad_fields(self):
+        client = Client()
+        self.assertRaises(InvalidSearchField, client.search_projects,
+                          invalid="test")
+
+    @use_xmlrpc_server()
+    def test_get_releases(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar', 'version': '1.1'},
+            {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'},
+            {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'},
+        ])
+
+        # use a lambda here to avoid an useless mock call
+        server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3']
+
+        releases = client.get_releases('FooBar (<=1.2)')
+        # dont call release_data and release_url; just return name and version.
+        self.assertEqual(2, len(releases))
+        versions = releases.get_versions()
+        self.assertIn('1.1', versions)
+        self.assertIn('1.2', versions)
+        self.assertNotIn('1.3', versions)
+
+        self.assertRaises(ProjectNotFound, client.get_releases, 'Foo')
+
+    @use_xmlrpc_server()
+    def test_get_distributions(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar', 'version': '1.1',
+             'url': 'http://example.org/foobar-1.1-sdist.tar.gz',
+             'digest': '1234567',
+             'type': 'sdist', 'python_version': 'source'},
+            {'name':'FooBar', 'version': '1.1',
+             'url': 'http://example.org/foobar-1.1-bdist.tar.gz',
+             'digest': '8912345', 'type': 'bdist'},
+        ])
+
+        releases = client.get_releases('FooBar', '1.1')
+        client.get_distributions('FooBar', '1.1')
+        release = releases.get_release('1.1')
+        self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz',
+                        release['sdist'].url['url'])
+        self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz',
+                release['bdist'].url['url'])
+        self.assertEqual(release['sdist'].python_version, 'source')
+
+    @use_xmlrpc_server()
+    def test_get_metadata(self, server):
+        client = self._get_client(server)
+        server.xmlrpc.set_distributions([
+            {'name': 'FooBar',
+             'version': '1.1',
+             'keywords': '',
+             'obsoletes_dist': ['FooFoo'],
+             'requires_external': ['Foo'],
+            }])
+        release = client.get_metadata('FooBar', '1.1')
+        self.assertEqual(['Foo'], release.metadata['requires_external'])
+        self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist'])
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestXMLRPCClient))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_resources.py b/distutils2/tests/test_resources.py
deleted file mode 100644
--- a/distutils2/tests/test_resources.py
+++ /dev/null
@@ -1,174 +0,0 @@
-# -*- encoding: utf-8 -*-
-"""Tests for distutils.data."""
-import pkgutil
-import sys
-
-from distutils2._backport.pkgutil import resource_open
-from distutils2._backport.pkgutil import resource_path
-from distutils2._backport.pkgutil import disable_cache
-from distutils2._backport.pkgutil import enable_cache
-from distutils2.command.install_dist import install_dist
-from distutils2.resources import resources_dests
-from distutils2.tests import run_unittest
-from distutils2.tests import unittest
-from distutils2.tests.test_util import GlobTestCaseBase
-import os
-import tempfile
-
-
-class DataFilesTestCase(GlobTestCaseBase):
-
-    def assertRulesMatch(self, rules, spec):
-        tempdir = self.build_files_tree(spec)
-        expected = self.clean_tree(spec)
-        result = resources_dests(tempdir, rules)
-        self.assertEquals(expected, result)
-
-    def clean_tree(self, spec):
-        files = {}
-        for path, value in spec.items():
-            if value is not None:
-                path = self.os_dependant_path(path)
-                files[path] = value
-        return files
-
-    def test_simple_glob(self):
-        rules = [('', '*.tpl', '{data}')]
-        spec  = {'coucou.tpl': '{data}/coucou.tpl',
-            'Donotwant': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_multiple_match(self):
-        rules = [('scripts', '*.bin', '{appdata}'),
-            ('scripts', '*', '{appscript}')]
-        spec  = {'scripts/script.bin': '{appscript}/script.bin',
-            'Babarlikestrawberry': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_set_match(self):
-        rules = [('scripts', '*.{bin,sh}', '{appscript}')]
-        spec  = {'scripts/script.bin': '{appscript}/script.bin',
-            'scripts/babar.sh':  '{appscript}/babar.sh',
-            'Babarlikestrawberry': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_set_match_multiple(self):
-        rules = [('scripts', 'script{s,}.{bin,sh}', '{appscript}')]
-        spec  = {'scripts/scripts.bin': '{appscript}/scripts.bin',
-            'scripts/script.sh':  '{appscript}/script.sh',
-            'Babarlikestrawberry': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_set_match_exclude(self):
-        rules = [('scripts', '*', '{appscript}'),
-            ('', '**/*.sh', None)]
-        spec  = {'scripts/scripts.bin': '{appscript}/scripts.bin',
-            'scripts/script.sh':  None,
-            'Babarlikestrawberry': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_glob_in_base(self):
-        rules = [('scrip*', '*.bin', '{appscript}')]
-        spec  = {'scripts/scripts.bin': '{appscript}/scripts.bin',
-                 'scripouille/babar.bin': '{appscript}/babar.bin',
-                 'scriptortu/lotus.bin': '{appscript}/lotus.bin',
-                 'Babarlikestrawberry': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_recursive_glob(self):
-        rules = [('', '**/*.bin', '{binary}')]
-        spec  = {'binary0.bin': '{binary}/binary0.bin',
-            'scripts/binary1.bin': '{binary}/scripts/binary1.bin',
-            'scripts/bin/binary2.bin': '{binary}/scripts/bin/binary2.bin',
-            'you/kill/pandabear.guy': None}
-        self.assertRulesMatch(rules, spec)
-
-    def test_final_exemple_glob(self):
-        rules = [
-            ('mailman/database/schemas/', '*', '{appdata}/schemas'),
-            ('', '**/*.tpl', '{appdata}/templates'),
-            ('', 'developer-docs/**/*.txt', '{doc}'),
-            ('', 'README', '{doc}'),
-            ('mailman/etc/', '*', '{config}'),
-            ('mailman/foo/', '**/bar/*.cfg', '{config}/baz'),
-            ('mailman/foo/', '**/*.cfg', '{config}/hmm'),
-            ('', 'some-new-semantic.sns', '{funky-crazy-category}')
-        ]
-        spec = {
-            'README': '{doc}/README',
-            'some.tpl': '{appdata}/templates/some.tpl',
-            'some-new-semantic.sns': '{funky-crazy-category}/some-new-semantic.sns',
-            'mailman/database/mailman.db': None,
-            'mailman/database/schemas/blah.schema': '{appdata}/schemas/blah.schema',
-            'mailman/etc/my.cnf': '{config}/my.cnf',
-            'mailman/foo/some/path/bar/my.cfg': '{config}/hmm/some/path/bar/my.cfg',
-            'mailman/foo/some/path/other.cfg': '{config}/hmm/some/path/other.cfg',
-            'developer-docs/index.txt': '{doc}/developer-docs/index.txt',
-            'developer-docs/api/toc.txt': '{doc}/developer-docs/api/toc.txt',
-        }
-        self.maxDiff = None
-        self.assertRulesMatch(rules, spec)
-
-    def test_resource_open(self):
-
-
-        #Create a fake-dist
-        temp_site_packages = tempfile.mkdtemp()
-
-        dist_name = 'test'
-        dist_info = os.path.join(temp_site_packages, 'test-0.1.dist-info')
-        os.mkdir(dist_info)
-
-        metadata_path = os.path.join(dist_info, 'METADATA')
-        resources_path = os.path.join(dist_info, 'RESOURCES')
-
-        metadata_file = open(metadata_path, 'w')
-
-        metadata_file.write(
-"""Metadata-Version: 1.2
-Name: test
-Version: 0.1
-Summary: test
-Author: me
-        """)
-
-        metadata_file.close()
-
-        test_path = 'test.cfg'
-
-        _, test_resource_path = tempfile.mkstemp()
-
-        test_resource_file = open(test_resource_path, 'w')
-
-        content = 'Config'
-        test_resource_file.write(content)
-        test_resource_file.close()
-
-        resources_file = open(resources_path, 'w')
-
-        resources_file.write("""%s,%s""" % (test_path, test_resource_path))
-        resources_file.close()
-
-        #Add fake site-packages to sys.path to retrieve fake dist
-        old_sys_path = sys.path
-        sys.path.insert(0, temp_site_packages)
-
-        #Force pkgutil to rescan the sys.path
-        disable_cache()
-
-        #Try to retrieve resources paths and files
-        self.assertEqual(resource_path(dist_name, test_path), test_resource_path)
-        self.assertRaises(KeyError, resource_path, dist_name, 'notexis')
-
-        self.assertEqual(resource_open(dist_name, test_path).read(), content)
-        self.assertRaises(KeyError, resource_open, dist_name, 'notexis')
-
-        sys.path = old_sys_path
-
-        enable_cache()
-
-def test_suite():
-    return unittest.makeSuite(DataFilesTestCase)
-
-if __name__ == '__main__':
-    run_unittest(test_suite())
diff --git a/distutils2/tests/test_run.py b/distutils2/tests/test_run.py
--- a/distutils2/tests/test_run.py
+++ b/distutils2/tests/test_run.py
@@ -1,14 +1,14 @@
 """Tests for distutils2.run."""
 
-import StringIO
 import os
+import sys
 import shutil
-import sys
+from tempfile import mkstemp
+from StringIO import StringIO
 
-import distutils2
+from distutils2 import install
+from distutils2.tests import unittest, support, TESTFN
 from distutils2.run import main
-from distutils2.tests import captured_stdout
-from distutils2.tests import unittest, support
 
 # setup script that uses __file__
 setup_using___file__ = """\
@@ -29,7 +29,8 @@
 """
 
 
-class CoreTestCase(support.EnvironGuard, unittest.TestCase):
+class CoreTestCase(support.TempdirManager, support.LoggingCatcher,
+                   unittest.TestCase):
 
     def setUp(self):
         super(CoreTestCase, self).setUp()
@@ -45,16 +46,37 @@
         super(CoreTestCase, self).tearDown()
 
     def cleanup_testfn(self):
-        path = distutils2.tests.TESTFN
+        path = TESTFN
         if os.path.isfile(path):
             os.remove(path)
         elif os.path.isdir(path):
             shutil.rmtree(path)
 
-    def write_setup(self, text, path=distutils2.tests.TESTFN):
-        open(path, "w").write(text)
+    def write_setup(self, text, path=TESTFN):
+        with open(path, "w") as fp:
+            fp.write(text)
         return path
 
+    # TODO restore the tests removed six months ago and port them to pysetup
+
+    def test_install(self):
+        # making sure install returns 0 or 1 exit codes
+        project = os.path.join(os.path.dirname(__file__), 'package.tgz')
+        install_path = self.mkdtemp()
+        old_get_path = install.get_path
+        install.get_path = lambda path: install_path
+        old_mod = os.stat(install_path).st_mode
+        os.chmod(install_path, 0)
+        old_stderr = sys.stderr
+        sys.stderr = StringIO()
+        try:
+            self.assertFalse(install.install(project))
+            self.assertEqual(main(['install', 'blabla']), 1)
+        finally:
+            sys.stderr = old_stderr
+            os.chmod(install_path, old_mod)
+            install.get_path = old_get_path
+
 
 def test_suite():
     return unittest.makeSuite(CoreTestCase)
diff --git a/distutils2/tests/test_uninstall.py b/distutils2/tests/test_uninstall.py
--- a/distutils2/tests/test_uninstall.py
+++ b/distutils2/tests/test_uninstall.py
@@ -2,10 +2,16 @@
 import os
 import sys
 from StringIO import StringIO
-from distutils2._backport.pkgutil import disable_cache, enable_cache
-from distutils2.tests import unittest, support, run_unittest
-from distutils2.errors import DistutilsError
+import stat
+import distutils2.util
+
+from distutils2.database import disable_cache, enable_cache
+from distutils2.run import main
+from distutils2.errors import PackagingError
 from distutils2.install import remove
+from distutils2.command.install_dist import install_dist
+
+from distutils2.tests import unittest, support
 
 SETUP_CFG = """
 [metadata]
@@ -14,13 +20,17 @@
 
 [files]
 packages =
-    %(name)s
-    %(name)s.sub
+    %(pkg)s
+    %(pkg)s.sub
 """
 
+
 class UninstallTestCase(support.TempdirManager,
-                     support.LoggingCatcher,
-                     unittest.TestCase):
+                        support.LoggingCatcher,
+                        support.EnvironRestorer,
+                        unittest.TestCase):
+
+    restore_environ = ['PLAT']
 
     def setUp(self):
         super(UninstallTestCase, self).setUp()
@@ -29,60 +39,91 @@
         self.addCleanup(os.chdir, os.getcwd())
         self.addCleanup(enable_cache)
         self.root_dir = self.mkdtemp()
+        self.cwd = os.getcwd()
         disable_cache()
 
+    def tearDown(self):
+        os.chdir(self.cwd)
+        distutils2.util._path_created.clear()
+        super(UninstallTestCase, self).tearDown()
+
     def run_setup(self, *args):
         # run setup with args
         args = ['run'] + list(args)
-        from distutils2.run import main
         dist = main(args)
         return dist
 
     def get_path(self, dist, name):
-        from distutils2.command.install_dist import install_dist
         cmd = install_dist(dist)
         cmd.prefix = self.root_dir
         cmd.finalize_options()
-        return getattr(cmd, 'install_'+name)
+        return getattr(cmd, 'install_' + name)
 
-    def make_dist(self, pkg_name='foo', **kw):
-        dirname = self.mkdtemp()
-        kw['name'] = pkg_name
+    def make_dist(self, name='Foo', **kw):
+        kw['name'] = name
+        pkg = name.lower()
         if 'version' not in kw:
             kw['version'] = '0.1'
-        self.write_file((dirname, 'setup.cfg'), SETUP_CFG % kw)
-        os.mkdir(os.path.join(dirname, pkg_name))
-        self.write_file((dirname, '__init__.py'), '#')
-        self.write_file((dirname, pkg_name+'_utils.py'), '#')
-        os.mkdir(os.path.join(dirname, pkg_name, 'sub'))
-        self.write_file((dirname, pkg_name, 'sub', '__init__.py'), '#')
-        self.write_file((dirname, pkg_name, 'sub', pkg_name+'_utils.py'), '#')
-        return dirname
+        project_dir, dist = self.create_dist(**kw)
+        kw['pkg'] = pkg
 
-    def install_dist(self, pkg_name='foo', dirname=None, **kw):
+        pkg_dir = os.path.join(project_dir, pkg)
+        os.mkdir(pkg_dir)
+        os.mkdir(os.path.join(pkg_dir, 'sub'))
+
+        self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw)
+        self.write_file((pkg_dir, '__init__.py'), '#')
+        self.write_file((pkg_dir, pkg + '_utils.py'), '#')
+        self.write_file((pkg_dir, 'sub', '__init__.py'), '#')
+        self.write_file((pkg_dir, 'sub', pkg + '_utils.py'), '#')
+
+        return project_dir
+
+    def install_dist(self, name='Foo', dirname=None, **kw):
         if not dirname:
-            dirname = self.make_dist(pkg_name, **kw)
+            dirname = self.make_dist(name, **kw)
         os.chdir(dirname)
-        dist = self.run_setup('install_dist', '--prefix='+self.root_dir)
+        old_out = sys.stderr
+        sys.stderr = StringIO()
+        dist = self.run_setup('install_dist', '--prefix=' + self.root_dir)
         install_lib = self.get_path(dist, 'purelib')
         return dist, install_lib
 
     def test_uninstall_unknow_distribution(self):
-        self.assertRaises(DistutilsError, remove, 'foo', paths=[self.root_dir])
+        self.assertRaises(PackagingError, remove, 'Foo',
+                          paths=[self.root_dir])
 
     def test_uninstall(self):
         dist, install_lib = self.install_dist()
+        self.assertIsFile(install_lib, 'foo', '__init__.py')
         self.assertIsFile(install_lib, 'foo', 'sub', '__init__.py')
-        self.assertIsFile(install_lib, 'foo-0.1.dist-info', 'RECORD')
-        remove('foo', paths=[install_lib])
+        self.assertIsFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
+        self.assertTrue(remove('Foo', paths=[install_lib]))
         self.assertIsNotFile(install_lib, 'foo', 'sub', '__init__.py')
-        self.assertIsNotFile(install_lib, 'foo-0.1.dist-info', 'RECORD')
+        self.assertIsNotFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
 
+    def test_remove_issue(self):
+        # makes sure if there are OSErrors (like permission denied)
+        # remove() stops and display a clean error
+        dist, install_lib = self.install_dist('Meh')
 
+        # breaking os.rename
+        old = os.rename
+
+        def _rename(source, target):
+            raise OSError
+
+        os.rename = _rename
+        try:
+            self.assertFalse(remove('Meh', paths=[install_lib]))
+        finally:
+            os.rename = old
+
+        self.assertTrue(remove('Meh', paths=[install_lib]))
 
 
 def test_suite():
     return unittest.makeSuite(UninstallTestCase)
 
 if __name__ == '__main__':
-    run_unittest(test_suite())
+    unittest.main(defaultTest='test_suite')
diff --git a/distutils2/tests/test_unixccompiler.py b/distutils2/tests/test_unixccompiler.py
--- a/distutils2/tests/test_unixccompiler.py
+++ b/distutils2/tests/test_unixccompiler.py
@@ -1,15 +1,17 @@
-"""Tests for distutils.unixccompiler."""
+"""Tests for distutils2.unixccompiler."""
 import sys
 
-from distutils2._backport import sysconfig
+import sysconfig
 from distutils2.compiler.unixccompiler import UnixCCompiler
 from distutils2.tests import unittest
 
+
 class UnixCCompilerTestCase(unittest.TestCase):
 
     def setUp(self):
         self._backup_platform = sys.platform
         self._backup_get_config_var = sysconfig.get_config_var
+
         class CompilerWrapper(UnixCCompiler):
             def rpath_foo(self):
                 return self.runtime_library_dir_option('/foo')
@@ -19,16 +21,11 @@
         sys.platform = self._backup_platform
         sysconfig.get_config_var = self._backup_get_config_var
 
+    @unittest.skipIf(sys.platform == 'win32', 'irrelevant on win32')
     def test_runtime_libdir_option(self):
 
-        # not tested under windows
-        if sys.platform == 'win32':
-            return
-
-        # Issue#5900
-        #
-        # Ensure RUNPATH is added to extension modules with RPATH if
-        # GNU ld is used
+        # Issue #5900: Ensure RUNPATH is added to extension
+        # modules with RPATH if GNU ld is used
 
         # darwin
         sys.platform = 'darwin'
@@ -37,6 +34,7 @@
         # hp-ux
         sys.platform = 'hp-ux'
         old_gcv = sysconfig.get_config_var
+
         def gcv(v):
             return 'xxx'
         sysconfig.get_config_var = gcv
@@ -64,6 +62,7 @@
 
         # GCC GNULD
         sys.platform = 'bar'
+
         def gcv(v):
             if v == 'CC':
                 return 'gcc'
@@ -74,6 +73,7 @@
 
         # GCC non-GNULD
         sys.platform = 'bar'
+
         def gcv(v):
             if v == 'CC':
                 return 'gcc'
@@ -85,6 +85,7 @@
         # GCC GNULD with fully qualified configuration prefix
         # see #7617
         sys.platform = 'bar'
+
         def gcv(v):
             if v == 'CC':
                 return 'x86_64-pc-linux-gnu-gcc-4.4.2'
@@ -93,9 +94,9 @@
         sysconfig.get_config_var = gcv
         self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
 
-
         # non-GCC GNULD
         sys.platform = 'bar'
+
         def gcv(v):
             if v == 'CC':
                 return 'cc'
@@ -106,6 +107,7 @@
 
         # non-GCC non-GNULD
         sys.platform = 'bar'
+
         def gcv(v):
             if v == 'CC':
                 return 'cc'
@@ -116,6 +118,7 @@
 
         # AIX C/C++ linker
         sys.platform = 'aix'
+
         def gcv(v):
             return 'xxx'
         sysconfig.get_config_var = gcv
diff --git a/distutils2/tests/test_util.py b/distutils2/tests/test_util.py
--- a/distutils2/tests/test_util.py
+++ b/distutils2/tests/test_util.py
@@ -1,27 +1,25 @@
-"""Tests for distutils.util."""
+"""Tests for distutils2.util."""
 import os
 import sys
-from copy import copy
+import time
+import logging
+import tempfile
+import subprocess
 from StringIO import StringIO
-import subprocess
-import time
 
-from distutils2.tests import captured_stdout
-from distutils2.tests import unittest
-from distutils2.errors import (DistutilsPlatformError,
-                               DistutilsByteCompileError,
-                               DistutilsFileError,
-                               DistutilsExecError)
-from distutils2.util import (convert_path, change_root,
-                             check_environ, split_quoted, strtobool,
-                             rfc822_escape, get_compiler_versions,
-                             _find_exe_version, _MAC_OS_X_LD_VERSION,
-                             byte_compile, find_packages, spawn, find_executable,
-                             _nt_quote_args, get_pypirc_path, generate_pypirc,
-                             read_pypirc, resolve_name, iglob, RICH_GLOB)
-
+from distutils2.tests import support, unittest
+from distutils2.tests.test_config import SETUP_CFG
+from distutils2.errors import (
+    PackagingPlatformError, PackagingByteCompileError, PackagingFileError,
+    PackagingExecError, InstallationException)
 from distutils2 import util
-from distutils2.tests import unittest, support
+from distutils2.dist import Distribution
+from distutils2.util import (
+    convert_path, change_root, split_quoted, strtobool, rfc822_escape,
+    get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages,
+    spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob,
+    RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_distutils2,
+    get_install_method, cfg_to_args, encode_multipart)
 
 
 PYPIRC = """\
@@ -56,11 +54,37 @@
 password:xxx
 """
 
+EXPECTED_MULTIPART_OUTPUT = [
+    b'---x',
+    b'Content-Disposition: form-data; name="username"',
+    b'',
+    b'wok',
+    b'---x',
+    b'Content-Disposition: form-data; name="password"',
+    b'',
+    b'secret',
+    b'---x',
+    b'Content-Disposition: form-data; name="picture"; filename="wok.png"',
+    b'',
+    b'PNG89',
+    b'---x--',
+    b'',
+]
+
 
 class FakePopen(object):
     test_class = None
-    def __init__(self, cmd, shell, stdout, stderr):
-        self.cmd = cmd.split()[0]
+
+    def __init__(self, args, bufsize=0, executable=None,
+                 stdin=None, stdout=None, stderr=None,
+                 preexec_fn=None, close_fds=False,
+                 shell=False, cwd=None, env=None, universal_newlines=False,
+                 startupinfo=None, creationflags=0,
+                 restore_signals=True, start_new_session=False,
+                 pass_fds=()):
+        if isinstance(args, basestring):
+            args = args.split()
+        self.cmd = args[0]
         exes = self.test_class._exes
         if self.cmd not in exes:
             # we don't want to call the system, returning an empty
@@ -71,16 +95,27 @@
             self.stdout = StringIO(exes[self.cmd])
             self.stderr = StringIO()
 
-class UtilTestCase(support.EnvironGuard,
+    def communicate(self, input=None, timeout=None):
+        return self.stdout.read(), self.stderr.read()
+
+    def wait(self, timeout=None):
+        return 0
+
+
+class UtilTestCase(support.EnvironRestorer,
                    support.TempdirManager,
                    support.LoggingCatcher,
                    unittest.TestCase):
 
+    restore_environ = ['HOME', 'PLAT']
+
     def setUp(self):
         super(UtilTestCase, self).setUp()
-        self.tmp_dir = self.mkdtemp()
-        self.rc = os.path.join(self.tmp_dir, '.pypirc')
-        os.environ['HOME'] = self.tmp_dir
+        self.addCleanup(os.chdir, os.getcwd())
+        tempdir = self.mkdtemp()
+        self.rc = os.path.join(tempdir, '.pypirc')
+        os.environ['HOME'] = tempdir
+        os.chdir(tempdir)
         # saving the environment
         self.name = os.name
         self.platform = sys.platform
@@ -89,7 +124,6 @@
         self.join = os.path.join
         self.isabs = os.path.isabs
         self.splitdrive = os.path.splitdrive
-        #self._config_vars = copy(sysconfig._config_vars)
 
         # patching os.uname
         if hasattr(os, 'uname'):
@@ -105,7 +139,7 @@
         util.find_executable = self._find_executable
         self._exes = {}
         self.old_popen = subprocess.Popen
-        self.old_stdout  = sys.stdout
+        self.old_stdout = sys.stdout
         self.old_stderr = sys.stderr
         FakePopen.test_class = self
         subprocess.Popen = FakePopen
@@ -123,10 +157,9 @@
             os.uname = self.uname
         else:
             del os.uname
-        #sysconfig._config_vars = copy(self._config_vars)
         util.find_executable = self.old_find_executable
         subprocess.Popen = self.old_popen
-        sys.old_stdout  = self.old_stdout
+        sys.old_stdout = self.old_stdout
         sys.old_stderr = self.old_stderr
         super(UtilTestCase, self).tearDown()
 
@@ -139,6 +172,7 @@
     def test_convert_path(self):
         # linux/mac
         os.sep = '/'
+
         def _join(path):
             return '/'.join(path)
         os.path.join = _join
@@ -148,6 +182,7 @@
 
         # win
         os.sep = '\\'
+
         def _join(*path):
             return '\\'.join(path)
         os.path.join = _join
@@ -163,9 +198,11 @@
     def test_change_root(self):
         # linux/mac
         os.name = 'posix'
+
         def _isabs(path):
             return path[0] == '/'
         os.path.isabs = _isabs
+
         def _join(*path):
             return '/'.join(path)
         os.path.join = _join
@@ -177,14 +214,17 @@
 
         # windows
         os.name = 'nt'
+
         def _isabs(path):
             return path.startswith('c:\\')
         os.path.isabs = _isabs
+
         def _splitdrive(path):
             if path.startswith('c:'):
-                return ('', path.replace('c:', ''))
-            return ('', path)
+                return '', path.replace('c:', '')
+            return '', path
         os.path.splitdrive = _splitdrive
+
         def _join(*path):
             return '\\'.join(path)
         os.path.join = _join
@@ -196,7 +236,7 @@
 
         # BugsBunny os (it's a great os)
         os.name = 'BugsBunny'
-        self.assertRaises(DistutilsPlatformError,
+        self.assertRaises(PackagingPlatformError,
                           change_root, 'c:\\root', 'its\\here')
 
         # XXX platforms to be covered: os2, mac
@@ -213,13 +253,13 @@
             self.assertTrue(strtobool(y))
 
         for n in no:
-            self.assertTrue(not strtobool(n))
+            self.assertFalse(strtobool(n))
 
     def test_rfc822_escape(self):
         header = 'I am a\npoor\nlonesome\nheader\n'
         res = rfc822_escape(header)
         wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s'
-                  'header%(8s)s') % {'8s': '\n'+8*' '}
+                  'header%(8s)s') % {'8s': '\n' + 8 * ' '}
         self.assertEqual(res, wanted)
 
     def test_find_exe_version(self):
@@ -289,17 +329,17 @@
     @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
                          'sys.dont_write_bytecode not supported')
     def test_dont_write_bytecode(self):
-        # makes sure byte_compile raise a DistutilsError
+        # makes sure byte_compile raise a PackagingError
         # if sys.dont_write_bytecode is True
         old_dont_write_bytecode = sys.dont_write_bytecode
         sys.dont_write_bytecode = True
         try:
-            self.assertRaises(DistutilsByteCompileError, byte_compile, [])
+            self.assertRaises(PackagingByteCompileError, byte_compile, [])
         finally:
             sys.dont_write_bytecode = old_dont_write_bytecode
 
     def test_newer(self):
-        self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx')
+        self.assertRaises(PackagingFileError, util.newer, 'xxx', 'xxx')
         self.newer_f1 = self.mktempfile()
         time.sleep(1)
         self.newer_f2 = self.mktempfile()
@@ -340,28 +380,32 @@
         self.write_file(os.path.join(pkg5, '__init__.py'))
 
         res = find_packages([root], ['pkg1.pkg2'])
-        self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6']))
+        self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3',
+                                         'pkg1.pkg3.pkg6']))
 
     def test_resolve_name(self):
-        self.assertEqual(str(42), resolve_name('__builtin__.str')(42))
+        self.assertIs(str, resolve_name('__builtins__.str'))
         self.assertEqual(
             UtilTestCase.__name__,
             resolve_name("distutils2.tests.test_util.UtilTestCase").__name__)
         self.assertEqual(
             UtilTestCase.test_resolve_name.__name__,
-            resolve_name("distutils2.tests.test_util.UtilTestCase.test_resolve_name").__name__)
+            resolve_name("distutils2.tests.test_util.UtilTestCase."
+                         "test_resolve_name").__name__)
 
         self.assertRaises(ImportError, resolve_name,
                           "distutils2.tests.test_util.UtilTestCaseNot")
         self.assertRaises(ImportError, resolve_name,
-                          "distutils2.tests.test_util.UtilTestCase.nonexistent_attribute")
+                          "distutils2.tests.test_util.UtilTestCase."
+                          "nonexistent_attribute")
 
     def test_import_nested_first_time(self):
         tmp_dir = self.mkdtemp()
         os.makedirs(os.path.join(tmp_dir, 'a', 'b'))
         self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '')
         self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '')
-        self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'), 'class Foo: pass')
+        self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'),
+                                    'class Foo: pass')
 
         try:
             sys.path.append(tmp_dir)
@@ -401,23 +445,11 @@
         file_handle.close()
         self.assertEqual(new_content, converted_content)
 
-    def test_nt_quote_args(self):
-
-        for (args, wanted) in ((['with space', 'nospace'],
-                                ['"with space"', 'nospace']),
-                               (['nochange', 'nospace'],
-                                ['nochange', 'nospace'])):
-            res = _nt_quote_args(args)
-            self.assertEqual(res, wanted)
-
-
     @unittest.skipUnless(os.name in ('nt', 'posix'),
                          'runs only under posix or nt')
     def test_spawn(self):
-        # Do not patch subprocess on unix because
-        # distutils2.util._spawn_posix uses it
-        if os.name in 'posix':
-            subprocess.Popen = self.old_popen
+        # no patching of Popen here
+        subprocess.Popen = self.old_popen
         tmpdir = self.mkdtemp()
 
         # creating something executable
@@ -425,24 +457,24 @@
         if os.name == 'posix':
             exe = os.path.join(tmpdir, 'foo.sh')
             self.write_file(exe, '#!/bin/sh\nexit 1')
-            os.chmod(exe, 0777)
+            os.chmod(exe, 0o777)
         else:
             exe = os.path.join(tmpdir, 'foo.bat')
             self.write_file(exe, 'exit 1')
 
-        os.chmod(exe, 0777)
-        self.assertRaises(DistutilsExecError, spawn, [exe])
+        os.chmod(exe, 0o777)
+        self.assertRaises(PackagingExecError, spawn, [exe])
 
         # now something that works
         if os.name == 'posix':
             exe = os.path.join(tmpdir, 'foo.sh')
             self.write_file(exe, '#!/bin/sh\nexit 0')
-            os.chmod(exe, 0777)
+            os.chmod(exe, 0o777)
         else:
             exe = os.path.join(tmpdir, 'foo.bat')
             self.write_file(exe, 'exit 0')
 
-        os.chmod(exe, 0777)
+        os.chmod(exe, 0o777)
         spawn([exe])  # should work without any error
 
     def test_server_registration(self):
@@ -454,8 +486,7 @@
         self.write_file(self.rc, PYPIRC)
         config = read_pypirc()
 
-        config = config.items()
-        config.sort()
+        config = sorted(config.items())
         expected = [('password', 'xxxx'), ('realm', 'pypi'),
                     ('repository', 'http://pypi.python.org/pypi'),
                     ('server', 'pypi'), ('username', 'me')]
@@ -464,8 +495,7 @@
         # old format
         self.write_file(self.rc, PYPIRC_OLD)
         config = read_pypirc()
-        config = config.items()
-        config.sort()
+        config = sorted(config.items())
         expected = [('password', 'secret'), ('realm', 'pypi'),
                     ('repository', 'http://pypi.python.org/pypi'),
                     ('server', 'server-login'), ('username', 'tarek')]
@@ -473,12 +503,53 @@
 
     def test_server_empty_registration(self):
         rc = get_pypirc_path()
-        self.assertTrue(not os.path.exists(rc))
+        self.assertFalse(os.path.exists(rc))
         generate_pypirc('tarek', 'xxx')
         self.assertTrue(os.path.exists(rc))
-        content = open(rc).read()
+        with open(rc) as f:
+            content = f.read()
         self.assertEqual(content, WANTED)
 
+    def test_cfg_to_args(self):
+        opts = {'description-file': 'README', 'extra-files': '',
+                'setup-hooks': 'distutils2.tests.test_config.version_hook'}
+        self.write_file('setup.cfg', SETUP_CFG % opts, encoding='utf-8')
+        self.write_file('README', 'loooong description')
+
+        args = cfg_to_args()
+        # use Distribution to get the contents of the setup.cfg file
+        dist = Distribution()
+        dist.parse_config_files()
+        metadata = dist.metadata
+
+        self.assertEqual(args['name'], metadata['Name'])
+        # + .dev1 because the test SETUP_CFG also tests a hook function in
+        # test_config.py for appending to the version string
+        self.assertEqual(args['version'] + '.dev1', metadata['Version'])
+        self.assertEqual(args['author'], metadata['Author'])
+        self.assertEqual(args['author_email'], metadata['Author-Email'])
+        self.assertEqual(args['maintainer'], metadata['Maintainer'])
+        self.assertEqual(args['maintainer_email'],
+                         metadata['Maintainer-Email'])
+        self.assertEqual(args['description'], metadata['Summary'])
+        self.assertEqual(args['long_description'], metadata['Description'])
+        self.assertEqual(args['classifiers'], metadata['Classifier'])
+        self.assertEqual(args['requires'], metadata['Requires-Dist'])
+        self.assertEqual(args['provides'], metadata['Provides-Dist'])
+
+        self.assertEqual(args['package_dir'].get(''), dist.package_dir)
+        self.assertEqual(args['packages'], dist.packages)
+        self.assertEqual(args['scripts'], dist.scripts)
+        self.assertEqual(args['py_modules'], dist.py_modules)
+
+    def test_encode_multipart(self):
+        fields = [('username', 'wok'), ('password', 'secret')]
+        files = [('picture', 'wok.png', b'PNG89')]
+        content_type, body = encode_multipart(fields, files, b'-x')
+        self.assertEqual(b'multipart/form-data; boundary=-x', content_type)
+        self.assertEqual(EXPECTED_MULTIPART_OUTPUT, body.split(b'\r\n'))
+
+
 class GlobTestCaseBase(support.TempdirManager,
                        support.LoggingCatcher,
                        unittest.TestCase):
@@ -499,51 +570,59 @@
         return tempdir
 
     @staticmethod
-    def os_dependant_path(path):
+    def os_dependent_path(path):
         path = path.rstrip('/').split('/')
         return os.path.join(*path)
 
     def clean_tree(self, spec):
         files = []
-        for path, includes in list(spec.items()):
+        for path, includes in spec.items():
             if includes:
-                files.append(self.os_dependant_path(path))
+                files.append(self.os_dependent_path(path))
         return files
 
+
 class GlobTestCase(GlobTestCaseBase):
 
+    def setUp(self):
+        super(GlobTestCase, self).setUp()
+        self.cwd = os.getcwd()
+
+    def tearDown(self):
+        os.chdir(self.cwd)
+        super(GlobTestCase, self).tearDown()
 
     def assertGlobMatch(self, glob, spec):
         """"""
-        tempdir  = self.build_files_tree(spec)
+        tempdir = self.build_files_tree(spec)
         expected = self.clean_tree(spec)
-        self.addCleanup(os.chdir, os.getcwd())
         os.chdir(tempdir)
         result = list(iglob(glob))
         self.assertItemsEqual(expected, result)
 
     def test_regex_rich_glob(self):
-        matches = RICH_GLOB.findall(r"babar aime les {fraises} est les {huitres}")
-        self.assertEquals(["fraises","huitres"], matches)
+        matches = RICH_GLOB.findall(
+                                r"babar aime les {fraises} est les {huitres}")
+        self.assertEqual(["fraises", "huitres"], matches)
 
     def test_simple_glob(self):
         glob = '*.tp?'
-        spec  = {'coucou.tpl': True,
+        spec = {'coucou.tpl': True,
                  'coucou.tpj': True,
                  'Donotwant': False}
         self.assertGlobMatch(glob, spec)
 
     def test_simple_glob_in_dir(self):
-        glob = 'babar/*.tp?'
-        spec  = {'babar/coucou.tpl': True,
+        glob = os.path.join('babar', '*.tp?')
+        spec = {'babar/coucou.tpl': True,
                  'babar/coucou.tpj': True,
                  'babar/toto.bin': False,
                  'Donotwant': False}
         self.assertGlobMatch(glob, spec)
 
     def test_recursive_glob_head(self):
-        glob = '**/tip/*.t?l'
-        spec  = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+        glob = os.path.join('**', 'tip', '*.t?l')
+        spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
                  'babar/z/tip/coucou.tpl': True,
                  'babar/tip/coucou.tpl': True,
                  'babar/zeop/tip/babar/babar.tpl': False,
@@ -555,7 +634,7 @@
         self.assertGlobMatch(glob, spec)
 
     def test_recursive_glob_tail(self):
-        glob = 'babar/**'
+        glob = os.path.join('babar', '**')
         spec = {'babar/zaza/': True,
                 'babar/zaza/zuzu/': True,
                 'babar/zaza/zuzu/babar.xml': True,
@@ -569,8 +648,8 @@
         self.assertGlobMatch(glob, spec)
 
     def test_recursive_glob_middle(self):
-        glob = 'babar/**/tip/*.t?l'
-        spec  = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+        glob = os.path.join('babar', '**', 'tip', '*.t?l')
+        spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
                  'babar/z/tip/coucou.tpl': True,
                  'babar/tip/coucou.tpl': True,
                  'babar/zeop/tip/babar/babar.tpl': False,
@@ -582,8 +661,8 @@
         self.assertGlobMatch(glob, spec)
 
     def test_glob_set_tail(self):
-        glob = 'bin/*.{bin,sh,exe}'
-        spec  = {'bin/babar.bin': True,
+        glob = os.path.join('bin', '*.{bin,sh,exe}')
+        spec = {'bin/babar.bin': True,
                  'bin/zephir.sh': True,
                  'bin/celestine.exe': True,
                  'bin/cornelius.bat': False,
@@ -593,8 +672,8 @@
         self.assertGlobMatch(glob, spec)
 
     def test_glob_set_middle(self):
-        glob = 'xml/{babar,toto}.xml'
-        spec  = {'xml/babar.xml': True,
+        glob = os.path.join('xml', '{babar,toto}.xml')
+        spec = {'xml/babar.xml': True,
                  'xml/toto.xml': True,
                  'xml/babar.xslt': False,
                  'xml/cornelius.sgml': False,
@@ -604,8 +683,8 @@
         self.assertGlobMatch(glob, spec)
 
     def test_glob_set_head(self):
-        glob = '{xml,xslt}/babar.*'
-        spec  = {'xml/babar.xml': True,
+        glob = os.path.join('{xml,xslt}', 'babar.*')
+        spec = {'xml/babar.xml': True,
                  'xml/toto.xml': False,
                  'xslt/babar.xslt': True,
                  'xslt/toto.xslt': False,
@@ -614,8 +693,10 @@
         self.assertGlobMatch(glob, spec)
 
     def test_glob_all(self):
-        glob = '{xml/*,xslt/**}/babar.xml'
-        spec  = {'xml/a/babar.xml': True,
+        dirs = '{%s,%s}' % (os.path.join('xml', '*'),
+                            os.path.join('xslt', '**'))
+        glob = os.path.join(dirs, 'babar.xml')
+        spec = {'xml/a/babar.xml': True,
                  'xml/b/babar.xml': True,
                  'xml/a/c/babar.xml': False,
                  'xslt/a/babar.xml': True,
@@ -632,31 +713,285 @@
             '/**ddsfs',
             '**##1e"&e',
             'DSFb**c009',
-            '{'
-            '{aaQSDFa'
-            '}'
-            'aQSDFSaa}'
-            '{**a,'
-            ',**a}'
-            '{a**,'
-            ',b**}'
-            '{a**a,babar}'
-            '{bob,b**z}'
-            ]
-        msg = "%r is not supposed to be a valid pattern"
+            '{',
+            '{aaQSDFa',
+            '}',
+            'aQSDFSaa}',
+            '{**a,',
+            ',**a}',
+            '{a**,',
+            ',b**}',
+            '{a**a,babar}',
+            '{bob,b**z}',
+        ]
         for pattern in invalids:
-            try:
-                iglob(pattern)
-            except ValueError:
-                continue
-            else:
-                self.fail("%r is not a valid iglob pattern" % pattern)
+            self.assertRaises(ValueError, iglob, pattern)
 
 
+class EggInfoToDistInfoTestCase(support.TempdirManager,
+                                support.LoggingCatcher,
+                                unittest.TestCase):
+
+    def get_metadata_file_paths(self, distinfo_path):
+        req_metadata_files = ['METADATA', 'RECORD', 'INSTALLER']
+        metadata_file_paths = []
+        for metadata_file in req_metadata_files:
+            path = os.path.join(distinfo_path, metadata_file)
+            metadata_file_paths.append(path)
+        return metadata_file_paths
+
+    def test_egginfo_to_distinfo_setuptools(self):
+        distinfo = 'hello-0.1.1-py3.3.dist-info'
+        egginfo = 'hello-0.1.1-py3.3.egg-info'
+        dirs = [egginfo]
+        files = ['hello.py', 'hello.pyc']
+        extra_metadata = ['dependency_links.txt', 'entry_points.txt',
+                          'not-zip-safe', 'PKG-INFO', 'top_level.txt',
+                          'SOURCES.txt']
+        for f in extra_metadata:
+            files.append(os.path.join(egginfo, f))
+
+        tempdir, record_file = self.build_dist_tree(files, dirs)
+        distinfo_path = os.path.join(tempdir, distinfo)
+        egginfo_path = os.path.join(tempdir, egginfo)
+        metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+        egginfo_to_distinfo(record_file)
+        # test that directories and files get created
+        self.assertTrue(os.path.isdir(distinfo_path))
+        self.assertTrue(os.path.isdir(egginfo_path))
+
+        for mfile in metadata_file_paths:
+            self.assertTrue(os.path.isfile(mfile))
+
+    def test_egginfo_to_distinfo_distutils(self):
+        distinfo = 'hello-0.1.1-py3.3.dist-info'
+        egginfo = 'hello-0.1.1-py3.3.egg-info'
+        # egginfo is a file in distutils which contains the metadata
+        files = ['hello.py', 'hello.pyc', egginfo]
+
+        tempdir, record_file = self.build_dist_tree(files, dirs=[])
+        distinfo_path = os.path.join(tempdir, distinfo)
+        egginfo_path = os.path.join(tempdir, egginfo)
+        metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+        egginfo_to_distinfo(record_file)
+        # test that directories and files get created
+        self.assertTrue(os.path.isdir(distinfo_path))
+        self.assertTrue(os.path.isfile(egginfo_path))
+
+        for mfile in metadata_file_paths:
+            self.assertTrue(os.path.isfile(mfile))
+
+    def build_dist_tree(self, files, dirs):
+        tempdir = self.mkdtemp()
+        record_file_path = os.path.join(tempdir, 'RECORD')
+        file_paths, dir_paths = ([], [])
+        for d in dirs:
+            path = os.path.join(tempdir, d)
+            os.makedirs(path)
+            dir_paths.append(path)
+        for f in files:
+            path = os.path.join(tempdir, f)
+            with open(path, 'w') as _f:
+                _f.write(f)
+            file_paths.append(path)
+
+        with open(record_file_path, 'w') as record_file:
+            for fpath in file_paths:
+                record_file.write(fpath + '\n')
+            for dpath in dir_paths:
+                record_file.write(dpath + '\n')
+
+        return (tempdir, record_file_path)
+
+
+class PackagingLibChecks(support.TempdirManager,
+                         support.LoggingCatcher,
+                         unittest.TestCase):
+
+    def setUp(self):
+        super(PackagingLibChecks, self).setUp()
+        self._empty_dir = self.mkdtemp()
+
+    def test_empty_package_is_not_based_on_anything(self):
+        self.assertFalse(is_setuptools(self._empty_dir))
+        self.assertFalse(is_distutils(self._empty_dir))
+        self.assertFalse(is_distutils2(self._empty_dir))
+
+    def test_setup_py_importing_setuptools_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._setuptools_setup_py_pkg()))
+
+    def test_egg_info_dir_and_setup_py_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._setuptools_egg_info_pkg()))
+
+    def test_egg_info_and_non_setuptools_setup_py_is_setuptools_based(self):
+        self.assertTrue(is_setuptools(self._egg_info_with_no_setuptools()))
+
+    def test_setup_py_not_importing_setuptools_is_not_setuptools_based(self):
+        self.assertFalse(is_setuptools(self._random_setup_py_pkg()))
+
+    def test_setup_py_importing_distutils_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._distutils_setup_py_pkg()))
+
+    def test_pkg_info_file_and_setup_py_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._distutils_pkg_info()))
+
+    def test_pkg_info_and_non_distutils_setup_py_is_distutils_based(self):
+        self.assertTrue(is_distutils(self._pkg_info_with_no_distutils()))
+
+    def test_setup_py_not_importing_distutils_is_not_distutils_based(self):
+        self.assertFalse(is_distutils(self._random_setup_py_pkg()))
+
+    def test_setup_cfg_with_no_metadata_section_is_not_distutils2_based(self):
+        self.assertFalse(is_distutils2(self._setup_cfg_with_no_metadata_pkg()))
+
+    def test_setup_cfg_with_valid_metadata_section_is_distutils2_based(self):
+        self.assertTrue(is_distutils2(self._valid_setup_cfg_pkg()))
+
+    def test_setup_cfg_and_invalid_setup_cfg_is_not_distutils2_based(self):
+        self.assertFalse(is_distutils2(self._invalid_setup_cfg_pkg()))
+
+    def test_get_install_method_with_setuptools_pkg(self):
+        path = self._setuptools_setup_py_pkg()
+        self.assertEqual("setuptools", get_install_method(path))
+
+    def test_get_install_method_with_distutils_pkg(self):
+        path = self._distutils_pkg_info()
+        self.assertEqual("distutils", get_install_method(path))
+
+    def test_get_install_method_with_distutils2_pkg(self):
+        path = self._valid_setup_cfg_pkg()
+        self.assertEqual("distutils2", get_install_method(path))
+
+    def test_get_install_method_with_unknown_pkg(self):
+        path = self._invalid_setup_cfg_pkg()
+        self.assertRaises(InstallationException, get_install_method, path)
+
+    def test_is_setuptools_logs_setup_py_text_found(self):
+        is_setuptools(self._setuptools_setup_py_pkg())
+        expected = ['setup.py file found.',
+                    'No egg-info directory found.',
+                    'Found setuptools text in setup.py.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_setuptools_logs_setup_py_text_not_found(self):
+        directory = self._random_setup_py_pkg()
+        is_setuptools(directory)
+        expected = ['setup.py file found.', 'No egg-info directory found.',
+                    'No setuptools text found in setup.py.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_setuptools_logs_egg_info_dir_found(self):
+        is_setuptools(self._setuptools_egg_info_pkg())
+        expected = ['setup.py file found.', 'Found egg-info directory.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_distutils_logs_setup_py_text_found(self):
+        is_distutils(self._distutils_setup_py_pkg())
+        expected = ['setup.py file found.',
+                    'No PKG-INFO file found.',
+                    'Found distutils text in setup.py.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_distutils_logs_setup_py_text_not_found(self):
+        directory = self._random_setup_py_pkg()
+        is_distutils(directory)
+        expected = ['setup.py file found.', 'No PKG-INFO file found.',
+                    'No distutils text found in setup.py.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_distutils_logs_pkg_info_file_found(self):
+        is_distutils(self._distutils_pkg_info())
+        expected = ['setup.py file found.', 'PKG-INFO file found.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_distutils2_logs_setup_cfg_found(self):
+        is_distutils2(self._valid_setup_cfg_pkg())
+        expected = ['setup.cfg file found.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def test_is_distutils2_logs_setup_cfg_not_found(self):
+        is_distutils2(self._empty_dir)
+        expected = ['No setup.cfg file found.']
+        self.assertEqual(expected, self.get_logs(logging.DEBUG))
+
+    def _write_setuptools_setup_py(self, directory):
+        self.write_file((directory, 'setup.py'),
+                "from setuptools import setup")
+
+    def _write_distutils_setup_py(self, directory):
+        self.write_file([directory, 'setup.py'],
+                "from distutils.core import setup")
+
+    def _write_distutils2_setup_cfg(self, directory):
+        self.write_file([directory, 'setup.cfg'],
+                        ("[metadata]\n"
+                         "name = mypackage\n"
+                         "version = 0.1.0\n"))
+
+    def _setuptools_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_setuptools_setup_py(tmp)
+        return tmp
+
+    def _distutils_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_distutils_setup_py(tmp)
+        return tmp
+
+    def _valid_setup_cfg_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_distutils2_setup_cfg(tmp)
+        return tmp
+
+    def _setuptools_egg_info_pkg(self):
+        tmp = self.mkdtemp()
+        self._write_setuptools_setup_py(tmp)
+        tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+        return tmp
+
+    def _distutils_pkg_info(self):
+        tmp = self._distutils_setup_py_pkg()
+        self.write_file([tmp, 'PKG-INFO'], '')
+        return tmp
+
+    def _setup_cfg_with_no_metadata_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file([tmp, 'setup.cfg'],
+                        ("[othersection]\n"
+                         "foo = bar\n"))
+        return tmp
+
+    def _invalid_setup_cfg_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file([tmp, 'setup.cfg'],
+                        ("[metadata]\n"
+                         "name = john\n"
+                         "last_name = doe\n"))
+        return tmp
+
+    def _egg_info_with_no_setuptools(self):
+        tmp = self._random_setup_py_pkg()
+        tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+        return tmp
+
+    def _pkg_info_with_no_distutils(self):
+        tmp = self._random_setup_py_pkg()
+        self.write_file([tmp, 'PKG-INFO'], '')
+        return tmp
+
+    def _random_setup_py_pkg(self):
+        tmp = self.mkdtemp()
+        self.write_file((tmp, 'setup.py'), "from mypackage import setup")
+        return tmp
+
 
 def test_suite():
     suite = unittest.makeSuite(UtilTestCase)
     suite.addTest(unittest.makeSuite(GlobTestCase))
+    suite.addTest(unittest.makeSuite(EggInfoToDistInfoTestCase))
+    suite.addTest(unittest.makeSuite(PackagingLibChecks))
     return suite
 
 
diff --git a/distutils2/tests/test_version.py b/distutils2/tests/test_version.py
--- a/distutils2/tests/test_version.py
+++ b/distutils2/tests/test_version.py
@@ -1,4 +1,4 @@
-"""Tests for distutils.version."""
+"""Tests for distutils2.version."""
 import doctest
 import os
 
@@ -8,6 +8,7 @@
 from distutils2.version import VersionPredicate
 from distutils2.tests import unittest
 
+
 class VersionTestCase(unittest.TestCase):
 
     versions = ((V('1.0'), '1.0'),
@@ -36,7 +37,7 @@
         for v, s in self.versions:
             self.assertEqual(hash(v), hash(V(s)))
 
-        versions = set([v for v,s in self.versions])
+        versions = set([v for v, s in self.versions])
         for v, s in self.versions:
             self.assertIn(v, versions)
 
@@ -63,10 +64,11 @@
 
         self.assertEqual(str(V('1980.0')), '1980.0')
         self.assertRaises(HugeMajorVersionNumError, V, '1981.0')
-        self.assertEqual(str(V('1981.0', error_on_huge_major_num=False)), '1981.0')
+        self.assertEqual(str(V('1981.0', error_on_huge_major_num=False)),
+                         '1981.0')
 
     def test_comparison(self):
-        r"""
+        comparison_doctest_string = r"""
         >>> V('1.2.0') == '1.2'
         Traceback (most recent call last):
         ...
@@ -132,9 +134,7 @@
         ...  < V('1.0.post456'))
         True
         """
-        # must be a simpler way to call the docstrings
-        doctest.run_docstring_examples(self.test_comparison, globals(),
-                                       name='test_comparison')
+        doctest.script_from_examples(comparison_doctest_string)
 
     def test_suggest_normalized_version(self):
 
@@ -150,7 +150,7 @@
         self.assertEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
         self.assertEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
         self.assertEqual(suggest('2.4preview1'), '2.4c1')
-        self.assertEqual(suggest('2.4pre1') , '2.4c1')
+        self.assertEqual(suggest('2.4pre1'), '2.4c1')
         self.assertEqual(suggest('2.1-rc2'), '2.1c2')
 
         # from pypi
@@ -199,8 +199,7 @@
         self.assertTrue(VersionPredicate('Hey 2.5').match('2.5.1'))
 
         # XXX need to silent the micro version in this case
-        #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3')
-
+        self.assertFalse(VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3'))
 
         # Make sure a predicate that ends with a number works
         self.assertTrue(VersionPredicate('virtualenv5 (1.0)').match('1.0'))
diff --git a/distutils2/tests/xxmodule.c b/distutils2/tests/xxmodule.c
deleted file mode 100644
--- a/distutils2/tests/xxmodule.c
+++ /dev/null
@@ -1,379 +0,0 @@
-
-/* Use this file as a template to start implementing a module that
-   also declares object types. All occurrences of 'Xxo' should be changed
-   to something reasonable for your objects. After that, all other
-   occurrences of 'xx' should be changed to something reasonable for your
-   module. If your module is named foo your sourcefile should be named
-   foomodule.c.
-
-   You will probably want to delete all references to 'x_attr' and add
-   your own types of attributes instead.  Maybe you want to name your
-   local variables other than 'self'.  If your object type is needed in
-   other files, you'll have to create a file "foobarobject.h"; see
-   intobject.h for an example. */
-
-/* Xxo objects */
-
-#include "Python.h"
-
-static PyObject *ErrorObject;
-
-typedef struct {
-	PyObject_HEAD
-	PyObject	*x_attr;	/* Attributes dictionary */
-} XxoObject;
-
-static PyTypeObject Xxo_Type;
-
-#define XxoObject_Check(v)	(Py_TYPE(v) == &Xxo_Type)
-
-static XxoObject *
-newXxoObject(PyObject *arg)
-{
-	XxoObject *self;
-	self = PyObject_New(XxoObject, &Xxo_Type);
-	if (self == NULL)
-		return NULL;
-	self->x_attr = NULL;
-	return self;
-}
-
-/* Xxo methods */
-
-static void
-Xxo_dealloc(XxoObject *self)
-{
-	Py_XDECREF(self->x_attr);
-	PyObject_Del(self);
-}
-
-static PyObject *
-Xxo_demo(XxoObject *self, PyObject *args)
-{
-	if (!PyArg_ParseTuple(args, ":demo"))
-		return NULL;
-	Py_INCREF(Py_None);
-	return Py_None;
-}
-
-static PyMethodDef Xxo_methods[] = {
-	{"demo",	(PyCFunction)Xxo_demo,	METH_VARARGS,
-		PyDoc_STR("demo() -> None")},
-	{NULL,		NULL}		/* sentinel */
-};
-
-static PyObject *
-Xxo_getattr(XxoObject *self, char *name)
-{
-	if (self->x_attr != NULL) {
-		PyObject *v = PyDict_GetItemString(self->x_attr, name);
-		if (v != NULL) {
-			Py_INCREF(v);
-			return v;
-		}
-	}
-	return Py_FindMethod(Xxo_methods, (PyObject *)self, name);
-}
-
-static int
-Xxo_setattr(XxoObject *self, char *name, PyObject *v)
-{
-	if (self->x_attr == NULL) {
-		self->x_attr = PyDict_New();
-		if (self->x_attr == NULL)
-			return -1;
-	}
-	if (v == NULL) {
-		int rv = PyDict_DelItemString(self->x_attr, name);
-		if (rv < 0)
-			PyErr_SetString(PyExc_AttributeError,
-			        "delete non-existing Xxo attribute");
-		return rv;
-	}
-	else
-		return PyDict_SetItemString(self->x_attr, name, v);
-}
-
-static PyTypeObject Xxo_Type = {
-	/* The ob_type field must be initialized in the module init function
-	 * to be portable to Windows without using C++. */
-	PyVarObject_HEAD_INIT(NULL, 0)
-	"xxmodule.Xxo",		/*tp_name*/
-	sizeof(XxoObject),	/*tp_basicsize*/
-	0,			/*tp_itemsize*/
-	/* methods */
-	(destructor)Xxo_dealloc, /*tp_dealloc*/
-	0,			/*tp_print*/
-	(getattrfunc)Xxo_getattr, /*tp_getattr*/
-	(setattrfunc)Xxo_setattr, /*tp_setattr*/
-	0,			/*tp_compare*/
-	0,			/*tp_repr*/
-	0,			/*tp_as_number*/
-	0,			/*tp_as_sequence*/
-	0,			/*tp_as_mapping*/
-	0,			/*tp_hash*/
-        0,                      /*tp_call*/
-        0,                      /*tp_str*/
-        0,                      /*tp_getattro*/
-        0,                      /*tp_setattro*/
-        0,                      /*tp_as_buffer*/
-        Py_TPFLAGS_DEFAULT,     /*tp_flags*/
-        0,                      /*tp_doc*/
-        0,                      /*tp_traverse*/
-        0,                      /*tp_clear*/
-        0,                      /*tp_richcompare*/
-        0,                      /*tp_weaklistoffset*/
-        0,                      /*tp_iter*/
-        0,                      /*tp_iternext*/
-        0,                      /*tp_methods*/
-        0,                      /*tp_members*/
-        0,                      /*tp_getset*/
-        0,                      /*tp_base*/
-        0,                      /*tp_dict*/
-        0,                      /*tp_descr_get*/
-        0,                      /*tp_descr_set*/
-        0,                      /*tp_dictoffset*/
-        0,                      /*tp_init*/
-        0,                      /*tp_alloc*/
-        0,                      /*tp_new*/
-        0,                      /*tp_free*/
-        0,                      /*tp_is_gc*/
-};
-/* --------------------------------------------------------------------- */
-
-/* Function of two integers returning integer */
-
-PyDoc_STRVAR(xx_foo_doc,
-"foo(i,j)\n\
-\n\
-Return the sum of i and j.");
-
-static PyObject *
-xx_foo(PyObject *self, PyObject *args)
-{
-	long i, j;
-	long res;
-	if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
-		return NULL;
-	res = i+j; /* XXX Do something here */
-	return PyInt_FromLong(res);
-}
-
-
-/* Function of no arguments returning new Xxo object */
-
-static PyObject *
-xx_new(PyObject *self, PyObject *args)
-{
-	XxoObject *rv;
-
-	if (!PyArg_ParseTuple(args, ":new"))
-		return NULL;
-	rv = newXxoObject(args);
-	if (rv == NULL)
-		return NULL;
-	return (PyObject *)rv;
-}
-
-/* Example with subtle bug from extensions manual ("Thin Ice"). */
-
-static PyObject *
-xx_bug(PyObject *self, PyObject *args)
-{
-	PyObject *list, *item;
-
-	if (!PyArg_ParseTuple(args, "O:bug", &list))
-		return NULL;
-
-	item = PyList_GetItem(list, 0);
-	/* Py_INCREF(item); */
-	PyList_SetItem(list, 1, PyInt_FromLong(0L));
-	PyObject_Print(item, stdout, 0);
-	printf("\n");
-	/* Py_DECREF(item); */
-
-	Py_INCREF(Py_None);
-	return Py_None;
-}
-
-/* Test bad format character */
-
-static PyObject *
-xx_roj(PyObject *self, PyObject *args)
-{
-	PyObject *a;
-	long b;
-	if (!PyArg_ParseTuple(args, "O#:roj", &a, &b))
-		return NULL;
-	Py_INCREF(Py_None);
-	return Py_None;
-}
-
-
-/* ---------- */
-
-static PyTypeObject Str_Type = {
-	/* The ob_type field must be initialized in the module init function
-	 * to be portable to Windows without using C++. */
-	PyVarObject_HEAD_INIT(NULL, 0)
-	"xxmodule.Str",		/*tp_name*/
-	0,			/*tp_basicsize*/
-	0,			/*tp_itemsize*/
-	/* methods */
-	0,			/*tp_dealloc*/
-	0,			/*tp_print*/
-	0,			/*tp_getattr*/
-	0,			/*tp_setattr*/
-	0,			/*tp_compare*/
-	0,			/*tp_repr*/
-	0,			/*tp_as_number*/
-	0,			/*tp_as_sequence*/
-	0,			/*tp_as_mapping*/
-	0,			/*tp_hash*/
-	0,			/*tp_call*/
-	0,			/*tp_str*/
-	0,			/*tp_getattro*/
-	0,			/*tp_setattro*/
-	0,			/*tp_as_buffer*/
-	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-	0,			/*tp_doc*/
-	0,			/*tp_traverse*/
-	0,			/*tp_clear*/
-	0,			/*tp_richcompare*/
-	0,			/*tp_weaklistoffset*/
-	0,			/*tp_iter*/
-	0,			/*tp_iternext*/
-	0,			/*tp_methods*/
-	0,			/*tp_members*/
-	0,			/*tp_getset*/
-	0, /* see initxx */	/*tp_base*/
-	0,			/*tp_dict*/
-	0,			/*tp_descr_get*/
-	0,			/*tp_descr_set*/
-	0,			/*tp_dictoffset*/
-	0,			/*tp_init*/
-	0,			/*tp_alloc*/
-	0,			/*tp_new*/
-	0,			/*tp_free*/
-	0,			/*tp_is_gc*/
-};
-
-/* ---------- */
-
-static PyObject *
-null_richcompare(PyObject *self, PyObject *other, int op)
-{
-	Py_INCREF(Py_NotImplemented);
-	return Py_NotImplemented;
-}
-
-static PyTypeObject Null_Type = {
-	/* The ob_type field must be initialized in the module init function
-	 * to be portable to Windows without using C++. */
-	PyVarObject_HEAD_INIT(NULL, 0)
-	"xxmodule.Null",	/*tp_name*/
-	0,			/*tp_basicsize*/
-	0,			/*tp_itemsize*/
-	/* methods */
-	0,			/*tp_dealloc*/
-	0,			/*tp_print*/
-	0,			/*tp_getattr*/
-	0,			/*tp_setattr*/
-	0,			/*tp_compare*/
-	0,			/*tp_repr*/
-	0,			/*tp_as_number*/
-	0,			/*tp_as_sequence*/
-	0,			/*tp_as_mapping*/
-	0,			/*tp_hash*/
-	0,			/*tp_call*/
-	0,			/*tp_str*/
-	0,			/*tp_getattro*/
-	0,			/*tp_setattro*/
-	0,			/*tp_as_buffer*/
-	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-	0,			/*tp_doc*/
-	0,			/*tp_traverse*/
-	0,			/*tp_clear*/
-	null_richcompare,	/*tp_richcompare*/
-	0,			/*tp_weaklistoffset*/
-	0,			/*tp_iter*/
-	0,			/*tp_iternext*/
-	0,			/*tp_methods*/
-	0,			/*tp_members*/
-	0,			/*tp_getset*/
-	0, /* see initxx */	/*tp_base*/
-	0,			/*tp_dict*/
-	0,			/*tp_descr_get*/
-	0,			/*tp_descr_set*/
-	0,			/*tp_dictoffset*/
-	0,			/*tp_init*/
-	0,			/*tp_alloc*/
-	0, /* see initxx */	/*tp_new*/
-	0,			/*tp_free*/
-	0,			/*tp_is_gc*/
-};
-
-
-/* ---------- */
-
-
-/* List of functions defined in the module */
-
-static PyMethodDef xx_methods[] = {
-	{"roj",		xx_roj,		METH_VARARGS,
-		PyDoc_STR("roj(a,b) -> None")},
-	{"foo",		xx_foo,		METH_VARARGS,
-	 	xx_foo_doc},
-	{"new",		xx_new,		METH_VARARGS,
-		PyDoc_STR("new() -> new Xx object")},
-	{"bug",		xx_bug,		METH_VARARGS,
-		PyDoc_STR("bug(o) -> None")},
-	{NULL,		NULL}		/* sentinel */
-};
-
-PyDoc_STRVAR(module_doc,
-"This is a template module just for instruction.");
-
-/* Initialization function for the module (*must* be called initxx) */
-
-PyMODINIT_FUNC
-initxx(void)
-{
-	PyObject *m;
-
-	/* Due to cross platform compiler issues the slots must be filled
-	 * here. It's required for portability to Windows without requiring
-	 * C++. */
-	Null_Type.tp_base = &PyBaseObject_Type;
-	Null_Type.tp_new = PyType_GenericNew;
-	Str_Type.tp_base = &PyUnicode_Type;
-
-	/* Finalize the type object including setting type of the new type
-	 * object; doing it here is required for portability, too. */
-	if (PyType_Ready(&Xxo_Type) < 0)
-		return;
-
-	/* Create the module and add the functions */
-	m = Py_InitModule3("xx", xx_methods, module_doc);
-	if (m == NULL)
-		return;
-
-	/* Add some symbolic constants to the module */
-	if (ErrorObject == NULL) {
-		ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
-		if (ErrorObject == NULL)
-			return;
-	}
-	Py_INCREF(ErrorObject);
-	PyModule_AddObject(m, "error", ErrorObject);
-
-	/* Add Str */
-	if (PyType_Ready(&Str_Type) < 0)
-		return;
-	PyModule_AddObject(m, "Str", (PyObject *)&Str_Type);
-
-	/* Add Null */
-	if (PyType_Ready(&Null_Type) < 0)
-		return;
-	PyModule_AddObject(m, "Null", (PyObject *)&Null_Type);
-}
diff --git a/distutils2/util.py b/distutils2/util.py
--- a/distutils2/util.py
+++ b/distutils2/util.py
@@ -1,46 +1,47 @@
-"""distutils.util
+"""Miscellaneous utility functions."""
 
-Miscellaneous utility functions.
-"""
+import codecs
+import os
+import re
+import csv
+import sys
+import errno
+import shutil
+import string
+import hashlib
+import tarfile
+import zipfile
+import posixpath
+import subprocess
+import sysconfig
+from glob import iglob as std_iglob
+from fnmatch import fnmatchcase
+from inspect import getsource
+from ConfigParser import RawConfigParser
 
-
-import os
-import posixpath
-import re
-import string
-import sys
-from subprocess import call as sub_call
-from copy import copy
-from fnmatch import fnmatchcase
-try:
-    from glob import iglob as std_iglob
-except ImportError:
-    from glob import glob as std_iglob  # for python < 2.5
-from ConfigParser import RawConfigParser
-from inspect import getsource
-
-from distutils2.errors import (DistutilsPlatformError, DistutilsFileError,
-                               DistutilsByteCompileError, DistutilsExecError)
 from distutils2 import logger
-from distutils2._backport import sysconfig as _sysconfig
+from distutils2.errors import (PackagingPlatformError, PackagingFileError,
+                              PackagingByteCompileError, PackagingExecError,
+                              InstallationException, PackagingInternalError)
 
 _PLATFORM = None
+_DEFAULT_INSTALLER = 'distutils2'
 
 
 def newer(source, target):
-    """Tells if the target is newer than the source.
+    """Tell if the target is newer than the source.
 
-    Return true if 'source' exists and is more recently modified than
+    Returns true if 'source' exists and is more recently modified than
     'target', or if 'source' exists and 'target' doesn't.
 
-    Return false if both exist and 'target' is the same age or younger
-    than 'source'. Raise DistutilsFileError if 'source' does not exist.
+    Returns false if both exist and 'target' is the same age or younger
+    than 'source'. Raise PackagingFileError if 'source' does not exist.
 
     Note that this test is not very accurate: files created in the same second
     will have the same "age".
     """
     if not os.path.exists(source):
-        raise DistutilsFileError("file '%s' does not exist" %
+        raise PackagingFileError("file '%s' does not exist" %
                                  os.path.abspath(source))
     if not os.path.exists(target):
         return True
@@ -56,15 +57,15 @@
     """
     global _PLATFORM
     if _PLATFORM is None:
-        _PLATFORM = _sysconfig.get_platform()
+        _PLATFORM = sysconfig.get_platform()
     return _PLATFORM
 
 
 def set_platform(identifier):
-    """Sets the platform string identifier returned by get_platform().
+    """Set the platform string identifier returned by get_platform().
 
     Note that this change doesn't impact the value returned by
-    sysconfig.get_platform() and is local to Distutils
+    sysconfig.get_platform(); it is local to distutils2.
     """
     global _PLATFORM
     _PLATFORM = identifier
@@ -73,7 +74,7 @@
 def convert_path(pathname):
     """Return 'pathname' as a name that will work on the native filesystem.
 
-    i.e. split it on '/' and put it back together again using the current
+    The path is split on '/' and put back together again using the current
     directory separator.  Needed because filenames in the setup script are
     always supplied in Unix style, and have to be converted to the local
     convention before we can actually use them in the filesystem.  Raises
@@ -101,9 +102,8 @@
     """Return 'pathname' with 'new_root' prepended.
 
     If 'pathname' is relative, this is equivalent to
-    "os.path.join(new_root,pathname)".
-    Otherwise, it requires making 'pathname' relative and then joining the
-    two, which is tricky on DOS/Windows and Mac OS.
+    os.path.join(new_root,pathname). Otherwise, it requires making 'pathname'
+    relative and then joining the two, which is tricky on DOS/Windows.
     """
     if os.name == 'posix':
         if not os.path.isabs(pathname):
@@ -112,22 +112,22 @@
             return os.path.join(new_root, pathname[1:])
 
     elif os.name == 'nt':
-        (drive, path) = os.path.splitdrive(pathname)
+        drive, path = os.path.splitdrive(pathname)
         if path[0] == '\\':
             path = path[1:]
         return os.path.join(new_root, path)
 
     elif os.name == 'os2':
-        (drive, path) = os.path.splitdrive(pathname)
+        drive, path = os.path.splitdrive(pathname)
         if path[0] == os.sep:
             path = path[1:]
         return os.path.join(new_root, path)
 
     else:
-        raise DistutilsPlatformError("nothing known about "
+        raise PackagingPlatformError("nothing known about "
                                      "platform '%s'" % os.name)
 
-_environ_checked = 0
+_environ_checked = False
 
 
 def check_environ():
@@ -148,9 +148,9 @@
         os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
 
     if 'PLAT' not in os.environ:
-        os.environ['PLAT'] = _sysconfig.get_platform()
+        os.environ['PLAT'] = sysconfig.get_platform()
 
-    _environ_checked = 1
+    _environ_checked = True
 
 
 def subst_vars(s, local_vars):
@@ -174,8 +174,8 @@
 
     try:
         return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
-    except KeyError, var:
-        raise ValueError("invalid variable '$%s'" % var)
+    except KeyError:
+        raise ValueError("invalid variable '$%s'" % sys.exc_info()[1])
 
 
 # Needed by 'split_quoted()'
@@ -240,7 +240,7 @@
             if m is None:
                 raise ValueError("bad string (mismatched %s quotes?)" % s[end])
 
-            (beg, end) = m.span()
+            beg, end = m.span()
             s = s[:beg] + s[beg + 1:end - 1] + s[end:]
             pos = m.end() - 2
 
@@ -251,10 +251,18 @@
     return words
 
 
-def execute(func, args, msg=None, verbose=0, dry_run=0):
+def split_multiline(value):
+    """Split a multiline string into a list, excluding blank lines."""
+
+    return [element for element in
+            (line.strip() for line in value.split('\n'))
+            if element]
+
+
+def execute(func, args, msg=None, verbose=0, dry_run=False):
     """Perform some action that affects the outside world.
 
-    eg. by writing to the filesystem).  Such actions are special because
+    Some actions (e.g. writing to the filesystem) are special because
     they are disabled by the 'dry_run' flag.  This method takes care of all
     that bureaucracy for you; all you have to do is supply the
     function to call and an argument tuple for it (to embody the
@@ -272,7 +280,7 @@
 
 
 def strtobool(val):
-    """Convert a string representation of truth to true (1) or false (0).
+    """Convert a string representation of truth to a boolean.
 
     True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
     are 'n', 'no', 'f', 'false', 'off', and '0'.  Raises ValueError if
@@ -280,15 +288,15 @@
     """
     val = val.lower()
     if val in ('y', 'yes', 't', 'true', 'on', '1'):
-        return 1
+        return True
     elif val in ('n', 'no', 'f', 'false', 'off', '0'):
-        return 0
+        return False
     else:
         raise ValueError("invalid truth value %r" % (val,))
 
 
-def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None,
-                  verbose=1, dry_run=0, direct=None):
+def byte_compile(py_files, optimize=0, force=False, prefix=None,
+                 base_dir=None, verbose=0, dry_run=False, direct=None):
     """Byte-compile a collection of Python source files to either .pyc
     or .pyo files in the same directory.
 
@@ -319,8 +327,9 @@
     it set to None.
     """
     # nothing is done if sys.dont_write_bytecode is True
+    # FIXME this should not raise an error
     if hasattr(sys, 'dont_write_bytecode') and sys.dont_write_bytecode:
-        raise DistutilsByteCompileError('byte-compiling is disabled.')
+        raise PackagingByteCompileError('byte-compiling is disabled.')
 
     # First, if the caller didn't force us into direct or indirect mode,
     # figure out which mode we should be in.  We take a conservative
@@ -339,15 +348,18 @@
     # run it with the appropriate flags.
     if not direct:
         from tempfile import mkstemp
+        # XXX script_fd may leak, use something better than mkstemp
         script_fd, script_name = mkstemp(".py")
+        os.close(script_fd)
+        script_fd = None
         logger.info("writing byte-compilation script '%s'", script_name)
         if not dry_run:
             if script_fd is not None:
-                script = os.fdopen(script_fd, "w")
+                script = os.fdopen(script_fd, "w", encoding='utf-8')
             else:
-                script = open(script_name, "w")
+                script = codecs.open(script_name, "w", encoding='utf-8')
 
-            try:
+            with script:
                 script.write("""\
 from distutils2.util import byte_compile
 files = [
@@ -371,23 +383,20 @@
                 script.write("""
 byte_compile(files, optimize=%r, force=%r,
              prefix=%r, base_dir=%r,
-             verbose=%r, dry_run=0,
-             direct=1)
+             verbose=%r, dry_run=False,
+             direct=True)
 """ % (optimize, force, prefix, base_dir, verbose))
 
-            finally:
-                script.close()
-
         cmd = [sys.executable, script_name]
         if optimize == 1:
             cmd.insert(1, "-O")
         elif optimize == 2:
             cmd.insert(1, "-OO")
 
-        env = copy(os.environ)
-        env['PYTHONPATH'] = ':'.join(sys.path)
+        env = os.environ.copy()
+        env['PYTHONPATH'] = os.path.pathsep.join(sys.path)
         try:
-            spawn(cmd, dry_run=dry_run, env=env)
+            spawn(cmd, env=env)
         finally:
             execute(os.remove, (script_name,), "removing %s" % script_name,
                     dry_run=dry_run)
@@ -430,8 +439,9 @@
 
 
 def rfc822_escape(header):
-    """Return a version of the string escaped for inclusion in an
-    RFC-822 header, by ensuring there are 8 spaces space after each newline.
+    """Return a form of *header* suitable for inclusion in an RFC 822-header.
+
+    This function ensures there are 8 spaces after each newline.
     """
     lines = header.split('\n')
     sep = '\n' + 8 * ' '
@@ -443,7 +453,7 @@
 
 
 def _find_ld_version():
-    """Finds the ld version. The version scheme differs under Mac OSX."""
+    """Find the ld version.  The version scheme differs under Mac OS X."""
     if sys.platform == 'darwin':
         return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION)
     else:
@@ -453,7 +463,7 @@
 def _find_exe_version(cmd, pattern=_RE_VERSION):
     """Find the version of an executable by running `cmd` in the shell.
 
-    `pattern` is a compiled regular expression. If not provided, default
+    `pattern` is a compiled regular expression.  If not provided, defaults
     to _RE_VERSION. If the command is not found, or the output does not
     match the mattern, returns None.
     """
@@ -463,7 +473,7 @@
         return None
     pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
     try:
-        stdout, stderr = pipe.stdout.read(), pipe.stderr.read()
+        stdout, stderr = pipe.communicate()
     finally:
         pipe.stdout.close()
         pipe.stderr.close()
@@ -481,7 +491,7 @@
 
 
 def get_compiler_versions():
-    """Returns a tuple providing the versions of gcc, ld and dllwrap
+    """Return a tuple providing the versions of gcc, ld and dllwrap
 
     For each command, if a command is not found, None is returned.
     Otherwise a string with the version is returned.
@@ -533,22 +543,25 @@
 
 
 def write_file(filename, contents):
-    """Create a file with the specified name and write 'contents' (a
-    sequence of strings without line terminators) to it.
+    """Create *filename* and write *contents* to it.
+
+    *contents* is a sequence of strings without line terminators.
     """
-    try:
-        f = open(filename, "w")
+    with open(filename, "w") as f:
         for line in contents:
             f.write(line + "\n")
-    finally:
-        f.close()
 
 
 def _is_package(path):
-    """Returns True if path is a package (a dir with an __init__ file."""
-    if not os.path.isdir(path):
-        return False
-    return os.path.isfile(os.path.join(path, '__init__.py'))
+    return os.path.isdir(path) and os.path.isfile(
+        os.path.join(path, '__init__.py'))
+
+
+# Code taken from the pip project
+def _is_archive_file(name):
+    archives = ('.zip', '.tar.gz', '.tar.bz2', '.tgz', '.tar')
+    ext = splitext(name)[1].lower()
+    return ext in archives
 
 
 def _under(path, root):
@@ -563,7 +576,7 @@
 
 
 def _package_name(root_path, path):
-    """Returns a dotted package name, given a subpath."""
+    # Return a dotted package name, given a subpath
     if not _under(path, root_path):
         raise ValueError('"%s" is not a subpath of "%s"' % (path, root_path))
     return path[len(root_path) + 1:].replace(os.sep, '.')
@@ -639,8 +652,8 @@
     for part in parts[1:]:
         try:
             ret = getattr(ret, part)
-        except AttributeError, exc:
-            raise ImportError(exc)
+        except AttributeError:
+            raise ImportError(sys.exc_info()[1])
 
     return ret
 
@@ -654,9 +667,76 @@
     return base, ext
 
 
+def unzip_file(filename, location, flatten=True):
+    """Unzip the file *filename* into the *location* directory."""
+    if not os.path.exists(location):
+        os.makedirs(location)
+    with open(filename, 'rb') as zipfp:
+        zip = zipfile.ZipFile(zipfp)
+        leading = has_leading_dir(zip.namelist()) and flatten
+        for name in zip.namelist():
+            data = zip.read(name)
+            fn = name
+            if leading:
+                fn = split_leading_dir(name)[1]
+            fn = os.path.join(location, fn)
+            dir = os.path.dirname(fn)
+            if not os.path.exists(dir):
+                os.makedirs(dir)
+            if fn.endswith('/') or fn.endswith('\\'):
+                # A directory
+                if not os.path.exists(fn):
+                    os.makedirs(fn)
+            else:
+                with open(fn, 'wb') as fp:
+                    fp.write(data)
+
+
+def untar_file(filename, location):
+    """Untar the file *filename* into the *location* directory."""
+    if not os.path.exists(location):
+        os.makedirs(location)
+    if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'):
+        mode = 'r:gz'
+    elif (filename.lower().endswith('.bz2')
+          or filename.lower().endswith('.tbz')):
+        mode = 'r:bz2'
+    elif filename.lower().endswith('.tar'):
+        mode = 'r'
+    else:
+        mode = 'r:*'
+    with tarfile.open(filename, mode) as tar:
+        leading = has_leading_dir(member.name for member in tar.getmembers())
+        for member in tar.getmembers():
+            fn = member.name
+            if leading:
+                fn = split_leading_dir(fn)[1]
+            path = os.path.join(location, fn)
+            if member.isdir():
+                if not os.path.exists(path):
+                    os.makedirs(path)
+            else:
+                try:
+                    fp = tar.extractfile(member)
+                except (KeyError, AttributeError):
+                    # Some corrupt tar files seem to produce this
+                    # (specifically bad symlinks)
+                    continue
+                try:
+                    if not os.path.exists(os.path.dirname(path)):
+                        os.makedirs(os.path.dirname(path))
+                        with open(path, 'wb') as destfp:
+                            shutil.copyfileobj(fp, destfp)
+                finally:
+                    fp.close()
+
+
 def has_leading_dir(paths):
-    """Returns true if all the paths have the same leading path name
-    (i.e., everything is in one subdirectory in an archive)"""
+    """Return true if all the paths have the same leading path name.
+
+    In other words, check that everything is in one subdirectory in an
+    archive.
+    """
     common_prefix = None
     for path in paths:
         prefix, rest = split_leading_dir(path)
@@ -680,8 +760,11 @@
     else:
         return path, ''
 
+if sys.platform == 'darwin':
+    _cfg_target = None
+    _cfg_target_split = None
 
-def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
+def spawn(cmd, search_path=True, verbose=0, dry_run=False, env=None):
     """Run another program specified as a command list 'cmd' in a new process.
 
     'cmd' is just the argument list for the new process, ie.
@@ -697,105 +780,45 @@
     If 'env' is given, it's a environment dictionary used for the execution
     environment.
 
-    Raise DistutilsExecError if running the program fails in any way; just
+    Raise PackagingExecError if running the program fails in any way; just
     return on success.
     """
-    if os.name == 'posix':
-        _spawn_posix(cmd, search_path, dry_run=dry_run, env=env)
-    elif os.name == 'nt':
-        _spawn_nt(cmd, search_path, dry_run=dry_run, env=env)
-    elif os.name == 'os2':
-        _spawn_os2(cmd, search_path, dry_run=dry_run, env=env)
-    else:
-        raise DistutilsPlatformError(
-              "don't know how to spawn programs on platform '%s'" % os.name)
+    logger.debug('spawn: running %r', cmd)
+    if dry_run:
+        logger.debug('dry run, no process actually spawned')
+        return
+    if sys.platform == 'darwin':
+        global _cfg_target, _cfg_target_split
+        if _cfg_target is None:
+            _cfg_target = sysconfig.get_config_var(
+                                  'MACOSX_DEPLOYMENT_TARGET') or ''
+            if _cfg_target:
+                _cfg_target_split = [int(x) for x in _cfg_target.split('.')]
+        if _cfg_target:
+            # ensure that the deployment target of build process is not less
+            # than that used when the interpreter was built. This ensures
+            # extension modules are built with correct compatibility values
+            env = env or os.environ
+            cur_target = env.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target)
+            if _cfg_target_split > [int(x) for x in cur_target.split('.')]:
+                my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: '
+                          'now "%s" but "%s" during configure'
+                                % (cur_target, _cfg_target))
+                raise PackagingPlatformError(my_msg)
+            env = dict(env, MACOSX_DEPLOYMENT_TARGET=cur_target)
 
-
-def _nt_quote_args(args):
-    """Quote command-line arguments for DOS/Windows conventions.
-
-    Just wraps every argument which contains blanks in double quotes, and
-    returns a new argument list.
-    """
-    # XXX this doesn't seem very robust to me -- but if the Windows guys
-    # say it'll work, I guess I'll have to accept it.  (What if an arg
-    # contains quotes?  What other magic characters, other than spaces,
-    # have to be escaped?  Is there an escaping mechanism other than
-    # quoting?)
-    for i, arg in enumerate(args):
-        if ' ' in arg:
-            args[i] = '"%s"' % arg
-    return args
-
-
-def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0, env=None):
-    executable = cmd[0]
-    cmd = _nt_quote_args(cmd)
-    if search_path:
-        # either we find one or it stays the same
-        executable = find_executable(executable) or executable
-    logger.info(' '.join([executable] + cmd[1:]))
-    if not dry_run:
-        # spawn for NT requires a full path to the .exe
-        try:
-            if env is None:
-                rc = os.spawnv(os.P_WAIT, executable, cmd)
-            else:
-                rc = os.spawnve(os.P_WAIT, executable, cmd, env)
-
-        except OSError, exc:
-            # this seems to happen when the command isn't found
-            raise DistutilsExecError(
-                  "command '%s' failed: %s" % (cmd[0], exc[-1]))
-        if rc != 0:
-            # and this reflects the command running but failing
-            raise DistutilsExecError(
-                  "command '%s' failed with exit status %d" % (cmd[0], rc))
-
-
-def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0, env=None):
-    executable = cmd[0]
-    if search_path:
-        # either we find one or it stays the same
-        executable = find_executable(executable) or executable
-    logger.info(' '.join([executable] + cmd[1:]))
-    if not dry_run:
-        # spawnv for OS/2 EMX requires a full path to the .exe
-        try:
-            if env is None:
-                rc = os.spawnv(os.P_WAIT, executable, cmd)
-            else:
-                rc = os.spawnve(os.P_WAIT, executable, cmd, env)
-
-        except OSError, exc:
-            # this seems to happen when the command isn't found
-            raise DistutilsExecError(
-                  "command '%s' failed: %s" % (cmd[0], exc[-1]))
-        if rc != 0:
-            # and this reflects the command running but failing
-            logger.debug("command '%s' failed with exit status %d",
-                         (cmd[0], rc))
-            raise DistutilsExecError(
-                  "command '%s' failed with exit status %d" % (cmd[0], rc))
-
-
-def _spawn_posix(cmd, search_path=1, verbose=1, dry_run=0, env=None):
-    cmd = ' '.join(cmd)
-    if verbose:
-        logger.info(cmd)
-    if dry_run:
-        return
-    exit_status = sub_call(cmd, shell=True, env=env)
+    exit_status = subprocess.call(cmd, env=env)
     if exit_status != 0:
-        msg = "command '%s' failed with exit status %d"
-        raise DistutilsExecError(msg % (cmd, exit_status))
+        msg = "command %r failed with exit status %d"
+        raise PackagingExecError(msg % (cmd, exit_status))
 
 
 def find_executable(executable, path=None):
-    """Tries to find 'executable' in the directories listed in 'path'.
+    """Try to find 'executable' in the directories listed in 'path'.
 
-    A string listing directories separated by 'os.pathsep'; defaults to
-    os.environ['PATH'].  Returns the complete filename or None if not found.
+    *path* is a string listing directories separated by 'os.pathsep' and
+    defaults to os.environ['PATH'].  Returns the complete filename or None
+    if not found.
     """
     if path is None:
         path = os.environ['PATH']
@@ -830,27 +853,24 @@
 
 
 def get_pypirc_path():
-    """Returns rc file path."""
+    """Return path to pypirc config file."""
     return os.path.join(os.path.expanduser('~'), '.pypirc')
 
 
 def generate_pypirc(username, password):
-    """Creates a default .pypirc file."""
+    """Create a default .pypirc file."""
     rc = get_pypirc_path()
-    f = open(rc, 'w')
+    with open(rc, 'w') as f:
+        f.write(DEFAULT_PYPIRC % (username, password))
     try:
-        f.write(DEFAULT_PYPIRC % (username, password))
-    finally:
-        f.close()
-    try:
-        os.chmod(rc, 0600)
+        os.chmod(rc, 0o600)
     except OSError:
         # should do something better here
         pass
 
 
 def read_pypirc(repository=DEFAULT_REPOSITORY, realm=DEFAULT_REALM):
-    """Reads the .pypirc file."""
+    """Read the .pypirc file."""
     rc = get_pypirc_path()
     if os.path.exists(rc):
         config = RawConfigParser()
@@ -875,8 +895,7 @@
                 current['username'] = config.get(server, 'username')
 
                 # optional params
-                for key, default in (('repository',
-                                       DEFAULT_REPOSITORY),
+                for key, default in (('repository', DEFAULT_REPOSITORY),
                                      ('realm', DEFAULT_REALM),
                                      ('password', None)):
                     if config.has_option(server, key):
@@ -922,13 +941,13 @@
 
     if fixer_names:
         for fixername in fixer_names:
-            fixers.extend([fixer for fixer in
-                           get_fixers_from_package(fixername)])
+            fixers.extend(fixer for fixer in
+                          get_fixers_from_package(fixername))
     r = RefactoringTool(fixers, options=options)
     r.refactor(files, write=True, doctests_only=doctests_only)
 
 
-class Mixin2to3:
+class Mixin2to3(object):
     """ Wrapper class for commands that run 2to3.
     To configure 2to3, setup scripts may either change
     the class variables, or inherit from this class
@@ -950,23 +969,22 @@
                         self.options, self.explicit)
 
 RICH_GLOB = re.compile(r'\{([^}]*)\}')
-_CHECK_RECURSIVE_GLOB = re.compile(r'[^/,{]\*\*|\*\*[^/,}]')
+_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
 _CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$')
 
 
 def iglob(path_glob):
-    """Richer glob than the std glob module support ** and {opt1,opt2,opt3}"""
+    """Extended globbing function that supports ** and {opt1,opt2,opt3}."""
     if _CHECK_RECURSIVE_GLOB.search(path_glob):
-        msg = """Invalid glob %r: Recursive glob "**" must be used alone"""
+        msg = """invalid glob %r: recursive glob "**" must be used alone"""
         raise ValueError(msg % path_glob)
     if _CHECK_MISMATCH_SET.search(path_glob):
-        msg = """Invalid glob %r: Mismatching set marker '{' or '}'"""
+        msg = """invalid glob %r: mismatching set marker '{' or '}'"""
         raise ValueError(msg % path_glob)
     return _iglob(path_glob)
 
 
 def _iglob(path_glob):
-    """Actual logic of the iglob function"""
     rich_path_glob = RICH_GLOB.split(path_glob, 1)
     if len(rich_path_glob) > 1:
         assert len(rich_path_glob) == 3, rich_path_glob
@@ -985,24 +1003,24 @@
             if radical == '':
                 radical = '*'
             else:
+                # we support both
                 radical = radical.lstrip('/')
-            for (path, dir, files) in os.walk(prefix):
+                radical = radical.lstrip('\\')
+            for path, dir, files in os.walk(prefix):
                 path = os.path.normpath(path)
                 for file in _iglob(os.path.join(path, radical)):
                     yield file
 
 
 def cfg_to_args(path='setup.cfg'):
-    """ Distutils2 to distutils1 compatibility util.
+    """Compatibility helper to use setup.cfg in setup.py.
 
-        This method uses an existing setup.cfg to generate a dictionnary of
-        keywords that can be used by distutils.core.setup(kwargs**).
+    This functions uses an existing setup.cfg to generate a dictionnary of
+    keywords that can be used by distutils.core.setup(**kwargs).  It is used
+    by generate_setup_py.
 
-        :param file:
-            The setup.cfg path.
-        :raises DistutilsFileError:
-            When the setup.cfg file is not found.
-
+    *file* is the path to the setup.cfg file.  If it doesn't exist,
+    PackagingFileError is raised.
     """
     # We need to declare the following constants here so that it's easier to
     # generate the setup.py afterwards, using inspect.getsource.
@@ -1024,16 +1042,20 @@
                         "requires": ("metadata", "requires_dist"),
                         "provides": ("metadata", "provides_dist"),  # **
                         "obsoletes": ("metadata", "obsoletes_dist"),  # **
+                        "package_dir": ("files", 'packages_root'),
                         "packages": ("files",),
                         "scripts": ("files",),
                         "py_modules": ("files", "modules"),  # **
                         }
 
     MULTI_FIELDS = ("classifiers",
+                    "platforms",
                     "requires",
-                    "platforms",
+                    "provides",
+                    "obsoletes",
                     "packages",
-                    "scripts")
+                    "scripts",
+                    "py_modules")
 
     def has_get_option(config, section, option):
         if config.has_option(section, option):
@@ -1043,40 +1065,46 @@
         else:
             return False
 
-    # The method source code really starts here.
+    # The real code starts here
     config = RawConfigParser()
-    if not os.path.exists(file):
-        raise DistutilsFileError("file '%s' does not exist" %
-                                 os.path.abspath(file))
-    config.read(path)
+    if not os.path.exists(path):
+        raise PackagingFileError("file '%s' does not exist" %
+                                 os.path.abspath(path))
+    with codecs.open(path, encoding='utf-8') as f:
+        config.readfp(f)
 
     kwargs = {}
     for arg in D1_D2_SETUP_ARGS:
         if len(D1_D2_SETUP_ARGS[arg]) == 2:
-            # The distutils field name is different than distutils2's.
+            # The distutils field name is different than distutils2's
             section, option = D1_D2_SETUP_ARGS[arg]
 
-        elif len(D1_D2_SETUP_ARGS[arg]) == 1:
-            # The distutils field name is the same thant distutils2's.
+        else:
+            # The distutils field name is the same thant distutils2's
             section = D1_D2_SETUP_ARGS[arg][0]
             option = arg
 
         in_cfg_value = has_get_option(config, section, option)
         if not in_cfg_value:
             # There is no such option in the setup.cfg
-            if arg == "long_description":
-                filename = has_get_option(config, section, "description_file")
-                if filename:
-                    in_cfg_value = open(filename).read()
+            if arg == 'long_description':
+                filenames = has_get_option(config, section, 'description-file')
+                if filenames:
+                    filenames = split_multiline(filenames)
+                    in_cfg_value = []
+                    for filename in filenames:
+                        with open(filename) as fp:
+                            in_cfg_value.append(fp.read())
+                    in_cfg_value = '\n\n'.join(in_cfg_value)
             else:
                 continue
 
+        if arg == 'package_dir' and in_cfg_value:
+            in_cfg_value = {'': in_cfg_value}
+
         if arg in MULTI_FIELDS:
-            # Special behaviour when we have a multi line option
-            if "\n" in in_cfg_value:
-                in_cfg_value = in_cfg_value.strip().split('\n')
-            else:
-                in_cfg_value = list((in_cfg_value,))
+            # support multiline options
+            in_cfg_value = split_multiline(in_cfg_value)
 
         kwargs[arg] = in_cfg_value
 
@@ -1084,7 +1112,7 @@
 
 
 _SETUP_TMPL = """\
-# This script was automatically generated by Distutils2
+# This script was automatically generated by distutils2
 import os
 from distutils.core import setup
 from ConfigParser import RawConfigParser
@@ -1096,16 +1124,978 @@
 
 
 def generate_setup_py():
-    """Generates a distutils compatible setup.py using an existing setup.cfg.
+    """Generate a distutils compatible setup.py using an existing setup.cfg.
 
-        :raises DistutilsFileError:
-            When a setup.py already exists.
+    Raises a PackagingFileError when a setup.py already exists.
     """
     if os.path.exists("setup.py"):
-        raise DistutilsFileError("A pre existing setup.py file exists")
+        raise PackagingFileError("a setup.py file already exists")
 
-    handle = open("setup.py", "w")
+    with codecs.open("setup.py", "w", encoding='utf-8') as fp:
+        fp.write(_SETUP_TMPL % {'func': getsource(cfg_to_args)})
+
+
+# Taken from the pip project
+# https://github.com/pypa/pip/blob/master/pip/util.py
+def ask(message, options):
+    """Prompt the user with *message*; *options* contains allowed responses."""
+    while True:
+        response = input(message)
+        response = response.strip().lower()
+        if response not in options:
+            print('invalid response:', repr(response))
+            print('choose one of', ', '.join(repr(o) for o in options))
+        else:
+            return response
+
+
+def _parse_record_file(record_file):
+    distinfo, extra_metadata, installed = ({}, [], [])
+    with open(record_file, 'r') as rfile:
+        for path in rfile:
+            path = path.strip()
+            if path.endswith('egg-info') and os.path.isfile(path):
+                distinfo_dir = path.replace('egg-info', 'dist-info')
+                metadata = path
+                egginfo = path
+            elif path.endswith('egg-info') and os.path.isdir(path):
+                distinfo_dir = path.replace('egg-info', 'dist-info')
+                egginfo = path
+                for metadata_file in os.listdir(path):
+                    metadata_fpath = os.path.join(path, metadata_file)
+                    if metadata_file == 'PKG-INFO':
+                        metadata = metadata_fpath
+                    else:
+                        extra_metadata.append(metadata_fpath)
+            elif 'egg-info' in path and os.path.isfile(path):
+                # skip extra metadata files
+                continue
+            else:
+                installed.append(path)
+
+    distinfo['egginfo'] = egginfo
+    distinfo['metadata'] = metadata
+    distinfo['distinfo_dir'] = distinfo_dir
+    distinfo['installer_path'] = os.path.join(distinfo_dir, 'INSTALLER')
+    distinfo['metadata_path'] = os.path.join(distinfo_dir, 'METADATA')
+    distinfo['record_path'] = os.path.join(distinfo_dir, 'RECORD')
+    distinfo['requested_path'] = os.path.join(distinfo_dir, 'REQUESTED')
+    installed.extend([distinfo['installer_path'], distinfo['metadata_path']])
+    distinfo['installed'] = installed
+    distinfo['extra_metadata'] = extra_metadata
+    return distinfo
+
+
+def _write_record_file(record_path, installed_files):
+    with codecs.open(record_path, 'w', encoding='utf-8') as f:
+        writer = csv.writer(f, delimiter=',', lineterminator=os.linesep,
+                            quotechar='"')
+
+        for fpath in installed_files:
+            if fpath.endswith('.pyc') or fpath.endswith('.pyo'):
+                # do not put size and md5 hash, as in PEP-376
+                writer.writerow((fpath, '', ''))
+            else:
+                hash = hashlib.md5()
+                with open(fpath, 'rb') as fp:
+                    hash.update(fp.read())
+                md5sum = hash.hexdigest()
+                size = os.path.getsize(fpath)
+                writer.writerow((fpath, md5sum, size))
+
+        # add the RECORD file itself
+        writer.writerow((record_path, '', ''))
+    return record_path
+
+
+def egginfo_to_distinfo(record_file, installer=_DEFAULT_INSTALLER,
+                        requested=False, remove_egginfo=False):
+    """Create files and directories required for PEP 376
+
+    :param record_file: path to RECORD file as produced by setup.py --record
+    :param installer: installer name
+    :param requested: True if not installed as a dependency
+    :param remove_egginfo: delete egginfo dir?
+    """
+    distinfo = _parse_record_file(record_file)
+    distinfo_dir = distinfo['distinfo_dir']
+    if os.path.isdir(distinfo_dir) and not os.path.islink(distinfo_dir):
+        shutil.rmtree(distinfo_dir)
+    elif os.path.exists(distinfo_dir):
+        os.unlink(distinfo_dir)
+
+    os.makedirs(distinfo_dir)
+
+    # copy setuptools extra metadata files
+    if distinfo['extra_metadata']:
+        for path in distinfo['extra_metadata']:
+            shutil.copy2(path, distinfo_dir)
+            new_path = path.replace('egg-info', 'dist-info')
+            distinfo['installed'].append(new_path)
+
+    metadata_path = distinfo['metadata_path']
+    logger.info('creating %s', metadata_path)
+    shutil.copy2(distinfo['metadata'], metadata_path)
+
+    installer_path = distinfo['installer_path']
+    logger.info('creating %s', installer_path)
+    with open(installer_path, 'w') as f:
+        f.write(installer)
+
+    if requested:
+        requested_path = distinfo['requested_path']
+        logger.info('creating %s', requested_path)
+        open(requested_path, 'wb').close()
+        distinfo['installed'].append(requested_path)
+
+    record_path = distinfo['record_path']
+    logger.info('creating %s', record_path)
+    _write_record_file(record_path, distinfo['installed'])
+
+    if remove_egginfo:
+        egginfo = distinfo['egginfo']
+        logger.info('removing %s', egginfo)
+        if os.path.isfile(egginfo):
+            os.remove(egginfo)
+        else:
+            shutil.rmtree(egginfo)
+
+
+def _has_egg_info(srcdir):
+    if os.path.isdir(srcdir):
+        for item in os.listdir(srcdir):
+            full_path = os.path.join(srcdir, item)
+            if item.endswith('.egg-info') and os.path.isdir(full_path):
+                logger.debug("Found egg-info directory.")
+                return True
+    logger.debug("No egg-info directory found.")
+    return False
+
+
+def _has_setuptools_text(setup_py):
+    return _has_text(setup_py, 'setuptools')
+
+
+def _has_distutils_text(setup_py):
+    return _has_text(setup_py, 'distutils')
+
+
+def _has_text(setup_py, installer):
+    installer_pattern = re.compile('import {0}|from {0}'.format(installer))
+    with codecs.open(setup_py, 'r', encoding='utf-8') as setup:
+        for line in setup:
+            if re.search(installer_pattern, line):
+                logger.debug("Found %s text in setup.py.", installer)
+                return True
+    logger.debug("No %s text found in setup.py.", installer)
+    return False
+
+
+def _has_required_metadata(setup_cfg):
+    config = RawConfigParser()
+    with codecs.open(setup_cfg, 'r', encoding='utf8') as f:
+        config.readfp(f)
+    return (config.has_section('metadata') and
+            'name' in config.options('metadata') and
+            'version' in config.options('metadata'))
+
+
+def _has_pkg_info(srcdir):
+    pkg_info = os.path.join(srcdir, 'PKG-INFO')
+    has_pkg_info = os.path.isfile(pkg_info)
+    if has_pkg_info:
+        logger.debug("PKG-INFO file found.")
+    else:
+        logger.debug("No PKG-INFO file found.")
+    return has_pkg_info
+
+
+def _has_setup_py(srcdir):
+    setup_py = os.path.join(srcdir, 'setup.py')
+    if os.path.isfile(setup_py):
+        logger.debug('setup.py file found.')
+        return True
+    return False
+
+
+def _has_setup_cfg(srcdir):
+    setup_cfg = os.path.join(srcdir, 'setup.cfg')
+    if os.path.isfile(setup_cfg):
+        logger.debug('setup.cfg file found.')
+        return True
+    logger.debug("No setup.cfg file found.")
+    return False
+
+
+def is_setuptools(path):
+    """Check if the project is based on setuptools.
+
+    :param path: path to source directory containing a setup.py script.
+
+    Return True if the project requires setuptools to install, else False.
+    """
+    srcdir = os.path.abspath(path)
+    setup_py = os.path.join(srcdir, 'setup.py')
+
+    return _has_setup_py(srcdir) and (_has_egg_info(srcdir) or
+                                      _has_setuptools_text(setup_py))
+
+
+def is_distutils(path):
+    """Check if the project is based on distutils.
+
+    :param path: path to source directory containing a setup.py script.
+
+    Return True if the project requires distutils to install, else False.
+    """
+    srcdir = os.path.abspath(path)
+    setup_py = os.path.join(srcdir, 'setup.py')
+
+    return _has_setup_py(srcdir) and (_has_pkg_info(srcdir) or
+                                      _has_distutils_text(setup_py))
+
+
+def is_distutils2(path):
+    """Check if the project is based on distutils2
+
+    :param path: path to source directory containing a setup.cfg file.
+
+    Return True if the project has a valid setup.cfg, else False.
+    """
+    srcdir = os.path.abspath(path)
+    setup_cfg = os.path.join(srcdir, 'setup.cfg')
+
+    return _has_setup_cfg(srcdir) and _has_required_metadata(setup_cfg)
+
+
+def get_install_method(path):
+    """Check if the project is based on distutils2, setuptools, or distutils
+
+    :param path: path to source directory containing a setup.cfg file,
+                 or setup.py.
+
+    Returns a string representing the best install method to use.
+    """
+    if is_distutils2(path):
+        return "distutils2"
+    elif is_setuptools(path):
+        return "setuptools"
+    elif is_distutils(path):
+        return "distutils"
+    else:
+        raise InstallationException('Cannot detect install method')
+
+
+# XXX to be replaced by shutil.copytree
+def copy_tree(src, dst, preserve_mode=True, preserve_times=True,
+              preserve_symlinks=False, update=False, verbose=True,
+              dry_run=False):
+    from distutils.file_util import copy_file
+
+    if not dry_run and not os.path.isdir(src):
+        raise PackagingFileError(
+              "cannot copy tree '%s': not a directory" % src)
     try:
-        handle.write(_SETUP_TMPL % {'func': getsource(cfg_to_args)})
+        names = os.listdir(src)
+    except os.error:
+        errstr = sys.exc_info()[1][1]
+        if dry_run:
+            names = []
+        else:
+            raise PackagingFileError(
+                  "error listing files in '%s': %s" % (src, errstr))
+
+    if not dry_run:
+        _mkpath(dst, verbose=verbose)
+
+    outputs = []
+
+    for n in names:
+        src_name = os.path.join(src, n)
+        dst_name = os.path.join(dst, n)
+
+        if preserve_symlinks and os.path.islink(src_name):
+            link_dest = os.readlink(src_name)
+            if verbose >= 1:
+                logger.info("linking %s -> %s", dst_name, link_dest)
+            if not dry_run:
+                os.symlink(link_dest, dst_name)
+            outputs.append(dst_name)
+
+        elif os.path.isdir(src_name):
+            outputs.extend(
+                copy_tree(src_name, dst_name, preserve_mode,
+                          preserve_times, preserve_symlinks, update,
+                          verbose=verbose, dry_run=dry_run))
+        else:
+            copy_file(src_name, dst_name, preserve_mode,
+                      preserve_times, update, verbose=verbose,
+                      dry_run=dry_run)
+            outputs.append(dst_name)
+
+    return outputs
+
+# cache for by mkpath() -- in addition to cheapening redundant calls,
+# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
+_path_created = set()
+
+
+# I don't use os.makedirs because a) it's new to Python 1.5.2, and
+# b) it blows up if the directory already exists (I want to silently
+# succeed in that case).
+def _mkpath(name, mode=0o777, verbose=True, dry_run=False):
+    # Detect a common bug -- name is None
+    if not isinstance(name, basestring):
+        raise PackagingInternalError(
+              "mkpath: 'name' must be a string (got %r)" % (name,))
+
+    # XXX what's the better way to handle verbosity? print as we create
+    # each directory in the path (the current behaviour), or only announce
+    # the creation of the whole path? (quite easy to do the latter since
+    # we're not using a recursive algorithm)
+
+    name = os.path.normpath(name)
+    created_dirs = []
+    if os.path.isdir(name) or name == '':
+        return created_dirs
+    if os.path.abspath(name) in _path_created:
+        return created_dirs
+
+    head, tail = os.path.split(name)
+    tails = [tail]                      # stack of lone dirs to create
+
+    while head and tail and not os.path.isdir(head):
+        head, tail = os.path.split(head)
+        tails.insert(0, tail)          # push next higher dir onto stack
+
+    # now 'head' contains the deepest directory that already exists
+    # (that is, the child of 'head' in 'name' is the highest directory
+    # that does *not* exist)
+    for d in tails:
+        head = os.path.join(head, d)
+        abs_head = os.path.abspath(head)
+
+        if abs_head in _path_created:
+            continue
+
+        if verbose >= 1:
+            logger.info("creating %s", head)
+
+        if not dry_run:
+            try:
+                os.mkdir(head, mode)
+            except OSError:
+                exc = sys.exc_info()[1]
+                if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
+                    raise PackagingFileError(
+                          "could not create '%s': %s" % (head, exc.args[-1]))
+            created_dirs.append(head)
+
+        _path_created.add(abs_head)
+    return created_dirs
+
+
+def encode_multipart(fields, files, boundary=None):
+    """Prepare a multipart HTTP request.
+
+    *fields* is a sequence of (name: str, value: str) elements for regular
+    form fields, *files* is a sequence of (name: str, filename: str, value:
+    bytes) elements for data to be uploaded as files.
+
+    Returns (content_type: bytes, body: bytes) ready for httplib.HTTP.
+    """
+    # Taken from
+    # http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/
+
+    if boundary is None:
+        boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+    elif not isinstance(boundary, bytes):
+        raise TypeError('boundary must be bytes, not %r' % type(boundary))
+
+    l = []
+    for key, values in fields:
+        # handle multiple entries for the same name
+        if not isinstance(values, (tuple, list)):
+            values=[values]
+
+        for value in values:
+            l.extend((
+                b'--' + boundary,
+                ('Content-Disposition: form-data; name="%s"' %
+                 key).encode('utf-8'),
+                b'',
+                value.encode('utf-8')))
+
+    for key, filename, value in files:
+        l.extend((
+            b'--' + boundary,
+            ('Content-Disposition: form-data; name="%s"; filename="%s"' %
+             (key, filename)).encode('utf-8'),
+            b'',
+            value))
+
+    l.append(b'--' + boundary + b'--')
+    l.append(b'')
+
+    body = b'\r\n'.join(l)
+    content_type = b'multipart/form-data; boundary=' + boundary
+    return content_type, body
+    
+# shutil stuff
+
+try:
+    import bz2
+    _BZ2_SUPPORTED = True
+except ImportError:
+    _BZ2_SUPPORTED = False
+
+try:
+    from pwd import getpwnam
+except ImportError:
+    getpwnam = None
+
+try:
+    from grp import getgrnam
+except ImportError:
+    getgrnam = None
+
+def _get_gid(name):
+    """Returns a gid, given a group name."""
+    if getgrnam is None or name is None:
+        return None
+    try:
+        result = getgrnam(name)
+    except KeyError:
+        result = None
+    if result is not None:
+        return result[2]
+    return None
+
+def _get_uid(name):
+    """Returns an uid, given a user name."""
+    if getpwnam is None or name is None:
+        return None
+    try:
+        result = getpwnam(name)
+    except KeyError:
+        result = None
+    if result is not None:
+        return result[2]
+    return None
+
+def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
+                  owner=None, group=None, logger=None):
+    """Create a (possibly compressed) tar file from all the files under
+    'base_dir'.
+
+    'compress' must be "gzip" (the default), "bzip2", or None.
+
+    'owner' and 'group' can be used to define an owner and a group for the
+    archive that is being built. If not provided, the current owner and group
+    will be used.
+
+    The output tar file will be named 'base_name' +  ".tar", possibly plus
+    the appropriate compression extension (".gz", or ".bz2").
+
+    Returns the output filename.
+    """
+    tar_compression = {'gzip': 'gz', None: ''}
+    compress_ext = {'gzip': '.gz'}
+
+    if _BZ2_SUPPORTED:
+        tar_compression['bzip2'] = 'bz2'
+        compress_ext['bzip2'] = '.bz2'
+
+    # flags for compression program, each element of list will be an argument
+    if compress is not None and compress not in compress_ext.keys():
+        raise ValueError("bad value for 'compress', or compression format not "
+                         "supported : {0}".format(compress))
+
+    archive_name = base_name + '.tar' + compress_ext.get(compress, '')
+    archive_dir = os.path.dirname(archive_name)
+
+    if not os.path.exists(archive_dir):
+        if logger is not None:
+            logger.info("creating %s" % archive_dir)
+        if not dry_run:
+            os.makedirs(archive_dir)
+
+    # creating the tarball
+    if logger is not None:
+        logger.info('Creating tar archive')
+
+    uid = _get_uid(owner)
+    gid = _get_gid(group)
+
+    def _set_uid_gid(tarinfo):
+        if gid is not None:
+            tarinfo.gid = gid
+            tarinfo.gname = group
+        if uid is not None:
+            tarinfo.uid = uid
+            tarinfo.uname = owner
+        return tarinfo
+
+    if not dry_run:
+        tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
+        try:
+            #tar.add(base_dir, filter=_set_uid_gid)
+            tar.add(base_dir)
+        finally:
+            tar.close()
+
+    return archive_name
+
+def _call_external_zip(base_dir, zip_filename, verbose=False, dry_run=False):
+    # XXX see if we want to keep an external call here
+    if verbose:
+        zipoptions = "-r"
+    else:
+        zipoptions = "-rq"
+    from distutils.errors import DistutilsExecError
+    from distutils.spawn import spawn
+    try:
+        spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run)
+    except DistutilsExecError:
+        # XXX really should distinguish between "couldn't find
+        # external 'zip' command" and "zip failed".
+        raise ExecError("unable to create zip file '%s': "
+            "could neither import the 'zipfile' module nor "
+            "find a standalone zip utility") % zip_filename
+
+def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0, logger=None):
+    """Create a zip file from all the files under 'base_dir'.
+
+    The output zip file will be named 'base_name' + ".zip".  Uses either the
+    "zipfile" Python module (if available) or the InfoZIP "zip" utility
+    (if installed and found on the default search path).  If neither tool is
+    available, raises ExecError.  Returns the name of the output zip
+    file.
+    """
+    zip_filename = base_name + ".zip"
+    archive_dir = os.path.dirname(base_name)
+
+    if not os.path.exists(archive_dir):
+        if logger is not None:
+            logger.info("creating %s", archive_dir)
+        if not dry_run:
+            os.makedirs(archive_dir)
+
+    # If zipfile module is not available, try spawning an external 'zip'
+    # command.
+    try:
+        import zipfile
+    except ImportError:
+        zipfile = None
+
+    if zipfile is None:
+        _call_external_zip(base_dir, zip_filename, verbose, dry_run)
+    else:
+        if logger is not None:
+            logger.info("creating '%s' and adding '%s' to it",
+                        zip_filename, base_dir)
+
+        if not dry_run:
+            zip = zipfile.ZipFile(zip_filename, "w",
+                                  compression=zipfile.ZIP_DEFLATED)
+
+            for dirpath, dirnames, filenames in os.walk(base_dir):
+                for name in filenames:
+                    path = os.path.normpath(os.path.join(dirpath, name))
+                    if os.path.isfile(path):
+                        zip.write(path, path)
+                        if logger is not None:
+                            logger.info("adding '%s'", path)
+            zip.close()
+
+    return zip_filename
+
+_ARCHIVE_FORMATS = {
+    'gztar': (_make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
+    'bztar': (_make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
+    'tar':   (_make_tarball, [('compress', None)], "uncompressed tar file"),
+    'zip':   (_make_zipfile, [],"ZIP file")
+    }
+
+if _BZ2_SUPPORTED:
+    _ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')],
+                                "bzip2'ed tar-file")
+
+def get_archive_formats():
+    """Returns a list of supported formats for archiving and unarchiving.
+
+    Each element of the returned sequence is a tuple (name, description)
+    """
+    formats = [(name, registry[2]) for name, registry in
+               _ARCHIVE_FORMATS.items()]
+    formats.sort()
+    return formats
+
+def register_archive_format(name, function, extra_args=None, description=''):
+    """Registers an archive format.
+
+    name is the name of the format. function is the callable that will be
+    used to create archives. If provided, extra_args is a sequence of
+    (name, value) tuples that will be passed as arguments to the callable.
+    description can be provided to describe the format, and will be returned
+    by the get_archive_formats() function.
+    """
+    if extra_args is None:
+        extra_args = []
+    if not isinstance(function, collections.Callable):
+        raise TypeError('The %s object is not callable' % function)
+    if not isinstance(extra_args, (tuple, list)):
+        raise TypeError('extra_args needs to be a sequence')
+    for element in extra_args:
+        if not isinstance(element, (tuple, list)) or len(element) !=2 :
+            raise TypeError('extra_args elements are : (arg_name, value)')
+
+    _ARCHIVE_FORMATS[name] = (function, extra_args, description)
+
+def unregister_archive_format(name):
+    del _ARCHIVE_FORMATS[name]
+
+def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
+                 dry_run=0, owner=None, group=None, logger=None):
+    """Create an archive file (eg. zip or tar).
+
+    'base_name' is the name of the file to create, minus any format-specific
+    extension; 'format' is the archive format: one of "zip", "tar", "bztar"
+    or "gztar".
+
+    'root_dir' is a directory that will be the root directory of the
+    archive; ie. we typically chdir into 'root_dir' before creating the
+    archive.  'base_dir' is the directory where we start archiving from;
+    ie. 'base_dir' will be the common prefix of all files and
+    directories in the archive.  'root_dir' and 'base_dir' both default
+    to the current directory.  Returns the name of the archive file.
+
+    'owner' and 'group' are used when creating a tar archive. By default,
+    uses the current owner and group.
+    """
+    save_cwd = os.getcwd()
+    base_name = fsencode(base_name)
+    if root_dir is not None:
+        if logger is not None:
+            logger.debug("changing into '%s'", root_dir)
+        base_name = os.path.abspath(base_name)
+        if not dry_run:
+            os.chdir(root_dir)
+
+    if base_dir is None:
+        base_dir = os.curdir
+
+    kwargs = {'dry_run': dry_run, 'logger': logger}
+
+    try:
+        format_info = _ARCHIVE_FORMATS[format]
+    except KeyError:
+        raise ValueError("unknown archive format '%s'" % format)
+
+    func = format_info[0]
+    for arg, val in format_info[1]:
+        kwargs[arg] = val
+
+    if format != 'zip':
+        kwargs['owner'] = owner
+        kwargs['group'] = group
+
+    try:
+        filename = func(base_name, base_dir, **kwargs)
     finally:
-        handle.close()
+        if root_dir is not None:
+            if logger is not None:
+                logger.debug("changing back to '%s'", save_cwd)
+            os.chdir(save_cwd)
+
+    return filename
+
+
+def get_unpack_formats():
+    """Returns a list of supported formats for unpacking.
+
+    Each element of the returned sequence is a tuple
+    (name, extensions, description)
+    """
+    formats = [(name, info[0], info[3]) for name, info in
+               _UNPACK_FORMATS.items()]
+    formats.sort()
+    return formats
+
+def _check_unpack_options(extensions, function, extra_args):
+    """Checks what gets registered as an unpacker."""
+    # first make sure no other unpacker is registered for this extension
+    existing_extensions = {}
+    for name, info in _UNPACK_FORMATS.items():
+        for ext in info[0]:
+            existing_extensions[ext] = name
+
+    for extension in extensions:
+        if extension in existing_extensions:
+            msg = '%s is already registered for "%s"'
+            raise RegistryError(msg % (extension,
+                                       existing_extensions[extension]))
+
+    if not isinstance(function, collections.Callable):
+        raise TypeError('The registered function must be a callable')
+
+
+def register_unpack_format(name, extensions, function, extra_args=None,
+                           description=''):
+    """Registers an unpack format.
+
+    `name` is the name of the format. `extensions` is a list of extensions
+    corresponding to the format.
+
+    `function` is the callable that will be
+    used to unpack archives. The callable will receive archives to unpack.
+    If it's unable to handle an archive, it needs to raise a ReadError
+    exception.
+
+    If provided, `extra_args` is a sequence of
+    (name, value) tuples that will be passed as arguments to the callable.
+    description can be provided to describe the format, and will be returned
+    by the get_unpack_formats() function.
+    """
+    if extra_args is None:
+        extra_args = []
+    _check_unpack_options(extensions, function, extra_args)
+    _UNPACK_FORMATS[name] = extensions, function, extra_args, description
+
+def unregister_unpack_format(name):
+    """Removes the pack format from the registery."""
+    del _UNPACK_FORMATS[name]
+
+def _ensure_directory(path):
+    """Ensure that the parent directory of `path` exists"""
+    dirname = os.path.dirname(path)
+    if not os.path.isdir(dirname):
+        os.makedirs(dirname)
+
+def _unpack_zipfile(filename, extract_dir):
+    """Unpack zip `filename` to `extract_dir`
+    """
+    try:
+        import zipfile
+    except ImportError:
+        raise ReadError('zlib not supported, cannot unpack this archive.')
+
+    if not zipfile.is_zipfile(filename):
+        raise ReadError("%s is not a zip file" % filename)
+
+    zip = zipfile.ZipFile(filename)
+    try:
+        for info in zip.infolist():
+            name = info.filename
+
+            # don't extract absolute paths or ones with .. in them
+            if name.startswith('/') or '..' in name:
+                continue
+
+            target = os.path.join(extract_dir, *name.split('/'))
+            if not target:
+                continue
+
+            _ensure_directory(target)
+            if not name.endswith('/'):
+                # file
+                data = zip.read(info.filename)
+                f = open(target,'wb')
+                try:
+                    f.write(data)
+                finally:
+                    f.close()
+                    del data
+    finally:
+        zip.close()
+
+def _unpack_tarfile(filename, extract_dir):
+    """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
+    """
+    try:
+        tarobj = tarfile.open(filename)
+    except tarfile.TarError:
+        raise ReadError(
+            "%s is not a compressed or uncompressed tar file" % filename)
+    try:
+        tarobj.extractall(extract_dir)
+    finally:
+        tarobj.close()
+
+_UNPACK_FORMATS = {
+    'gztar': (['.tar.gz', '.tgz'], _unpack_tarfile, [], "gzip'ed tar-file"),
+    'tar':   (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
+    'zip':   (['.zip'], _unpack_zipfile, [], "ZIP file")
+    }
+
+if _BZ2_SUPPORTED:
+    _UNPACK_FORMATS['bztar'] = (['.bz2'], _unpack_tarfile, [],
+                                "bzip2'ed tar-file")
+
+def _find_unpack_format(filename):
+    for name, info in _UNPACK_FORMATS.items():
+        for extension in info[0]:
+            if filename.endswith(extension):
+                return name
+    return None
+
+def unpack_archive(filename, extract_dir=None, format=None):
+    """Unpack an archive.
+
+    `filename` is the name of the archive.
+
+    `extract_dir` is the name of the target directory, where the archive
+    is unpacked. If not provided, the current working directory is used.
+
+    `format` is the archive format: one of "zip", "tar", or "gztar". Or any
+    other registered format. If not provided, unpack_archive will use the
+    filename extension and see if an unpacker was registered for that
+    extension.
+
+    In case none is found, a ValueError is raised.
+    """
+    if extract_dir is None:
+        extract_dir = os.getcwd()
+
+    if format is not None:
+        try:
+            format_info = _UNPACK_FORMATS[format]
+        except KeyError:
+            raise ValueError("Unknown unpack format '{0}'".format(format))
+
+        func = format_info[1]
+        func(filename, extract_dir, **dict(format_info[2]))
+    else:
+        # we need to look at the registered unpackers supported extensions
+        format = _find_unpack_format(filename)
+        if format is None:
+            raise ReadError("Unknown archive format '{0}'".format(filename))
+
+        func = _UNPACK_FORMATS[format][1]
+        kwargs = dict(_UNPACK_FORMATS[format][2])
+        func(filename, extract_dir, **kwargs)
+        
+# tokenize stuff
+
+cookie_re = re.compile("coding[:=]\s*([-\w.]+)")
+
+def _get_normal_name(orig_enc):
+    """Imitates get_normal_name in tokenizer.c."""
+    # Only care about the first 12 characters.
+    enc = orig_enc[:12].lower().replace("_", "-")
+    if enc == "utf-8" or enc.startswith("utf-8-"):
+        return "utf-8"
+    if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \
+       enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")):
+        return "iso-8859-1"
+    return orig_enc
+
+def detect_encoding(readline):
+    """
+    The detect_encoding() function is used to detect the encoding that should
+    be used to decode a Python source file.  It requires one argment, readline,
+    in the same way as the tokenize() generator.
+
+    It will call readline a maximum of twice, and return the encoding used
+    (as a string) and a list of any lines (left as bytes) it has read in.
+
+    It detects the encoding from the presence of a utf-8 bom or an encoding
+    cookie as specified in pep-0263.  If both a bom and a cookie are present,
+    but disagree, a SyntaxError will be raised.  If the encoding cookie is an
+    invalid charset, raise a SyntaxError.  Note that if a utf-8 bom is found,
+    'utf-8-sig' is returned.
+
+    If no encoding is specified, then the default of 'utf-8' will be returned.
+    """
+    bom_found = False
+    encoding = None
+    default = 'utf-8'
+    def read_or_stop():
+        try:
+            return readline()
+        except StopIteration:
+            return b''
+
+    def find_cookie(line):
+        try:
+            line_string = line.decode('ascii')
+        except UnicodeDecodeError:
+            return None
+
+        matches = cookie_re.findall(line_string)
+        if not matches:
+            return None
+        encoding = _get_normal_name(matches[0])
+        try:
+            codec = codecs.lookup(encoding)
+        except LookupError:
+            # This behaviour mimics the Python interpreter
+            raise SyntaxError("unknown encoding: " + encoding)
+
+        if bom_found:
+            if codec.name != 'utf-8':
+                # This behaviour mimics the Python interpreter
+                raise SyntaxError('encoding problem: utf-8')
+            encoding += '-sig'
+        return encoding
+
+    first = read_or_stop()
+    if first.startswith(codecs.BOM_UTF8):
+        bom_found = True
+        first = first[3:]
+        default = 'utf-8-sig'
+    if not first:
+        return default, []
+
+    encoding = find_cookie(first)
+    if encoding:
+        return encoding, [first]
+
+    second = read_or_stop()
+    if not second:
+        return default, [first]
+
+    encoding = find_cookie(second)
+    if encoding:
+        return encoding, [first, second]
+
+    return default, [first, second]
+
+# functools stuff
+
+def cmp_to_key(mycmp):
+    """Convert a cmp= function into a key= function"""
+    class K(object):
+        __slots__ = ['obj']
+        def __init__(self, obj):
+            self.obj = obj
+        def __lt__(self, other):
+            return mycmp(self.obj, other.obj) < 0
+        def __gt__(self, other):
+            return mycmp(self.obj, other.obj) > 0
+        def __eq__(self, other):
+            return mycmp(self.obj, other.obj) == 0
+        def __le__(self, other):
+            return mycmp(self.obj, other.obj) <= 0
+        def __ge__(self, other):
+            return mycmp(self.obj, other.obj) >= 0
+        def __ne__(self, other):
+            return mycmp(self.obj, other.obj) != 0
+        __hash__ = None
+    return K
+
+# os stuff
+
+def fsencode(filename):
+    """
+    Encode filename to the filesystem encoding with 'surrogateescape' error
+    handler, return bytes unchanged. On Windows, use 'strict' error handler if
+    the file system encoding is 'mbcs' (which is the default encoding).
+    """
+    if isinstance(filename, str):
+        return filename
+    elif isinstance(filename, unicode):
+        return filename.encode(sys.getfilesystemencoding())
+    else:
+        raise TypeError("expect bytes or str, not %s" % type(filename).__name__)
+
diff --git a/distutils2/version.py b/distutils2/version.py
--- a/distutils2/version.py
+++ b/distutils2/version.py
@@ -1,3 +1,5 @@
+"""Implementation of the versioning scheme defined in PEP 386."""
+
 import re
 
 from distutils2.errors import IrrationalVersionError, HugeMajorVersionNumError
@@ -113,11 +115,11 @@
             dev = groups.get('dev')
             postdev = []
             if post is not None:
-                postdev.extend([_FINAL_MARKER[0], 'post', int(post)])
+                postdev.extend((_FINAL_MARKER[0], 'post', int(post)))
                 if dev is None:
                     postdev.append(_FINAL_MARKER[0])
             if dev is not None:
-                postdev.extend(['dev', int(dev)])
+                postdev.extend(('dev', int(dev)))
                 self.is_final = False
             parts.append(tuple(postdev))
         else:
@@ -125,7 +127,7 @@
         self.parts = tuple(parts)
         if error_on_huge_major_num and self.parts[0][0] > 1980:
             raise HugeMajorVersionNumError("huge major version number, %r, "
-                "which might cause future problems: %r" % (self.parts[0][0], s))
+               "which might cause future problems: %r" % (self.parts[0][0], s))
 
     def _parse_numdots(self, s, full_ver_str, drop_trailing_zeros=True,
                        pad_zeros_length=0):
@@ -180,6 +182,7 @@
         return "%s('%s')" % (self.__class__.__name__, self)
 
     def _cannot_compare(self, other):
+        import pdb; pdb.set_trace()
         raise TypeError("cannot compare %s and %s"
                 % (type(self).__name__, type(other).__name__))
 
@@ -324,7 +327,8 @@
 
 # A predicate is: "ProjectName (VERSION1, VERSION2, ..)
 _PREDICATE = re.compile(r"(?i)^\s*(\w[\s\w-]*(?:\.\w*)*)(.*)")
-_VERSIONS = re.compile(r"^\s*\((?P<versions>.*)\)\s*$|^\s*(?P<versions2>.*)\s*$")
+_VERSIONS = re.compile(r"^\s*\((?P<versions>.*)\)\s*$|^\s*"
+                        "(?P<versions2>.*)\s*$")
 _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$")
 _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$")
 
@@ -381,7 +385,7 @@
 
     def match(self, version):
         """Check if the provided version matches the predicates."""
-        if isinstance(version, str):
+        if isinstance(version, basestring):
             version = NormalizedVersion(version)
         for operator, predicate in self.predicates:
             if not self._operators[operator](version, predicate):
@@ -441,6 +445,6 @@
     """Return a VersionPredicate object, from a string or an already
     existing object.
     """
-    if isinstance(requirements, str):
+    if isinstance(requirements, basestring):
         requirements = VersionPredicate(requirements)
     return requirements
diff --git a/sysconfig.cfg b/sysconfig.cfg
new file mode 100644
--- /dev/null
+++ b/sysconfig.cfg
@@ -0,0 +1,111 @@
+[globals]
+# These are useful categories that can be referenced at run time,
+# using packaging.database.get_file.
+# Configuration files
+config = {confdir}/{distribution.name}
+# Non-writable data that is independent of architecture (images, many xml/text files)
+appdata = {datadir}/{distribution.name}
+# Non-writable data that is architecture-dependent (some binary data formats)
+appdata.arch = {libdir}/{distribution.name}
+# Data, written by the app/lib, that must be preserved (databases)
+appdata.persistent = {statedir}/lib/{distribution.name}
+# Data, written by the app/lib, that can be safely discarded (cache)
+appdata.disposable = {statedir}/cache/{distribution.name}
+# Help or documentation files
+help = {datadir}/{distribution.name}
+icon = {datadir}/pixmaps
+scripts = {base}/bin
+
+# Non-runtime files.  These are valid categories for marking files for
+# install, but they should not be referenced by the app/lib at run time.
+# Help or documentation files
+doc = {datadir}/doc/{distribution.name}
+# GNU info documentation files
+info = {datadir}/info
+# man pages
+man = {datadir}/man
+
+[posix_prefix]
+# Configuration directories.  Some of these come straight out of the
+# configure script.  They are for implementing the other variables, not to
+# be used directly in [resource_locations].
+confdir = /etc
+datadir = /usr/share
+libdir = /usr/lib  ; or /usr/lib64 on a multilib system
+statedir = /var
+# User resource directory
+local = ~/.local/{distribution.name}
+
+stdlib = {base}/lib/python{py_version_short}
+platstdlib = {platbase}/lib/python{py_version_short}
+purelib = {base}/lib/python{py_version_short}/site-packages
+platlib = {platbase}/lib/python{py_version_short}/site-packages
+include = {base}/include/python{py_version_short}{abiflags}
+platinclude = {platbase}/include/python{py_version_short}{abiflags}
+data = {base}
+
+[posix_home]
+stdlib = {base}/lib/python
+platstdlib = {base}/lib/python
+purelib = {base}/lib/python
+platlib = {base}/lib/python
+include = {base}/include/python
+platinclude = {base}/include/python
+scripts = {base}/bin
+data = {base}
+
+[nt]
+stdlib = {base}/Lib
+platstdlib = {base}/Lib
+purelib = {base}/Lib/site-packages
+platlib = {base}/Lib/site-packages
+include = {base}/Include
+platinclude = {base}/Include
+scripts = {base}/Scripts
+data = {base}
+
+[os2]
+stdlib = {base}/Lib
+platstdlib = {base}/Lib
+purelib = {base}/Lib/site-packages
+platlib = {base}/Lib/site-packages
+include = {base}/Include
+platinclude = {base}/Include
+scripts = {base}/Scripts
+data = {base}
+
+[os2_home]
+stdlib = {userbase}/lib/python{py_version_short}
+platstdlib = {userbase}/lib/python{py_version_short}
+purelib = {userbase}/lib/python{py_version_short}/site-packages
+platlib = {userbase}/lib/python{py_version_short}/site-packages
+include = {userbase}/include/python{py_version_short}
+scripts = {userbase}/bin
+data = {userbase}
+
+[nt_user]
+stdlib = {userbase}/Python{py_version_nodot}
+platstdlib = {userbase}/Python{py_version_nodot}
+purelib = {userbase}/Python{py_version_nodot}/site-packages
+platlib = {userbase}/Python{py_version_nodot}/site-packages
+include = {userbase}/Python{py_version_nodot}/Include
+scripts = {userbase}/Scripts
+data = {userbase}
+
+[posix_user]
+stdlib = {userbase}/lib/python{py_version_short}
+platstdlib = {userbase}/lib/python{py_version_short}
+purelib = {userbase}/lib/python{py_version_short}/site-packages
+platlib = {userbase}/lib/python{py_version_short}/site-packages
+include = {userbase}/include/python{py_version_short}
+scripts = {userbase}/bin
+data = {userbase}
+
+[osx_framework_user]
+stdlib = {userbase}/lib/python
+platstdlib = {userbase}/lib/python
+purelib = {userbase}/lib/python/site-packages
+platlib = {userbase}/lib/python/site-packages
+include = {userbase}/include
+scripts = {userbase}/bin
+data = {userbase}
diff --git a/sysconfig.py b/sysconfig.py
new file mode 100644
--- /dev/null
+++ b/sysconfig.py
@@ -0,0 +1,766 @@
+"""Access to Python's configuration information."""
+
+import os
+import re
+import sys
+from os.path import pardir, realpath
+from ConfigParser import RawConfigParser
+
+__all__ = [
+    'get_config_h_filename',
+    'get_config_var',
+    'get_config_vars',
+    'get_makefile_filename',
+    'get_path',
+    'get_path_names',
+    'get_paths',
+    'get_platform',
+    'get_python_version',
+    'get_scheme_names',
+    'parse_config_h',
+]
+
+# let's read the configuration file
+# XXX _CONFIG_DIR will be set by the Makefile later
+_CONFIG_DIR = os.path.normpath(os.path.dirname(__file__))
+_CONFIG_FILE = os.path.join(_CONFIG_DIR, 'sysconfig.cfg')
+_SCHEMES = RawConfigParser()
+_SCHEMES.read(_CONFIG_FILE)
+_VAR_REPL = re.compile(r'\{([^{]*?)\}')
+
+
+def _expand_globals(config):
+    if config.has_section('globals'):
+        globals = config.items('globals')
+    else:
+        globals = tuple()
+
+    sections = config.sections()
+    for section in sections:
+        if section == 'globals':
+            continue
+        for option, value in globals:
+            if config.has_option(section, option):
+                continue
+            config.set(section, option, value)
+    config.remove_section('globals')
+
+    # now expanding local variables defined in the cfg file
+    #
+    for section in config.sections():
+        variables = dict(config.items(section))
+
+        def _replacer(matchobj):
+            name = matchobj.group(1)
+            if name in variables:
+                return variables[name]
+            return matchobj.group(0)
+
+        for option, value in config.items(section):
+            config.set(section, option, _VAR_REPL.sub(_replacer, value))
+
+_expand_globals(_SCHEMES)
+
+ # FIXME don't rely on sys.version here, its format is an implementatin detail
+ # of CPython, use sys.version_info or sys.hexversion
+_PY_VERSION = sys.version.split()[0]
+_PY_VERSION_SHORT = sys.version[:3]
+_PY_VERSION_SHORT_NO_DOT = _PY_VERSION[0] + _PY_VERSION[2]
+_PREFIX = os.path.normpath(sys.prefix)
+_EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
+_CONFIG_VARS = None
+_USER_BASE = None
+
+
+def _safe_realpath(path):
+    try:
+        return realpath(path)
+    except OSError:
+        return path
+
+if sys.executable:
+    _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable))
+else:
+    # sys.executable can be empty if argv[0] has been changed and Python is
+    # unable to retrieve the real program name
+    _PROJECT_BASE = _safe_realpath(os.getcwd())
+
+if os.name == "nt" and "pcbuild" in _PROJECT_BASE[-8:].lower():
+    _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir))
+# PC/VS7.1
+if os.name == "nt" and "\\pc\\v" in _PROJECT_BASE[-10:].lower():
+    _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
+# PC/AMD64
+if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower():
+    _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
+
+
+def is_python_build():
+    for fn in ("Setup.dist", "Setup.local"):
+        if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
+            return True
+    return False
+
+_PYTHON_BUILD = is_python_build()
+
+if _PYTHON_BUILD:
+    for scheme in ('posix_prefix', 'posix_home'):
+        _SCHEMES.set(scheme, 'include', '{srcdir}/Include')
+        _SCHEMES.set(scheme, 'platinclude', '{projectbase}/.')
+
+
+def _subst_vars(path, local_vars):
+    """In the string `path`, replace tokens like {some.thing} with the
+    corresponding value from the map `local_vars`.
+
+    If there is no corresponding value, leave the token unchanged.
+    """
+    def _replacer(matchobj):
+        name = matchobj.group(1)
+        if name in local_vars:
+            return local_vars[name]
+        elif name in os.environ:
+            return os.environ[name]
+        return matchobj.group(0)
+    return _VAR_REPL.sub(_replacer, path)
+
+
+def _extend_dict(target_dict, other_dict):
+    target_keys = target_dict.keys()
+    for key, value in other_dict.items():
+        if key in target_keys:
+            continue
+        target_dict[key] = value
+
+
+def _expand_vars(scheme, vars):
+    res = {}
+    if vars is None:
+        vars = {}
+    _extend_dict(vars, get_config_vars())
+
+    for key, value in _SCHEMES.items(scheme):
+        if os.name in ('posix', 'nt'):
+            value = os.path.expanduser(value)
+        res[key] = os.path.normpath(_subst_vars(value, vars))
+    return res
+
+
+def format_value(value, vars):
+    def _replacer(matchobj):
+        name = matchobj.group(1)
+        if name in vars:
+            return vars[name]
+        return matchobj.group(0)
+    return _VAR_REPL.sub(_replacer, value)
+
+
+def _get_default_scheme():
+    if os.name == 'posix':
+        # the default scheme for posix is posix_prefix
+        return 'posix_prefix'
+    return os.name
+
+
+def _getuserbase():
+    env_base = os.environ.get("PYTHONUSERBASE", None)
+
+    def joinuser(*args):
+        return os.path.expanduser(os.path.join(*args))
+
+    # what about 'os2emx', 'riscos' ?
+    if os.name == "nt":
+        base = os.environ.get("APPDATA") or "~"
+        if env_base:
+            return env_base
+        else:
+            return joinuser(base, "Python")
+
+    if sys.platform == "darwin":
+        framework = get_config_var("PYTHONFRAMEWORK")
+        if framework:
+            if env_base:
+                return env_base
+            else:
+                return joinuser("~", "Library", framework, "%d.%d" %
+                                sys.version_info[:2])
+
+    if env_base:
+        return env_base
+    else:
+        return joinuser("~", ".local")
+
+
+def _parse_makefile(filename, vars=None):
+    """Parse a Makefile-style file.
+
+    A dictionary containing name/value pairs is returned.  If an
+    optional dictionary is passed in as the second argument, it is
+    used instead of a new dictionary.
+    """
+    # Regexes needed for parsing Makefile (and similar syntaxes,
+    # like old-style Setup files).
+    _variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
+    _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
+    _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
+
+    if vars is None:
+        vars = {}
+    done = {}
+    notdone = {}
+
+    with open(filename) as f:
+        lines = f.readlines()
+
+    for line in lines:
+        if line.startswith('#') or line.strip() == '':
+            continue
+        m = _variable_rx.match(line)
+        if m:
+            n, v = m.group(1, 2)
+            v = v.strip()
+            # `$$' is a literal `$' in make
+            tmpv = v.replace('$$', '')
+
+            if "$" in tmpv:
+                notdone[n] = v
+            else:
+                try:
+                    v = int(v)
+                except ValueError:
+                    # insert literal `$'
+                    done[n] = v.replace('$$', '$')
+                else:
+                    done[n] = v
+
+    # do variable interpolation here
+    variables = list(notdone.keys())
+
+    # Variables with a 'PY_' prefix in the makefile. These need to
+    # be made available without that prefix through sysconfig.
+    # Special care is needed to ensure that variable expansion works, even
+    # if the expansion uses the name without a prefix.
+    renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
+
+    while len(variables) > 0:
+        for name in tuple(variables):
+            value = notdone[name]
+            m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
+            if m is not None:
+                n = m.group(1)
+                found = True
+                if n in done:
+                    item = str(done[n])
+                elif n in notdone:
+                    # get it on a subsequent round
+                    found = False
+                elif n in os.environ:
+                    # do it like make: fall back to environment
+                    item = os.environ[n]
+
+                elif n in renamed_variables:
+                    if (name.startswith('PY_') and
+                        name[3:] in renamed_variables):
+                        item = ""
+
+                    elif 'PY_' + n in notdone:
+                        found = False
+
+                    else:
+                        item = str(done['PY_' + n])
+
+                else:
+                    done[n] = item = ""
+
+                if found:
+                    after = value[m.end():]
+                    value = value[:m.start()] + item + after
+                    if "$" in after:
+                        notdone[name] = value
+                    else:
+                        try:
+                            value = int(value)
+                        except ValueError:
+                            done[name] = value.strip()
+                        else:
+                            done[name] = value
+                        variables.remove(name)
+
+                        if name.startswith('PY_') \
+                        and name[3:] in renamed_variables:
+
+                            name = name[3:]
+                            if name not in done:
+                                done[name] = value
+
+            else:
+                # bogus variable reference (e.g. "prefix=$/opt/python");
+                # just drop it since we can't deal
+                done[name] = value
+                variables.remove(name)
+
+    # strip spurious spaces
+    for k, v in done.items():
+        if isinstance(v, str):
+            done[k] = v.strip()
+
+    # save the results in the global dictionary
+    vars.update(done)
+    return vars
+
+
+def get_makefile_filename():
+    """Return the path of the Makefile."""
+    if _PYTHON_BUILD:
+        return os.path.join(_PROJECT_BASE, "Makefile")
+    if hasattr(sys, 'abiflags'):
+        config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags)
+    else:
+        config_dir_name = 'config'
+    return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile')
+
+
+def _init_posix(vars):
+    """Initialize the module as appropriate for POSIX systems."""
+    # load the installed Makefile:
+    makefile = get_makefile_filename()
+    try:
+        _parse_makefile(makefile, vars)
+    except IOError as e:
+        msg = "invalid Python installation: unable to open %s" % makefile
+        if hasattr(e, "strerror"):
+            msg = msg + " (%s)" % e.strerror
+        raise IOError(msg)
+    # load the installed pyconfig.h:
+    config_h = get_config_h_filename()
+    try:
+        with open(config_h) as f:
+            parse_config_h(f, vars)
+    except IOError as e:
+        msg = "invalid Python installation: unable to open %s" % config_h
+        if hasattr(e, "strerror"):
+            msg = msg + " (%s)" % e.strerror
+        raise IOError(msg)
+    # On AIX, there are wrong paths to the linker scripts in the Makefile
+    # -- these paths are relative to the Python source, but when installed
+    # the scripts are in another directory.
+    if _PYTHON_BUILD:
+        vars['LDSHARED'] = vars['BLDSHARED']
+
+
+def _init_non_posix(vars):
+    """Initialize the module as appropriate for NT"""
+    # set basic install directories
+    vars['LIBDEST'] = get_path('stdlib')
+    vars['BINLIBDEST'] = get_path('platstdlib')
+    vars['INCLUDEPY'] = get_path('include')
+    vars['SO'] = '.pyd'
+    vars['EXE'] = '.exe'
+    vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT
+    vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable))
+
+#
+# public APIs
+#
+
+
+def parse_config_h(fp, vars=None):
+    """Parse a config.h-style file.
+
+    A dictionary containing name/value pairs is returned.  If an
+    optional dictionary is passed in as the second argument, it is
+    used instead of a new dictionary.
+    """
+    if vars is None:
+        vars = {}
+    define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
+    undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
+
+    while True:
+        line = fp.readline()
+        if not line:
+            break
+        m = define_rx.match(line)
+        if m:
+            n, v = m.group(1, 2)
+            try:
+                v = int(v)
+            except ValueError:
+                pass
+            vars[n] = v
+        else:
+            m = undef_rx.match(line)
+            if m:
+                vars[m.group(1)] = 0
+    return vars
+
+
+def get_config_h_filename():
+    """Return the path of pyconfig.h."""
+    if _PYTHON_BUILD:
+        if os.name == "nt":
+            inc_dir = os.path.join(_PROJECT_BASE, "PC")
+        else:
+            inc_dir = _PROJECT_BASE
+    else:
+        inc_dir = get_path('platinclude')
+    return os.path.join(inc_dir, 'pyconfig.h')
+
+
+def get_scheme_names():
+    """Return a tuple containing the schemes names."""
+    return tuple(sorted(_SCHEMES.sections()))
+
+
+def get_path_names():
+    """Return a tuple containing the paths names."""
+    # xxx see if we want a static list
+    return _SCHEMES.options('posix_prefix')
+
+
+def get_paths(scheme=_get_default_scheme(), vars=None, expand=True):
+    """Return a mapping containing an install scheme.
+
+    ``scheme`` is the install scheme name. If not provided, it will
+    return the default scheme for the current platform.
+    """
+    if expand:
+        return _expand_vars(scheme, vars)
+    else:
+        return dict(_SCHEMES.items(scheme))
+
+
+def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True):
+    """Return a path corresponding to the scheme.
+
+    ``scheme`` is the install scheme name.
+    """
+    return get_paths(scheme, vars, expand)[name]
+
+
+def get_config_vars(*args):
+    """With no arguments, return a dictionary of all configuration
+    variables relevant for the current platform.
+
+    On Unix, this means every variable defined in Python's installed Makefile;
+    On Windows and Mac OS it's a much smaller set.
+
+    With arguments, return a list of values that result from looking up
+    each argument in the configuration variable dictionary.
+    """
+    global _CONFIG_VARS
+    if _CONFIG_VARS is None:
+        _CONFIG_VARS = {}
+        # Normalized versions of prefix and exec_prefix are handy to have;
+        # in fact, these are the standard versions used most places in the
+        # distutils2 module.
+        _CONFIG_VARS['prefix'] = _PREFIX
+        _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
+        _CONFIG_VARS['py_version'] = _PY_VERSION
+        _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
+        _CONFIG_VARS['py_version_nodot'] = _PY_VERSION[0] + _PY_VERSION[2]
+        _CONFIG_VARS['base'] = _PREFIX
+        _CONFIG_VARS['platbase'] = _EXEC_PREFIX
+        _CONFIG_VARS['projectbase'] = _PROJECT_BASE
+        try:
+            _CONFIG_VARS['abiflags'] = sys.abiflags
+        except AttributeError:
+            # sys.abiflags may not be defined on all platforms.
+            _CONFIG_VARS['abiflags'] = ''
+
+        if os.name in ('nt', 'os2'):
+            _init_non_posix(_CONFIG_VARS)
+        if os.name == 'posix':
+            _init_posix(_CONFIG_VARS)
+        # Setting 'userbase' is done below the call to the
+        # init function to enable using 'get_config_var' in
+        # the init-function.
+        if sys.version >= '2.6':
+            _CONFIG_VARS['userbase'] = _getuserbase()
+
+        if 'srcdir' not in _CONFIG_VARS:
+            _CONFIG_VARS['srcdir'] = _PROJECT_BASE
+        else:
+            _CONFIG_VARS['srcdir'] = _safe_realpath(_CONFIG_VARS['srcdir'])
+
+        # Convert srcdir into an absolute path if it appears necessary.
+        # Normally it is relative to the build directory.  However, during
+        # testing, for example, we might be running a non-installed python
+        # from a different directory.
+        if _PYTHON_BUILD and os.name == "posix":
+            base = _PROJECT_BASE
+            try:
+                cwd = os.getcwd()
+            except OSError:
+                cwd = None
+            if (not os.path.isabs(_CONFIG_VARS['srcdir']) and
+                base != cwd):
+                # srcdir is relative and we are not in the same directory
+                # as the executable. Assume executable is in the build
+                # directory and make srcdir absolute.
+                srcdir = os.path.join(base, _CONFIG_VARS['srcdir'])
+                _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir)
+
+        if sys.platform == 'darwin':
+            kernel_version = os.uname()[2]  # Kernel version (8.4.3)
+            major_version = int(kernel_version.split('.')[0])
+
+            if major_version < 8:
+                # On Mac OS X before 10.4, check if -arch and -isysroot
+                # are in CFLAGS or LDFLAGS and remove them if they are.
+                # This is needed when building extensions on a 10.3 system
+                # using a universal build of python.
+                for key in ('LDFLAGS', 'BASECFLAGS',
+                        # a number of derived variables. These need to be
+                        # patched up as well.
+                        'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
+                    flags = _CONFIG_VARS[key]
+                    flags = re.sub('-arch\s+\w+\s', ' ', flags)
+                    flags = re.sub('-isysroot [^ \t]*', ' ', flags)
+                    _CONFIG_VARS[key] = flags
+            else:
+                # Allow the user to override the architecture flags using
+                # an environment variable.
+                # NOTE: This name was introduced by Apple in OSX 10.5 and
+                # is used by several scripting languages distributed with
+                # that OS release.
+                if 'ARCHFLAGS' in os.environ:
+                    arch = os.environ['ARCHFLAGS']
+                    for key in ('LDFLAGS', 'BASECFLAGS',
+                        # a number of derived variables. These need to be
+                        # patched up as well.
+                        'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
+
+                        flags = _CONFIG_VARS[key]
+                        flags = re.sub('-arch\s+\w+\s', ' ', flags)
+                        flags = flags + ' ' + arch
+                        _CONFIG_VARS[key] = flags
+
+                # If we're on OSX 10.5 or later and the user tries to
+                # compiles an extension using an SDK that is not present
+                # on the current machine it is better to not use an SDK
+                # than to fail.
+                #
+                # The major usecase for this is users using a Python.org
+                # binary installer  on OSX 10.6: that installer uses
+                # the 10.4u SDK, but that SDK is not installed by default
+                # when you install Xcode.
+                #
+                CFLAGS = _CONFIG_VARS.get('CFLAGS', '')
+                m = re.search('-isysroot\s+(\S+)', CFLAGS)
+                if m is not None:
+                    sdk = m.group(1)
+                    if not os.path.exists(sdk):
+                        for key in ('LDFLAGS', 'BASECFLAGS',
+                             # a number of derived variables. These need to be
+                             # patched up as well.
+                            'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'):
+
+                            flags = _CONFIG_VARS[key]
+                            flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags)
+                            _CONFIG_VARS[key] = flags
+
+    if args:
+        vals = []
+        for name in args:
+            vals.append(_CONFIG_VARS.get(name))
+        return vals
+    else:
+        return _CONFIG_VARS
+
+
+def get_config_var(name):
+    """Return the value of a single variable using the dictionary returned by
+    'get_config_vars()'.
+
+    Equivalent to get_config_vars().get(name)
+    """
+    return get_config_vars().get(name)
+
+
+def get_platform():
+    """Return a string that identifies the current platform.
+
+    This is used mainly to distinguish platform-specific build directories and
+    platform-specific built distributions.  Typically includes the OS name
+    and version and the architecture (as supplied by 'os.uname()'),
+    although the exact information included depends on the OS; eg. for IRIX
+    the architecture isn't particularly important (IRIX only runs on SGI
+    hardware), but for Linux the kernel version isn't particularly
+    important.
+
+    Examples of returned values:
+       linux-i586
+       linux-alpha (?)
+       solaris-2.6-sun4u
+       irix-5.3
+       irix64-6.2
+
+    Windows will return one of:
+       win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
+       win-ia64 (64bit Windows on Itanium)
+       win32 (all others - specifically, sys.platform is returned)
+
+    For other non-POSIX platforms, currently just returns 'sys.platform'.
+    """
+    if os.name == 'nt':
+        # sniff sys.version for architecture.
+        prefix = " bit ("
+        i = sys.version.find(prefix)
+        if i == -1:
+            return sys.platform
+        j = sys.version.find(")", i)
+        look = sys.version[i+len(prefix):j].lower()
+        if look == 'amd64':
+            return 'win-amd64'
+        if look == 'itanium':
+            return 'win-ia64'
+        return sys.platform
+
+    if os.name != "posix" or not hasattr(os, 'uname'):
+        # XXX what about the architecture? NT is Intel or Alpha,
+        # Mac OS is M68k or PPC, etc.
+        return sys.platform
+
+    # Try to distinguish various flavours of Unix
+    osname, host, release, version, machine = os.uname()
+
+    # Convert the OS name to lowercase, remove '/' characters
+    # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh")
+    osname = osname.lower().replace('/', '')
+    machine = machine.replace(' ', '_')
+    machine = machine.replace('/', '-')
+
+    if osname[:5] == "linux":
+        # At least on Linux/Intel, 'machine' is the processor --
+        # i386, etc.
+        # XXX what about Alpha, SPARC, etc?
+        return  "%s-%s" % (osname, machine)
+    elif osname[:5] == "sunos":
+        if release[0] >= "5":           # SunOS 5 == Solaris 2
+            osname = "solaris"
+            release = "%d.%s" % (int(release[0]) - 3, release[2:])
+        # fall through to standard osname-release-machine representation
+    elif osname[:4] == "irix":              # could be "irix64"!
+        return "%s-%s" % (osname, release)
+    elif osname[:3] == "aix":
+        return "%s-%s.%s" % (osname, version, release)
+    elif osname[:6] == "cygwin":
+        osname = "cygwin"
+        rel_re = re.compile(r'[\d.]+')
+        m = rel_re.match(release)
+        if m:
+            release = m.group()
+    elif osname[:6] == "darwin":
+        #
+        # For our purposes, we'll assume that the system version from
+        # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set
+        # to. This makes the compatibility story a bit more sane because the
+        # machine is going to compile and link as if it were
+        # MACOSX_DEPLOYMENT_TARGET.
+        cfgvars = get_config_vars()
+        macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET')
+
+        if True:
+            # Always calculate the release of the running machine,
+            # needed to determine if we can build fat binaries or not.
+
+            macrelease = macver
+            # Get the system version. Reading this plist is a documented
+            # way to get the system version (see the documentation for
+            # the Gestalt Manager)
+            try:
+                f = open('/System/Library/CoreServices/SystemVersion.plist')
+            except IOError:
+                # We're on a plain darwin box, fall back to the default
+                # behaviour.
+                pass
+            else:
+                try:
+                    m = re.search(r'<key>ProductUserVisibleVersion</key>\s*'
+                                  r'<string>(.*?)</string>', f.read())
+                finally:
+                    f.close()
+                if m is not None:
+                    macrelease = '.'.join(m.group(1).split('.')[:2])
+                # else: fall back to the default behaviour
+
+        if not macver:
+            macver = macrelease
+
+        if macver:
+            release = macver
+            osname = "macosx"
+
+            if ((macrelease + '.') >= '10.4.' and
+                '-arch' in get_config_vars().get('CFLAGS', '').strip()):
+                # The universal build will build fat binaries, but not on
+                # systems before 10.4
+                #
+                # Try to detect 4-way universal builds, those have machine-type
+                # 'universal' instead of 'fat'.
+
+                machine = 'fat'
+                cflags = get_config_vars().get('CFLAGS')
+
+                archs = re.findall('-arch\s+(\S+)', cflags)
+                archs = tuple(sorted(set(archs)))
+
+                if len(archs) == 1:
+                    machine = archs[0]
+                elif archs == ('i386', 'ppc'):
+                    machine = 'fat'
+                elif archs == ('i386', 'x86_64'):
+                    machine = 'intel'
+                elif archs == ('i386', 'ppc', 'x86_64'):
+                    machine = 'fat3'
+                elif archs == ('ppc64', 'x86_64'):
+                    machine = 'fat64'
+                elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'):
+                    machine = 'universal'
+                else:
+                    raise ValueError(
+                       "Don't know machine value for archs=%r" % (archs,))
+
+            elif machine == 'i386':
+                # On OSX the machine type returned by uname is always the
+                # 32-bit variant, even if the executable architecture is
+                # the 64-bit variant
+                if sys.maxsize >= 2**32:
+                    machine = 'x86_64'
+
+            elif machine in ('PowerPC', 'Power_Macintosh'):
+                # Pick a sane name for the PPC architecture.
+                # See 'i386' case
+                if sys.maxsize >= 2**32:
+                    machine = 'ppc64'
+                else:
+                    machine = 'ppc'
+
+    return "%s-%s-%s" % (osname, release, machine)
+
+
+def get_python_version():
+    return _PY_VERSION_SHORT
+
+
+def _print_dict(title, data):
+    for index, (key, value) in enumerate(sorted(data.items())):
+        if index == 0:
+            print('%s: ' % (title))
+        print('\t%s = "%s"' % (key, value))
+
+
+def _main():
+    """Display all information sysconfig detains."""
+    print('Platform: "%s"' % get_platform())
+    print('Python version: "%s"' % get_python_version())
+    print('Current installation scheme: "%s"' % _get_default_scheme())
+    print()
+    _print_dict('Paths', get_paths())
+    print()
+    _print_dict('Variables', get_config_vars())
+
+
+if __name__ == '__main__':
+    _main()
diff --git a/sysconfig.pyc b/sysconfig.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..079e97423a58fe96f99768806912ceaf470cb70c
GIT binary patch
literal 19476
zc%0RIYiu0Jc3ySQi$ikA;ak+RR}!y=6d$X{>T0#z)lwuS(JN7OQ at hk^$sM((n_^4D
z?%{NgMDCI|j<xUucYST=VZd=<1AYJoY$Sf<N3I>@<sklJ!115_4B!|};3P(X3nU1F
z0Ll4IRrL(HTKkF at 2@pwacD+tjojP^uJLgpEPrg at ZzwkdlSgZ*3r$|5V(@*q!DIr`T
z9 at 5hgt|^`w;-R76P4Upw?<w&x#qTNcEG?=T at h~H*S at AF{4CJQ8vz(~5iHB{J&ImUv
z+?;URgqs&`yKp;%+bP_FaJz)tEuM6Uhk4-{!fzLThkof4UO~Jx at b6)l&g>Q*H7Sah
zG?t?9+Qds5Y>)7Igx at Q?A}Sl6Df~X+_HfI7;r5CzsM3J&22hV{q=Y{x{2{b<`^50W
zBSL1m{la=UEW9I}jb8&|c<ABLmcdcIV@&z@{>&4cc at oTVEIc2+A>2Xk`ZQNKBf?YE
zbuu}ftEPBX1btL|nyHRpWHJ}YZwPltJT*mlg}6y$JuBQ{amqi3T2Hf*?&%g5QL7AC
zr05#5+l0#7MCB+`Io3+$^sA_VQ0lKDbesvDcx^)NN#;#ScqLxMQQ?h>@Eu`nUls1O
zm|Ghc565_6&kJu%xWvuVpkOq!&ItD`XKnqMxxzF at nC7@}zrimP!ac_?lfo_W%am|O
z`Q=UFj`7O{td)CSd_g0-DBN+*yd>NSez`2%Nq%{Yt4(pWE5dz~GruX^3;aSuzQ`|c
z3-3*qDpPpZgnLPRK^x_r{h60Jb6U7>ag%q2dxc-#6Ye+p<+^aM^2__eeVboqgnNx&
zZU}EyxbHCcJBSxIK-b{8n_{{2F81&QJ!UEuFN&-<w3c?_jW9SDS(PwY_n$QCPV9$)
z<p=9weUslO{r^pW7E30zi?j5oId#{s$8Gdl_nd0Yi8o3H3cKlHJ1=IL(vnk;ym~x9
zc~88!EXkKQ%IkjB3!F_a?%H4S+^I8x{aH~$VAFZ#p>2~un<~Ko5^6}4Q5?njmaHi5
z)TycyV{UP;&R`CdpL_L)W*Zk(c2wE$Ha$hEOHMoTl6gc0 at Ny@T>B-9alL$SmeqQ?I
z)6dt&u8*$nRlt-S{dCYz3f0FcXIBWH)hEe%NLw0dL#6i&5wr^<HnD7!Kv|04O|iW$
zc1 at D|PsLtJ?6PvjD(6VNuk()Co)NnSRd`#hnqn_4_A=Z)jmo=e?02sGh%2Kcqe^x&
za+f?3D at 1AqMOif<WBWyO5OZrj`i^r#j(Inwl+#ZN@#8|Qv*!OX8f;c<Pw%TUw|0L2
zpmf)?hJIe`W_b*z9L9g6Vf?;Wp<`yb1g<T{;Ng>M_}HmN%u`eS^ns at r#eSZGZHD*>
zscQ1Vpf^enFB_^7t35^82Q?ojoD|>;I;w|_niO^j80~?|M)>$?oTdbXLDqz9k$C#f
z<Ej^xKp`5T$J9o+=}kTN17~NdRu7+gl{lJm8yBaVl0xE!#^t(4l34NTZ=o%E5=OUC
zF!DyJ9UVYsHk_y&5ebMThUUlKX2frCqgIt0K$S;vyJ`^DxIW997qjHnz0L5smyp6t
zT)z^_aeOpmm+kqbJF-RNaVe+wxg4+!3ic2(bIty^g6b;6yr?!j8w*lO*lG8^9$T>c
z%iea)3EZ+;ZVME~m^nqG&&->hMowgjO*#7AY4p+WV at 4repu7RIJ6ohYUQQpA@v(?3
zF!;eJXF+b19y?tkX0d at RtOWfc9n*Gd7a`#h`a=cF#sii>G?lu~HZ%|JEzj5MVO>f>
znpFd-K%po98dC;u%aOD0m9-}QpeYV1ql^B==sSqH<67l$!>_vj{~(1NVM435%Ib6f
zza!ZtlU!ld*>ta5_P<B at jNvfQte*dMdf*UE)3$t5><Ms!HUe==y`(TH0&ZXrZ{ILO
zIw?ae*~*C(TKVM?`d*A6a?2jc;-u?Ganz9{t6{~d@`&$-ZUZ|6llW`uOTk0MFtX2T
zA at n2Yd*<@f&<|p$-SU!s?}z4R at 0V}PEYEY%{d&V=IlM_iqqZn0tp|r%mTvA?pA5{>
z$9e*SC2)i`DjzAeF!Hy{wK|<m+q?&52D)Je_(7%GaJ>k}%I8tNLMP|m)Pl_9O^)+O
zS3<v^t6 at EMB5!YMvSO;kBdrgSQzB8f2CG}WaCAIi$#Bp!yMLb^3xO4HcvckG{osl9
z2om;a+)}!S&i`j#5Ls3KnP+_-(U2$O4XU?iz3}4=G;Fu%PbI9^y{HxjE@<#hx7O?7
zCR5&YYStrOKR&J>O}2BJg>{tHJ<E@*Abb^xajWV%&pjr><FFb*Wy1-cc<!X^_A9D>
z@%vWwhKO0BQP4 at k%Zcyj7(m))95bqSyB9q7>tP^u0G842(Hf2yygB*T6f;(r&(Wl_
zMg13zPDA!}9FG&psIVpMD|j4GvsYeExI0+qG#*DW54wid5H6AJ&at#*l(ez7_#olA
zf+1JIN8%U*f-yeUQ#jdipi71WWeiJ{kw#l8nWl`D1NxLV?lW&EvH|f}rSRBdrw;tZ
zClvS_LXc##r5FwQ$f6hfjf5euxzWLRI&xx<jvVyxBhA(5GzxT@;G{{Ik{lsFM~@9=
z2l0ZY^|VcdgxfY4v|-{gOQytev>}b{$ASjyr`3@&xAwzL#*y9v-byj=23gbkIpMc4
zXfsfQDazbDWDaaKlmUlL5B|POzzsVeOkHGE$co7R2DrlNAjym5iBjs&i?A35o(<c;
z=0(8iQ(m6EJ%4w8nb(H*0!zKr%UBh8PB#GYQ9mF=4dr5gGg(V$ZwaEg69)5GFj55e
zG-&;xtHwY9WPbw9`+3!X4cd*O2_#T728}bu;T7oO|5dQbTIN+s#h!b8mas~IRPINT
zuTmp#=q?Vbk-67j1Zg9dS)c#iM2{HVnZ$Es>Mes&#M;z>;gXRysUb5JkL>p_k_!oE
z62WWA$?FOWW?5#1;;lQ4sy6I?5md4~+ at hH=c)%EaP7nCsiWX$#@-`>Q*yBb0f;W21
z))h9cbiFid^Tw^E*|)XFLWSuvs@!EUbF(aFdSkQu3smMQ&5kh07MpqDBsXkr4~JQv
zO~7_m9&K=#S%WXrVtMPk(%OGTi%#y5b#Q at o1OVgG$M<jFTl{c&-UgIPup-uCW&f(R
z!b2karG(B(ou`wv;zMc%kMmlvc=waijt;eakY at ltXqsO~Q<DeDdX10gnWd$<nfo&_
zy80F?9qH2GSJ$b(Akf&IO!cNcb9a8_p8Wy0z2iTwJM|q_jU(>lh|4Aei-p3Z`DA9W
z(^!9zEZNt!TuQ5njv|axyB2KBm^X-WDwN?)VJ>k!LKJ1dJNpC89;DbDTd$Le`68 at 8
zJG^3~@qt+&`D<u?ni)1==F>%En2xiI*<%7^<oLbY7&7sj)g-LE9=!p^(k(j{@~9<1
zYBhKQWHP0Wsa<yLe&sC0TZWIzEZ8Maz%=a~)6X!d$cfK}#cob~D<}5o)N8|jKz^Pe
zLt6asgYCZ%yLmb@|5WVJ+1HM9kOUe#4DOS5Yyf!AvWY+BO9?(q_ARvY=sLu1hX{Xx
zay#I5l-_3A5IBW9#a=<|78D)z-od_T-llt9*lD|6tmM0hM1egPopkDM8pci$OTEj!
zXllC>jFK@~U}N%xG$s>j?Vz at sNt!+YC6)Y=$Gt+kl4$$g%E7zW4M+7}QEcBa<D%H@
zMy5+6NsI8RX>4CG;~teew|2(d>*100h~d3nC<vdPz09csgRvwn`GM8Si}2qVO|GD<
zQ;^c!>%+KcbeMEMsil53)L~TV7s2lk#ZGjG3UFEtpiS@#oE@|K93?$e!U;4*yU%31
zBA0$ji5^~1o<n$y({53mQWc0WEdsUM`eW0eO4M}dV4o<G!&4RX5WBf?TCtdNcKby5
z at 2HDj%-%l&CZF0H5Gp;4w6)t$Jq(B;;viOiAX)WZ#oOtvf0WXzE}7X&t9~X~^&z?H
zy-J?YJ2eW(40!+=GD&OZAH^G-Z2MSn?H~(AlMF0x53fls8ImNo^;;<e!`k{|`uR=6
z_?nKWXl3h5!&upV59Nqd2=)SC8w^5x<Kqxz;wIdM{VeOvX$(?Fe at -3zd1?hVzzO!+
zVf;7=*xB<(P_*!qT8?#BIcz7Qc&ADhBfTo)aK-}GCcQ|y7k=m1 at dMa}aIZ~C`?BWv
z^$5meowQj!Ae)eFN+)0f((&X}Tj@~UniEByYx#lYMAGc_h`MXh;nUdkf_U8WW2(m_
z8nR{-dyZ>`MA-_w7cKOp4a&;1^ocX^+04YlHTC<=iMPvZ=SNqgv3KdGG&wdJ0bZ at 1
zT`iS9p>nFVrhaqPvC-Ah=or`d{A()gE%NzwCWfw-K+)8P-PyBTc9svc*_(G}ZY|5h
zYfir7M44S$;snD>K9iO{F0&JiaaGi8YJZBOtBjJ9LU~E*(p6CjH*2&=e7Nzc6xN7o
zYz+|F_TzF{dJM~yOBR_-)ro&qKk%djG9v3Nj^I6D69jID8o#j{l_4*8r5q4EQaYcx
z?MGO6cC2ydRX>XDt9m+BFR%xNwkkZQPShrn^Ea;BlV3fk$*5DKph0m%=?G<;hP(75
zNPrX2NKo9UxlZhrhV(hDBMl6V<>9J*zUBJ1%vkpWmohIL%(#dQ_Oz05#d8kAnBS3E
z{^Y_Muj)FFP%<#B-nd|sp8-Fgb5Xomd(JM^xQC~Lk-5o$B59ui-80GcutC#DJ9v#b
z%UP#oV}gf67wn&vQ4k%B`In&ZcR(lKO-4tz3F|9sWX&^1uL(cd83GyI^mD}MG<&JE
z%S at 47)@2?udsDsHteFyh2E4cJ#wjvcy4il~Hwq?fxE`usG<uA7lYUZ1Qe6a8ibN@)
zLZGId>QWv2HjZZ6jloobEx#kFf|0=4#F^m=hBSPhF{D+#PBx~O0mVrU0B=lI<1Pm~
zD2r6!x+w+;{0D?D=-7nUG$D{zaAa3ng(*^oETt9}gw+=-Bbdj~G<es#agyasr#Rs}
z_SdV<lZf4;lKB%O(MV*UOvFE?9rsv<38Xuep>6rbhYNS+T3|R3xIM17+=df5F>RD(
zEkV?*{L#F<yg*HtZ{M@;+uue5)NO|IRJhCa>l_I?jNI55^HoDTR%j9XX at Q2!tdRuB
z3VMMogBM6fzjk_r^rJ|l+*k>g=#Vw}ke#DLmH;U(6HtXgfKm28sl^+hLn*~RY!f!x
z;1E?{W6M+#sUIb5O3fd{5`w11&NVPsx?Iw98k-6YX2fSlan5}!!}wJOtJHWHoqpY@
z(m%+kh*mF~ZzNg6y9{opIf_d*9{b57_*oVLKlYuf|C#inZnCc(XP;A})u at qTi0Rx7
z>(<h}<%I_p%oJ}knv5n{>(W8%yDI+6C!tfVa=`bR)d-+c5FW-6V2w!ghSaPF^~~my
zRT_y#7x at Udac6G%_KZD0$F+2V-Alao_D^aVT)4;j-Tn at 8e-;mRNnoSeF7FmRAPx3U
zLD0}fDqk6jQ`-b$BhQY_=m{6;sI0w<7JK55z5;fQ5*CTe>?oUs(8ACZsl1UQO;m($
z$rvDggY+3=sF{<_8A+%qrNwzm>Kkc4?Xv4&8tIfyr)4@(AGFLahgZounTC-s079o@
zS=vEwta5zz8bUGUR0tNugmK*h$p;Mk==|%*$KrkaOT9*S7H-VVFW;B33=#;bZ8&*j
zVNsRZmxKy}E-cR8`EYK2>0|c&5EEU76wCKy+S`^3HUD6qsVjxUdW^`;E!c~tHm!Ez
zBd=0R<;8pDxqJ7Uij4`8M&hFxiz{`S^h$b_MhxLS;7U8xS;vwPkohiSI*^7gmWHBH
z<dw%?Hk>hqB*6Ax1$zNukiNn%EWJs}o|Z{%PjCM&^hNj^GCZGFnu#<?EhCI2ZPRA7
zV>ko76yBj+?Ll_a+NRCItcH=2gJq4{IWkiENK>`3rpmFA!perqp<_w(wyv^*I at rSy
z`>{|*_d{h|89ytG{#hrrqJPE8jsNe_{~4E*PT*OL-1ouu^P?8Pe><y)sZYk%tTTkU
zy!BUCrShrvCbVayz9{`m>`ij;K$KD9T6MdIv9*oLGAi{9k17Nj6=aOFCRHRz$*}@@
zqL at cR3HxuL)sNuuH}Uw<L+%RJ%%Pz}@;QJ#fWDN-u|FxJpq>P08G>m at A8!J&L@|CW
z6=Qg*WlO+&Gc40u!^v}#)cMv;c9a5u73fV1p at LyLvLeM_MyI2b;+5#DwxcC;Qu+|V
z!lhaJvQY8eW(9ft;2=5>0BItt7PO>-y?{!G;w^adomP}oG=|M~v(rd?=?V7+CHG>E
zFv+_)@)VH%*l4^tb;shXBCY$3w+8#bBi?h-r1al5%`39NJlB3-sCyrW_PyJf%HK1z
z)7JcIRG=XC`xsT^dhD;A0N?3S!*7WfysuY6>WJv at L&yI4rDW)-N=hw2vIt- at sblzI
z<W-VgZAIv1*-e(kO=k96tux!Ca~Kn9nVXY9F||fYX+C-+laC%j)qw;1t1&Akp(M%7
zv}jt-eb;kY<-PE$Ri;sI?gkYL(T#xtkX>oi$*!dK%8 at PApz`nE#N(LevJBcXd;>eA
z3WD^=xZI(54h{BehK|c*bEH5j=vx0U-}|M=?CQ|xOJ?-268%qF`OABL|J4J6v`zVc
zE#7ifi7GHo$&;Ra^SA%OVWkZs``d}M$?#d1`nd&Cznzju at -$<LN(z|5VeKXH<#f#M
zcm|khAu;AOFrosPX|^QrD=UVFkeg#L8B3_CTIcC4%WniaVSaHOI+oCLB{)IwA<yuk
zhv5SuPF1F at 7C5@kXpd>k#c&WZ^pU0Atmjc$*jGwD at y+e08R;`-4>+jzD9fi8DdqT7
zgY2auvZ>;tO2O}|^2x#tfAK)4-{FQZEQ>&key_UHuo4W@#QJz*1v#<P$WJEKFZ_X)
zE;_^ag^J at nP02G%RvP4>zpBUKeN`}|>^n8Mtv at l6N%K{5jPBb8lNnaAjA7giI0_)<
zAH%)#mmI|#PEeUUAfA$%TNcLlqT!wub8B;Kpb8+lVM2THad>E5YJ8j_U`jMIPwdO2
z7ujrj>!i7|RZZdYHO6w1SMp;yzmtM;hxgjV_JVLnsK0l=kN)U|l0&;~VrYLy-;p=6
zq5Dq>_lOEl?&X<`CEQa8`5=cQJ`nr72wfTOc|*9C3Sub!o`(M(VUG8R9q-_J_uSgl
zD~I>S*Ufw)wX(I5GBEqCS}F;mUKGX?fsn}dS*V%0wc{Kh>cq`_Irv at u6@sz#MQVjg
zIHbKyLtZYO!`9uQeZ7MFR6&?v(P&(2W2Y6x0*R8^*>@G2^-kjDqh?j_xkEbo>vcA_
zxEEL-2LARq)gh&}={Z4UdCxuSMANb)SY*VU+yYmck8-tB>K#pP#Sh%@1sRV43M<yV
zWjbA`37un&%7ztfqFtR(cdUlMlYZ5V;o8Mq8SF#jIx!VSIFR^WSv3rwL0LCyOviI7
zO*$AYPkY~W_2!%oKzMm!UEa>?5Yj;0cUx~~-^K4{vK(y?43d{g`L@!5;vjUx*hY{%
z3H@>qm#_3SZ}3T<8lOsxHgRBv(M7)J5Fv4K@`}XjDr74W7~d1L?+FHZWJddJBg1ts
z4e^=fdF9Su6AJp1*{rhumPS?+aFee_=T~2xUyYcM#rF^=d^&vUApwO?KDF{|W88km
zp1sZOqn_3()V#F1e14QImuTQX5tWoyqusNk@=BMwSrn;RN+8}6tFV7h><>oZiej|S
zhxGrf4SN#UV?TRu@#exUT!dbhu at 6uxFWK`q7aqtO^AF}{bq4#0nm!8qr-il!;DzAz
z=BpJmaa{KwH)0vmXt5v=l(&Cg_oL*=hD9Wu89$|w)DthRym0L^Li;`V4tV4|j-&x{
z1CRT<puz5tXF3_xs(vhuN(84kDq`DsghCmU3a;TQF252Q^$5A~eeWN_V?|3WhVhT}
z#GZL%kyn##13}w#o`!Xu!<V~c3<7>^b^W8V!J^7ms0`Y;&(yuJYINX=&uaiDbn{&R
zC%6<vN5ROZ+m&NxQ2mDEjx13VHYxq25iURop+7rhWR<Pjfv`e)*f?nx+3qT2QmHhD
zB?jnkZ?0$*49+o;(`EE__8Pq$u;^>aKS@?wmahjN#f=K>4os{a>^<q-_6>dcT*B7B
zE%q6!(hI_ViHH<o)oUuaMx;%kmUBb9Nvy{^to6<v_=u)7dQR!&`K^Xe)S*ZT3V)^A
zB;}MKNTU+&vQd at Mq9f7oq1OtlJ#A``n`Mv-VttxFf4}v{i9&2H+y9B%Q*upSyHhu>
z+Sq~a<9>Ql&DA#~;bfD>n8E&iZazfF?G3n2h5nW%lUzy5;>J`;-^W5un<AV^E~^nv
zCg4{COl?zWnY!xSHxhx%C)B}eMt$}oU2=PTtyA{x>n|N&@~1Jl_|mSvdPGi}&S3zn
za?-%a7J*`nuA5=;E;mJdYT3}`5>n0(hRFgv4g)O`upXxdgXqg0!d+Wc2xxgk96 at SW
zoZ>)NR&0GcZ3O?zWQs?L;;{ogW?OnZLgmM~yqC&PaCwfaq;!>&ROJ*`xlUD1 at x509
zQ3Va<^1ypOUw%Q7QD0b;c#lm1JtW^u=)J-8R!kTGsKIwy{2u-gP+nu{72(hDbrZsD
z0R4TiPEgjRoT2?K&d{wtGmQtI9p`Zk;M2j~E<k#DgO^6MLZ}y6{8^~+0I+m~Vd*H%
z=o>)pa<J|>bm1Qb3<Yb4g(a^Bp##R1oubyxS1V|YDkJ>T;BjX0B_f2OfaXK)D6d;L
znIvOEP7 at P6ucSs^-~dSfOB%yCB&4;Hyg-Ec9LEQXyHbjX4<|%S2>%pr;YjR?nnm6q
zoI_JkRvM)tB1CxWWtvm+<HB~`pU$36vM;n|znf%VY|VZz$-dm0eLcy3OJ~OxzJH{`
zbwdow5ZTs`Q|&f<d5PL5<)$NG|332yt>4tGb=4VW(%Gcyjil-&UDci9H)_Cg29xue
zl5}h at NOFm_)NiC(Tny^N3Wl)0OKR0$_r2(^0_OLD$LSxtpf?>qsO~@&<BJ%g)bOK?
zq{T#3^D0EJV(}*^RHsgwHLRltLg7sA)pcX0$M^$ouu1B6r{-4(q3>AwV-p at Efq1BU
zCIw)x&TxK5ISy_=rd}I$&nh{Q6*X#vEcmFg&V|t=!wx!MM#m+MDj5=uC;ah<<!w8a
zxb;&~-8W6e1J;PFFi^vI$MT*`vQe{OFFcUg{b2uo6r3Y|I)uwBjjB_}WHxIA(_s&l
z(2nR)X1x(G<>gxoOlZTYyDyx&H#$ztk(SdPKWJ=o4-(EK6aQ)-Od{z=pQGE(W)1cj
z9n#6^EA5$UT!H>n(Z_`U*3~QfbDU~z!?8-&M|E}-Qb&F?LAJm|)Cev&bb(*@w<q43
zywsY$a(Uv)<V9^^Ns|mNK5brw0gyv7h!=h^!C!+}r7M>o`*G5S<~nnC4*AYA$J)Mn
zrF`Y`xU~?(UKKC%cduN&KW-5O9c?A$JFld)K&<f_o2^wZT_nmhEdG2WvL-Aopb)}w
z1fwUl?6yXFM&-<I(i(9~vYLYl8Nb%m$&^%ki`M#SBZ^hy$U4_b^_;W-+3sRnz`~$Q
z{53NhL!<Im44A{VLBcBO%a>E)3-x;D at _PEe7RoG}l7`(s(djZjC;W^Cz`Wk_c5yT@
zB0t at v1r8&5PYbo*l3!Iy2}mv?N6ilyeZKp{7kOy)89F=uHV=!5qH<QF`O3}{8Y|EC
zlT~+e?Yy)h5m&uCGkb6OL3wU|>CU~6 at 6IpYFW;ZBZ_VG2-o#9n`EJ0Jx)41z8`iyL
zum0Sxc#$gmNNx~fFAN5JEi(Pg+j(!P9=eT6{2`gIANi3_5LH#2o<i|zG&U`D<a?#b
zvFoGLQ!2G+e^;ohZanf$rxHezR~O!#ygbQo6N%lx^Imu2eReGyrjj+6u3q6PwOWPA
zNqLqQ6GWP8QTt2s)0kRC6;c+x<=KAnp##Vo0ivA?p$47>;S18yq=Ets%Aq{aee~`~
z-M6$Aw-mnc>PxfoqB-8ncby7#97Y?>m7;$VQ2!|&|6b^z1t<SZXm@&2D)YF?e at K$)
z{Z*2JD%#>tS~d9#JpR*x5(G3`Oi{L2|3J$ZZmzLCX$Oe_V=nT#2BhA8Ds&988Ln&w
zClf<~Lzwbf2QG)P$;n+HqRE6S)i9Db4W%vnhj?sgc48!?CO9Nz|0W*8Ld89~eAC&E
z{Lj1+PvRfyt>p7leyI%?lKe(F;{PiEc^hFAtJ{ZF55Ac{Xwu^)R(Axe<+>;BRK69t
z{)9hWQbI~H!Y&wk)nxXP-6suR)+!K at Ls>~6J`XGDSJ7~|XlB{cY$N;9qTd<*X*1tQ
zwWW at wk5E~!*^}-j8 at GpS&oQ&dz<V#Jx;Y-(Z}jByMz@(uXVYx$rZQ&A$QylT2ho~H
z<&1W=TeH;mbdGv6dyOMzA6dDG?;iM}mi=ZSjVpYGRFTrX&8Qx@&j^V%R`X*&wi<t0
z{2+luI9>i at Vmdaz$V;L^tMu6ZP(?*|Ii#FKMd#Mu;X4s#%g2gI#JRvGCfS99w{A1&
zOR4{w0OrlD+sSu}{GA-DZJyppbj{*$aegGS-nCATM5lT8vMTX{#->+yVo%C$^Ycc2
z__6w)*>z$^X(-p*{sF&<W4Ba|RIB`Q@;{r&=gPwZ@>6(492r~HHIz$&ChPKz6!o4`
zpD*G)CvM%2`Fcs5)}J(zT`V&*LSCZdOne*;7H|l6&X4FY-eI41D+){oPB0LG&uEhG
z)hwxl`x>*81AXdtngVC406dyi;JWqRnhMv+TFlrb{+~ZEo{zKxeX&Id`EnYV#PVg|
z#=|sJfRDA=;XgY=>oVZ|_e_0l$m}%mH$!_cW2k%8s8KFU=jp-E$!&aXSeny{s9bi#
zO1Ui6H>=-eT|_vXKfXjS^1mqnt=9jC#WtH{h+jm8FEX$T at mGZY=#&3%m2z1MlX8pB
zE!eE0_xVb1F5H=yVc@?M`W)cz4Ecb8UX>dCU+ArG|FTf3+Wu#Fa2))f<H2XZKNZTe
zUB<U){9V6vhC&HS at 9Mu2`qcU_cmTD?yBQpQ<?{h|BIS3i9IL*SEGj!!aSKu2M}+fL
zx?%aMA)mUK9S8m8*~Jo&(_+A`s}@ONA;}mC+i=9tDjr%`KxJB9&wG#kfjDuqN?rzy
zVk%{P^>03NlFo!YypXB<$@X*~z4fxcl5}}0pKUizrF+eD#wq$ajUUR(n>qGR_8G at g
u`C`5;U&!b4x%`RF=lPEO at qDrKRK7Ex$!7;T2io#4sqB~YX)5n182<;4*uX*n

diff --git a/sysconfig.pyo b/sysconfig.pyo
new file mode 100644
index 0000000000000000000000000000000000000000..079e97423a58fe96f99768806912ceaf470cb70c
GIT binary patch
literal 19476
zc%0RIYiu0Jc3ySQi$ikA;ak+RR}!y=6d$X{>T0#z)lwuS(JN7OQ at hk^$sM((n_^4D
z?%{NgMDCI|j<xUucYST=VZd=<1AYJoY$Sf<N3I>@<sklJ!115_4B!|};3P(X3nU1F
z0Ll4IRrL(HTKkF at 2@pwacD+tjojP^uJLgpEPrg at ZzwkdlSgZ*3r$|5V(@*q!DIr`T
z9 at 5hgt|^`w;-R76P4Upw?<w&x#qTNcEG?=T at h~H*S at AF{4CJQ8vz(~5iHB{J&ImUv
z+?;URgqs&`yKp;%+bP_FaJz)tEuM6Uhk4-{!fzLThkof4UO~Jx at b6)l&g>Q*H7Sah
zG?t?9+Qds5Y>)7Igx at Q?A}Sl6Df~X+_HfI7;r5CzsM3J&22hV{q=Y{x{2{b<`^50W
zBSL1m{la=UEW9I}jb8&|c<ABLmcdcIV@&z@{>&4cc at oTVEIc2+A>2Xk`ZQNKBf?YE
zbuu}ftEPBX1btL|nyHRpWHJ}YZwPltJT*mlg}6y$JuBQ{amqi3T2Hf*?&%g5QL7AC
zr05#5+l0#7MCB+`Io3+$^sA_VQ0lKDbesvDcx^)NN#;#ScqLxMQQ?h>@Eu`nUls1O
zm|Ghc565_6&kJu%xWvuVpkOq!&ItD`XKnqMxxzF at nC7@}zrimP!ac_?lfo_W%am|O
z`Q=UFj`7O{td)CSd_g0-DBN+*yd>NSez`2%Nq%{Yt4(pWE5dz~GruX^3;aSuzQ`|c
z3-3*qDpPpZgnLPRK^x_r{h60Jb6U7>ag%q2dxc-#6Ye+p<+^aM^2__eeVboqgnNx&
zZU}EyxbHCcJBSxIK-b{8n_{{2F81&QJ!UEuFN&-<w3c?_jW9SDS(PwY_n$QCPV9$)
z<p=9weUslO{r^pW7E30zi?j5oId#{s$8Gdl_nd0Yi8o3H3cKlHJ1=IL(vnk;ym~x9
zc~88!EXkKQ%IkjB3!F_a?%H4S+^I8x{aH~$VAFZ#p>2~un<~Ko5^6}4Q5?njmaHi5
z)TycyV{UP;&R`CdpL_L)W*Zk(c2wE$Ha$hEOHMoTl6gc0 at Ny@T>B-9alL$SmeqQ?I
z)6dt&u8*$nRlt-S{dCYz3f0FcXIBWH)hEe%NLw0dL#6i&5wr^<HnD7!Kv|04O|iW$
zc1 at D|PsLtJ?6PvjD(6VNuk()Co)NnSRd`#hnqn_4_A=Z)jmo=e?02sGh%2Kcqe^x&
za+f?3D at 1AqMOif<WBWyO5OZrj`i^r#j(Inwl+#ZN@#8|Qv*!OX8f;c<Pw%TUw|0L2
zpmf)?hJIe`W_b*z9L9g6Vf?;Wp<`yb1g<T{;Ng>M_}HmN%u`eS^ns at r#eSZGZHD*>
zscQ1Vpf^enFB_^7t35^82Q?ojoD|>;I;w|_niO^j80~?|M)>$?oTdbXLDqz9k$C#f
z<Ej^xKp`5T$J9o+=}kTN17~NdRu7+gl{lJm8yBaVl0xE!#^t(4l34NTZ=o%E5=OUC
zF!DyJ9UVYsHk_y&5ebMThUUlKX2frCqgIt0K$S;vyJ`^DxIW997qjHnz0L5smyp6t
zT)z^_aeOpmm+kqbJF-RNaVe+wxg4+!3ic2(bIty^g6b;6yr?!j8w*lO*lG8^9$T>c
z%iea)3EZ+;ZVME~m^nqG&&->hMowgjO*#7AY4p+WV at 4repu7RIJ6ohYUQQpA@v(?3
zF!;eJXF+b19y?tkX0d at RtOWfc9n*Gd7a`#h`a=cF#sii>G?lu~HZ%|JEzj5MVO>f>
znpFd-K%po98dC;u%aOD0m9-}QpeYV1ql^B==sSqH<67l$!>_vj{~(1NVM435%Ib6f
zza!ZtlU!ld*>ta5_P<B at jNvfQte*dMdf*UE)3$t5><Ms!HUe==y`(TH0&ZXrZ{ILO
zIw?ae*~*C(TKVM?`d*A6a?2jc;-u?Ganz9{t6{~d@`&$-ZUZ|6llW`uOTk0MFtX2T
zA at n2Yd*<@f&<|p$-SU!s?}z4R at 0V}PEYEY%{d&V=IlM_iqqZn0tp|r%mTvA?pA5{>
z$9e*SC2)i`DjzAeF!Hy{wK|<m+q?&52D)Je_(7%GaJ>k}%I8tNLMP|m)Pl_9O^)+O
zS3<v^t6 at EMB5!YMvSO;kBdrgSQzB8f2CG}WaCAIi$#Bp!yMLb^3xO4HcvckG{osl9
z2om;a+)}!S&i`j#5Ls3KnP+_-(U2$O4XU?iz3}4=G;Fu%PbI9^y{HxjE@<#hx7O?7
zCR5&YYStrOKR&J>O}2BJg>{tHJ<E@*Abb^xajWV%&pjr><FFb*Wy1-cc<!X^_A9D>
z@%vWwhKO0BQP4 at k%Zcyj7(m))95bqSyB9q7>tP^u0G842(Hf2yygB*T6f;(r&(Wl_
zMg13zPDA!}9FG&psIVpMD|j4GvsYeExI0+qG#*DW54wid5H6AJ&at#*l(ez7_#olA
zf+1JIN8%U*f-yeUQ#jdipi71WWeiJ{kw#l8nWl`D1NxLV?lW&EvH|f}rSRBdrw;tZ
zClvS_LXc##r5FwQ$f6hfjf5euxzWLRI&xx<jvVyxBhA(5GzxT@;G{{Ik{lsFM~@9=
z2l0ZY^|VcdgxfY4v|-{gOQytev>}b{$ASjyr`3@&xAwzL#*y9v-byj=23gbkIpMc4
zXfsfQDazbDWDaaKlmUlL5B|POzzsVeOkHGE$co7R2DrlNAjym5iBjs&i?A35o(<c;
z=0(8iQ(m6EJ%4w8nb(H*0!zKr%UBh8PB#GYQ9mF=4dr5gGg(V$ZwaEg69)5GFj55e
zG-&;xtHwY9WPbw9`+3!X4cd*O2_#T728}bu;T7oO|5dQbTIN+s#h!b8mas~IRPINT
zuTmp#=q?Vbk-67j1Zg9dS)c#iM2{HVnZ$Es>Mes&#M;z>;gXRysUb5JkL>p_k_!oE
z62WWA$?FOWW?5#1;;lQ4sy6I?5md4~+ at hH=c)%EaP7nCsiWX$#@-`>Q*yBb0f;W21
z))h9cbiFid^Tw^E*|)XFLWSuvs@!EUbF(aFdSkQu3smMQ&5kh07MpqDBsXkr4~JQv
zO~7_m9&K=#S%WXrVtMPk(%OGTi%#y5b#Q at o1OVgG$M<jFTl{c&-UgIPup-uCW&f(R
z!b2karG(B(ou`wv;zMc%kMmlvc=waijt;eakY at ltXqsO~Q<DeDdX10gnWd$<nfo&_
zy80F?9qH2GSJ$b(Akf&IO!cNcb9a8_p8Wy0z2iTwJM|q_jU(>lh|4Aei-p3Z`DA9W
z(^!9zEZNt!TuQ5njv|axyB2KBm^X-WDwN?)VJ>k!LKJ1dJNpC89;DbDTd$Le`68 at 8
zJG^3~@qt+&`D<u?ni)1==F>%En2xiI*<%7^<oLbY7&7sj)g-LE9=!p^(k(j{@~9<1
zYBhKQWHP0Wsa<yLe&sC0TZWIzEZ8Maz%=a~)6X!d$cfK}#cob~D<}5o)N8|jKz^Pe
zLt6asgYCZ%yLmb@|5WVJ+1HM9kOUe#4DOS5Yyf!AvWY+BO9?(q_ARvY=sLu1hX{Xx
zay#I5l-_3A5IBW9#a=<|78D)z-od_T-llt9*lD|6tmM0hM1egPopkDM8pci$OTEj!
zXllC>jFK@~U}N%xG$s>j?Vz at sNt!+YC6)Y=$Gt+kl4$$g%E7zW4M+7}QEcBa<D%H@
zMy5+6NsI8RX>4CG;~teew|2(d>*100h~d3nC<vdPz09csgRvwn`GM8Si}2qVO|GD<
zQ;^c!>%+KcbeMEMsil53)L~TV7s2lk#ZGjG3UFEtpiS@#oE@|K93?$e!U;4*yU%31
zBA0$ji5^~1o<n$y({53mQWc0WEdsUM`eW0eO4M}dV4o<G!&4RX5WBf?TCtdNcKby5
z at 2HDj%-%l&CZF0H5Gp;4w6)t$Jq(B;;viOiAX)WZ#oOtvf0WXzE}7X&t9~X~^&z?H
zy-J?YJ2eW(40!+=GD&OZAH^G-Z2MSn?H~(AlMF0x53fls8ImNo^;;<e!`k{|`uR=6
z_?nKWXl3h5!&upV59Nqd2=)SC8w^5x<Kqxz;wIdM{VeOvX$(?Fe at -3zd1?hVzzO!+
zVf;7=*xB<(P_*!qT8?#BIcz7Qc&ADhBfTo)aK-}GCcQ|y7k=m1 at dMa}aIZ~C`?BWv
z^$5meowQj!Ae)eFN+)0f((&X}Tj@~UniEByYx#lYMAGc_h`MXh;nUdkf_U8WW2(m_
z8nR{-dyZ>`MA-_w7cKOp4a&;1^ocX^+04YlHTC<=iMPvZ=SNqgv3KdGG&wdJ0bZ at 1
zT`iS9p>nFVrhaqPvC-Ah=or`d{A()gE%NzwCWfw-K+)8P-PyBTc9svc*_(G}ZY|5h
zYfir7M44S$;snD>K9iO{F0&JiaaGi8YJZBOtBjJ9LU~E*(p6CjH*2&=e7Nzc6xN7o
zYz+|F_TzF{dJM~yOBR_-)ro&qKk%djG9v3Nj^I6D69jID8o#j{l_4*8r5q4EQaYcx
z?MGO6cC2ydRX>XDt9m+BFR%xNwkkZQPShrn^Ea;BlV3fk$*5DKph0m%=?G<;hP(75
zNPrX2NKo9UxlZhrhV(hDBMl6V<>9J*zUBJ1%vkpWmohIL%(#dQ_Oz05#d8kAnBS3E
z{^Y_Muj)FFP%<#B-nd|sp8-Fgb5Xomd(JM^xQC~Lk-5o$B59ui-80GcutC#DJ9v#b
z%UP#oV}gf67wn&vQ4k%B`In&ZcR(lKO-4tz3F|9sWX&^1uL(cd83GyI^mD}MG<&JE
z%S at 47)@2?udsDsHteFyh2E4cJ#wjvcy4il~Hwq?fxE`usG<uA7lYUZ1Qe6a8ibN@)
zLZGId>QWv2HjZZ6jloobEx#kFf|0=4#F^m=hBSPhF{D+#PBx~O0mVrU0B=lI<1Pm~
zD2r6!x+w+;{0D?D=-7nUG$D{zaAa3ng(*^oETt9}gw+=-Bbdj~G<es#agyasr#Rs}
z_SdV<lZf4;lKB%O(MV*UOvFE?9rsv<38Xuep>6rbhYNS+T3|R3xIM17+=df5F>RD(
zEkV?*{L#F<yg*HtZ{M@;+uue5)NO|IRJhCa>l_I?jNI55^HoDTR%j9XX at Q2!tdRuB
z3VMMogBM6fzjk_r^rJ|l+*k>g=#Vw}ke#DLmH;U(6HtXgfKm28sl^+hLn*~RY!f!x
z;1E?{W6M+#sUIb5O3fd{5`w11&NVPsx?Iw98k-6YX2fSlan5}!!}wJOtJHWHoqpY@
z(m%+kh*mF~ZzNg6y9{opIf_d*9{b57_*oVLKlYuf|C#inZnCc(XP;A})u at qTi0Rx7
z>(<h}<%I_p%oJ}knv5n{>(W8%yDI+6C!tfVa=`bR)d-+c5FW-6V2w!ghSaPF^~~my
zRT_y#7x at Udac6G%_KZD0$F+2V-Alao_D^aVT)4;j-Tn at 8e-;mRNnoSeF7FmRAPx3U
zLD0}fDqk6jQ`-b$BhQY_=m{6;sI0w<7JK55z5;fQ5*CTe>?oUs(8ACZsl1UQO;m($
z$rvDggY+3=sF{<_8A+%qrNwzm>Kkc4?Xv4&8tIfyr)4@(AGFLahgZounTC-s079o@
zS=vEwta5zz8bUGUR0tNugmK*h$p;Mk==|%*$KrkaOT9*S7H-VVFW;B33=#;bZ8&*j
zVNsRZmxKy}E-cR8`EYK2>0|c&5EEU76wCKy+S`^3HUD6qsVjxUdW^`;E!c~tHm!Ez
zBd=0R<;8pDxqJ7Uij4`8M&hFxiz{`S^h$b_MhxLS;7U8xS;vwPkohiSI*^7gmWHBH
z<dw%?Hk>hqB*6Ax1$zNukiNn%EWJs}o|Z{%PjCM&^hNj^GCZGFnu#<?EhCI2ZPRA7
zV>ko76yBj+?Ll_a+NRCItcH=2gJq4{IWkiENK>`3rpmFA!perqp<_w(wyv^*I at rSy
z`>{|*_d{h|89ytG{#hrrqJPE8jsNe_{~4E*PT*OL-1ouu^P?8Pe><y)sZYk%tTTkU
zy!BUCrShrvCbVayz9{`m>`ij;K$KD9T6MdIv9*oLGAi{9k17Nj6=aOFCRHRz$*}@@
zqL at cR3HxuL)sNuuH}Uw<L+%RJ%%Pz}@;QJ#fWDN-u|FxJpq>P08G>m at A8!J&L@|CW
z6=Qg*WlO+&Gc40u!^v}#)cMv;c9a5u73fV1p at LyLvLeM_MyI2b;+5#DwxcC;Qu+|V
z!lhaJvQY8eW(9ft;2=5>0BItt7PO>-y?{!G;w^adomP}oG=|M~v(rd?=?V7+CHG>E
zFv+_)@)VH%*l4^tb;shXBCY$3w+8#bBi?h-r1al5%`39NJlB3-sCyrW_PyJf%HK1z
z)7JcIRG=XC`xsT^dhD;A0N?3S!*7WfysuY6>WJv at L&yI4rDW)-N=hw2vIt- at sblzI
z<W-VgZAIv1*-e(kO=k96tux!Ca~Kn9nVXY9F||fYX+C-+laC%j)qw;1t1&Akp(M%7
zv}jt-eb;kY<-PE$Ri;sI?gkYL(T#xtkX>oi$*!dK%8 at PApz`nE#N(LevJBcXd;>eA
z3WD^=xZI(54h{BehK|c*bEH5j=vx0U-}|M=?CQ|xOJ?-268%qF`OABL|J4J6v`zVc
zE#7ifi7GHo$&;Ra^SA%OVWkZs``d}M$?#d1`nd&Cznzju at -$<LN(z|5VeKXH<#f#M
zcm|khAu;AOFrosPX|^QrD=UVFkeg#L8B3_CTIcC4%WniaVSaHOI+oCLB{)IwA<yuk
zhv5SuPF1F at 7C5@kXpd>k#c&WZ^pU0Atmjc$*jGwD at y+e08R;`-4>+jzD9fi8DdqT7
zgY2auvZ>;tO2O}|^2x#tfAK)4-{FQZEQ>&key_UHuo4W@#QJz*1v#<P$WJEKFZ_X)
zE;_^ag^J at nP02G%RvP4>zpBUKeN`}|>^n8Mtv at l6N%K{5jPBb8lNnaAjA7giI0_)<
zAH%)#mmI|#PEeUUAfA$%TNcLlqT!wub8B;Kpb8+lVM2THad>E5YJ8j_U`jMIPwdO2
z7ujrj>!i7|RZZdYHO6w1SMp;yzmtM;hxgjV_JVLnsK0l=kN)U|l0&;~VrYLy-;p=6
zq5Dq>_lOEl?&X<`CEQa8`5=cQJ`nr72wfTOc|*9C3Sub!o`(M(VUG8R9q-_J_uSgl
zD~I>S*Ufw)wX(I5GBEqCS}F;mUKGX?fsn}dS*V%0wc{Kh>cq`_Irv at u6@sz#MQVjg
zIHbKyLtZYO!`9uQeZ7MFR6&?v(P&(2W2Y6x0*R8^*>@G2^-kjDqh?j_xkEbo>vcA_
zxEEL-2LARq)gh&}={Z4UdCxuSMANb)SY*VU+yYmck8-tB>K#pP#Sh%@1sRV43M<yV
zWjbA`37un&%7ztfqFtR(cdUlMlYZ5V;o8Mq8SF#jIx!VSIFR^WSv3rwL0LCyOviI7
zO*$AYPkY~W_2!%oKzMm!UEa>?5Yj;0cUx~~-^K4{vK(y?43d{g`L@!5;vjUx*hY{%
z3H@>qm#_3SZ}3T<8lOsxHgRBv(M7)J5Fv4K@`}XjDr74W7~d1L?+FHZWJddJBg1ts
z4e^=fdF9Su6AJp1*{rhumPS?+aFee_=T~2xUyYcM#rF^=d^&vUApwO?KDF{|W88km
zp1sZOqn_3()V#F1e14QImuTQX5tWoyqusNk@=BMwSrn;RN+8}6tFV7h><>oZiej|S
zhxGrf4SN#UV?TRu@#exUT!dbhu at 6uxFWK`q7aqtO^AF}{bq4#0nm!8qr-il!;DzAz
z=BpJmaa{KwH)0vmXt5v=l(&Cg_oL*=hD9Wu89$|w)DthRym0L^Li;`V4tV4|j-&x{
z1CRT<puz5tXF3_xs(vhuN(84kDq`DsghCmU3a;TQF252Q^$5A~eeWN_V?|3WhVhT}
z#GZL%kyn##13}w#o`!Xu!<V~c3<7>^b^W8V!J^7ms0`Y;&(yuJYINX=&uaiDbn{&R
zC%6<vN5ROZ+m&NxQ2mDEjx13VHYxq25iURop+7rhWR<Pjfv`e)*f?nx+3qT2QmHhD
zB?jnkZ?0$*49+o;(`EE__8Pq$u;^>aKS@?wmahjN#f=K>4os{a>^<q-_6>dcT*B7B
zE%q6!(hI_ViHH<o)oUuaMx;%kmUBb9Nvy{^to6<v_=u)7dQR!&`K^Xe)S*ZT3V)^A
zB;}MKNTU+&vQd at Mq9f7oq1OtlJ#A``n`Mv-VttxFf4}v{i9&2H+y9B%Q*upSyHhu>
z+Sq~a<9>Ql&DA#~;bfD>n8E&iZazfF?G3n2h5nW%lUzy5;>J`;-^W5un<AV^E~^nv
zCg4{COl?zWnY!xSHxhx%C)B}eMt$}oU2=PTtyA{x>n|N&@~1Jl_|mSvdPGi}&S3zn
za?-%a7J*`nuA5=;E;mJdYT3}`5>n0(hRFgv4g)O`upXxdgXqg0!d+Wc2xxgk96 at SW
zoZ>)NR&0GcZ3O?zWQs?L;;{ogW?OnZLgmM~yqC&PaCwfaq;!>&ROJ*`xlUD1 at x509
zQ3Va<^1ypOUw%Q7QD0b;c#lm1JtW^u=)J-8R!kTGsKIwy{2u-gP+nu{72(hDbrZsD
z0R4TiPEgjRoT2?K&d{wtGmQtI9p`Zk;M2j~E<k#DgO^6MLZ}y6{8^~+0I+m~Vd*H%
z=o>)pa<J|>bm1Qb3<Yb4g(a^Bp##R1oubyxS1V|YDkJ>T;BjX0B_f2OfaXK)D6d;L
znIvOEP7 at P6ucSs^-~dSfOB%yCB&4;Hyg-Ec9LEQXyHbjX4<|%S2>%pr;YjR?nnm6q
zoI_JkRvM)tB1CxWWtvm+<HB~`pU$36vM;n|znf%VY|VZz$-dm0eLcy3OJ~OxzJH{`
zbwdow5ZTs`Q|&f<d5PL5<)$NG|332yt>4tGb=4VW(%Gcyjil-&UDci9H)_Cg29xue
zl5}h at NOFm_)NiC(Tny^N3Wl)0OKR0$_r2(^0_OLD$LSxtpf?>qsO~@&<BJ%g)bOK?
zq{T#3^D0EJV(}*^RHsgwHLRltLg7sA)pcX0$M^$ouu1B6r{-4(q3>AwV-p at Efq1BU
zCIw)x&TxK5ISy_=rd}I$&nh{Q6*X#vEcmFg&V|t=!wx!MM#m+MDj5=uC;ah<<!w8a
zxb;&~-8W6e1J;PFFi^vI$MT*`vQe{OFFcUg{b2uo6r3Y|I)uwBjjB_}WHxIA(_s&l
z(2nR)X1x(G<>gxoOlZTYyDyx&H#$ztk(SdPKWJ=o4-(EK6aQ)-Od{z=pQGE(W)1cj
z9n#6^EA5$UT!H>n(Z_`U*3~QfbDU~z!?8-&M|E}-Qb&F?LAJm|)Cev&bb(*@w<q43
zywsY$a(Uv)<V9^^Ns|mNK5brw0gyv7h!=h^!C!+}r7M>o`*G5S<~nnC4*AYA$J)Mn
zrF`Y`xU~?(UKKC%cduN&KW-5O9c?A$JFld)K&<f_o2^wZT_nmhEdG2WvL-Aopb)}w
z1fwUl?6yXFM&-<I(i(9~vYLYl8Nb%m$&^%ki`M#SBZ^hy$U4_b^_;W-+3sRnz`~$Q
z{53NhL!<Im44A{VLBcBO%a>E)3-x;D at _PEe7RoG}l7`(s(djZjC;W^Cz`Wk_c5yT@
zB0t at v1r8&5PYbo*l3!Iy2}mv?N6ilyeZKp{7kOy)89F=uHV=!5qH<QF`O3}{8Y|EC
zlT~+e?Yy)h5m&uCGkb6OL3wU|>CU~6 at 6IpYFW;ZBZ_VG2-o#9n`EJ0Jx)41z8`iyL
zum0Sxc#$gmNNx~fFAN5JEi(Pg+j(!P9=eT6{2`gIANi3_5LH#2o<i|zG&U`D<a?#b
zvFoGLQ!2G+e^;ohZanf$rxHezR~O!#ygbQo6N%lx^Imu2eReGyrjj+6u3q6PwOWPA
zNqLqQ6GWP8QTt2s)0kRC6;c+x<=KAnp##Vo0ivA?p$47>;S18yq=Ets%Aq{aee~`~
z-M6$Aw-mnc>PxfoqB-8ncby7#97Y?>m7;$VQ2!|&|6b^z1t<SZXm@&2D)YF?e at K$)
z{Z*2JD%#>tS~d9#JpR*x5(G3`Oi{L2|3J$ZZmzLCX$Oe_V=nT#2BhA8Ds&988Ln&w
zClf<~Lzwbf2QG)P$;n+HqRE6S)i9Db4W%vnhj?sgc48!?CO9Nz|0W*8Ld89~eAC&E
z{Lj1+PvRfyt>p7leyI%?lKe(F;{PiEc^hFAtJ{ZF55Ac{Xwu^)R(Axe<+>;BRK69t
z{)9hWQbI~H!Y&wk)nxXP-6suR)+!K at Ls>~6J`XGDSJ7~|XlB{cY$N;9qTd<*X*1tQ
zwWW at wk5E~!*^}-j8 at GpS&oQ&dz<V#Jx;Y-(Z}jByMz@(uXVYx$rZQ&A$QylT2ho~H
z<&1W=TeH;mbdGv6dyOMzA6dDG?;iM}mi=ZSjVpYGRFTrX&8Qx@&j^V%R`X*&wi<t0
z{2+luI9>i at Vmdaz$V;L^tMu6ZP(?*|Ii#FKMd#Mu;X4s#%g2gI#JRvGCfS99w{A1&
zOR4{w0OrlD+sSu}{GA-DZJyppbj{*$aegGS-nCATM5lT8vMTX{#->+yVo%C$^Ycc2
z__6w)*>z$^X(-p*{sF&<W4Ba|RIB`Q@;{r&=gPwZ@>6(492r~HHIz$&ChPKz6!o4`
zpD*G)CvM%2`Fcs5)}J(zT`V&*LSCZdOne*;7H|l6&X4FY-eI41D+){oPB0LG&uEhG
z)hwxl`x>*81AXdtngVC406dyi;JWqRnhMv+TFlrb{+~ZEo{zKxeX&Id`EnYV#PVg|
z#=|sJfRDA=;XgY=>oVZ|_e_0l$m}%mH$!_cW2k%8s8KFU=jp-E$!&aXSeny{s9bi#
zO1Ui6H>=-eT|_vXKfXjS^1mqnt=9jC#WtH{h+jm8FEX$T at mGZY=#&3%m2z1MlX8pB
zE!eE0_xVb1F5H=yVc@?M`W)cz4Ecb8UX>dCU+ArG|FTf3+Wu#Fa2))f<H2XZKNZTe
zUB<U){9V6vhC&HS at 9Mu2`qcU_cmTD?yBQpQ<?{h|BIS3i9IL*SEGj!!aSKu2M}+fL
zx?%aMA)mUK9S8m8*~Jo&(_+A`s}@ONA;}mC+i=9tDjr%`KxJB9&wG#kfjDuqN?rzy
zVk%{P^>03NlFo!YypXB<$@X*~z4fxcl5}}0pKUizrF+eD#wq$ajUUR(n>qGR_8G at g
u`C`5;U&!b4x%`RF=lPEO at qDrKRK7Ex$!7;T2io#4sqB~YX)5n182<;4*uX*n

diff --git a/test_distutils2.py b/test_distutils2.py
new file mode 100644
--- /dev/null
+++ b/test_distutils2.py
@@ -0,0 +1,5 @@
+import sys
+from distutils2.tests.__main__ import test_main
+
+if __name__ == '__main__':
+    test_main()

-- 
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list